markdown_record 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +225 -0
  4. data/Rakefile +9 -0
  5. data/app/assets/config/markdown_cms_manifest.js +1 -0
  6. data/app/assets/stylesheets/markdown_cms/application.css +15 -0
  7. data/app/controllers/markdown_record/application_controller.rb +11 -0
  8. data/app/controllers/markdown_record/content_controller.rb +19 -0
  9. data/app/controllers/markdown_record/html_controller.rb +11 -0
  10. data/app/controllers/markdown_record/json_controller.rb +11 -0
  11. data/app/helpers/markdown_record/application_helper.rb +4 -0
  12. data/app/helpers/markdown_record/controller_helpers.rb +39 -0
  13. data/app/helpers/markdown_record/view_helpers.rb +84 -0
  14. data/app/models/markdown_record/demo/dsl_command.rb +10 -0
  15. data/app/models/markdown_record/demo/section.rb +9 -0
  16. data/app/models/markdown_record/tests/child_model.rb +15 -0
  17. data/app/models/markdown_record/tests/fake_active_record_model.rb +13 -0
  18. data/app/models/markdown_record/tests/model.rb +15 -0
  19. data/app/models/markdown_record/tests/other_child_model.rb +15 -0
  20. data/config/routes.rb +13 -0
  21. data/lib/generators/markdown_record_generator.rb +44 -0
  22. data/lib/markdown_record/association.rb +131 -0
  23. data/lib/markdown_record/associations.rb +106 -0
  24. data/lib/markdown_record/base.rb +25 -0
  25. data/lib/markdown_record/cli.rb +54 -0
  26. data/lib/markdown_record/configuration.rb +66 -0
  27. data/lib/markdown_record/content_associations.rb +46 -0
  28. data/lib/markdown_record/content_dsl/attribute.rb +22 -0
  29. data/lib/markdown_record/content_dsl/directory_fragment.rb +22 -0
  30. data/lib/markdown_record/content_dsl/disable.rb +22 -0
  31. data/lib/markdown_record/content_dsl/enable.rb +22 -0
  32. data/lib/markdown_record/content_dsl/end_attribute.rb +22 -0
  33. data/lib/markdown_record/content_dsl/end_model.rb +22 -0
  34. data/lib/markdown_record/content_dsl/fragment.rb +22 -0
  35. data/lib/markdown_record/content_dsl/model.rb +23 -0
  36. data/lib/markdown_record/content_dsl/render_format.rb +22 -0
  37. data/lib/markdown_record/content_dsl/render_strategy.rb +22 -0
  38. data/lib/markdown_record/content_dsl/use_layout.rb +22 -0
  39. data/lib/markdown_record/content_dsl.rb +37 -0
  40. data/lib/markdown_record/content_fragment.rb +123 -0
  41. data/lib/markdown_record/engine.rb +13 -0
  42. data/lib/markdown_record/errors/base.rb +6 -0
  43. data/lib/markdown_record/errors/duplicate_filename_error.rb +21 -0
  44. data/lib/markdown_record/errors/duplicate_id_error.rb +11 -0
  45. data/lib/markdown_record/errors/missing_parent_error.rb +11 -0
  46. data/lib/markdown_record/file_saver.rb +39 -0
  47. data/lib/markdown_record/html_renderer.rb +194 -0
  48. data/lib/markdown_record/indexer.rb +34 -0
  49. data/lib/markdown_record/json_renderer.rb +270 -0
  50. data/lib/markdown_record/model_inflator.rb +107 -0
  51. data/lib/markdown_record/path_utilities.rb +86 -0
  52. data/lib/markdown_record/rendering.rb +57 -0
  53. data/lib/markdown_record/routes_renderer.rb +0 -0
  54. data/lib/markdown_record/validator.rb +59 -0
  55. data/lib/markdown_record/version.rb +3 -0
  56. data/lib/markdown_record.rb +28 -0
  57. data/templates/Thorfile +3 -0
  58. data/templates/base/content/example_content.md +3 -0
  59. data/templates/base/layouts/_concatenated_layout.html.erb +8 -0
  60. data/templates/base/layouts/_custom_layout.html.erb +7 -0
  61. data/templates/base/layouts/_file_layout.html.erb +8 -0
  62. data/templates/base/layouts/_global_layout.html.erb +8 -0
  63. data/templates/demo/assets/images/ruby-logo.png +0 -0
  64. data/templates/demo/content/10_custom_models_and_associations.md.erb +78 -0
  65. data/templates/demo/content/11_controller_helpers.md.erb +20 -0
  66. data/templates/demo/content/12_configuration.md.erb +26 -0
  67. data/templates/demo/content/13_sandbox/1_foo.md +12 -0
  68. data/templates/demo/content/13_sandbox/2_sandbox_nested/1_bar.md +18 -0
  69. data/templates/demo/content/1_home.md.erb +40 -0
  70. data/templates/demo/content/2_installation.md.erb +129 -0
  71. data/templates/demo/content/3_rendering_basics.md.erb +71 -0
  72. data/templates/demo/content/4_content_dsl.md.erb +104 -0
  73. data/templates/demo/content/5_routes.md.erb +43 -0
  74. data/templates/demo/content/6_model_basics.md.erb +105 -0
  75. data/templates/demo/content/7_content_frags.md.erb +78 -0
  76. data/templates/demo/content/8_erb_syntax_and_view_helpers.md.erb +88 -0
  77. data/templates/demo/content/9_layouts.md.erb +15 -0
  78. data/templates/demo/layouts/_concatenated_layout.html.erb +12 -0
  79. data/templates/demo/layouts/_custom_layout.html.erb +14 -0
  80. data/templates/demo/layouts/_file_layout.html.erb +15 -0
  81. data/templates/demo/layouts/_global_layout.html.erb +108 -0
  82. data/templates/markdown_record_initializer.rb +3 -0
  83. data/templates/render_content.thor +57 -0
  84. metadata +270 -0
@@ -0,0 +1,78 @@
1
+ <!--fragment { "author": "Bryant Morrill", "parent_id": "content/home", "name": "Custom Models and Associations" } -->
2
+ <!---model { "type": "markdown_record/demo/section", "id": 8, "name": "Custom Models and Associations" } -->
3
+
4
+ # Custom Models and Associations
5
+
6
+ This sectional will discuss how to create MarkdownContent models and defines associations on them, as well as how to define associations between MarkdownContent and ActiveRecord models.
7
+
8
+ To make a MarkdownContent, it only needs to inherit from `MarkdownContent::Base`. Once you have done that, you can define attributes and associations on it to other MarkdownContent models.
9
+
10
+ ## Attributes
11
+
12
+ `MarkdownContent::Base` includes `ActiveAttr::Model` to provide attribute functionality. Please see how to use the ActiveAttr gem's interface <%= link_to("here", "https://github.com/cgriego/active_attr") %>.
13
+
14
+ ## Asscociations
15
+
16
+ You can define custom associations on your model using the `belongs_to_content`, `has_one_content` and `has_many_content` methods like so:
17
+
18
+ ```ruby
19
+ module MarkdownRecord
20
+ module Demo
21
+ class Section < ::MarkdownRecord::Base
22
+ attribute :name
23
+
24
+ has_many_content :dsl_commands
25
+ end
26
+ end
27
+ end
28
+ ```
29
+
30
+ ```ruby
31
+ module MarkdownRecord
32
+ module Demo
33
+ class DslCommand < ::MarkdownRecord::Base
34
+ attribute :name
35
+ attribute :description
36
+
37
+ belongs_to_content :section
38
+ end
39
+ end
40
+ end
41
+ ```
42
+
43
+ Once the associations are defined, you will want to make sure your JSON objects in your markdown have the required fields. For the example above, the DslCommand model will automatically be given a `section_id` field that will need to be populated in your markdown like so:
44
+
45
+ ```html
46
+ <!---model { "type": "markdown_record/demo/dsl_command", "id": 6, "name": "directory_fragment", "section_id": 1 } -->
47
+ ```
48
+
49
+ Then you can use the association in your code just like ActiveRecord associations:
50
+
51
+ ```ruby
52
+ MarkdownRecord::Demo::Section.find(3).dsl_commands.all
53
+ =>
54
+ [#<MarkdownRecord::Demo::DslCommand section_id: 1, description: "...", ...>, ...]
55
+ ```
56
+
57
+ ```ruby
58
+ MarkdownRecord::Demo::DslCommand.find(1).section
59
+ => #<MarkdownRecord::Demo::Section filename: "content_dsl", id: 3, name: "Content DSL", subdirectory: "content", type: "markdown_record/demo/section">
60
+ ```
61
+
62
+ `has_many_content` will define a method that returns an `MarkdownRecord::Association` object, which you can then chain queries onto. `belongs_to_content` and `has_one_content` will define methods that returns a single MarkdownContent model.
63
+
64
+ ### Associations on ActiveRecord Models
65
+
66
+ You can easily integrate MarkdownRecord and ActiveRecord models together using the `MarkdownRecord::ContentAssociations` module. Just include it in your ActiveRecord model like so:
67
+
68
+ ```ruby
69
+ class Product < ActiveRecord::Base
70
+ include MarkdownRecord::ContentAssociations
71
+
72
+ has_one_content :user_manual
73
+ has_many_content :research_notes
74
+ end
75
+ ````
76
+
77
+ You can define the same associations as described for MarkdownRecord models for ActiveRecord models. An important difference between the two cases, however, is that there are no foreign keys on the MarkdownRecord models that point to ActiveRecord models. Instead, MarkdownRecord expects to find an attribute on the ActiveRecord model that can be used for querying the MarkdownRecord models. For example, the `Product` model above should have a `user_manual_id` column or attribute. This effectively makes `has_one_content` and `belongs_to_content` aliases for each other. In the case of `has_many_content`, the model should have an attribute that is a JSON array of ids that point to the related MarkdownRecord models. In the example above, it would be called `research_note_ids`.
78
+
@@ -0,0 +1,20 @@
1
+ <!--fragment { "author": "Bryant Morrill", "parent_id": "content/home", "name": "Controller Helpers" } -->
2
+ <!---model { "type": "markdown_record/demo/section", "id": 9, "name": "Controller Helpers" } -->
3
+
4
+ # Controller Helpers
5
+
6
+ This section describes the controller helpers provided by MarkdownRecord to help interact with MarkdownRecord.
7
+
8
+ To use controller helpers in a controller, simply include the `MarkdownRecord::ControllerHelpers` module like so:
9
+
10
+ ```ruby
11
+ include ::MarkdownRecord::ControllerHelpers
12
+ ```
13
+
14
+ When the module is included, you will be able to call the following methods in your controller:
15
+
16
+ - `content_fragment`: finds a content fragment based on a passed id. If no id is passed it looks for `params[:content_path]` instead.
17
+ - `render_html`: takes an optional content fragment and an optional layout, then renders the fragment as HTML. If no fragment is provided, it calls `content_fragment` to get it using `params[:content_path]`. If no layout is provided, it uses the public_layout configured for MarkdownRecord (by default this is the application layout).
18
+ - `render_json`: takes an optional content fragment and renders it as JSON. If no fragment is provided, it calls `content_fragment` to get it using `params[:content_path]`.
19
+ - `download_html`: same a `render_html` but renders a file for download.
20
+ - `download_json`: same a `render_json` but renders a file for download.
@@ -0,0 +1,26 @@
1
+ <!--fragment { "author": "Bryant Morrill", "parent_id": "content/home", "name": "Configuration" } -->
2
+ <!---model { "type": "markdown_record/demo/section", "id": 9, "name": "Configuration" } -->
3
+
4
+ # Configuration
5
+
6
+ Much of MarkdownRecord's behavior is configurable. This section covers what can be configured.
7
+
8
+ You can configure `MarkdownRecord` in the initializer that is generated upon installation. The config attributes available are:
9
+
10
+ - `content_root`: the path to the directory containing your markdown source files. This is `markdown_record/content` inside your application root by default.
11
+ - `rendered_content_root`: the path to the directory where rendered content will be saves. This is `markdown_record/rendered` inside your application root by default.
12
+ - `layout_directory`: the path to the directory containing the layouts used during rendering. This is `markdown_record/layouts` inside your application root by default.
13
+ - `concatenated_layout_path`: the relative path (from `layout_directory`) to the layout that should be used for files rendered from the concatenated contents of a directory.
14
+ - `file_layout_path`: the relative path (from `layout_directory`) to the layout that should be used for files rendered from a single markdown source file.
15
+ - `global_layout_path`: the relative path (from `layout_directory`) to the layout that should be used for all rendered files.
16
+ - `markdown_extensions`: MarkdownRecord uses the `Redcarpet` gem under the hood to process markdown. This value is the configuration that gets passed to `::Redcarpet::Markdown`. The default value is `{:fenced_code_blocks => true, :disable_indented_code_blocks => true, :no_intra_emphasis => true}`. Please see <%= link_to("Redcarpet's documentation", "https://github.com/vmg/redcarpet") %> for more details.
17
+ - `html_render_options`: the `render_options` that get passed to Redcarpet's `Redcarpet::Render::HTML` class. The default value is an empty Hash. Please see <%= link_to("Redcarpet's documentation", "https://github.com/vmg/redcarpet") %> for more details.
18
+ - `public_layout`: the path to the layout (from the application root) that should be used when the controllers provided by MarkdownRecord render HTML. The default value is "layouts/application".
19
+ - `render_strategy`: the render strategy that should be use when running the `render_content` Thor task. The default value is `:full` but it can be changed to `:directory` of `:file`. If the `:file` strategy is used then MarkdownRecord will render your markdown content each time MarkdownRecord models queries are executed.
20
+ - `html_routes`: the routes that are generated to render only HTML. This value is an array of symbols that can include `:show` and `:download`. By default the value is `[:show]`.
21
+ - `json_routes`: the routes that are generated to render only JSON. This value is an array of symbols that can include `:show` and `:download`. By default the value is `[:show]`.
22
+ - `content_routes`: the routes that are generated to render either HTML or JSON depending on the request. This value is an array of symbols that can include `:show` and `:download`. By default the value is `[:show]`.
23
+ - `mount_path`: the path where the MarkdownRecord engine is mounted inside the host application. It is `mdr` by default.
24
+ - `render_content_fragment_json`: a boolean value that determines whether MarkdownRecord renders content fragments and saves them to the `rendered_content_root` in JSON files with the `_fragments` suffix. If this is set to `false`, then MarkdownRecord will render your markdown content each time MarkdownRecord models queries are executed. This is set to `true` by default.
25
+ - `render_controller`: the controller used to render ERB content during the rendering process. If this isn't set, then the host application's `ApplicationController` will be used. By default this is `nil`.
26
+ - `ignore_numeric_prefix`: configures whether or not MarkdownRecord ignores the numeric prefixes of filenames. The default value is `true`. When it is set to `false`, the numeric prefixes will be included in the `filename` attribute of models, the `id` attribute of content fragments, and the `content_path` parameter of requests to the routes for rendered content.
@@ -0,0 +1,12 @@
1
+ <!--directory_fragment { "author": "You", "name": "Sandbox", "parent_id": "content/home" } -->
2
+ <!--use_layout:_custom_layout.html.erb-->
3
+
4
+ Feel free to use this directory as a sandbox to experiment with the things you learn from the guide, without fear of messing up the guide itself. The things defined here are used for automated tests only, so feel free to delete/alter anything you want here.
5
+
6
+ When you are done experimenting and want to start creating for real, feel free to delete the entire guide and use the online hosted version for reference going forward.
7
+
8
+ *Note: you will need to add the .erb extension to this file to use ERB syntax in it.*
9
+
10
+ <!--model { "id": 1, "type": "markdown_record/tests/model", "string_field": "foo", "int_field": 100, "float_field": 95.5, "bool_field": true, "date_field": "03/13/2023", "maybe_field": null, "hash_field": {} }-->
11
+
12
+ <!--model { "id": 2, "type": "markdown_record/tests/model", "string_field": "bar", "int_field": 42, "float_field": 99.9, "bool_field": false, "date_field": "12/25/2020", "maybe_field": 50, "hash_field": { "some_data": { "some_field": 999 }} }-->
@@ -0,0 +1,18 @@
1
+ <!--directory_fragment { "author": "You", "name": "Sandbox", "parent_id": "content/sandbox/foo" } -->
2
+ <!--use_layout:_custom_layout.html.erb-->
3
+
4
+ Feel free to use this directory as a sandbox to experiment with the things you learn from the guide, without fear of messing up the guide itself. The things defined here are used for automated tests only, so feel free to delete/alter anything you want here.
5
+
6
+ When you are done experimenting and want to start creating for real, feel free to delete the entire guide and use the online hosted version for reference going forward.
7
+
8
+ *Note: you will need to add the .erb extension to this file to use ERB syntax in it.*
9
+
10
+ <!--model { "id": 1, "type": "markdown_record/tests/child_model", "model_id": 1, "string_field": "hey", "int_field": 100, "float_field": 95.5, "bool_field": true, "date_field": "03/13/2023", "maybe_field": null, "hash_field": {} }-->
11
+
12
+ <!--model { "id": 2, "type": "markdown_record/tests/child_model", "model_id": 1, "string_field": "asdf", "int_field": 333, "float_field": 10.5, "bool_field": false, "date_field": "01/01/2000", "maybe_field": 7, "hash_field": {} }-->
13
+
14
+ <!--model { "id": 3, "type": "markdown_record/tests/child_model", "model_id": 2, "string_field": "qwert", "int_field": 42, "float_field": 1776, "bool_field": true, "date_field": "09/11/2001", "maybe_field": null, "hash_field": { "some_data": { "some_field": 555 }} }-->
15
+
16
+ <!--model { "id": 4, "type": "markdown_record/tests/child_model", "model_id": 2, "string_field": "ho", "int_field": 42, "float_field": 99.9, "bool_field": false, "date_field": "12/25/2020", "maybe_field": 50, "hash_field": { "some_data": { "some_field": 999 }} }-->
17
+
18
+ <!--model { "id": 1, "type": "markdown_record/tests/other_child_model", "model_id": 2, "string_field": "ho", "int_field": 42, "float_field": 99.9, "bool_field": false, "date_field": "12/25/2020", "maybe_field": 50, "hash_field": { "some_data": { "some_field": 999 }} }-->
@@ -0,0 +1,40 @@
1
+ <!--directory_fragment { "name": "Demo", "author": "Bryant Morrill" } -->
2
+ <!--fragment { "name": "Home", "author": "Bryant Morrill" } -->
3
+
4
+ # MarkdownRecord
5
+
6
+ Welcome to MarkdownRecord!
7
+
8
+ This document will walk you through the many powerful features of MarkdownRecord, and is created with MarkdownRecord itself. Hopefully, you are rendering this page locally from your own application, and will be able to get a peak under the hood to see how it all works by looking directly at the source markdown files.
9
+
10
+ If you haven't installed it locally yet, you can find instructions <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/installation"), "here") %>.
11
+
12
+ After following the installation guide, you should see a `markdown_record` folder inside your application root. This folder contains three subdirectories: `content`, `layouts` and `rendered`.
13
+
14
+ Feel free to poke around and explore the files in the directories mentioned above as you go through this guide. They will serve as a great reference to help make sense of the features and concepts described here.
15
+
16
+ ## Workflow
17
+
18
+ The general workflow while using MarkdownRecord, once installed in your application, is as follows:
19
+
20
+ 1. Write markdown content, using the Content DSL to define data within your markdown content.
21
+ 2. Run the provided Thor task to render your markdown content into HTML and JSON.
22
+ 3. Use the built in `MarkdownRecord::ContentFragment` model to directly reference your rendered content inside your application.
23
+ 4. Use the provided view helpers to link to the rendered content in your application's views, relying only on the controllers provided by the engine for serving the content if you wish.
24
+ 5. Use model classes inheriting from `MarkdownRecord::Base` to interact with your html and json rendered content, taking advantage of the associations you can define manually as well a the automatic associations they have based on content location.
25
+
26
+ The next step is to read through the <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/rendering_basics"), "Rendering Basics") %> section to understand the basic rendering process of markdown source content into HTML and JSON.
27
+
28
+ The following links will take you to other sections of this guide. Each section builds upon the last, so it is recommended to read through them in order if you are new to MarkdownRecord.
29
+
30
+ - <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/content_dsl"))%>
31
+ - <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/routes")) %>
32
+ - <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/model_basics")) %>
33
+ - <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/content_frags")) %>
34
+ - <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/erb_syntax_and_view_helpers")) %>
35
+ - <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/layouts")) %>
36
+ - <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/custom_models_and_associations")) %>
37
+ - <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/controller_helpers")) %>
38
+ - <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/configuration")) %>
39
+ - <%= link_to_markdown_record(::MarkdownRecord::ContentFragment.find("content/sandbox")) %>
40
+
@@ -0,0 +1,129 @@
1
+ <!--fragment { "author": "Bryant Morrill", "name": "Installation", "parent_id": "content/home" } -->
2
+ <!--model { "type": "markdown_record/demo/section", "id": 1, "name": "Installation" } -->
3
+
4
+ # Installation
5
+
6
+ This section explains how to install MarkdownRecord into a host application.
7
+
8
+ First, add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem "markdown_record"
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ ```bash
17
+ $ bundle install
18
+ ```
19
+
20
+ Then, from the root directory of your application run:
21
+
22
+ ```bash
23
+ $ rails g markdown_record --demo
24
+ ```
25
+
26
+ *Note: if you are already familiar with MarkdownRecord and don't want to install the demo content, you can omit the --demo argument.*
27
+
28
+ The above command will install the engine, resulting in the following output and changes to your application:
29
+
30
+ ```bash
31
+ create markdown_record/content
32
+ create markdown_record/layouts
33
+ create markdown_record/rendered
34
+ exist markdown_record/content
35
+ create markdown_record/content/controller_helpers.md.erb
36
+ create markdown_record/content/configuration.md.erb
37
+ create markdown_record/content/sandbox/foo.md
38
+ create markdown_record/content/home.md.erb
39
+ create markdown_record/content/installation.md.erb
40
+ create markdown_record/content/rendering_basics.md.erb
41
+ create markdown_record/content/content_dsl.md.erb
42
+ create markdown_record/content/routes.md.erb
43
+ create markdown_record/content/model_basics.md.erb
44
+ create markdown_record/content/content_frags.md.erb
45
+ create markdown_record/content/erb_syntax_and_view_helpers.md.erb
46
+ create markdown_record/content/custom_models_and_associations.md.erb
47
+ exist markdown_record/layouts
48
+ create markdown_record/layouts/_concatenated_layout.html.erb
49
+ create markdown_record/layouts/_custom_layout.html.erb
50
+ create markdown_record/layouts/_file_layout.html.erb
51
+ create markdown_record/layouts/_global_layout.html.erb
52
+ create app/assets/images
53
+ create app/assets/images/ruby-logo.png
54
+ create config/initializers/markdown_record.rb
55
+ create Thorfile
56
+ create lib/tasks/render_content.thor
57
+ gsub config/routes.rb
58
+ ```
59
+
60
+ The files and folders inside `markdown_record/content` are for demo purposes only, and can be deleted once you are ready to create your own content.
61
+
62
+ By default, MarkdownRecord will look in the `markdown_record/content` directory for all your content, and all rendered content will be saved to the `markdown_record/rendered` directory.
63
+
64
+ The engine will be mounted in `config/routes.rb` under the `mdr` path by default.
65
+
66
+ An initializer will be created for you, where you will be able to configure the engine to use different directories and change other default settings. A later section of this guide will provide more details on configuration options.
67
+
68
+ A `Thorfile` and some thor tasks will also be added to your application. These tasks are used to render your content to HTML and JSON.
69
+
70
+ The final step in the installation process is to render the demo content that was installed in `markdown_record/content`. To do so, run this Thor task in your application's root directory:
71
+
72
+ ```bash
73
+ thor render_content:all -s
74
+ ```
75
+
76
+ You should see the following output:
77
+
78
+ ```bash
79
+ ---------------------------------------------------------------
80
+ rendering html and json content with options {:concat=>true, :deep=>true, :save=>true, :layout=>"_concatenated_layout.html.erb", :render_content_fragment_json=>true} ...
81
+ ---------------------------------------------------------------
82
+ rendered: /markdown_record/rendered/content_fragments.json
83
+ rendered: /markdown_record/rendered/content.json
84
+ rendered: /markdown_record/rendered/content/custom_models_and_associations_fragments.json
85
+ rendered: /markdown_record/rendered/content/custom_models_and_associations.json
86
+ rendered: /markdown_record/rendered/content/erb_syntax_and_view_helpers_fragments.json
87
+ rendered: /markdown_record/rendered/content/erb_syntax_and_view_helpers.json
88
+ rendered: /markdown_record/rendered/content/content_frags_fragments.json
89
+ rendered: /markdown_record/rendered/content/content_frags.json
90
+ rendered: /markdown_record/rendered/content/model_basics_fragments.json
91
+ rendered: /markdown_record/rendered/content/model_basics.json
92
+ rendered: /markdown_record/rendered/content/routes_fragments.json
93
+ rendered: /markdown_record/rendered/content/routes.json
94
+ rendered: /markdown_record/rendered/content/content_dsl_fragments.json
95
+ rendered: /markdown_record/rendered/content/content_dsl.json
96
+ rendered: /markdown_record/rendered/content/rendering_basics_fragments.json
97
+ rendered: /markdown_record/rendered/content/rendering_basics.json
98
+ rendered: /markdown_record/rendered/content/installation_fragments.json
99
+ rendered: /markdown_record/rendered/content/installation.json
100
+ rendered: /markdown_record/rendered/content/home_fragments.json
101
+ rendered: /markdown_record/rendered/content/home.json
102
+ rendered: /markdown_record/rendered/content/sandbox_fragments.json
103
+ rendered: /markdown_record/rendered/content/sandbox.json
104
+ rendered: /markdown_record/rendered/content/sandbox/foo_fragments.json
105
+ rendered: /markdown_record/rendered/content/sandbox/foo.json
106
+ rendered: /markdown_record/rendered/content/configuration_fragments.json
107
+ rendered: /markdown_record/rendered/content/configuration.json
108
+ rendered: /markdown_record/rendered/content/controller_helpers_fragments.json
109
+ rendered: /markdown_record/rendered/content/controller_helpers.json
110
+ rendered: /markdown_record/rendered/content.html
111
+ rendered: /markdown_record/rendered/content/custom_models_and_associations.html
112
+ rendered: /markdown_record/rendered/content/erb_syntax_and_view_helpers.html
113
+ rendered: /markdown_record/rendered/content/content_frags.html
114
+ rendered: /markdown_record/rendered/content/model_basics.html
115
+ rendered: /markdown_record/rendered/content/routes.html
116
+ rendered: /markdown_record/rendered/content/content_dsl.html
117
+ rendered: /markdown_record/rendered/content/rendering_basics.html
118
+ rendered: /markdown_record/rendered/content/installation.html
119
+ rendered: /markdown_record/rendered/content/home.html
120
+ rendered: /markdown_record/rendered/content/sandbox.html
121
+ rendered: /markdown_record/rendered/content/sandbox/foo.html
122
+ rendered: /markdown_record/rendered/content/configuration.html
123
+ rendered: /markdown_record/rendered/content/controller_helpers.html
124
+ ---------------------------------------------------------------
125
+ 42 files rendered.
126
+ 42 files saved.
127
+ ```
128
+
129
+ Congratulations! You have installed MarkdownRecord. If you are not viewing this from the host application already, go ahead and start your Rails server and navigate to your <%= link_to("local host demo", "http://localhost:3000/mdr/content/home") %> to continue following this guide.
@@ -0,0 +1,71 @@
1
+ <!--fragment { "author": "Bryant Morrill", "name": "Rendering Basics", "parent_id": "content/home" } -->
2
+ <!--model { "type": "markdown_record/demo/section", "id": 2, "name": "Rendering Basics" } -->
3
+
4
+ # Rendering Basics
5
+
6
+ This section will cover the basic concepts of MarkdownRecord.
7
+
8
+ ## Important Directories
9
+
10
+ MarkdownRecord expects to find a `markdown_record` directory in your project's root folder, and by default looks for the following subdirectories inside it:
11
+
12
+ - `content`: this is where all your original source files will go. When you write a new markdown file, it will live in here.
13
+ - `layouts`: this is where the layouts used when rendering your markdown source files to HTML should go. This folder should already have a some layouts that you can customize to your liking.
14
+ - `rendered`: this is where all the rendered JSON and HTML files go. You should never have to edit any of the files in here. However, you may want to delete this folder and re-render everything (the folder will be generated again) at certain times, such as when you delete a source file and want to remove the corresponding rendered files and data from your application.
15
+
16
+ ## Rendering Output
17
+
18
+ By default, MarkdownRecord renders each markdown file in `content` as both a JSON file and an HTML file in the `rendered` directory. It also renders each directory into concatenated JSON and an HTML files containing the rendered content of all their contents. The organization and naming of the rendered files should mirror the organization of the source files and directories in `content`, with the concatenated files at the same level of their respective directories.
19
+
20
+ In addition, MarkdownRecord also renders and extra JSON file with the `_fragments` suffix. This file contains the auto generated data required to populate `::MarkdownRecord::ContentFragment` models, and is kept separate to keep them from getting mixed in with the JSON rendered from the content files you create.
21
+
22
+ *IMPORTANT: because of the special meaning of the `_fragments` suffix, the markdown content files that you create should not have `_fragments` in their name to avoid unexpected problems.*
23
+
24
+ ## Numeric Prefixes
25
+
26
+ You will notice that each file and directory for this demo in the `content` directory has a numeric prefix. These prefixes are how MarkdownRecord determines the order of content in the concatenated files. By using a numeric prefix, the exact order can be specified leading to predictable results.
27
+
28
+ By default, MarkdownRecord ignores these numeric prefixes for all other purposes. This means that they should not be included in id values for content fragments or in the `content_path` parameter when loading rendered content in the browser (more on both these topics will be discussed later in this guide.). Because of this, it is important that the portion of each filename that comes after the numeric prefix be unique within a subdirectory. If two filename only differ in their numeric prefix, there will be a conflict.
29
+
30
+ MarkdownRecord can render content using three different strategies: `file`, `directory` and `full`. The default strategy is `full` and will render all files and directories. The `file` strategy will render only files and will not concatenate source content for each directory. The `directory` strategy will only render the concatenated files for each directory.
31
+
32
+ ## File Extensions
33
+
34
+ MarkdownRecord supports ERB syntax in markdown files that have a `.md.erb` extension. If you don't want MarkdownRecord to process your files as ERB templates, they should have the `.md` extension instead.
35
+
36
+ ## Thor tasks
37
+
38
+ The MarkdownRecord rendering process is executed via Thor tasks, just like the one used in the installation instructions. The one there looks like this:
39
+
40
+ ```bash
41
+ thor render_content:all -s
42
+ ```
43
+ Note the `all` part of that command, which indicates that both JSON and HTML will be rendered. You could use `json` or `html` there to only render one type of content.
44
+
45
+ The `-s` argument indicates that the rendered content should be saved. If that argument is omitted, you will see the output of the rendering but it will only do a dry run without saving anything.
46
+
47
+ You can see more options for the Thor task by running:
48
+
49
+ ```bash
50
+ thor render_content -h
51
+ ```
52
+
53
+ ```bash
54
+ Commands:
55
+ thor render_content:all # renders html and json content
56
+ thor render_content:help [COMMAND] # Describe available commands or one specific command
57
+ thor render_content:html # renders html content
58
+ thor render_content:json # renders json content
59
+
60
+ Options:
61
+ d, [--subdirectory=SUBDIRECTORY]
62
+ s, [--save], [--no-save]
63
+ r, [--strat=STRAT]
64
+ f, [--frag], [--no-frag]
65
+ ```
66
+
67
+ ### Validation
68
+
69
+ MarkdownRecord validates your filenames and JSON data before rendering. If it finds any problems, it will output a message to the terminal and nothing will be rendered.
70
+
71
+
@@ -0,0 +1,104 @@
1
+ <!--fragment { "author": "Bryant Morrill", "parent_id": "content/home", "name": "Content DSL" } -->
2
+ <!--model { "type": "markdown_record/demo/section", "id": 3, "name": "Content DSL" } -->
3
+
4
+ # Content DSL
5
+
6
+ This section describes the Content DSL MarkdownRecord provides to allow you to define application data right alongside your written markdown content.
7
+
8
+ While writing documentation in your markdown source files, you can define json data using HTML comments which will then be made available to you within your application code (if you define matching models), or directly served via built in routes.
9
+
10
+ To do this, simply write an HTML comment like this in your markdown files:
11
+
12
+ ```html
13
+ <!---model
14
+ {
15
+ "type": "markdown_record/demo/dsl_command",
16
+ "id": 1,
17
+ "name": "model",
18
+ "example_id": 1
19
+ }
20
+ -->
21
+ ```
22
+
23
+ *Note: you can write these JSON object inline as well to be more concise.*
24
+
25
+ As you can see, all that is required is a JSON object inside a special comment that defines your data. The `model` part of the comment is a DSL command that tells MarkdownRecord how to process the comment, and in this case it will take your JSON object and include it in its final rendered JSON output, which will automatically make it available via the provided routes and controllers. If this part is missing or unrecognized, then the comment will be treated as a plain HTML comment and nothing will be done with its contents.
26
+
27
+ There are several different DSL commands that you can use to define application data within your markdown source documents. They are as follows:
28
+
29
+ <!--model { "type": "markdown_record/demo/dsl_command", "id": 1, "name": "model", "section_id": 3 } -->
30
+ <!--attribute:description:string-->
31
+ - `model` is the main command that tells MarkdownRecord that you want to define a model or JSON object. The only attribute required in the json object you provide is the `type` attribute, which is used as an index in the final JSON output (each index points to an array of objects of that type). The type can be whatever you want if you don't plan on using corresponding Ruby models. If you *do* plan to use corresponding Ruby models, then the `type` attribute should match the fully qualified class name of the model in underscore form (i.e. `Foo::BarBaz` would be `foo/bar_baz`).
32
+
33
+ MarkdownRecord puts each model in a stack structure during rendering, and remembers the order in which models are defined within a single markdown file. This is important, as the next command in this list allows you to add additional attributes where the values are taken from your markdown content itself. The model that attributes get assigned to in this way is always the model at the top of the stack.
34
+ <!--end_attribute-->
35
+
36
+ See the example above.
37
+ <!--end_model-->
38
+
39
+ <!--model { "type": "markdown_record/demo/dsl_command", "id": 2, "name": "attribute", "section_id": 3 } -->
40
+ <!--attribute:description:string-->
41
+ - `attribute:<attribute_name>:<type>` tells MarkdownRecord that any text that follows should be assigned to the top model on the stack as the value of the given attribute, until it sees the `end_attribute` command.
42
+
43
+ The `type` part of this command can be `html`, `md`, `int`, `float`, `string`, or omitted. MarkdownRecord will process the final value of the attribute differently based on this value. If the type is `html`, it will be rendered into an html string. If it is `md`, then it will be left as is and slightly cleaned up to remove leading and trailing white space. If it is `int` or `float`, it will be parsed accordingly into an numeric value, and if it is `string`, it will be stripped of any markdown syntax and all leading and trailing whitespace will be removed. The `string` option will also cause multiple newlines to be reduced to a single newline. If the type option is omitted, MarkdownRecord will not process the attribute value and it will contain the raw content read from the markdown file.
44
+ <!--end_attribute-->
45
+
46
+ Example:
47
+
48
+ ```html
49
+ <!---attribute:description:string-->
50
+ ```
51
+ <!--end_model-->
52
+ <!--model { "type": "markdown_record/demo/dsl_command", "id": 3, "name": "end_attribute", "section_id": 3 } -->
53
+ <!--attribute:description:string-->
54
+ - `end_attribute` tells MarkdownRecord to stop assigning text as an attribute value to the top model on the stack. If the top most model is popped before this command is given or the end of the source file is reached, then it implicitly stops assigning text to it.
55
+ <!--end_attribute-->
56
+
57
+ Example:
58
+
59
+ ```html
60
+ <!---end_attribute-->
61
+ ```
62
+ <!--end_model-->
63
+ <!--model { "type": "markdown_record/demo/dsl_command", "id": 4, "name": "end_model", "section_id": 3 } -->
64
+ <!--attribute:description:string-->
65
+ - `end_model` tells MarkdownRecord to pop the top model of the stack. This command isn't necessary unless you are defining models in a way that requires you to assign attributes to a model that is no longer the top model on the stack.
66
+ <!--end_attribute-->
67
+
68
+ Example:
69
+
70
+ ```html
71
+ <!---end_model-->
72
+ ```
73
+ <!--end_model-->
74
+ <!--model { "type": "markdown_record/demo/dsl_command", "id": 5, "name": "fragment", "section_id": 3 } -->
75
+ <!--attribute:description:string-->
76
+ - `fragment` is similar to `model` in that it expects a JSON object, but the object is assigned to the `meta` attribute of the `ContentFragment` corresponding to the current markdown file. As such, this command should only be used once in each file. This command provides a way of attaching custom data to ContentFragments, such as a layout you want to use for rendering from a controller, details about the content's author, or perhaps who should be able to access it, etc. You can then interact this data in your application code.
77
+
78
+ Since content is not just rendered for each individual file, but is also concatenated into rendered files for each directory, the ContentFragments representing these concatenated files will have each file's fragment meta data as a nested object, indexed by the respective file's path.
79
+ <!--end_attribute-->
80
+
81
+ Example:
82
+
83
+ ```html
84
+ <!---fragment { "author": "Bryant Morrill" } -->
85
+ ```
86
+ <!--end_model-->
87
+ <!--model { "type": "markdown_record/demo/dsl_command", "id": 6, "name": "directory_fragment", "section_id": 3 } -->
88
+ <!--attribute:description:string-->
89
+ - `directory_fragment` is used exactly the same way as `fragment` but defines the `meta` attribute of the `ContentFragment` for the current directory. As such, this should only be used once per directory level, otherwise subsequently defined data might overwrite preceding data.
90
+ <!--end_attribute-->
91
+ <!--end_model-->
92
+ <!--model { "type": "markdown_record/demo/dsl_command", "id": 7, "name": "use_layout", "section_id": 3 } -->
93
+ <!--attribute:description:string-->
94
+ - `use_layout:<path>` simply specifies a layout by its relative file path to use when rendering the current markdown file to HTML. This is useful for when you have specific markdown files that you want to render using a different layout than the layout configured for use with all other files.
95
+ <!--end_attribute-->
96
+
97
+ Example:
98
+
99
+ ```html
100
+ <!--use_layout:_custom_layout.html.erb-->
101
+ ```
102
+ <!--end_model-->
103
+
104
+ You can view this document's source files in `markdown_record/content` to see some of the above DSL commands in use.
@@ -0,0 +1,43 @@
1
+ <!--fragment { "author": "Bryant Morrill", "parent_id": "content/home", "name": "Routes" } -->
2
+ <!--model { "type": "markdown_record/demo/section", "id": 4, "name": "Routes" } -->
3
+
4
+ # Routes
5
+
6
+ This section discusses the automatically available routes you can use to render markdown content as HTML or JSON.
7
+
8
+ If you are reading this document in a browser by rendering it from your application, then you will have already ran the Thor command necessary to render the demo content as HTML and JSON. You can also see the HTML and JSON files in the `rendered` directory. In fact, the content you are viewing right now as you read this is one of those files, served to you via the built in routes that come with MarkdownRecord.
9
+
10
+ All of the following routes should now resolve successfully:
11
+
12
+ - `<application root path>/mdr/html/<content_path>`
13
+ - `<application root path>/mdr/json/<content_path>`
14
+ - `<application root path>/<content_path>`
15
+
16
+
17
+ `content_path` in the above routes is the path to a rendered file or directory (relative to the `rendered` folder). When you navigate to a path that points to a directory, you will see the concatenated contents of everything that is in that directory.
18
+
19
+ The `html` routes only return HTML, while the `json` routes only return JSON. The generic route returns either HTML or JSON depending on the ACCEPT header of your request or the extension of the url you are accessing (for example, appending `.json` at the end of the current url will load all the JSON models defined in this this document's markdown source files using the Content DSL commands described above).
20
+
21
+ In addition to the above routes, routes for downloading content are provided as well. They are:
22
+
23
+ - `<application root path>/mdr/html/download/<content_path>`
24
+ - `<application root path>/mdr/json/download/<content_path>`
25
+ - `<application root path>/download/<content_path>`
26
+
27
+ The download routes are not enabled by default, but can be enabled if you want to be able to download your rendered content.
28
+
29
+ Here is a complete list of links to all the rendered HTML files of this demo (each item has a JSON and an HTML version), all of which use the routes described above:
30
+
31
+ <% ::MarkdownRecord::ContentFragment.all.each do |frag| %>
32
+ - <%= link_to_markdown_record(frag, frag.id) %><br>
33
+ <% end %>
34
+
35
+ <% ::MarkdownRecord::ContentFragment.all.each do |frag| %>
36
+ - <%= link_to_markdown_record_html(frag, frag.html_route) %><br>
37
+ <% end %>
38
+
39
+ <% ::MarkdownRecord::ContentFragment.all.each do |frag| %>
40
+ - <%= link_to_markdown_record_json(frag, frag.json_route) %><br>
41
+ <% end %>
42
+
43
+ Some of the above routes are the rendered versions of individual files, and some are the concatenated and rendered contents of an entire directory.