markdown_record 0.1.3
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +225 -0
- data/Rakefile +9 -0
- data/app/assets/config/markdown_cms_manifest.js +1 -0
- data/app/assets/stylesheets/markdown_cms/application.css +15 -0
- data/app/controllers/markdown_record/application_controller.rb +11 -0
- data/app/controllers/markdown_record/content_controller.rb +19 -0
- data/app/controllers/markdown_record/html_controller.rb +11 -0
- data/app/controllers/markdown_record/json_controller.rb +11 -0
- data/app/helpers/markdown_record/application_helper.rb +4 -0
- data/app/helpers/markdown_record/controller_helpers.rb +39 -0
- data/app/helpers/markdown_record/view_helpers.rb +84 -0
- data/app/models/markdown_record/demo/dsl_command.rb +10 -0
- data/app/models/markdown_record/demo/section.rb +9 -0
- data/app/models/markdown_record/tests/child_model.rb +15 -0
- data/app/models/markdown_record/tests/fake_active_record_model.rb +13 -0
- data/app/models/markdown_record/tests/model.rb +15 -0
- data/app/models/markdown_record/tests/other_child_model.rb +15 -0
- data/config/routes.rb +13 -0
- data/lib/generators/markdown_record_generator.rb +44 -0
- data/lib/markdown_record/association.rb +131 -0
- data/lib/markdown_record/associations.rb +106 -0
- data/lib/markdown_record/base.rb +25 -0
- data/lib/markdown_record/cli.rb +54 -0
- data/lib/markdown_record/configuration.rb +66 -0
- data/lib/markdown_record/content_associations.rb +46 -0
- data/lib/markdown_record/content_dsl/attribute.rb +22 -0
- data/lib/markdown_record/content_dsl/directory_fragment.rb +22 -0
- data/lib/markdown_record/content_dsl/disable.rb +22 -0
- data/lib/markdown_record/content_dsl/enable.rb +22 -0
- data/lib/markdown_record/content_dsl/end_attribute.rb +22 -0
- data/lib/markdown_record/content_dsl/end_model.rb +22 -0
- data/lib/markdown_record/content_dsl/fragment.rb +22 -0
- data/lib/markdown_record/content_dsl/model.rb +23 -0
- data/lib/markdown_record/content_dsl/render_format.rb +22 -0
- data/lib/markdown_record/content_dsl/render_strategy.rb +22 -0
- data/lib/markdown_record/content_dsl/use_layout.rb +22 -0
- data/lib/markdown_record/content_dsl.rb +37 -0
- data/lib/markdown_record/content_fragment.rb +123 -0
- data/lib/markdown_record/engine.rb +13 -0
- data/lib/markdown_record/errors/base.rb +6 -0
- data/lib/markdown_record/errors/duplicate_filename_error.rb +21 -0
- data/lib/markdown_record/errors/duplicate_id_error.rb +11 -0
- data/lib/markdown_record/errors/missing_parent_error.rb +11 -0
- data/lib/markdown_record/file_saver.rb +39 -0
- data/lib/markdown_record/html_renderer.rb +194 -0
- data/lib/markdown_record/indexer.rb +34 -0
- data/lib/markdown_record/json_renderer.rb +270 -0
- data/lib/markdown_record/model_inflator.rb +107 -0
- data/lib/markdown_record/path_utilities.rb +86 -0
- data/lib/markdown_record/rendering.rb +57 -0
- data/lib/markdown_record/routes_renderer.rb +0 -0
- data/lib/markdown_record/validator.rb +59 -0
- data/lib/markdown_record/version.rb +3 -0
- data/lib/markdown_record.rb +28 -0
- data/templates/Thorfile +3 -0
- data/templates/base/content/example_content.md +3 -0
- data/templates/base/layouts/_concatenated_layout.html.erb +8 -0
- data/templates/base/layouts/_custom_layout.html.erb +7 -0
- data/templates/base/layouts/_file_layout.html.erb +8 -0
- data/templates/base/layouts/_global_layout.html.erb +8 -0
- data/templates/demo/assets/images/ruby-logo.png +0 -0
- data/templates/demo/content/10_custom_models_and_associations.md.erb +78 -0
- data/templates/demo/content/11_controller_helpers.md.erb +20 -0
- data/templates/demo/content/12_configuration.md.erb +26 -0
- data/templates/demo/content/13_sandbox/1_foo.md +12 -0
- data/templates/demo/content/13_sandbox/2_sandbox_nested/1_bar.md +18 -0
- data/templates/demo/content/1_home.md.erb +40 -0
- data/templates/demo/content/2_installation.md.erb +129 -0
- data/templates/demo/content/3_rendering_basics.md.erb +71 -0
- data/templates/demo/content/4_content_dsl.md.erb +104 -0
- data/templates/demo/content/5_routes.md.erb +43 -0
- data/templates/demo/content/6_model_basics.md.erb +105 -0
- data/templates/demo/content/7_content_frags.md.erb +78 -0
- data/templates/demo/content/8_erb_syntax_and_view_helpers.md.erb +88 -0
- data/templates/demo/content/9_layouts.md.erb +15 -0
- data/templates/demo/layouts/_concatenated_layout.html.erb +12 -0
- data/templates/demo/layouts/_custom_layout.html.erb +14 -0
- data/templates/demo/layouts/_file_layout.html.erb +15 -0
- data/templates/demo/layouts/_global_layout.html.erb +108 -0
- data/templates/markdown_record_initializer.rb +3 -0
- data/templates/render_content.thor +57 -0
- metadata +270 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
require "markdown_record/errors/duplicate_id_error"
|
2
|
+
|
3
|
+
module MarkdownRecord
|
4
|
+
class Association
|
5
|
+
attr_accessor :base_filters
|
6
|
+
attr_accessor :search_filters
|
7
|
+
attr_reader :fulfilled
|
8
|
+
|
9
|
+
def initialize(base_filters, search_filters = {})
|
10
|
+
@base_filters = base_filters
|
11
|
+
@include_fragments = false
|
12
|
+
@search_filters = search_filters
|
13
|
+
@data = []
|
14
|
+
@fulfilled = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute
|
18
|
+
reset
|
19
|
+
final_filters = self.search_filters.merge(self.base_filters)
|
20
|
+
if @include_fragments
|
21
|
+
final_filters.merge!(:klass => ::MarkdownRecord::ContentFragment)
|
22
|
+
else
|
23
|
+
final_filters.merge!(:exclude_fragments => true)
|
24
|
+
end
|
25
|
+
results = MarkdownRecord::ModelInflator.new.where(final_filters)
|
26
|
+
@data.push(*results)
|
27
|
+
@fulfilled = true
|
28
|
+
end
|
29
|
+
|
30
|
+
def fragmentize
|
31
|
+
reset
|
32
|
+
@include_fragments = true
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_fragments
|
37
|
+
execute unless fulfilled
|
38
|
+
return @data if @include_fragments
|
39
|
+
|
40
|
+
@data.map { |m| m.fragment }
|
41
|
+
end
|
42
|
+
|
43
|
+
def reset
|
44
|
+
@data = []
|
45
|
+
@fulfilled = false
|
46
|
+
end
|
47
|
+
|
48
|
+
def all
|
49
|
+
execute unless fulfilled
|
50
|
+
@data
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_a
|
54
|
+
execute unless fulfilled
|
55
|
+
@data
|
56
|
+
end
|
57
|
+
|
58
|
+
def not(filters = {})
|
59
|
+
search_filters[:__not__] ||= {}
|
60
|
+
search_filters[:__not__].merge!(filters)
|
61
|
+
execute
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def where(filters = {})
|
66
|
+
search_filters.merge!(filters)
|
67
|
+
execute
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def each(...)
|
72
|
+
execute unless fulfilled
|
73
|
+
all unless fulfilled
|
74
|
+
@data.each(...)
|
75
|
+
end
|
76
|
+
|
77
|
+
def map(...)
|
78
|
+
execute unless fulfilled
|
79
|
+
execute
|
80
|
+
@data.map(...)
|
81
|
+
end
|
82
|
+
|
83
|
+
def count(...)
|
84
|
+
execute unless fulfilled
|
85
|
+
@data.count(...)
|
86
|
+
end
|
87
|
+
|
88
|
+
def any?(...)
|
89
|
+
execute unless fulfilled
|
90
|
+
@data.any?(...)
|
91
|
+
end
|
92
|
+
|
93
|
+
def empty?(...)
|
94
|
+
execute unless fulfilled
|
95
|
+
@data.empty?(...)
|
96
|
+
end
|
97
|
+
|
98
|
+
def first(...)
|
99
|
+
execute unless fulfilled
|
100
|
+
@data.first(...)
|
101
|
+
end
|
102
|
+
|
103
|
+
def last(...)
|
104
|
+
execute unless fulfilled
|
105
|
+
@data.first(...)
|
106
|
+
end
|
107
|
+
|
108
|
+
def second(...)
|
109
|
+
execute unless fulfilled
|
110
|
+
@data.second(...)
|
111
|
+
end
|
112
|
+
|
113
|
+
def third(...)
|
114
|
+
execute unless fulfilled
|
115
|
+
@data.third(...)
|
116
|
+
end
|
117
|
+
|
118
|
+
def fourth(...)
|
119
|
+
execute unless fulfilled
|
120
|
+
@data.fourth(...)
|
121
|
+
end
|
122
|
+
|
123
|
+
def __find__(id)
|
124
|
+
reset
|
125
|
+
search_filters.merge!({:id => id})
|
126
|
+
execute
|
127
|
+
raise ::MarkdownRecord::Errors::DuplicateIdError.new(@base_filters[:klass].name, id) if @data.count > 1
|
128
|
+
@data.first
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module MarkdownRecord
|
2
|
+
module Associations
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include ::MarkdownRecord::PathUtilities
|
5
|
+
|
6
|
+
class_methods do
|
7
|
+
def has_many_content(association, **options)
|
8
|
+
klass = infer_klass(association, options)
|
9
|
+
raise ArgumentError.new("The association class name could not be inferred, and was not provided") if klass.nil?
|
10
|
+
|
11
|
+
foreign_key = "#{self.name.demodulize.underscore}_id".to_sym
|
12
|
+
|
13
|
+
define_method(association) do
|
14
|
+
klass.new_association({:klass => klass, foreign_key => self.id})
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_one_content(association, **options)
|
19
|
+
klass = infer_klass(association, options)
|
20
|
+
raise ArgumentError.new("The association class name could not be inferred, and was not provided") if klass.nil?
|
21
|
+
|
22
|
+
foreign_key = "#{self.name.demodulize.underscore}_id".to_sym
|
23
|
+
|
24
|
+
define_method(association) do
|
25
|
+
results = klass.new_association({:klass => klass, foreign_key => self.id}).all
|
26
|
+
if results.count > 1
|
27
|
+
raise ArgumentError.new("Multiple MarkdownRecords belong to the same record in a has_one_content assocition: #{self.class.name} has a has_one_content #{association} and the following records ids were found #{ results.map(&:id) } ")
|
28
|
+
end
|
29
|
+
|
30
|
+
results.first
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def belongs_to_content(association, **options)
|
35
|
+
klass = infer_klass(association, options)
|
36
|
+
|
37
|
+
raise ArgumentError.new("The association class name could not be inferred, and was not provided") if klass.nil?
|
38
|
+
|
39
|
+
foreign_key = "#{association}_id".to_sym
|
40
|
+
self.attribute foreign_key unless self.attributes[foreign_key].present?
|
41
|
+
|
42
|
+
define_method(association) do
|
43
|
+
klass.find(self[foreign_key])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def all
|
48
|
+
new_association({ :klass => self }).all
|
49
|
+
end
|
50
|
+
|
51
|
+
def where(filters = {})
|
52
|
+
new_association({ :klass => self }, filters)
|
53
|
+
end
|
54
|
+
|
55
|
+
def find(id)
|
56
|
+
new_association({ :klass => self }).__find__(id)
|
57
|
+
end
|
58
|
+
|
59
|
+
def infer_klass(association, options)
|
60
|
+
class_name = options[:class_name]
|
61
|
+
class_name ||= association.to_s.singularize.camelize
|
62
|
+
|
63
|
+
if self.name.include?("::") && !class_name.include?("::")
|
64
|
+
klass = "#{self.name.split('::')[0...-1].join("::")}::#{class_name}".safe_constantize
|
65
|
+
end
|
66
|
+
|
67
|
+
klass ||= class_name.camelize.safe_constantize
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def siblings(filters = {})
|
72
|
+
self.class.new_association(filters.merge({:subdirectory => subdirectory}).merge!(not_self))
|
73
|
+
end
|
74
|
+
|
75
|
+
def class_siblings(filters = {})
|
76
|
+
self.class.new_association(filters.merge({:klass => self.class, :subdirectory => subdirectory, :__not__ => { :id => self.id }}))
|
77
|
+
end
|
78
|
+
|
79
|
+
def children(filters = {})
|
80
|
+
sub_start = "#{subdirectory}/".delete_prefix("/")
|
81
|
+
self.class.new_association(filters.merge({:subdirectory => Regexp.new("#{sub_start}[\\S|\\w]+")}).merge!(not_self))
|
82
|
+
end
|
83
|
+
|
84
|
+
def fragment
|
85
|
+
self.class.new_association.fragmentize.__find__(fragment_id)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def not_self
|
91
|
+
{
|
92
|
+
:__or__ => [
|
93
|
+
{ :__not__ => {
|
94
|
+
:id => self.id
|
95
|
+
}
|
96
|
+
},
|
97
|
+
{
|
98
|
+
:__not__ => {
|
99
|
+
:type => self.type
|
100
|
+
}
|
101
|
+
}
|
102
|
+
]
|
103
|
+
}
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "active_attr"
|
2
|
+
|
3
|
+
module MarkdownRecord
|
4
|
+
class Base
|
5
|
+
include ::ActiveAttr::Model
|
6
|
+
include ::MarkdownRecord::Associations
|
7
|
+
|
8
|
+
attribute :id
|
9
|
+
attribute :type, :type => String
|
10
|
+
attribute :subdirectory, :type => String
|
11
|
+
attribute :filename, :type => String
|
12
|
+
|
13
|
+
def self.new_association(base_filters = {}, search_filters = {})
|
14
|
+
MarkdownRecord::Association.new(base_filters, search_filters)
|
15
|
+
end
|
16
|
+
|
17
|
+
def fragment_id
|
18
|
+
Pathname.new(subdirectory).join(filename).to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.json_klass
|
22
|
+
name.underscore
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,54 @@
|
|
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
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module MarkdownRecord
|
2
|
+
class Configuration
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
attr_accessor :content_root
|
6
|
+
attr_accessor :rendered_content_root
|
7
|
+
attr_accessor :layout_directory
|
8
|
+
attr_accessor :concatenated_layout_path
|
9
|
+
attr_accessor :file_layout_path
|
10
|
+
attr_accessor :global_layout_path
|
11
|
+
attr_accessor :markdown_extensions
|
12
|
+
attr_accessor :html_render_options
|
13
|
+
attr_accessor :public_layout
|
14
|
+
attr_accessor :render_strategy
|
15
|
+
attr_accessor :html_routes
|
16
|
+
attr_accessor :json_routes
|
17
|
+
attr_accessor :content_routes
|
18
|
+
attr_accessor :mount_path
|
19
|
+
attr_accessor :render_content_fragment_json
|
20
|
+
attr_accessor :render_controller
|
21
|
+
attr_accessor :ignore_numeric_prefix
|
22
|
+
|
23
|
+
RENDER_STRATEGIES = [:full, :directory, :file]
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@content_root = Rails.root.join("markdown_record","content")
|
27
|
+
@rendered_content_root = Rails.root.join("markdown_record","rendered")
|
28
|
+
@layout_directory = Rails.root.join("markdown_record","layouts")
|
29
|
+
@html_render_options = {}
|
30
|
+
@markdown_extensions = { :fenced_code_blocks => true, :disable_indented_code_blocks => true, :no_intra_emphasis => true}
|
31
|
+
@concatenated_layout_path = "_concatenated_layout.html.erb"
|
32
|
+
@file_layout_path = "_file_layout.html.erb"
|
33
|
+
@global_layout_path = "_global_layout.html.erb"
|
34
|
+
@public_layout = "layouts/application"
|
35
|
+
@render_strategy = :full
|
36
|
+
@html_routes = [:show]
|
37
|
+
@json_routes = [:show]
|
38
|
+
@content_routes = [:show]
|
39
|
+
@mount_path = "mdr"
|
40
|
+
@render_content_fragment_json = true
|
41
|
+
@render_controller = nil
|
42
|
+
@ignore_numeric_prefix = true
|
43
|
+
end
|
44
|
+
|
45
|
+
def render_strategy_options(strategy = nil)
|
46
|
+
strat = strategy || @render_strategy
|
47
|
+
|
48
|
+
unless RENDER_STRATEGIES.include?(strat)
|
49
|
+
raise ::ArgumentError.new("Invalide render strategy.")
|
50
|
+
end
|
51
|
+
|
52
|
+
case strategy || @render_strategy
|
53
|
+
when :full
|
54
|
+
{:concat => true, :deep => true}
|
55
|
+
when :directory
|
56
|
+
{:concat => true, :deep => false}
|
57
|
+
when :file
|
58
|
+
{:concat => false, :deep => true}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def routing
|
63
|
+
{ :html => html_routes, :json => json_routes, :content => content_routes}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module MarkdownRecord
|
2
|
+
module ContentAssociations
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def has_many_content(association, **options)
|
7
|
+
klass = infer_klass(association, options)
|
8
|
+
raise ArgumentError.new("The association class name could not be inferred, and was not provided") if klass.nil?
|
9
|
+
|
10
|
+
foreign_key = "#{association.to_s.singularize}_ids"
|
11
|
+
raise ArgumentError.new("#{self} does not have the #{foreign_key} attribute required for this association.") unless self.attribute_names.include?(foreign_key)
|
12
|
+
|
13
|
+
define_method(association) do
|
14
|
+
klass.new_association({:klass => klass, :id => self[foreign_key.to_sym]})
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_one_content(association, **options)
|
19
|
+
belongs_to_content(association, **options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def belongs_to_content(association, **options)
|
23
|
+
klass = infer_klass(association, options)
|
24
|
+
raise ArgumentError.new("The association class name could not be inferred, and was not provided") if klass.nil?
|
25
|
+
|
26
|
+
foreign_key = "#{association.to_s.singularize}_id"
|
27
|
+
raise ArgumentError.new("#{self} does not have the #{foreign_key} attribute required for this association.") unless self.attribute_names.include?(foreign_key)
|
28
|
+
|
29
|
+
define_method(association) do
|
30
|
+
klass.find(self[foreign_key])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def infer_klass(association, options)
|
35
|
+
class_name = options[:class_name]
|
36
|
+
class_name ||= association.to_s.singularize.camelize
|
37
|
+
|
38
|
+
if self.name.include?("::") && !class_name.include?("::")
|
39
|
+
klass = "#{self.name.split('::')[0...-1].join("::")}::#{class_name}".safe_constantize
|
40
|
+
end
|
41
|
+
|
42
|
+
klass ||= class_name.camelize.safe_constantize
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,22 @@
|
|
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">)<!--\s*attribute\s*:\s*(\w+)\s*(?::((?:html|md|int|float|string)?))?-->(?!<\/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
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MarkdownRecord
|
2
|
+
module ContentDsl
|
3
|
+
module DirectoryFragment
|
4
|
+
REGEX = /(?<!`|`\\n|`html\\n)<!--\s*directory_fragment\s*({[\s"'\\\w:,.\[\]\{\}_\/]*})\s*-->(?!`|\\n`)/
|
5
|
+
ENCODED_REGEX = /(?<!<code>|<code class="html">)<!--\s*directory_fragment\s*({[\s"'\\\w:,.\[\]\{\}_\/]*})\s*-->(?!<\/code>)/
|
6
|
+
|
7
|
+
def directory_fragment_dsl(text)
|
8
|
+
match = text.match(REGEX)
|
9
|
+
|
10
|
+
if match
|
11
|
+
JSON.parse(match[1])
|
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
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MarkdownRecord
|
2
|
+
module ContentDsl
|
3
|
+
module Disable
|
4
|
+
REGEX = /<!--\s*disable\s*-->/
|
5
|
+
ENCODED_REGEX = /(?<!<code>|<code class="html">)<!--\s*disable\s*-->(?!<\/code>)/
|
6
|
+
|
7
|
+
def disable_dsl(text)
|
8
|
+
match = text.match(REGEX)
|
9
|
+
|
10
|
+
if match
|
11
|
+
true
|
12
|
+
else
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.remove_dsl(text)
|
18
|
+
text.gsub(ENCODED_REGEX, "")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MarkdownRecord
|
2
|
+
module ContentDsl
|
3
|
+
module Enable
|
4
|
+
REGEX = /<!--\s*enable\s*-->/
|
5
|
+
ENCODED_REGEX = /(?<!<code>|<code class="html">)<!--\s*enable\s*-->(?!<\/code>)/
|
6
|
+
|
7
|
+
def enable_dsl(text)
|
8
|
+
match = text.match(REGEX)
|
9
|
+
|
10
|
+
if match
|
11
|
+
true
|
12
|
+
else
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.remove_dsl(text)
|
18
|
+
text.gsub(ENCODED_REGEX, "")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MarkdownRecord
|
2
|
+
module ContentDsl
|
3
|
+
module EndAttribute
|
4
|
+
REGEX = /(?<!`|`\\n|`html\\n)<!--\s*end_attribute\s*-->(?!`|\\n`)/
|
5
|
+
ENCODED_REGEX = /(?<!<code>|<code class="html">)<!--\s*end_attribute\s*-->(?!<\/code>)/
|
6
|
+
|
7
|
+
def end_attribute_dsl(text)
|
8
|
+
match = text.match(REGEX)
|
9
|
+
|
10
|
+
if match
|
11
|
+
true
|
12
|
+
else
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.remove_dsl(text)
|
18
|
+
text.gsub(ENCODED_REGEX, "")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MarkdownRecord
|
2
|
+
module ContentDsl
|
3
|
+
module EndModel
|
4
|
+
REGEX = /(?<!`|`\\n|`html\\n)<!--\s*end_model\s*-->(?!`|\\n`)/
|
5
|
+
ENCODED_REGEX = /(?<!<code>|<code class="html">)<!--\s*end_model\s*-->(?!<\/code>)/
|
6
|
+
|
7
|
+
def end_model_dsl(text)
|
8
|
+
match = text.match(REGEX)
|
9
|
+
|
10
|
+
if match
|
11
|
+
true
|
12
|
+
else
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.remove_dsl(text)
|
18
|
+
text.gsub(ENCODED_REGEX, "")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MarkdownRecord
|
2
|
+
module ContentDsl
|
3
|
+
module Fragment
|
4
|
+
REGEX = /(?<!`|`\\n|`html\\n)<!--\s*fragment\s*({[\s"'\\\w:,.\[\]\{\}_\/]*})\s*-->(?!`|\\n`)/
|
5
|
+
ENCODED_REGEX = /(?<!<code>|<code class="html">)<!--\s*fragment\s+({[\s"'\\\w:,.\[\]\{\}_\/]*})\s*-->(?!<\/code>)/
|
6
|
+
|
7
|
+
def fragment_dsl(text)
|
8
|
+
match = text.match(REGEX)
|
9
|
+
|
10
|
+
if match
|
11
|
+
JSON.parse(match[1])
|
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
|
@@ -0,0 +1,23 @@
|
|
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">)<!--\s*model\s*({[\s"'\\\w:,.\[\]\{\}_\/]*})\s*-->(?!<\/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
|
@@ -0,0 +1,22 @@
|
|
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">)<!--\s*render_format\s*:\s*(.*)\s*-->(?!<\/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
|
@@ -0,0 +1,22 @@
|
|
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">)<!--\s*render_strategy\s*:\s*(.*)\s*-->(?!<\/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
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MarkdownRecord
|
2
|
+
module ContentDsl
|
3
|
+
module UseLayout
|
4
|
+
REGEX = /(?<!`|`\\n|`html\\n)<!--\s*use_layout\s*:\s*(.*)\s*-->(?!`|\\n`)/
|
5
|
+
ENCODED_REGEX = /(?<!<code>|<code class="html">)<!--\s*use_layout\s*:\s*(.*)\s*-->(?!<\/code>)/
|
6
|
+
|
7
|
+
def use_layout_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
|