brief 1.2.0 → 1.3.0
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 +4 -4
- data/CHANGELOG.md +70 -0
- data/Gemfile.lock +2 -2
- data/README.md +143 -70
- data/lib/brief/adapters/middleman.rb +2 -3
- data/lib/brief/briefcase.rb +28 -5
- data/lib/brief/cli/change.rb +7 -7
- data/lib/brief/cli/init.rb +10 -11
- data/lib/brief/cli/write.rb +23 -0
- data/lib/brief/configuration.rb +3 -4
- data/lib/brief/document/content_extractor.rb +2 -4
- data/lib/brief/document/front_matter.rb +4 -4
- data/lib/brief/document/rendering.rb +7 -9
- data/lib/brief/document/section/builder.rb +10 -10
- data/lib/brief/document/section/mapping.rb +5 -11
- data/lib/brief/document/section.rb +2 -3
- data/lib/brief/document/structure.rb +14 -15
- data/lib/brief/document/templating.rb +14 -0
- data/lib/brief/document.rb +20 -9
- data/lib/brief/document_mapper.rb +10 -10
- data/lib/brief/dsl.rb +5 -6
- data/lib/brief/model/definition.rb +37 -13
- data/lib/brief/model/persistence.rb +0 -2
- data/lib/brief/model.rb +50 -21
- data/lib/brief/repository.rb +2 -3
- data/lib/brief/util.rb +4 -4
- data/lib/brief/version.rb +1 -1
- data/lib/brief.rb +47 -33
- data/spec/fixtures/example/brief.rb +3 -0
- data/spec/fixtures/example/models/epic.rb +43 -5
- data/spec/fixtures/example/templates/user_story.md.erb +22 -0
- data/spec/lib/brief/briefcase_spec.rb +1 -1
- data/spec/lib/brief/dsl_spec.rb +1 -1
- data/spec/lib/brief/model_spec.rb +6 -1
- data/spec/lib/brief/persistence_spec.rb +1 -1
- data/spec/lib/brief/repository_spec.rb +1 -1
- data/spec/lib/brief/template_spec.rb +44 -0
- data/spec/spec_helper.rb +2 -2
- metadata +7 -2
@@ -1,15 +1,15 @@
|
|
1
1
|
class Brief::Document::Section
|
2
2
|
class Builder
|
3
|
-
def self.run(source, options={})
|
3
|
+
def self.run(source, options = {})
|
4
4
|
new(source, options).to_fragment
|
5
5
|
end
|
6
6
|
|
7
7
|
attr_accessor :source, :nodes, :low, :high
|
8
8
|
|
9
|
-
def initialize(source, options={})
|
9
|
+
def initialize(source, options = {})
|
10
10
|
@source = source.map do |item|
|
11
11
|
level, group = item
|
12
|
-
[level, group.map {|f| f.is_a?(String) ? Nokogiri::HTML.fragment(f) : f }]
|
12
|
+
[level, group.map { |f| f.is_a?(String) ? Nokogiri::HTML.fragment(f) : f }]
|
13
13
|
end
|
14
14
|
|
15
15
|
@low = options.fetch(:low, 1)
|
@@ -28,7 +28,7 @@ class Brief::Document::Section
|
|
28
28
|
next_level, next_fragments = source[n]
|
29
29
|
|
30
30
|
if next_level && (next_level == level) && (level > low)
|
31
|
-
new_fragment = (fragments + next_fragments).map(&:to_html).join(
|
31
|
+
new_fragment = (fragments + next_fragments).map(&:to_html).join('')
|
32
32
|
source[index] = [level, [Nokogiri::HTML.fragment(new_fragment)]]
|
33
33
|
source[n] = nil
|
34
34
|
end
|
@@ -49,7 +49,7 @@ class Brief::Document::Section
|
|
49
49
|
next_level, next_fragment = source[n]
|
50
50
|
|
51
51
|
if fragment && next_level && (next_level > level)
|
52
|
-
parent = fragment.css(
|
52
|
+
parent = fragment.css('section, article').first
|
53
53
|
parent.add_child(next_fragment)
|
54
54
|
source[index] = [level, fragment]
|
55
55
|
source[n] = nil
|
@@ -63,14 +63,14 @@ class Brief::Document::Section
|
|
63
63
|
|
64
64
|
self.nodes = source.map(&:last)
|
65
65
|
|
66
|
-
|
67
|
-
parent = node.css(
|
66
|
+
nodes.each do |node|
|
67
|
+
parent = node.css('section, article').first
|
68
68
|
if %w(h1 h2 h3 h4 h5 h6).include?(parent.children.first.name)
|
69
69
|
parent['data-heading'] = parent.children.first.text
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
|
73
|
+
nodes.map!(&:to_html)
|
74
74
|
end
|
75
75
|
|
76
76
|
def maxed_out?
|
@@ -82,8 +82,8 @@ class Brief::Document::Section
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def to_fragment
|
85
|
-
@html = nodes.join(
|
86
|
-
Nokogiri::HTML.fragment(@html ||
|
85
|
+
@html = nodes.join('') unless nodes.empty?
|
86
|
+
Nokogiri::HTML.fragment(@html || '<div/>')
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class Brief::Document::Section
|
2
2
|
class Mapping
|
3
|
-
def initialize(title, options={})
|
3
|
+
def initialize(title, options = {})
|
4
4
|
@title = title
|
5
5
|
@options = options
|
6
6
|
@config = {}.to_mash
|
@@ -14,23 +14,17 @@ class Brief::Document::Section
|
|
14
14
|
config.selectors
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
@config
|
19
|
-
end
|
17
|
+
attr_reader :config
|
20
18
|
|
21
|
-
|
22
|
-
@options
|
23
|
-
end
|
19
|
+
attr_reader :options
|
24
20
|
|
25
|
-
|
26
|
-
@title
|
27
|
-
end
|
21
|
+
attr_reader :title
|
28
22
|
|
29
23
|
def selector
|
30
24
|
@selector || :next
|
31
25
|
end
|
32
26
|
|
33
|
-
def each(*args, &
|
27
|
+
def each(*args, &_block)
|
34
28
|
@selector = args.first
|
35
29
|
self
|
36
30
|
end
|
@@ -19,10 +19,10 @@ class Brief::Document::Section
|
|
19
19
|
settings = config.selector_config[selector]
|
20
20
|
|
21
21
|
if Headings.include?(selector)
|
22
|
-
headings = fragment.css(
|
22
|
+
headings = fragment.css('article > h2')
|
23
23
|
articles = headings.map(&:parent)
|
24
24
|
|
25
|
-
|
25
|
+
unless settings.empty?
|
26
26
|
articles.compact.each do |article|
|
27
27
|
data.push(settings.inject({}.to_mash) do |memo, pair|
|
28
28
|
attribute, selector = pair
|
@@ -38,4 +38,3 @@ class Brief::Document::Section
|
|
38
38
|
@items = data
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
@@ -2,7 +2,7 @@ module Brief
|
|
2
2
|
class Document::Structure
|
3
3
|
attr_accessor :fragment, :content_lines
|
4
4
|
|
5
|
-
def initialize(fragment,content_lines=[])
|
5
|
+
def initialize(fragment, content_lines = [])
|
6
6
|
@fragment = fragment
|
7
7
|
@content_lines = content_lines
|
8
8
|
end
|
@@ -12,7 +12,7 @@ module Brief
|
|
12
12
|
if line.match(/^#/)
|
13
13
|
line = line.strip
|
14
14
|
level = line.count('#')
|
15
|
-
text = line.gsub('#','').strip
|
15
|
+
text = line.gsub('#', '').strip
|
16
16
|
|
17
17
|
if level > 0 && text.length > 0
|
18
18
|
line_number = index + 1
|
@@ -55,16 +55,16 @@ module Brief
|
|
55
55
|
|
56
56
|
mapping.map! do |item|
|
57
57
|
level, group = item
|
58
|
-
group.reject! {|i| i.text == "\n" }
|
58
|
+
group.reject! { |i| i.text == "\n" }
|
59
59
|
|
60
60
|
if level == 0
|
61
|
-
base_fragment = fragment = Nokogiri::HTML.fragment("<div class='brief top level'>#{ group.map(&:to_html).join(
|
61
|
+
base_fragment = fragment = Nokogiri::HTML.fragment("<div class='brief top level'>#{ group.map(&:to_html).join('') }</div>")
|
62
62
|
elsif level <= lowest_level
|
63
|
-
fragment = Nokogiri::HTML.fragment("<section>#{ group.map(&:to_html).join(
|
63
|
+
fragment = Nokogiri::HTML.fragment("<section>#{ group.map(&:to_html).join('') }</section>")
|
64
64
|
elsif level > lowest_level
|
65
65
|
# should be able to look at the document section mappings and
|
66
66
|
# apply custom css classes to these based on the name of the section
|
67
|
-
fragment = Nokogiri::HTML.fragment("<article>#{ group.map(&:to_html).join(
|
67
|
+
fragment = Nokogiri::HTML.fragment("<article>#{ group.map(&:to_html).join('') }</article>")
|
68
68
|
end
|
69
69
|
|
70
70
|
[level, [fragment]]
|
@@ -74,9 +74,9 @@ module Brief
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def levels
|
77
|
-
l = fragment.css(
|
77
|
+
l = fragment.css('[data-level]').map { |el| el.attr('data-level').to_i }
|
78
78
|
l.reject!(&:nil?)
|
79
|
-
l.reject! {|v| v.to_i == 0 }
|
79
|
+
l.reject! { |v| v.to_i == 0 }
|
80
80
|
l.uniq!
|
81
81
|
l
|
82
82
|
end
|
@@ -89,8 +89,8 @@ module Brief
|
|
89
89
|
levels.min
|
90
90
|
end
|
91
91
|
|
92
|
-
def headings_at_level(level, options={})
|
93
|
-
matches = heading_elements.select {|el| el.level.to_i == level.to_i }
|
92
|
+
def headings_at_level(level, options = {})
|
93
|
+
matches = heading_elements.select { |el| el.level.to_i == level.to_i }
|
94
94
|
|
95
95
|
if options[:text]
|
96
96
|
matches.map(&:text)
|
@@ -101,9 +101,8 @@ module Brief
|
|
101
101
|
|
102
102
|
def heading_with_text(text)
|
103
103
|
headings_with_text(text).tap do |results|
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
fail 'no section found with content: ' + text if results.length == 0
|
105
|
+
fail 'more than one section found with content: ' + text if results.length >= 2
|
107
106
|
end.first
|
108
107
|
end
|
109
108
|
|
@@ -120,7 +119,7 @@ module Brief
|
|
120
119
|
end
|
121
120
|
|
122
121
|
def heading_elements
|
123
|
-
@heading_elements ||= fragment.css(
|
122
|
+
@heading_elements ||= fragment.css('h1,h2,h3,h4,h5,h6').map do |el|
|
124
123
|
if el.attr('data-level').to_i > 0
|
125
124
|
{
|
126
125
|
level: el.attr('data-level'),
|
@@ -138,7 +137,7 @@ module Brief
|
|
138
137
|
end
|
139
138
|
|
140
139
|
def level(element)
|
141
|
-
element.name.to_s.gsub(/^h/i,'').to_i
|
140
|
+
element.name.to_s.gsub(/^h/i, '').to_i
|
142
141
|
end
|
143
142
|
end
|
144
143
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Brief::Document::Templating
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
def generate_content
|
5
|
+
model_class.generate_template_content_from(@frontmatter)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def create_from_data(data = {})
|
10
|
+
data = data.to_mash if data.is_a?(Hash)
|
11
|
+
new(data)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/brief/document.rb
CHANGED
@@ -2,19 +2,27 @@ module Brief
|
|
2
2
|
class Document
|
3
3
|
include Brief::Document::Rendering
|
4
4
|
include Brief::Document::FrontMatter
|
5
|
+
include Brief::Document::Templating
|
5
6
|
|
6
7
|
attr_accessor :path, :content, :frontmatter, :raw_content
|
7
8
|
|
8
|
-
def initialize(path, options={})
|
9
|
-
|
10
|
-
|
9
|
+
def initialize(path, options = {})
|
10
|
+
if path.respond_to?(:key?) && options.empty?
|
11
|
+
@frontmatter = path.to_mash
|
12
|
+
else
|
13
|
+
@path = Pathname(path)
|
14
|
+
end
|
15
|
+
|
16
|
+
@options = options.to_mash
|
11
17
|
|
12
|
-
if self.path.exist?
|
18
|
+
if @path && self.path.exist?
|
13
19
|
@raw_content = path.read
|
14
20
|
load_frontmatter
|
21
|
+
elsif options[:contents]
|
22
|
+
@raw_content = options[:contents]
|
15
23
|
end
|
16
24
|
|
17
|
-
|
25
|
+
model_class.try(:models).try(:<<, to_model) unless model_instance_registered?
|
18
26
|
end
|
19
27
|
|
20
28
|
def data
|
@@ -34,6 +42,10 @@ module Brief
|
|
34
42
|
@sections
|
35
43
|
end
|
36
44
|
|
45
|
+
def content
|
46
|
+
@content || generate_content
|
47
|
+
end
|
48
|
+
|
37
49
|
# Shortcut for querying the rendered HTML by css selectors.
|
38
50
|
#
|
39
51
|
# This will allow for model data attributes to be pulled from the
|
@@ -87,8 +99,8 @@ module Brief
|
|
87
99
|
# and ensures that there is a 1-1 relationship between a document path
|
88
100
|
# and the model.
|
89
101
|
def model_instance_registered?
|
90
|
-
|
91
|
-
model.path ==
|
102
|
+
model_class && model_class.models.any? do |model|
|
103
|
+
model.path == path
|
92
104
|
end
|
93
105
|
end
|
94
106
|
|
@@ -97,7 +109,7 @@ module Brief
|
|
97
109
|
end
|
98
110
|
|
99
111
|
def structure
|
100
|
-
@structure_analyzer ||= Brief::Document::Structure.new(fragment,
|
112
|
+
@structure_analyzer ||= Brief::Document::Structure.new(fragment, raw_content.lines.to_a)
|
101
113
|
end
|
102
114
|
|
103
115
|
def parser
|
@@ -120,4 +132,3 @@ module Brief
|
|
120
132
|
end
|
121
133
|
end
|
122
134
|
end
|
123
|
-
|
@@ -20,7 +20,7 @@ module Brief::DocumentMapper
|
|
20
20
|
|
21
21
|
def initialize(opts = {})
|
22
22
|
unless Brief::DocumentMapper::VALID_OPERATORS.include?(opts[:operator])
|
23
|
-
|
23
|
+
fail 'Operator not supported'
|
24
24
|
end
|
25
25
|
|
26
26
|
@attribute, @operator = opts[:attribute], opts[:operator]
|
@@ -36,18 +36,18 @@ module Brief::DocumentMapper
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def where(constraints_hash)
|
39
|
-
selector_hash = constraints_hash.reject { |key,
|
40
|
-
symbol_hash = constraints_hash.reject { |key,
|
39
|
+
selector_hash = constraints_hash.reject { |key, _value| !key.is_a? Selector }
|
40
|
+
symbol_hash = constraints_hash.reject { |key, _value| key.is_a? Selector }
|
41
41
|
symbol_hash.each do |attribute, value|
|
42
|
-
selector = Selector.new(:
|
43
|
-
selector_hash.update(
|
42
|
+
selector = Selector.new(attribute: attribute, operator: 'equal')
|
43
|
+
selector_hash.update(selector => value)
|
44
44
|
end
|
45
45
|
@where.merge! selector_hash
|
46
46
|
self
|
47
47
|
end
|
48
48
|
|
49
49
|
def order_by(field)
|
50
|
-
@order_by = field.is_a?(Symbol) ? {field => :asc} : field
|
50
|
+
@order_by = field.is_a?(Symbol) ? { field => :asc } : field
|
51
51
|
self
|
52
52
|
end
|
53
53
|
|
@@ -62,11 +62,11 @@ module Brief::DocumentMapper
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def first
|
65
|
-
|
65
|
+
all.first
|
66
66
|
end
|
67
67
|
|
68
68
|
def last
|
69
|
-
|
69
|
+
all.last
|
70
70
|
end
|
71
71
|
|
72
72
|
def run_query
|
@@ -129,7 +129,7 @@ module Brief::DocumentMapper
|
|
129
129
|
end
|
130
130
|
|
131
131
|
def inspect
|
132
|
-
"Query: #{ @where.map {|k,v| "#{k.attribute} #{k.operator} #{v}" }}"
|
132
|
+
"Query: #{ @where.map { |k, v| "#{k.attribute} #{k.operator} #{v}" }}"
|
133
133
|
end
|
134
134
|
|
135
135
|
def method_missing(meth, *args, &block)
|
@@ -153,7 +153,7 @@ class Symbol
|
|
153
153
|
|
154
154
|
unless method_defined?(:"<=>")
|
155
155
|
def <=>(other)
|
156
|
-
|
156
|
+
to_s <=> other.to_s
|
157
157
|
end
|
158
158
|
end
|
159
159
|
end
|
data/lib/brief/dsl.rb
CHANGED
@@ -2,8 +2,7 @@ module Brief
|
|
2
2
|
module DSL
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
def config(options={}, &block)
|
6
|
-
Brief::Configuration.instance.load_options(options) unless options.nil? || options.empty?
|
5
|
+
def config(options = {}, &block)
|
7
6
|
Brief::Configuration.instance.instance_eval(&block) if block_given?
|
8
7
|
end
|
9
8
|
|
@@ -23,7 +22,7 @@ module Brief
|
|
23
22
|
#
|
24
23
|
# this will find all of the Post models from the documents matching PATH_GLOB
|
25
24
|
# and call the publish method on them
|
26
|
-
def action(identifier,
|
25
|
+
def action(identifier, _options = {}, &block)
|
27
26
|
Object.class.class_eval do
|
28
27
|
command "#{identifier}" do |c|
|
29
28
|
c.syntax = "brief #{identifier}"
|
@@ -32,16 +31,16 @@ module Brief
|
|
32
31
|
c.action do |args, opts|
|
33
32
|
briefcase = Brief.case
|
34
33
|
|
35
|
-
path_args = args.select {|arg| arg.is_a?(String) && arg.match(/\.md$/) }
|
34
|
+
path_args = args.select { |arg| arg.is_a?(String) && arg.match(/\.md$/) }
|
36
35
|
|
37
36
|
path_args.select! do |arg|
|
38
37
|
path = briefcase.repository.root.join(arg)
|
39
38
|
path.exist?
|
40
39
|
end
|
41
40
|
|
42
|
-
path_args.map! {|p| briefcase.repository.root.join(p) }
|
41
|
+
path_args.map! { |p| briefcase.repository.root.join(p) }
|
43
42
|
|
44
|
-
models = path_args.map {|path| Brief::Document.new(path) }.map(&:to_model)
|
43
|
+
models = path_args.map { |path| Brief::Document.new(path) }.map(&:to_model)
|
45
44
|
|
46
45
|
block.call(Brief.case, models, opts)
|
47
46
|
end
|
@@ -6,15 +6,17 @@ module Brief
|
|
6
6
|
:content_schema,
|
7
7
|
:options,
|
8
8
|
:defined_helpers,
|
9
|
-
:section_mappings
|
9
|
+
:section_mappings,
|
10
|
+
:template_body,
|
11
|
+
:example_body
|
10
12
|
|
11
|
-
def initialize(name, options={})
|
13
|
+
def initialize(name, options = {})
|
12
14
|
@name = name
|
13
15
|
@options = options
|
14
|
-
@type_alias = options.fetch(:type_alias) { name.downcase.parameterize.gsub(/-/,'_') }
|
16
|
+
@type_alias = options.fetch(:type_alias) { name.downcase.parameterize.gsub(/-/, '_') }
|
15
17
|
@metadata_schema = {}.to_mash
|
16
18
|
@section_mappings = {}.to_mash
|
17
|
-
@content_schema = {attributes:{}}.to_mash
|
19
|
+
@content_schema = { attributes: {} }.to_mash
|
18
20
|
@model_class = options[:model_class]
|
19
21
|
end
|
20
22
|
|
@@ -44,13 +46,19 @@ module Brief
|
|
44
46
|
def apply_config
|
45
47
|
# define a virtus attribute mapping
|
46
48
|
metadata_schema.values.each do |settings|
|
47
|
-
|
49
|
+
begin
|
50
|
+
settings[:args] = Array(settings[:args])
|
51
|
+
settings[:args][1] = String if settings[:args][1] == ''
|
52
|
+
model_class.send(:attribute, *(settings[:args]))
|
53
|
+
rescue => e
|
54
|
+
raise "Error in metadata schema definition.\n #{ settings.inspect } \n\n #{e.message}"
|
55
|
+
end
|
48
56
|
end
|
49
57
|
|
50
58
|
# defined helpers adds an anonymous module include
|
51
|
-
Array(
|
59
|
+
Array(defined_helpers).each { |mod| model_class.send(:include, mod) }
|
52
60
|
|
53
|
-
model_class.defined_actions += Array(
|
61
|
+
model_class.defined_actions += Array(defined_actions)
|
54
62
|
true
|
55
63
|
end
|
56
64
|
|
@@ -61,23 +69,39 @@ module Brief
|
|
61
69
|
end
|
62
70
|
|
63
71
|
def model_class
|
64
|
-
@model_class || model_namespace.const_get(type_alias.camelize) rescue
|
72
|
+
@model_class || model_namespace.const_get(type_alias.camelize) rescue Brief.default_model_class
|
65
73
|
end
|
66
74
|
|
67
75
|
def model_namespace
|
68
76
|
Brief.configuration.model_namespace || Brief::Model
|
69
77
|
end
|
70
78
|
|
71
|
-
def meta(
|
79
|
+
def meta(_options = {}, &block)
|
72
80
|
@current = :meta
|
73
81
|
instance_eval(&block)
|
74
82
|
end
|
75
83
|
|
76
|
-
def content(
|
84
|
+
def content(_options = {}, &block)
|
77
85
|
@current = :content
|
78
86
|
instance_eval(&block)
|
79
87
|
end
|
80
88
|
|
89
|
+
def example(body = nil, _options = {})
|
90
|
+
if body.is_a?(Hash)
|
91
|
+
options = body
|
92
|
+
elsif body.is_a?(String)
|
93
|
+
self.example_body = body
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def template(body = nil, _options = {})
|
98
|
+
if body.is_a?(Hash)
|
99
|
+
options = body
|
100
|
+
elsif body.is_a?(String)
|
101
|
+
self.template_body = body
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
81
105
|
def has_actions?
|
82
106
|
!@defined_actions.empty?
|
83
107
|
end
|
@@ -120,17 +144,17 @@ module Brief
|
|
120
144
|
if meth.to_sym == :define_section
|
121
145
|
opts = args.extract_options!
|
122
146
|
identifier = args.first
|
123
|
-
|
147
|
+
section_mappings[identifier] ||= Brief::Document::Section::Mapping.new(identifier, opts)
|
124
148
|
section_mapping(identifier).instance_eval(&block) if block
|
125
149
|
else
|
126
|
-
|
150
|
+
content_schema.attributes[meth] = { args: args, block: block }
|
127
151
|
end
|
128
152
|
elsif inside_meta?
|
129
153
|
if args.first.is_a?(Hash)
|
130
154
|
args.unshift(String)
|
131
155
|
end
|
132
156
|
args.unshift(meth)
|
133
|
-
|
157
|
+
metadata_schema[meth] = { args: args, block: block }
|
134
158
|
else
|
135
159
|
super
|
136
160
|
end
|
data/lib/brief/model.rb
CHANGED
@@ -15,8 +15,8 @@ module Brief
|
|
15
15
|
|
16
16
|
class_attribute :models, :after_initialization_hooks, :defined_actions
|
17
17
|
|
18
|
-
self.models = Array(
|
19
|
-
self.defined_actions = Array(
|
18
|
+
self.models = Array(models).to_set
|
19
|
+
self.defined_actions = Array(defined_actions).to_set
|
20
20
|
|
21
21
|
class << self
|
22
22
|
include Enumerable
|
@@ -68,16 +68,24 @@ module Brief
|
|
68
68
|
table[type_alias]
|
69
69
|
end
|
70
70
|
|
71
|
+
def self.lookup_class_from_args(args = [])
|
72
|
+
args = Array(args)
|
73
|
+
|
74
|
+
if model_class = for_type(args.first)
|
75
|
+
model_class
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
71
79
|
def self.finalize
|
72
80
|
Virtus.finalize
|
73
81
|
classes.each(&:finalize)
|
74
82
|
end
|
75
83
|
|
76
84
|
def ==(other)
|
77
|
-
|
85
|
+
path == other.path
|
78
86
|
end
|
79
87
|
|
80
|
-
def extract_content(options={})
|
88
|
+
def extract_content(options = {})
|
81
89
|
document.extract_content(options)
|
82
90
|
end
|
83
91
|
|
@@ -90,7 +98,7 @@ module Brief
|
|
90
98
|
klass = self
|
91
99
|
|
92
100
|
klass.name ||= klass.to_s.split('::').last.humanize
|
93
|
-
klass.type_alias ||= klass.name.parameterize.gsub(/-/,'_')
|
101
|
+
klass.type_alias ||= klass.name.parameterize.gsub(/-/, '_')
|
94
102
|
|
95
103
|
klass.attribute_set.map(&:name).each do |attr|
|
96
104
|
unless klass.method_defined?("find_by_#{ attr }")
|
@@ -105,39 +113,35 @@ module Brief
|
|
105
113
|
Brief::Repository.define_document_finder_methods
|
106
114
|
end
|
107
115
|
|
108
|
-
def where(*args, &
|
116
|
+
def where(*args, &_block)
|
109
117
|
Brief::DocumentMapper::Query.new(self).send(:where, *args)
|
110
118
|
end
|
111
119
|
|
112
120
|
def each(*args, &block)
|
113
|
-
Array(
|
121
|
+
Array(models).send(:each, *args, &block)
|
114
122
|
end
|
115
123
|
|
116
124
|
def after_initialize(&block)
|
117
125
|
(self.after_initialization_hooks ||= []).push(block)
|
118
126
|
end
|
119
127
|
|
120
|
-
|
121
|
-
@name = value
|
122
|
-
end
|
128
|
+
attr_writer :name
|
123
129
|
|
124
130
|
def name
|
125
|
-
@name || to_s.split('::').last.underscore.gsub('_',' ').titlecase
|
131
|
+
@name || to_s.split('::').last.underscore.gsub('_', ' ').titlecase
|
126
132
|
end
|
127
133
|
|
128
|
-
|
129
|
-
@type_alias = value
|
130
|
-
end
|
134
|
+
attr_writer :type_alias
|
131
135
|
|
132
136
|
def type_alias
|
133
|
-
@type_alias || name.parameterize.gsub(/-/,'_')
|
137
|
+
@type_alias || name.parameterize.gsub(/-/, '_')
|
134
138
|
end
|
135
139
|
|
136
140
|
def definition
|
137
141
|
@definition ||= Brief::Model::Definition.new(name, type_alias: type_alias, model_class: self)
|
138
142
|
end
|
139
143
|
|
140
|
-
def definition=(
|
144
|
+
def definition=(_value)
|
141
145
|
@definition
|
142
146
|
end
|
143
147
|
|
@@ -149,18 +153,43 @@ module Brief
|
|
149
153
|
definition.send(:section_mappings, *args)
|
150
154
|
end
|
151
155
|
|
156
|
+
def generate_template_content_from(object, include_frontmatter = true)
|
157
|
+
@erb ||= ERB.new(template_body)
|
158
|
+
content = @erb.result(binding)
|
159
|
+
frontmatter = object.slice(*attribute_names)
|
160
|
+
|
161
|
+
base = ''
|
162
|
+
base += frontmatter.to_hash.to_yaml + "---\n" if include_frontmatter
|
163
|
+
base += content
|
164
|
+
|
165
|
+
base
|
166
|
+
end
|
167
|
+
|
168
|
+
def attribute_names
|
169
|
+
attribute_set.map(&:name)
|
170
|
+
end
|
171
|
+
|
172
|
+
def template_body(*args)
|
173
|
+
res = definition.send(:template_body, *args)
|
174
|
+
res.to_s.length == 0 ? example_body : res.to_s.strip
|
175
|
+
end
|
176
|
+
|
177
|
+
def example_body(*args)
|
178
|
+
definition.send(:example_body, *args).to_s.strip
|
179
|
+
end
|
180
|
+
|
152
181
|
def method_missing(meth, *args, &block)
|
153
|
-
if %w(meta content actions helpers).include?(meth.to_s)
|
182
|
+
if %w(meta content template example actions helpers).include?(meth.to_s)
|
154
183
|
definition.send(meth, *args, &block)
|
155
184
|
finalize
|
156
185
|
elsif meth.to_s.match(/^on_(.*)_change$/)
|
157
|
-
create_change_handler(
|
186
|
+
create_change_handler(Regexp.last_match[1], *args, &block)
|
158
187
|
else
|
159
188
|
super
|
160
189
|
end
|
161
190
|
end
|
162
191
|
|
163
|
-
def create_change_handler(
|
192
|
+
def create_change_handler(_attribute, *_args, &block)
|
164
193
|
block.call(self)
|
165
194
|
end
|
166
195
|
end
|
@@ -178,8 +207,8 @@ module Brief
|
|
178
207
|
end
|
179
208
|
end
|
180
209
|
|
181
|
-
def set_slug_from(column
|
182
|
-
self.slug = send(column).to_s.downcase.parameterize if
|
210
|
+
def set_slug_from(column = :name)
|
211
|
+
self.slug = send(column).to_s.downcase.parameterize if slug.to_s.length == 0
|
183
212
|
end
|
184
213
|
end
|
185
214
|
end
|
data/lib/brief/repository.rb
CHANGED
@@ -9,7 +9,7 @@ module Brief
|
|
9
9
|
documents.send(:each, *args, &block)
|
10
10
|
end
|
11
11
|
|
12
|
-
def initialize(briefcase, options={})
|
12
|
+
def initialize(briefcase, options = {})
|
13
13
|
@briefcase = briefcase
|
14
14
|
@options = options
|
15
15
|
|
@@ -40,7 +40,7 @@ module Brief
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def document_paths
|
43
|
-
Dir[root.join(
|
43
|
+
Dir[root.join('**/*.md').to_s].map { |p| Pathname(p) }
|
44
44
|
end
|
45
45
|
|
46
46
|
def self.define_document_finder_methods
|
@@ -52,6 +52,5 @@ module Brief
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
|
-
|
56
55
|
end
|
57
56
|
end
|