markdown_record 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|