markdown_record 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +63 -43
  3. data/app/helpers/markdown_record/application_helper.rb +23 -0
  4. data/app/helpers/markdown_record/controller_helpers.rb +1 -1
  5. data/app/helpers/markdown_record/view_helpers.rb +9 -3
  6. data/{lib → app/models}/markdown_record/base.rb +10 -0
  7. data/{lib → app/models}/markdown_record/content_fragment.rb +17 -2
  8. data/lib/generators/markdown_record_generator.rb +6 -0
  9. data/lib/markdown_record/configuration.rb +4 -0
  10. data/lib/markdown_record/file_sorting/base.rb +25 -0
  11. data/lib/markdown_record/file_sorting/date_sorter.rb +14 -0
  12. data/lib/markdown_record/file_sorting/sem_ver_sorter.rb +47 -0
  13. data/lib/markdown_record/{association.rb → models/association.rb} +20 -3
  14. data/lib/markdown_record/{associations.rb → models/associations.rb} +17 -9
  15. data/lib/markdown_record/{content_associations.rb → models/content_associations.rb} +4 -2
  16. data/lib/markdown_record/models/filtering.rb +13 -0
  17. data/lib/markdown_record/models/filters/array_filter.rb +11 -0
  18. data/lib/markdown_record/models/filters/base_filter.rb +35 -0
  19. data/lib/markdown_record/models/filters/hash_filter.rb +81 -0
  20. data/lib/markdown_record/models/filters/nil_filter.rb +11 -0
  21. data/lib/markdown_record/models/filters/range_filter.rb +11 -0
  22. data/lib/markdown_record/models/filters/regexp_filter.rb +11 -0
  23. data/lib/markdown_record/models/filters/symbol_filter.rb +14 -0
  24. data/lib/markdown_record/models/filters.rb +22 -0
  25. data/lib/markdown_record/models/model_inflator.rb +65 -0
  26. data/lib/markdown_record/path_utilities.rb +20 -11
  27. data/lib/markdown_record/rendering/content_dsl/attribute.rb +22 -0
  28. data/lib/markdown_record/{content_dsl → rendering/content_dsl}/directory_fragment.rb +3 -3
  29. data/lib/markdown_record/{content_dsl → rendering/content_dsl}/disable.rb +2 -2
  30. data/lib/markdown_record/{content_dsl → rendering/content_dsl}/enable.rb +2 -2
  31. data/lib/markdown_record/{content_dsl → rendering/content_dsl}/end_attribute.rb +3 -3
  32. data/lib/markdown_record/{content_dsl → rendering/content_dsl}/end_model.rb +3 -3
  33. data/lib/markdown_record/{content_dsl → rendering/content_dsl}/fragment.rb +3 -3
  34. data/lib/markdown_record/rendering/content_dsl/model.rb +22 -0
  35. data/lib/markdown_record/rendering/content_dsl/scope.rb +22 -0
  36. data/lib/markdown_record/{content_dsl → rendering/content_dsl}/use_layout.rb +3 -3
  37. data/lib/markdown_record/rendering/content_dsl.rb +45 -0
  38. data/lib/markdown_record/rendering/html_renderer.rb +22 -0
  39. data/lib/markdown_record/{indexer.rb → rendering/indexer.rb} +0 -10
  40. data/lib/markdown_record/rendering/json_renderer.rb +20 -0
  41. data/lib/markdown_record/rendering/nodes/html_base.rb +95 -0
  42. data/lib/markdown_record/rendering/nodes/html_directory.rb +51 -0
  43. data/lib/markdown_record/rendering/nodes/html_file.rb +48 -0
  44. data/lib/markdown_record/rendering/nodes/json_base.rb +55 -0
  45. data/lib/markdown_record/rendering/nodes/json_directory.rb +69 -0
  46. data/lib/markdown_record/rendering/nodes/json_file.rb +172 -0
  47. data/lib/markdown_record/{rendering.rb → rendering/rendering.rb} +1 -1
  48. data/lib/markdown_record/{validator.rb → rendering/validator.rb} +4 -4
  49. data/lib/markdown_record/version.rb +1 -1
  50. data/lib/markdown_record.rb +21 -14
  51. data/templates/demo/content/10_custom_models_and_associations.md.erb +27 -7
  52. data/templates/demo/content/11_controller_helpers.md.erb +2 -2
  53. data/templates/demo/content/12_configuration.md.erb +22 -3
  54. data/templates/demo/content/13_sandbox/1_foo.md +2 -1
  55. data/templates/demo/content/13_sandbox/2_sandbox_nested/1_bar.md +4 -1
  56. data/templates/demo/content/1_home.md.erb +30 -13
  57. data/templates/demo/content/2_installation.md.erb +61 -46
  58. data/templates/demo/content/3_rendering_basics.md.erb +3 -3
  59. data/templates/demo/content/4_content_dsl.md.erb +22 -11
  60. data/templates/demo/content/5_routes.md.erb +10 -7
  61. data/templates/demo/content/6_model_basics.md.erb +11 -11
  62. data/templates/demo/content/7_content_frags.md.erb +9 -6
  63. data/templates/demo/content/8_erb_syntax_and_view_helpers.md.erb +32 -11
  64. data/templates/demo/content/9_layouts.md.erb +3 -3
  65. data/templates/demo/layouts/_global_layout.html.erb +1 -0
  66. data/templates/demo/models/dsl_command.rb +6 -0
  67. data/templates/demo/models/section.rb +5 -0
  68. data/templates/render_content.thor +2 -1
  69. data/templates/tests/assets/images/ruby-logo.png +0 -0
  70. data/templates/tests/content/1_test_files_home.md.erb +31 -0
  71. data/templates/tests/content/2_content_dsl_tests/1_content_dsl.md.erb +162 -0
  72. data/templates/tests/content/2_content_dsl_tests/2_nested_directory/1_associations.md.erb +83 -0
  73. data/templates/tests/layouts/_concatenated_layout.html.erb +12 -0
  74. data/templates/tests/layouts/_custom_layout.html.erb +14 -0
  75. data/templates/tests/layouts/_file_layout.html.erb +15 -0
  76. data/templates/tests/layouts/_global_layout.html.erb +116 -0
  77. metadata +53 -34
  78. data/app/models/markdown_record/demo/dsl_command.rb +0 -10
  79. data/app/models/markdown_record/demo/section.rb +0 -9
  80. data/app/models/markdown_record/tests/child_model.rb +0 -15
  81. data/app/models/markdown_record/tests/fake_active_record_model.rb +0 -13
  82. data/app/models/markdown_record/tests/model.rb +0 -15
  83. data/app/models/markdown_record/tests/other_child_model.rb +0 -15
  84. data/lib/markdown_record/cli.rb +0 -54
  85. data/lib/markdown_record/content_dsl/attribute.rb +0 -22
  86. data/lib/markdown_record/content_dsl/model.rb +0 -23
  87. data/lib/markdown_record/content_dsl/render_format.rb +0 -22
  88. data/lib/markdown_record/content_dsl/render_strategy.rb +0 -22
  89. data/lib/markdown_record/content_dsl.rb +0 -37
  90. data/lib/markdown_record/html_renderer.rb +0 -194
  91. data/lib/markdown_record/json_renderer.rb +0 -270
  92. data/lib/markdown_record/model_inflator.rb +0 -107
  93. data/lib/markdown_record/routes_renderer.rb +0 -0
  94. /data/lib/markdown_record/{file_saver.rb → rendering/file_saver.rb} +0 -0
@@ -1,15 +0,0 @@
1
- module MarkdownRecord
2
- module Tests
3
- class OtherChildModel < ::MarkdownRecord::Base
4
- attribute :string_field
5
- attribute :int_field
6
- attribute :float_field
7
- attribute :bool_field
8
- attribute :date_field
9
- attribute :maybe_field
10
- attribute :hash_field
11
-
12
- belongs_to_content :model
13
- end
14
- end
15
- end
@@ -1,54 +0,0 @@
1
- require 'thor'
2
-
3
- module MarkdownRecord
4
-
5
- class Cli < Thor
6
- include ::Thor::Actions
7
-
8
- source_root File.expand_path("../../spec/dummy", __dir__)
9
-
10
- desc "install", "installs rendered comparison files for specs"
11
- def install
12
- # Install the gem in the dummy app
13
- system "cd spec/dummy && rails g markdown_record"
14
-
15
- # Run render_content thor task to generate content and save it
16
- system "cd spec/dummy && thor render_content:all -s"
17
-
18
- # Copy the generated content to the spec folder for the specs to use
19
- copy_file "markdown_record/rendered/content/part_1/chapter_1/content.html", "spec/rendered/chapter_1/content.html"
20
- copy_file "markdown_record/rendered/content/part_1/chapter_1/content.json", "spec/rendered/chapter_1/content.json"
21
- copy_file "markdown_record/rendered/content/part_1/chapter_1/content_fragments.json", "spec/rendered/chapter_1/content_fragments.json"
22
-
23
- copy_file "markdown_record/rendered/content/part_1/chapter_2/content.html", "spec/rendered/chapter_2/content.html"
24
- copy_file "markdown_record/rendered/content/part_1/chapter_2/content.json", "spec/rendered/chapter_2/content.json"
25
- copy_file "markdown_record/rendered/content/part_1/chapter_2/content_fragments.json", "spec/rendered/chapter_2/content_fragments.json"
26
-
27
- copy_file "markdown_record/rendered/content.html", "spec/rendered/concatenated/content.html"
28
- copy_file "markdown_record/rendered/content.json", "spec/rendered/concatenated/content.json"
29
- copy_file "markdown_record/rendered/content_fragments.json", "spec/rendered/concatenated/content_fragments.json"
30
-
31
- # Run render content again with a custom layout
32
- system "cd spec/dummy && thor render_content:all -s -l \"_custom_layout.html.erb\" -r full"
33
-
34
- # Copy the new files to the spec directory
35
- copy_file "markdown_record/rendered/content.html", "spec/rendered/custom_layout/content.html"
36
- copy_file "markdown_record/rendered/content/part_1/chapter_1/content.html", "spec/rendered/custom_layout/chapter_1/content.html"
37
-
38
- # Run render content again with a custom layout
39
- system "cd spec/dummy && thor render_file:json -s -f part_1/chapter_1/content.md"
40
- copy_file "markdown_record/rendered/content/part_1/chapter_1/content.json", "spec/rendered/chapter_1/no_frag_content.json"
41
-
42
- # Remove generated content and installed files
43
- FileUtils.remove_dir("spec/dummy/markdown_record/rendered", true)
44
- FileUtils.remove_dir("spec/dummy/markdown_record/content", true)
45
- FileUtils.remove_entry("spec/dummy/markdown_record/layouts/_concatenated_layout.html.erb", true)
46
- FileUtils.remove_entry("spec/dummy/markdown_record/layouts/_file_layout.html.erb", true)
47
- FileUtils.remove_entry("spec/dummy/lib/tasks/render_content.thor", true)
48
- FileUtils.remove_entry("spec/dummy/lib/tasks/render_file.thor", true)
49
- FileUtils.remove_entry("spec/dummy/config/initializers/markdown_record.rb", true)
50
- FileUtils.remove_entry("spec/dummy/Thorfile", true)
51
- gsub_file "spec/dummy/config/routes.rb", "\n mount MarkdownRecord::Engine, at: MarkdownRecord.config.mount_path, as: \"markdown_record\"", ""
52
- end
53
- end
54
- end
@@ -1,22 +0,0 @@
1
- module MarkdownRecord
2
- module ContentDsl
3
- module Attribute
4
- REGEX = /(?<!`|`\\n|`html\\n)<!--\s*attribute\s*:\s*(\w+)\s*(?::((?:html|md|int|float|string)?))?-->(?!`|\\n`)/
5
- ENCODED_REGEX = /(?<!<code>|<code class="html">)&lt;!--\s*attribute\s*:\s*(\w+)\s*(?::((?:html|md|int|float|string)?))?--&gt;(?!<\/code>)/
6
-
7
- def attribute_dsl(text)
8
- match = text.match(REGEX)
9
-
10
- if match
11
- [match[1].strip, match[2]&.strip]
12
- else
13
- nil
14
- end
15
- end
16
-
17
- def self.remove_dsl(text)
18
- text.gsub(ENCODED_REGEX, "")
19
- end
20
- end
21
- end
22
- end
@@ -1,23 +0,0 @@
1
- module MarkdownRecord
2
- module ContentDsl
3
- module Model
4
- TEMP = /<!--\s*model\s*({[\s"'\\\w:,.\[\]\{\}_\/]*})\s*-->/
5
- REGEX = /(?<!`|`\\n|`html\\n)<!--\s*model\s*({[\s"'\\\w:,.\[\]\{\}_\/]*})\s*-->(?!`|\\n`)/
6
- ENCODED_REGEX = /(?<!<code>|<code class="html">)&lt;!--\s*model\s*({[\s"'\\\w:,.\[\]\{\}_\/]*})\s*--&gt;(?!<\/code>)/
7
-
8
- def model_dsl(text)
9
- match = text.match(REGEX)
10
-
11
- if match
12
- JSON.parse(match[1])
13
- else
14
- nil
15
- end
16
- end
17
-
18
- def self.remove_dsl(text)
19
- text.gsub(ENCODED_REGEX, "")
20
- end
21
- end
22
- end
23
- end
@@ -1,22 +0,0 @@
1
- module MarkdownRecord
2
- module ContentDsl
3
- module RenderFormat
4
- REGEX = /(?<!`|`\\n|`html\\n)<!--\s*render_format\s*:\s*(.*)\s*-->(?!`|\\n`)/
5
- ENCODED_REGEX = /(?<!<code>|<code class="html">)&lt;!--\s*render_format\s*:\s*(.*)\s*--&gt;(?!<\/code>)/
6
-
7
- def render_format_dsl(text)
8
- match = text.match(REGEX)
9
-
10
- if match
11
- match[1].strip
12
- else
13
- nil
14
- end
15
- end
16
-
17
- def self.remove_dsl(text)
18
- text.gsub(ENCODED_REGEX, "")
19
- end
20
- end
21
- end
22
- end
@@ -1,22 +0,0 @@
1
- module MarkdownRecord
2
- module ContentDsl
3
- module RenderStrategy
4
- REGEX = /(?<!`|`\\n|`html\\n)<!--\s*render_strategy\s*:\s*(.*)\s*-->(?!`|\\n`)/
5
- ENCODED_REGEX = /(?<!<code>|<code class="html">)&lt;!--\s*render_strategy\s*:\s*(.*)\s*--&gt;(?!<\/code>)/
6
-
7
- def render_strategy_dsl(text)
8
- match = text.match(REGEX)
9
-
10
- if match
11
- match[1].strip
12
- else
13
- nil
14
- end
15
- end
16
-
17
- def self.remove_dsl(text)
18
- text.gsub(ENCODED_REGEX, "")
19
- end
20
- end
21
- end
22
- end
@@ -1,37 +0,0 @@
1
- require "markdown_record/content_dsl/model"
2
- require "markdown_record/content_dsl/attribute"
3
- require "markdown_record/content_dsl/end_attribute"
4
- require "markdown_record/content_dsl/end_model"
5
- require "markdown_record/content_dsl/fragment"
6
- require "markdown_record/content_dsl/directory_fragment"
7
- require "markdown_record/content_dsl/use_layout"
8
- require "markdown_record/content_dsl/disable"
9
- require "markdown_record/content_dsl/enable"
10
-
11
- module MarkdownRecord
12
- module ContentDsl
13
- include Model
14
- include Attribute
15
- include EndAttribute
16
- include EndModel
17
- include DirectoryFragment
18
- include Fragment
19
- include UseLayout
20
- include Disable
21
- include Enable
22
-
23
- HTML_COMMENT_REGEX = /(<!--(?:(?:\s|.)(?!-->))*(?:.|\s)-->)/
24
-
25
- def remove_dsl(text)
26
- text = Model.remove_dsl(text)
27
- text = Attribute.remove_dsl(text)
28
- text = EndAttribute.remove_dsl(text)
29
- text = EndModel.remove_dsl(text)
30
- text = Fragment.remove_dsl(text)
31
- text = UseLayout.remove_dsl(text)
32
- text = Disable.remove_dsl(text)
33
- text = Enable.remove_dsl(text)
34
- text
35
- end
36
- end
37
- end
@@ -1,194 +0,0 @@
1
- require "redcarpet"
2
- require "htmlbeautifier"
3
-
4
- module MarkdownRecord
5
- class HtmlRenderer < ::Redcarpet::Render::HTML
6
- include ::MarkdownRecord::PathUtilities
7
- include ::MarkdownRecord::ContentDsl
8
-
9
- HTML_SUBSTITUTIONS = {
10
- /<!---/ => "<!--",
11
- /&lt;!---/ => "&lt;!--"
12
- }
13
-
14
- def initialize(
15
- file_saver: ::MarkdownRecord::FileSaver.new)
16
- super(::MarkdownRecord.config.html_render_options)
17
- @indexer = ::MarkdownRecord::Indexer.new
18
- @markdown = ::Redcarpet::Markdown.new(self, ::MarkdownRecord.config.markdown_extensions)
19
- @file_saver = file_saver
20
- end
21
-
22
- def render_html_for_subdirectory(subdirectory: "", **options)
23
- content = @indexer.index(subdirectory: subdirectory)
24
- rendered_subdirectory = base_content_path.join(subdirectory)
25
-
26
- html_content = render_html_recursively(content, rendered_subdirectory.to_s)
27
- processed_html = process_html_recursively(html_content[rendered_subdirectory.to_s], rendered_subdirectory, options)
28
- final_html = finalize_html_recursively(processed_html, rendered_subdirectory)
29
-
30
- save_content_recursively(final_html, options, rendered_subdirectory)
31
- { :html_content => processed_html, :saved_files => @file_saver.saved_files }
32
- nil
33
- end
34
-
35
- private
36
-
37
- def render_html_recursively(file_or_directory, file_or_directory_name)
38
- case file_or_directory.class.name
39
- when Hash.name # if it is a directory
40
- directory_hash = { file_or_directory_name => {} } # hash representing the directory and its contents
41
-
42
- file_or_directory.each do |child_file_or_directory_name, child_file_or_directory|
43
- child_content_hash = render_html_recursively(child_file_or_directory, child_file_or_directory_name)
44
- directory_hash[file_or_directory_name].merge!(child_content_hash)
45
- end
46
-
47
- directory_hash
48
- when String.name # if it is a file
49
- { file_or_directory_name => render_html(file_or_directory) }
50
- end
51
- end
52
-
53
- def render_html(html)
54
- rendered_html = @markdown.render(html)
55
- remove_dsl(rendered_html)
56
- end
57
-
58
- def process_html_recursively(file_or_directory, full_path, options)
59
- case file_or_directory.class.name
60
- when Hash.name
61
- directory_hash = { full_path.to_s => {} } # hash representing the directory and its contents
62
-
63
- file_or_directory.each do |child_file_or_directory_name, child_file_or_directory|
64
- child_content_hash = process_html_recursively(child_file_or_directory, full_path.join(child_file_or_directory_name), options)
65
- directory_hash[full_path.to_s].merge!(child_content_hash)
66
- end
67
-
68
- if options[:concat]
69
- concatenated_html = concatenate_html_recursively(directory_hash[full_path.to_s], []).join("\r\n")
70
- directory_hash["#{full_path.to_s}.concat"] = process_html(concatenated_html, full_path.to_s, concatenated_layout)
71
- end
72
-
73
- directory_hash.compact
74
- when String.name # if it is a file
75
- if options[:deep]
76
- { full_path.to_s => process_html(file_or_directory, full_path, custom_layout(file_or_directory) || file_layout) }
77
- else
78
- { }
79
- end
80
- end
81
- end
82
-
83
- def process_html(html, full_path, layout = nil)
84
- processed_html = html.gsub(/<p>(\&lt;%(\S|\s)*?%\&gt;)<\/p>/){ CGI.unescapeHTML($1) }
85
- processed_html = processed_html.gsub(/(\&lt;%(\S|\s)*?%\&gt;)/){ CGI.unescapeHTML($1) }
86
- locals = erb_locals_from_path(full_path.to_s)
87
- processed_html = render_erb(processed_html, locals) if full_path.to_s.ends_with?(".md.erb")
88
- processed_html = render_erb(layout, locals.merge(html: processed_html)) if layout
89
- processed_html
90
- end
91
-
92
- def render_erb(html, locals)
93
- render_controller = ::MarkdownRecord.config.render_controller || ::ApplicationController
94
- rendered_html = render_controller.render(
95
- inline: html,
96
- locals: locals
97
- ).to_str
98
- rendered_html
99
- end
100
-
101
- def concatenate_html_recursively(content, html)
102
- case content.class.name
103
- when Hash.name
104
- sorted_hash_values(content).each do |v|
105
- concatenate_html_recursively(v, html)
106
- end
107
- when String.name
108
- html << content
109
- end
110
- html
111
- end
112
-
113
- def sorted_hash_values(hash)
114
- values = []
115
- keys = hash.keys.sort_by do |k|
116
- basename = k.split("/").last
117
- m = basename.match(/^(\d+)_/)
118
- m.try(:[], 1).to_i || k
119
- end
120
-
121
- keys.each do |k|
122
- values << hash[k] if !k.include?(".concat")
123
- end
124
- values
125
- end
126
-
127
- def custom_layout(html)
128
- layout = use_layout_dsl(html)
129
- return unless layout
130
-
131
- File.read(::MarkdownRecord.config.layout_directory.join(layout))
132
- end
133
-
134
- def global_layout
135
- global_layout_path = ::MarkdownRecord.config.global_layout_path
136
- @global_layout ||= global_layout_path ? layout(global_layout_path) : nil
137
- end
138
-
139
- def concatenated_layout
140
- concatenated_layout_path = ::MarkdownRecord.config.concatenated_layout_path
141
- @concatenated_layout ||= concatenated_layout_path ? layout(concatenated_layout_path) : nil
142
- end
143
-
144
- def file_layout
145
- file_layout_path = ::MarkdownRecord.config.file_layout_path
146
- @file_layout ||= file_layout_path ? layout(file_layout_path) : nil
147
- end
148
-
149
- def layout(path)
150
- File.read(::MarkdownRecord.config.layout_directory.join(path))
151
- end
152
-
153
- def finalize_html_recursively(content, rendered_subdirectory)
154
- case content.class.name
155
- when Hash.name
156
- final_hash = {}
157
- content.each do |key, value|
158
- child_path = rendered_subdirectory.join(key)
159
- final_hash[key] = finalize_html_recursively(value, child_path)
160
- end
161
- final_hash
162
- when String.name
163
- finalize_html(content, rendered_subdirectory)
164
- end
165
- end
166
-
167
- def finalize_html(html, full_path)
168
- locals = erb_locals_from_path(full_path)
169
- final_html = html
170
- final_html = render_erb(global_layout, locals.merge(html: final_html)) if global_layout
171
-
172
- HTML_SUBSTITUTIONS.each do |find, replace|
173
- final_html = final_html.gsub(find, replace)
174
- end
175
-
176
- final_html = final_html.squeeze("\n")
177
- final_html = HtmlBeautifier.beautify(final_html)
178
- final_html
179
- end
180
-
181
- def save_content_recursively(content, options, rendered_subdirectory)
182
- case content.class.name
183
- when Hash.name
184
- content.each do |key, value|
185
- child_path = rendered_subdirectory.join(key)
186
- save_content_recursively(value, options, child_path)
187
- end
188
- when String.name
189
- path = clean_path(rendered_subdirectory.to_s)
190
- @file_saver.save_to_file(content, "#{path}.html", options)
191
- end
192
- end
193
- end
194
- end
@@ -1,270 +0,0 @@
1
- require "redcarpet"
2
- require "redcarpet/render_strip"
3
-
4
- module MarkdownRecord
5
- class JsonRenderer
6
- include ::MarkdownRecord::PathUtilities
7
- include ::MarkdownRecord::ContentDsl
8
-
9
- def initialize(file_saver: ::MarkdownRecord::FileSaver.new)
10
- @indexer = MarkdownRecord::Indexer.new
11
- @json_models = {}
12
- @described_models = []
13
- @file_saver = file_saver
14
- end
15
-
16
- def render_models_for_subdirectory(subdirectory: "", **options)
17
- @directory_meta = {}
18
- content = @indexer.index(subdirectory: subdirectory)
19
-
20
- rendered_subdirectory = base_content_path.join(subdirectory)
21
- json_hash, _ = render_models_recursively(content, rendered_subdirectory, options)
22
-
23
- save_content_recursively(json_hash, options, Pathname.new(""))
24
- json_hash
25
- end
26
-
27
- def render_models_for_file(file_path:, **options)
28
- file = @indexer.file(file_path)
29
-
30
- # render json models
31
- json = render_models(file, file_path, options)
32
-
33
- # save json
34
- @file_saver.save_to_file(json.to_json, "#{clean_path(file_path)}.json", options)
35
-
36
- json
37
- end
38
-
39
- private
40
-
41
- def render_models_recursively(file_or_directory, full_path, options)
42
- case file_or_directory.class.name;
43
- when Hash.name # if it is a directory
44
- directory_hash, concat_hash = *render_nested_models(file_or_directory, full_path, options)
45
-
46
- # concatenate child hashes if :concat = true
47
- if options[:concat]
48
- concatenate_nested_models(full_path, directory_hash, concat_hash, options)
49
- end
50
-
51
- [directory_hash, concat_hash]
52
- when String.name # if it is a file
53
- content = render_models(file_or_directory, full_path, options)
54
-
55
- if options[:deep] && options[:render_content_fragment_json]
56
- add_content_fragment_for_file(content, full_path, false, @fragment_meta)
57
- end
58
-
59
- content_hash = { full_path.basename.to_s => content }
60
- result = [{}, {}]
61
- result[0] = content_hash if options[:deep]
62
- result[1] = content_hash if options[:concat]
63
- result
64
- end
65
- end
66
-
67
- def render_nested_models(file_or_directory, full_path, options)
68
- directory_hash = { full_path.basename.to_s => {} } # hash representing the directory and its contents
69
- concat_hash = { full_path.basename.to_s => {} }
70
- # iterate through directory contents
71
- file_or_directory.each do |child_file_or_directory_name, child_file_or_directory|
72
-
73
- # get full path for next recursion
74
- child_full_path = full_path.join(child_file_or_directory_name)
75
- # get response from next recursion
76
- child_content_hash, child_concat_hash = render_models_recursively(
77
- child_file_or_directory,
78
- child_full_path,
79
- options
80
- )
81
- # merge response hashes
82
- concat_hash[full_path.basename.to_s].merge!(child_concat_hash)
83
- directory_hash[full_path.basename.to_s].merge!(child_content_hash)
84
- end
85
-
86
- [directory_hash, concat_hash]
87
- end
88
-
89
- def concatenate_nested_models(full_path, directory_hash, concat_hash, options)
90
- concatenated_json = {}
91
- concatenate_json_recursively(concat_hash, concatenated_json)
92
-
93
- # add content fragments if render_content_fragment_json = true
94
- if options[:render_content_fragment_json]
95
- filename, subdirectory = full_path_to_parts(full_path)
96
- directory_meta = @directory_meta[clean_path("#{subdirectory}/#{filename}")] || {}
97
-
98
- add_content_fragment_for_file(concat_hash[full_path.basename.to_s], full_path, true, directory_meta)
99
- add_content_fragment_for_file(concatenated_json, full_path, true, directory_meta)
100
- end
101
-
102
- directory_hash["#{full_path.basename.to_s}.concat"] = concatenated_json
103
- end
104
-
105
- def render_models(content, full_path, options)
106
-
107
- filename, subdirectory = full_path_to_parts(full_path)
108
-
109
- @described_models = []
110
- @json_models = {}
111
- @fragment_meta = {}
112
- enabled = true
113
-
114
- content.split(HTML_COMMENT_REGEX).each do |text|
115
- dsl_command = HTML_COMMENT_REGEX =~ text
116
-
117
- if dsl_command
118
- enabled = false if disable_dsl(text)
119
- enabled = true if enable_dsl(text)
120
- end
121
-
122
- if dsl_command && enabled
123
- extract_model(text, filename, subdirectory)
124
- extract_fragment_meta(text, subdirectory)
125
- extract_attribute(text)
126
- pop_attribute(text)
127
- pop_model(text)
128
- else
129
- append_to_attribute(text)
130
- end
131
- end
132
-
133
- @described_models.each do |model|
134
- finalize_attribute(model)
135
- end
136
-
137
- @json_models.dup
138
- end
139
-
140
- def add_content_fragment_for_file(json, full_path, concatenated, meta)
141
- content_fragment = fragment_attributes_from_path(full_path).merge("meta" => meta, "concatenated" => concatenated)
142
-
143
- json["markdown_record/content_fragment"] ||= []
144
- json["markdown_record/content_fragment"] << content_fragment
145
- end
146
-
147
- def concatenate_json_recursively(directory_hash, concatenated_json)
148
- directory_hash.each do |key, value|
149
- case value.class.name
150
- when Hash.name
151
- concatenate_json_recursively(value, concatenated_json)
152
- when Array.name
153
- concatenated_json[key] ||= []
154
- concatenated_json[key] += value
155
- end
156
- end
157
- end
158
-
159
- def save_content_recursively(content, options, rendered_subdirectory)
160
- if content&.values&.first.is_a?(Array)
161
- fragments = content.slice("markdown_record/content_fragment")
162
- non_fragments = content.except("markdown_record/content_fragment")
163
- path = clean_path(rendered_subdirectory.to_s)
164
- @file_saver.save_to_file(non_fragments.to_json, "#{path}.json", options)
165
- @file_saver.save_to_file(fragments.to_json, "#{path}.json", options, true)
166
- else
167
- content.each do |key, value|
168
- child_path = rendered_subdirectory.join(key)
169
- save_content_recursively(value, options, child_path)
170
- end
171
- end
172
- end
173
-
174
- def extract_model(html, filename, subdirectory)
175
- model = model_dsl(html)
176
-
177
- if model
178
- return unless model["type"].present?
179
-
180
- model["subdirectory"] = clean_path(subdirectory)
181
- model["filename"] = clean_path(filename)
182
-
183
- # reset "type" to not have a prefix, in order to ensure
184
- # consistent results between the internal only :klass filter
185
- # which is used as an index in a hash (and has the prefix removed
186
- # for consistency and deterministic behavior)
187
- # and the externally filterable :type field.
188
- model["type"] = model["type"].delete_prefix("::").strip
189
-
190
- @json_models[model["type"]] ||= []
191
- @json_models[model["type"]] << model
192
-
193
- @described_models.push(model)
194
- end
195
- end
196
-
197
- def extract_fragment_meta(html, subdirectory)
198
- meta = fragment_dsl(html)
199
-
200
- if meta
201
- @fragment_meta = meta
202
- end
203
-
204
- directory_meta = directory_fragment_dsl(html)
205
-
206
- if directory_meta
207
- @directory_meta[subdirectory] = directory_meta
208
- end
209
- end
210
-
211
- def extract_attribute(text)
212
- attribute, type = attribute_dsl(text)
213
- type ||= "md"
214
-
215
- if @described_models.last && attribute
216
- model = @described_models.last
217
- model[:described_attribute_type] = type
218
- model[:described_attribute] = attribute
219
- model[model[:described_attribute]] = []
220
- end
221
- end
222
-
223
- def append_to_attribute(text)
224
- return if text.match(HTML_COMMENT_REGEX)
225
-
226
- @described_models.each do |model|
227
- next unless model[:described_attribute].present?
228
-
229
- model[model[:described_attribute]] ||= []
230
- model[model[:described_attribute]] << text
231
- end
232
- end
233
-
234
- def pop_attribute(text)
235
- if @described_models.last && end_attribute_dsl(text)
236
- finalize_attribute(@described_models.last)
237
- end
238
- end
239
-
240
- def pop_model(html)
241
- if end_model_dsl(html)
242
- @described_models.pop
243
- end
244
- end
245
-
246
- def finalize_attribute(model)
247
- return if model[:described_attribute].nil?
248
-
249
- attribute_name = model.delete(:described_attribute)
250
- attribute = model[attribute_name].join("\n")
251
- type = model.delete(:described_attribute_type)
252
-
253
- case type
254
- when "html"
255
- renderer = Redcarpet::Render::HTML.new
256
- attribute = Redcarpet::Markdown.new(renderer).render(attribute)
257
- when "md"
258
- attribute = attribute.strip
259
- when "int"
260
- attribute = attribute.strip.gsub("\n", "").to_i
261
- when "float"
262
- attribute = attribute.strip.gsub("\n", "").to_f
263
- when "string"
264
- attribute = Redcarpet::Markdown.new(::Redcarpet::Render::StripDown).render(attribute).strip
265
- end
266
-
267
- model[attribute_name] = attribute
268
- end
269
- end
270
- end