Repository: plotly/dash

Issue:
Need a mechanism to inline Dash for R examples within generated package help
It is customary to provide sample code within R's native documentation which is executable at package build time. This is helpful to package users looking for a quick demo for functions in packages, but also to developers wanting to demonstrate packages work as expected.

Repositories such as CRAN require this to be present, although their current automated package checks pass when a vignette exists. 

To avoid delays for future submissions, and to streamline the path for Dash component developers, we should introduce functionality which allows for easy inlining of function examples.

The `dash-info.yaml` file is a logical avenue to implement this feature. If this file exists, and if it contains examples, we should insert them at the end of the corresponding `.Rd` file, e.g.

```    
r_examples:
    - name: df_to_list
      dontrun: TRUE
      code: |
           # first, create data frame
           df <- read.csv(url(
                  'https://raw.githubusercontent.com/plotly/datasets/master/solar.csv'
               ),
              check.names=FALSE,
              stringsAsFactors=FALSE
           )
           # then convert to list-of-lists format for use in dashTable
           # the following snippet below will print as JSON
           # see the help for dashDataTable to see an actual app example
           dashDataTable(
                  id = 'table',
                  columns = lapply(colnames(df), function(x) {
                         list(name = x, id = x)
                  }),
                  data = df_to_list(df)
           )
```

...should generate the following `.Rd` markup:

```
\examples{
\dontrun{
           # first, create data frame
           df <- read.csv(url(
                  'https://raw.githubusercontent.com/plotly/datasets/master/solar.csv'
               ),
              check.names=FALSE,
              stringsAsFactors=FALSE
           )
           # then convert to list-of-lists format for use in dashTable
           # the following snippet below will print as JSON
           # see the help for dashDataTable to see an actual app example
           dashDataTable(
                  id = 'table',
                  columns = lapply(colnames(df), function(x) {
                         list(name = x, id = x)
                  }),
                  data = df_to_list(df)
           )}
}
```

@alexcjohnson @Marc-Andre-Rivet 


Discussion:

Resolving patch:
diff --git a/dash/development/_r_components_generation.py b/dash/development/_r_components_generation.py
--- a/dash/development/_r_components_generation.py
+++ b/dash/development/_r_components_generation.py
@@ -324,7 +324,15 @@ def generate_js_metadata(pkg_data, project_shortname):
     return function_string
 
 
-def write_help_file(name, props, description, prefix):
+# This method wraps code within arbitrary LaTeX-like tags, which are used
+# by R's internal help parser for constructing man pages
+def wrap(tag, code):
+    if tag == "":
+        return code
+    return '\\{}{{\n{}}}'.format(tag, code)
+
+
+def write_help_file(name, props, description, prefix, rpkg_data):
     """
     Write R documentation file (.Rd) given component name and properties
 
@@ -334,13 +342,15 @@ def write_help_file(name, props, description, prefix):
     props = the properties of the component
     description = the component's description, inserted into help file header
     prefix = the DashR library prefix (optional, can be a blank string)
+    rpkg_data = package metadata (optional)
 
     Returns
     -------
     writes an R help file to the man directory for the generated R package
 
     """
-    file_name = format_fn_name(prefix, name) + ".Rd"
+    funcname = format_fn_name(prefix, name)
+    file_name = funcname + ".Rd"
 
     default_argtext = ""
     item_text = ""
@@ -374,7 +384,7 @@ def write_help_file(name, props, description, prefix):
     file_path = os.path.join('man', file_name)
     with open(file_path, 'w') as f:
         f.write(help_string.format(
-            funcname=format_fn_name(prefix, name),
+            funcname=funcname,
             name=name,
             default_argtext=textwrap.fill(default_argtext,
                                           width=80,
@@ -382,9 +392,24 @@ def write_help_file(name, props, description, prefix):
             item_text=item_text,
             description=description.replace('\n', ' ')
         ))
-
-
-def write_class_file(name, props, description, project_shortname, prefix=None):
+    if rpkg_data is not None and 'r_examples' in rpkg_data:
+        ex = rpkg_data.get('r_examples')
+        the_ex = ([e for e in ex if e.get("name") == funcname] or [None])[0]
+        result = ""
+        if the_ex and "code" in the_ex.keys():
+            result += wrap("examples",
+                           wrap("dontrun" if the_ex.get("dontrun") else "",
+                                the_ex["code"]))
+            with open(file_path, 'a+') as fa:
+                fa.write(result + '\n')
+
+
+def write_class_file(name,
+                     props,
+                     description,
+                     project_shortname,
+                     prefix=None,
+                     rpkg_data=None):
     props = reorder_props(props=props)
 
     # generate the R help pages for each of the Dash components that we
@@ -392,7 +417,7 @@ def write_class_file(name, props, description, project_shortname, prefix=None):
     # we may eventually be able to generate similar documentation using
     # doxygen and an R plugin, but for now we'll just do it on our own
     # from within Python
-    write_help_file(name, props, description, prefix)
+    write_help_file(name, props, description, prefix, rpkg_data)
 
     import_string =\
         "# AUTO GENERATED FILE - DO NOT EDIT\n\n"
diff --git a/dash/development/component_generator.py b/dash/development/component_generator.py
--- a/dash/development/component_generator.py
+++ b/dash/development/component_generator.py
@@ -98,8 +98,17 @@ def generate_components(
             os.makedirs('man')
         if not os.path.exists('R'):
             os.makedirs('R')
+        if os.path.isfile("dash-info.yaml"):
+            with open("dash-info.yaml") as yamldata:
+                rpkg_data = yaml.safe_load(yamldata)
+        else:
+            rpkg_data = None
+        with open('package.json', 'r') as f:
+            pkg_data = safe_json_loads(f.read())
         generator_methods.append(
-            functools.partial(write_class_file, prefix=rprefix))
+            functools.partial(write_class_file,
+                              prefix=rprefix,
+                              rpkg_data=rpkg_data))
 
     components = generate_classes_files(
         project_shortname,
@@ -113,19 +122,6 @@ def generate_components(
     generate_imports(project_shortname, components)
 
     if rprefix is not None:
-        if os.path.isfile("dash-info.yaml"):
-            with open("dash-info.yaml") as yamldata:
-                rpkg_data = yaml.safe_load(yamldata)
-        else:
-            rpkg_data = None
-            print(
-                "Warning: dash-info.yaml missing; package metadata not loaded",
-                file=sys.stderr
-            )
-
-        with open('package.json', 'r') as f:
-            pkg_data = safe_json_loads(f.read())
-
         generate_exports(
             project_shortname,
             components,


Test patch: