brief 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|