brief 0.0.5 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +71 -55
- data/README.md +149 -48
- data/Rakefile +15 -5
- data/bin/brief +15 -28
- data/brief.gemspec +17 -11
- data/examples/blog/brief.rb +54 -0
- data/examples/blog/docs/an-intro-to-brief.html.md +9 -0
- data/lib/.DS_Store +0 -0
- data/lib/brief/briefcase.rb +78 -0
- data/lib/brief/cli/change.rb +14 -0
- data/lib/brief/cli/init.rb +66 -0
- data/{spec/fixtures/generated/project_overview.json → lib/brief/cli/publish.rb} +0 -0
- data/lib/brief/cli/write.rb +0 -0
- data/lib/brief/configuration.rb +19 -115
- data/lib/brief/core_ext.rb +11 -0
- data/lib/brief/document/content_extractor.rb +33 -0
- data/lib/brief/document/front_matter.rb +20 -0
- data/lib/brief/document/rendering.rb +37 -0
- data/lib/brief/document.rb +43 -38
- data/lib/brief/document_mapper.rb +158 -0
- data/lib/brief/dsl.rb +42 -0
- data/lib/brief/model/definition.rb +117 -0
- data/lib/brief/model.rb +177 -0
- data/lib/brief/repository.rb +57 -0
- data/lib/brief/version.rb +1 -7
- data/lib/brief.rb +35 -40
- data/spec/fixtures/example/brief.rb +27 -0
- data/spec/fixtures/example/docs/concept.html.md +5 -0
- data/spec/fixtures/example/docs/epic.html.md +19 -0
- data/spec/fixtures/example/docs/persona.html.md +5 -0
- data/spec/fixtures/example/docs/release.html.md +7 -0
- data/spec/fixtures/example/docs/resource.html.md +5 -0
- data/spec/fixtures/example/docs/user_story.html.md +24 -0
- data/spec/fixtures/example/docs/wireframe.html.md +5 -0
- data/spec/fixtures/example/models/epic.rb +16 -0
- data/spec/lib/brief/briefcase_spec.rb +27 -0
- data/spec/lib/brief/document_spec.rb +14 -22
- data/spec/lib/brief/dsl_spec.rb +4 -15
- data/spec/lib/brief/model_spec.rb +84 -0
- data/spec/lib/brief/repository_spec.rb +60 -0
- data/spec/spec_helper.rb +12 -14
- data/spec/support/test_helpers.rb +0 -0
- data/tasks/brief/release.rake +0 -0
- metadata +120 -110
- data/.gitignore +0 -1
- data/Guardfile +0 -5
- data/examples/project_overview.md +0 -23
- data/lib/brief/cli/commands/config.rb +0 -40
- data/lib/brief/cli/commands/publish.rb +0 -27
- data/lib/brief/cli/commands/write.rb +0 -27
- data/lib/brief/formatters/base.rb +0 -12
- data/lib/brief/formatters/github_milestone_rollup.rb +0 -52
- data/lib/brief/git.rb +0 -19
- data/lib/brief/github/wiki.rb +0 -9
- data/lib/brief/github.rb +0 -141
- data/lib/brief/github_client/authentication.rb +0 -32
- data/lib/brief/github_client/client.rb +0 -86
- data/lib/brief/github_client/commands.rb +0 -5
- data/lib/brief/github_client/issue_labels.rb +0 -65
- data/lib/brief/github_client/issues.rb +0 -22
- data/lib/brief/github_client/milestone_issues.rb +0 -13
- data/lib/brief/github_client/organization_activity.rb +0 -9
- data/lib/brief/github_client/organization_issues.rb +0 -13
- data/lib/brief/github_client/organization_repositories.rb +0 -20
- data/lib/brief/github_client/organization_users.rb +0 -9
- data/lib/brief/github_client/repository_events.rb +0 -8
- data/lib/brief/github_client/repository_issue_events.rb +0 -9
- data/lib/brief/github_client/repository_issues.rb +0 -8
- data/lib/brief/github_client/repository_labels.rb +0 -18
- data/lib/brief/github_client/repository_milestones.rb +0 -9
- data/lib/brief/github_client/request.rb +0 -181
- data/lib/brief/github_client/request_wrapper.rb +0 -121
- data/lib/brief/github_client/response_object.rb +0 -50
- data/lib/brief/github_client/single_repository.rb +0 -9
- data/lib/brief/github_client/user_activity.rb +0 -16
- data/lib/brief/github_client/user_gists.rb +0 -9
- data/lib/brief/github_client/user_info.rb +0 -9
- data/lib/brief/github_client/user_issues.rb +0 -13
- data/lib/brief/github_client/user_organizations.rb +0 -9
- data/lib/brief/github_client/user_repositories.rb +0 -9
- data/lib/brief/github_client.rb +0 -43
- data/lib/brief/handlers/base.rb +0 -62
- data/lib/brief/handlers/github_issue.rb +0 -41
- data/lib/brief/handlers/github_milestone.rb +0 -37
- data/lib/brief/handlers/github_wiki.rb +0 -11
- data/lib/brief/line.rb +0 -69
- data/lib/brief/parser.rb +0 -354
- data/lib/brief/publisher/handler_manager.rb +0 -47
- data/lib/brief/publisher.rb +0 -142
- data/lib/brief/tree.rb +0 -43
- data/lib/core_ext.rb +0 -37
- data/spec/fixtures/front_end_tutorial.md +0 -33
- data/spec/fixtures/generator_dsl_example.rb +0 -22
- data/spec/fixtures/project_overview.md +0 -48
- data/spec/fixtures/sample.md +0 -19
- data/spec/lib/brief/line_spec.rb +0 -11
- data/spec/lib/brief/parser_spec.rb +0 -12
data/lib/brief/parser.rb
DELETED
@@ -1,354 +0,0 @@
|
|
1
|
-
# This is my first attempt at breaking down raw markdown
|
2
|
-
# into a tree / hierarchy, so that it can be used to do other
|
3
|
-
# things / create other entities.
|
4
|
-
#
|
5
|
-
module Brief
|
6
|
-
class Parser
|
7
|
-
attr_accessor :content, :options, :raw_tree, :checksum
|
8
|
-
|
9
|
-
# Regexes
|
10
|
-
HeadingRegex = /^#+/
|
11
|
-
|
12
|
-
# Exceptions
|
13
|
-
MissingHeadings = Class.new(Exception)
|
14
|
-
HeadingsNotUnique = Class.new(Exception)
|
15
|
-
|
16
|
-
def initialize(content="",options={})
|
17
|
-
@options = options.dup
|
18
|
-
@content = content
|
19
|
-
@checksum = options.fetch(:checksum, Digest::MD5.hexdigest(content))
|
20
|
-
|
21
|
-
scan
|
22
|
-
parse
|
23
|
-
end
|
24
|
-
|
25
|
-
def tree
|
26
|
-
@tree ||= Brief::Tree.new(tree_nodes.dup.freeze)
|
27
|
-
end
|
28
|
-
|
29
|
-
def items(sorted=true)
|
30
|
-
sorted ? tree.items.sort_by(&:sort_index) : tree.items
|
31
|
-
end
|
32
|
-
|
33
|
-
def headings_at_level level, options={}
|
34
|
-
as_objects = options.fetch(:as_objects, false)
|
35
|
-
items = heading_lines.select {|line| line.level == level }.sort_by(&:sort_index)
|
36
|
-
as_objects ? items : items.map(&:content)
|
37
|
-
end
|
38
|
-
|
39
|
-
def next_sibling_of(heading_line)
|
40
|
-
if heading_line.is_a?(String)
|
41
|
-
heading_line = heading_lines.detect {|h| h.content == heading_line }
|
42
|
-
end
|
43
|
-
|
44
|
-
heading_lines.detect {|l| l.level <= heading_line.level && l.number > heading_line.number }
|
45
|
-
end
|
46
|
-
|
47
|
-
def body_content_for(heading_line)
|
48
|
-
if heading_line.is_a?(String)
|
49
|
-
heading_line = heading_lines.detect {|h| h.content == heading_line }
|
50
|
-
end
|
51
|
-
|
52
|
-
min = heading_line.number + 1
|
53
|
-
max = next_sibling_of(heading_line).try(:number).try(:-, 1) || last_line_number
|
54
|
-
|
55
|
-
parsed_lines.select {|p| p.number.between?(min,max) }.map(&:raw).join()
|
56
|
-
end
|
57
|
-
|
58
|
-
def lines_between_boundaries *bounds
|
59
|
-
bounds.map do |range|
|
60
|
-
a = range.min - 1
|
61
|
-
b = range.max - a
|
62
|
-
parsed_lines.slice(a, b).map(&:raw)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def last_line_number
|
67
|
-
parsed_lines.last.try(:number) || raw_lines.length + 1
|
68
|
-
end
|
69
|
-
|
70
|
-
def parser
|
71
|
-
self
|
72
|
-
end
|
73
|
-
|
74
|
-
def extract_frontmatter!
|
75
|
-
if raw_lines.any? {|l| l.match(/```settings/) }
|
76
|
-
markers = identify_codemarkers
|
77
|
-
|
78
|
-
if raw_lines[markers.first].to_s.match(/```settings/)
|
79
|
-
length = markers[1] - markers[0]
|
80
|
-
@front_matter = raw_lines.slice(markers[0] + 1, length - 1).join
|
81
|
-
remove_raw_lines(markers[0], markers[1])
|
82
|
-
@content = raw_lines.join.gsub(/^\n|\n$/,'')
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def front_matter
|
88
|
-
YAML.load(@front_matter) rescue {}
|
89
|
-
end
|
90
|
-
|
91
|
-
def identify_codemarkers
|
92
|
-
markers = []
|
93
|
-
raw_lines.each_with_index do |line, index|
|
94
|
-
if line.match(Brief::Line::CodeBlockRegex)
|
95
|
-
markers << index
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
markers
|
100
|
-
end
|
101
|
-
|
102
|
-
def scan
|
103
|
-
extract_frontmatter!
|
104
|
-
|
105
|
-
@code_markers = identify_codemarkers
|
106
|
-
@heading_markers = identify_heading_markers
|
107
|
-
|
108
|
-
@scanned = true
|
109
|
-
end
|
110
|
-
|
111
|
-
def identify_heading_markers
|
112
|
-
heading_markers = []
|
113
|
-
raw_lines.each_with_index do |line, index|
|
114
|
-
if line.match(Brief::Line::HeadingRegex) && !is_line_inside_code_region?(index)
|
115
|
-
heading_markers << index
|
116
|
-
end
|
117
|
-
end
|
118
|
-
heading_markers
|
119
|
-
end
|
120
|
-
|
121
|
-
def heading_boundaries
|
122
|
-
@heading_markers && @heading_markers.each_slice(2).map do |slice|
|
123
|
-
Range.new slice.first + 1, slice.last + 1
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def code_boundaries
|
128
|
-
@code_markers && @code_markers.each_slice(2).map do |slice|
|
129
|
-
Range.new slice.first + 1, slice.last + 1
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def valid?
|
134
|
-
@validated.presence || validate
|
135
|
-
end
|
136
|
-
|
137
|
-
def validate
|
138
|
-
scan unless @scanned
|
139
|
-
|
140
|
-
unless heading_lines.length > 0
|
141
|
-
raise MissingHeadings, 'A Brief must include some headings'
|
142
|
-
end
|
143
|
-
|
144
|
-
if heading_lines.uniq.length < heading_lines.length
|
145
|
-
raise HeadingsNotUnique, 'Headings inside a brief must be unique'
|
146
|
-
end
|
147
|
-
|
148
|
-
@validated = true
|
149
|
-
end
|
150
|
-
|
151
|
-
# This syntax isn't right, but it works.
|
152
|
-
# TODO: Research the best way to do this.
|
153
|
-
# middleman had an example of using the &method(:method_name)
|
154
|
-
# to treat a method definition as a proc
|
155
|
-
def tree_visitor node
|
156
|
-
subheadings = headings_under(node, as_objects: true)
|
157
|
-
children = subheadings.map(&method(:tree_visitor))
|
158
|
-
id = "#{ checksum }_#{ node.sort_index.join('_') }"
|
159
|
-
|
160
|
-
children.each_with_index do |child, index|
|
161
|
-
child[:heading_index] = index
|
162
|
-
end
|
163
|
-
|
164
|
-
base = {
|
165
|
-
id: id,
|
166
|
-
level: node.level,
|
167
|
-
children: children,
|
168
|
-
title: node.content,
|
169
|
-
sort_index: node.sort_index,
|
170
|
-
line_number: node.line_number
|
171
|
-
}
|
172
|
-
|
173
|
-
base.merge! content: body_content_for(node).to_s.strip if node.heading?
|
174
|
-
|
175
|
-
base
|
176
|
-
end
|
177
|
-
|
178
|
-
def tree_nodes
|
179
|
-
@tree_nodes ||= begin
|
180
|
-
nodes = []
|
181
|
-
|
182
|
-
headings_at_level(highest_level, as_objects: true).each_with_index do |node, index|
|
183
|
-
element = tree_visitor(node)
|
184
|
-
element[:heading_index] = index
|
185
|
-
nodes << element
|
186
|
-
end
|
187
|
-
|
188
|
-
nodes.map {|node| Hashie::Mash.new(node) }.flatten
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
def elements
|
193
|
-
@elements ||= tree.elements
|
194
|
-
end
|
195
|
-
|
196
|
-
def maximum_level
|
197
|
-
elements.map(&:level).max
|
198
|
-
end
|
199
|
-
|
200
|
-
def line_at(number)
|
201
|
-
parsed_lines.detect {|line| line.number == number }
|
202
|
-
end
|
203
|
-
|
204
|
-
def index_of(line)
|
205
|
-
lines.index(line)
|
206
|
-
end
|
207
|
-
|
208
|
-
def next_heading_after heading, options={}
|
209
|
-
heading_lines.detect {|line| line.number > heading.number }
|
210
|
-
end
|
211
|
-
|
212
|
-
def highest_level
|
213
|
-
level_boundaries.last
|
214
|
-
end
|
215
|
-
|
216
|
-
def lowest_level
|
217
|
-
level_boundaries.first
|
218
|
-
end
|
219
|
-
|
220
|
-
def level_boundaries
|
221
|
-
levels = heading_lines.map(&:level).uniq
|
222
|
-
[levels.max, levels.min]
|
223
|
-
end
|
224
|
-
|
225
|
-
def heading_lines
|
226
|
-
parsed_lines.select do |line|
|
227
|
-
line.heading?
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
def is_line_inside_code_region?(line_number)
|
232
|
-
code_boundaries.any? {|range| range.include?(line_number) }
|
233
|
-
end
|
234
|
-
|
235
|
-
def headings_after(heading_line)
|
236
|
-
if heading_line.is_a?(String)
|
237
|
-
heading_line = heading_lines.detect {|h| h.content == heading_line }
|
238
|
-
end
|
239
|
-
|
240
|
-
heading_lines.select {|line| line.number > heading_line.number }
|
241
|
-
end
|
242
|
-
|
243
|
-
def headings_under(heading_line, options={})
|
244
|
-
matches = []
|
245
|
-
continue = true
|
246
|
-
|
247
|
-
include_subtree = options.fetch(:all, false)
|
248
|
-
as_objects = options.fetch(:as_objects, false)
|
249
|
-
|
250
|
-
if heading_line.is_a?(String)
|
251
|
-
heading_line = heading_lines.detect {|h| h.content == heading_line }
|
252
|
-
end
|
253
|
-
|
254
|
-
headings_after(heading_line).each do |line|
|
255
|
-
continue = false if line.level <= heading_line.level
|
256
|
-
if continue && line.level > heading_line.level
|
257
|
-
matches << line unless (!include_subtree && line.level - heading_line.level > 1)
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
as_objects ? matches : matches.map(&:content)
|
262
|
-
end
|
263
|
-
|
264
|
-
def content_lines_under(heading_line, options={})
|
265
|
-
reject_blank = !options.fetch(:include_blank, true)
|
266
|
-
as_objects = options.fetch(:as_objects, false)
|
267
|
-
|
268
|
-
if heading_line.is_a?(String)
|
269
|
-
heading_line = heading_lines.detect {|h| h.content == heading_line }
|
270
|
-
end
|
271
|
-
|
272
|
-
return [] unless heading_line.respond_to?(:number)
|
273
|
-
|
274
|
-
min = heading_line.number
|
275
|
-
max = next_heading_after(heading_line).try(:number) || last_line_number
|
276
|
-
|
277
|
-
matches = content_lines.select do |line|
|
278
|
-
line.content? && line.number.between?(min, max)
|
279
|
-
end
|
280
|
-
|
281
|
-
matches.reject! {|m| m.content.blank? } if reject_blank
|
282
|
-
|
283
|
-
as_objects ? matches : matches.map(&:content)
|
284
|
-
end
|
285
|
-
|
286
|
-
def content_lines
|
287
|
-
parsed_lines.select(&:content?)
|
288
|
-
end
|
289
|
-
|
290
|
-
def code_block_markers
|
291
|
-
parsed_lines.select {|l| l.type == "code_block_marker" }
|
292
|
-
end
|
293
|
-
|
294
|
-
def raw_lines
|
295
|
-
@raw_lines ||= @content.lines.to_a
|
296
|
-
end
|
297
|
-
|
298
|
-
# I really need to get better with arrays
|
299
|
-
def remove_raw_lines start_index, end_index
|
300
|
-
original = @raw_lines.dup
|
301
|
-
copy = []
|
302
|
-
|
303
|
-
original.each_with_index do |line, index|
|
304
|
-
copy.push(line) unless index.between? start_index, end_index
|
305
|
-
end
|
306
|
-
|
307
|
-
@raw_lines = copy
|
308
|
-
end
|
309
|
-
|
310
|
-
def stripped_lines
|
311
|
-
@stripped ||= raw_lines.map(&:strip)
|
312
|
-
end
|
313
|
-
|
314
|
-
def parsed_lines
|
315
|
-
@parsed_lines
|
316
|
-
end
|
317
|
-
|
318
|
-
def parse clear=false
|
319
|
-
@parsed_lines = nil if clear
|
320
|
-
|
321
|
-
return @parsed_lines if @parsed_lines
|
322
|
-
|
323
|
-
parsed = []
|
324
|
-
|
325
|
-
raw = raw_lines
|
326
|
-
|
327
|
-
stripped_lines.each_with_index do |line,index|
|
328
|
-
is_code = code_boundaries.any? {|bounds| bounds.include?(index) }
|
329
|
-
line = Brief::Line.new(line, index, is_code)
|
330
|
-
line.raw = raw[index]
|
331
|
-
parsed << line
|
332
|
-
end
|
333
|
-
|
334
|
-
@parsed_lines = parsed
|
335
|
-
end
|
336
|
-
|
337
|
-
def code_blocks_by_language
|
338
|
-
return @code_samples if @code_samples
|
339
|
-
|
340
|
-
bounds = Array(parser && parser.send(:code_boundaries))
|
341
|
-
sections = parser.lines_between_boundaries(*bounds)
|
342
|
-
|
343
|
-
@code_samples = sections.inject({}) do |memo,section|
|
344
|
-
marker = section.first.split('```').last
|
345
|
-
language = marker.nil? ? :text : marker.strip
|
346
|
-
|
347
|
-
section.shift && section.pop
|
348
|
-
memo[language.to_sym] ||= []
|
349
|
-
memo[language.to_sym] << section.join("").sub(/^\s+/,'')
|
350
|
-
memo
|
351
|
-
end
|
352
|
-
end
|
353
|
-
end
|
354
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module Brief
|
2
|
-
class Publisher::HandlerManager
|
3
|
-
|
4
|
-
attr_reader :publisher, :document
|
5
|
-
|
6
|
-
def initialize(publisher, document)
|
7
|
-
@publisher = publisher
|
8
|
-
@document = document
|
9
|
-
end
|
10
|
-
|
11
|
-
def parser
|
12
|
-
@parser ||= document.parser
|
13
|
-
end
|
14
|
-
|
15
|
-
def handlers
|
16
|
-
find_parent = lambda do |parent_id|
|
17
|
-
parser.tree.find(parent_id)
|
18
|
-
end
|
19
|
-
|
20
|
-
get_manager = -> { self }
|
21
|
-
|
22
|
-
@handlers ||= publisher.order.inject({}) do |memo, level|
|
23
|
-
config = publisher.config_for_level(level)
|
24
|
-
klass_name = config.publish.klass
|
25
|
-
klass = klass_name.nil? ? Brief::Handlers::Base : (klass_name.to_s.constantize rescue Brief::Handlers::Base)
|
26
|
-
|
27
|
-
memo[level] = parser.tree.level(level).map do |element|
|
28
|
-
obj = klass.new(element)
|
29
|
-
|
30
|
-
obj.parent(&find_parent)
|
31
|
-
obj.get_manager(&get_manager)
|
32
|
-
|
33
|
-
obj
|
34
|
-
end
|
35
|
-
|
36
|
-
memo
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def run
|
41
|
-
publisher.order.each do |level|
|
42
|
-
objects = handlers[level]
|
43
|
-
objects.each(&:handle!)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
data/lib/brief/publisher.rb
DELETED
@@ -1,142 +0,0 @@
|
|
1
|
-
module Brief
|
2
|
-
class Publisher
|
3
|
-
|
4
|
-
class << self
|
5
|
-
attr_accessor :defined, :aliases
|
6
|
-
end
|
7
|
-
|
8
|
-
self.defined ||= {}
|
9
|
-
self.aliases ||= {}
|
10
|
-
|
11
|
-
def self.find query
|
12
|
-
query = query.join(" ") if query.respond_to?(:to_ary)
|
13
|
-
|
14
|
-
if defined[query].nil? && aliases.has_key?(query)
|
15
|
-
return find(aliases[query])
|
16
|
-
end
|
17
|
-
|
18
|
-
result = defined[query]
|
19
|
-
|
20
|
-
result
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.publisher publisher_name
|
24
|
-
defined[key]
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.define publisher_name, &config
|
28
|
-
obj = defined[publisher_name] ||= Brief::Publisher.new(publisher_name)
|
29
|
-
obj.instance_eval(&config) if block_given?
|
30
|
-
|
31
|
-
aliases[publisher_name.downcase] ||= obj.name
|
32
|
-
|
33
|
-
obj.aliases.each do |alias_name|
|
34
|
-
aliases[alias_name.to_s] ||= obj.name
|
35
|
-
end
|
36
|
-
|
37
|
-
obj
|
38
|
-
end
|
39
|
-
|
40
|
-
def process document
|
41
|
-
@manager = Brief::Publisher::HandlerManager.new(self, document).run
|
42
|
-
end
|
43
|
-
|
44
|
-
attr_accessor :command_alias, :sample, :name, :options
|
45
|
-
|
46
|
-
def initialize(name, options={})
|
47
|
-
@name = name
|
48
|
-
@options = options
|
49
|
-
@command_alias = options[:command_alias]
|
50
|
-
|
51
|
-
@before_blocks = {}
|
52
|
-
end
|
53
|
-
|
54
|
-
def before event_name, &block
|
55
|
-
(@before_blocks[event_name.to_sym] ||= []).push(block)
|
56
|
-
end
|
57
|
-
|
58
|
-
def run_before_hooks event_name, &success
|
59
|
-
results = Array(@before_blocks[event_name.to_sym]).map do |hook|
|
60
|
-
hook.call if hook.respond_to?(:call)
|
61
|
-
end
|
62
|
-
|
63
|
-
unless results.any? {|r| r == false }
|
64
|
-
instance_eval(&success) if block_given?
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def required_inputs
|
69
|
-
@options[:required_inputs] ||= {}
|
70
|
-
end
|
71
|
-
|
72
|
-
def requires_input field, options={}
|
73
|
-
cfg = required_inputs.fetch(field.to_sym) { options }
|
74
|
-
cfg.reverse_merge(options)
|
75
|
-
end
|
76
|
-
|
77
|
-
def levels &block
|
78
|
-
@state = :levels
|
79
|
-
instance_eval(&block) if block_given?
|
80
|
-
end
|
81
|
-
|
82
|
-
def sample sample_markdown=nil
|
83
|
-
options[:sample] = "#{sample_markdown}".gsub(/^\s+/,'') if sample_markdown
|
84
|
-
options[:sample]
|
85
|
-
end
|
86
|
-
|
87
|
-
def aliases *values
|
88
|
-
@command_aliases ||= []
|
89
|
-
@command_aliases += values
|
90
|
-
@command_aliases.uniq
|
91
|
-
end
|
92
|
-
|
93
|
-
def maximum_level
|
94
|
-
@maximum_level
|
95
|
-
end
|
96
|
-
|
97
|
-
def current_level
|
98
|
-
@current_level || 1
|
99
|
-
end
|
100
|
-
|
101
|
-
def order *levels
|
102
|
-
@order = levels if levels && levels.length > 0
|
103
|
-
@order || maximum_level.times.map {|n| n + 1 }
|
104
|
-
end
|
105
|
-
|
106
|
-
def level level_number, &block
|
107
|
-
@state = :level
|
108
|
-
@maximum_level = [@current_level || 0, level_number].compact.map(&:to_i).max
|
109
|
-
@current_level = level_number
|
110
|
-
instance_eval(&block) if block_given?
|
111
|
-
end
|
112
|
-
|
113
|
-
def level_config
|
114
|
-
@level_config ||= Hashie::Mash.new({})
|
115
|
-
end
|
116
|
-
|
117
|
-
def config_for_level level
|
118
|
-
level_config["level_#{level}".to_sym] ||= {}
|
119
|
-
end
|
120
|
-
|
121
|
-
def current_level_config
|
122
|
-
config_for_level(current_level)
|
123
|
-
end
|
124
|
-
|
125
|
-
def desc description
|
126
|
-
current_level_config[:description] = description
|
127
|
-
end
|
128
|
-
|
129
|
-
def define_handler handler, options={}
|
130
|
-
if options.is_a?(String)
|
131
|
-
options = {klass: options.classify}
|
132
|
-
end
|
133
|
-
|
134
|
-
current_level_config[handler] = options
|
135
|
-
end
|
136
|
-
|
137
|
-
def replaces_items_from_level n, options={}
|
138
|
-
current_level_config[:replace_options] = options
|
139
|
-
end
|
140
|
-
|
141
|
-
end
|
142
|
-
end
|
data/lib/brief/tree.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
module Brief
|
2
|
-
class Tree
|
3
|
-
def initialize(nodes)
|
4
|
-
@nodes = nodes
|
5
|
-
end
|
6
|
-
|
7
|
-
def find element_by_id
|
8
|
-
elements.detect {|el| el.id == element_by_id }
|
9
|
-
end
|
10
|
-
|
11
|
-
def elements
|
12
|
-
@elements ||= begin
|
13
|
-
@nodes.map do |node|
|
14
|
-
only = node.except(:children)
|
15
|
-
|
16
|
-
visitor = lambda do |child|
|
17
|
-
c_only = child.except(:children)
|
18
|
-
g_children = Array(child[:children])
|
19
|
-
|
20
|
-
g_children.each {|gchild| gchild.parent_id = c_only.id}
|
21
|
-
|
22
|
-
g_children = nil if g_children.length == 0
|
23
|
-
|
24
|
-
[c_only, g_children].compact
|
25
|
-
end
|
26
|
-
|
27
|
-
children = Array(node[:children]).map(&visitor).flatten
|
28
|
-
children.each {|child| child.parent_id ||= only.id }
|
29
|
-
|
30
|
-
children = nil if children.length == 0
|
31
|
-
|
32
|
-
[only, children].compact
|
33
|
-
end
|
34
|
-
|
35
|
-
end.flatten
|
36
|
-
end
|
37
|
-
|
38
|
-
def level level=1
|
39
|
-
elements.select {|el| el.level == level }
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
data/lib/core_ext.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
class Object
|
2
|
-
def present?
|
3
|
-
!nil?
|
4
|
-
end
|
5
|
-
|
6
|
-
def try(*a, &b)
|
7
|
-
if a.empty? && block_given?
|
8
|
-
yield self
|
9
|
-
else
|
10
|
-
public_send(*a, &b) if respond_to?(a.first)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def try!(*a, &b)
|
15
|
-
if a.empty? && block_given?
|
16
|
-
yield self
|
17
|
-
else
|
18
|
-
public_send(*a, &b)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class NilClass
|
24
|
-
def present?
|
25
|
-
false
|
26
|
-
end
|
27
|
-
def try(*args)
|
28
|
-
nil
|
29
|
-
end
|
30
|
-
|
31
|
-
def try!(*args)
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
require 'core_ext/hash'
|
37
|
-
require 'core_ext/string'
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# Backbone View Tutorial
|
2
|
-
|
3
|
-
Below is an example of how you can style a backbone view.
|
4
|
-
|
5
|
-
## Template Content
|
6
|
-
|
7
|
-
First write a basic template.
|
8
|
-
|
9
|
-
```slim
|
10
|
-
nav.nav.navbar-inverse
|
11
|
-
.container
|
12
|
-
.row-fluid
|
13
|
-
```
|
14
|
-
|
15
|
-
## Style Content
|
16
|
-
|
17
|
-
Make the text pink, because it is manly.
|
18
|
-
|
19
|
-
```scss
|
20
|
-
nav.navbar-inverse {
|
21
|
-
color: pink;
|
22
|
-
}
|
23
|
-
```
|
24
|
-
|
25
|
-
## Script Content
|
26
|
-
|
27
|
-
Then you'll wanna do some stuff with the coffeescript.
|
28
|
-
|
29
|
-
```coffeescript
|
30
|
-
MyView = Backbone.View.extend
|
31
|
-
className: "navbar-inverse"
|
32
|
-
```
|
33
|
-
|
@@ -1,22 +0,0 @@
|
|
1
|
-
Brief.define "Project Overview" do
|
2
|
-
aliases :project_overview
|
3
|
-
|
4
|
-
levels do
|
5
|
-
level(1) do
|
6
|
-
name "Wiki Page"
|
7
|
-
define_handler :create => "Brief::Handlers::GithubWiki"
|
8
|
-
replaces_items_from_level 2, :with => "Brief::Formatters::GithubMilestoneRollup"
|
9
|
-
end
|
10
|
-
|
11
|
-
level(2) do
|
12
|
-
name "Github Milestone"
|
13
|
-
define_handler :create => "Brief::Handlers::GithubMilestone"
|
14
|
-
replaces_items_from_level 3, :with => nil
|
15
|
-
end
|
16
|
-
|
17
|
-
level(3) do
|
18
|
-
name "Github Issue"
|
19
|
-
define_handler :create => "Brief::Handlers::GithubIssue"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|