brief 1.6.1 → 1.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e40ba8497cd64fef959861d1349e7a955bbf0df7
4
- data.tar.gz: 596ab5f961548a7ff2088d87bc0e4e68c3ef1b47
3
+ metadata.gz: 5994d22ccc22af64d2a24fb82f2cbc687eba9dad
4
+ data.tar.gz: 238e456cc9f8f35cf0b58e6d8963f857935b28ec
5
5
  SHA512:
6
- metadata.gz: 263dfac1ba20242d8aae3c3fa7d2876bef767141c7d05f66723ac068d10efb3e8b1c5e8fb4f06bc10e4dfd5bc85363d3ef02b6e5b78a3ae98b8163a3460d7737
7
- data.tar.gz: cf69dca6f61a98015ee53b8bc54dc64f32160b4533aa89d2200081bb38dd01cfb94e3fab0ee4097122ba0f6737fdd0b3352edb3b564d73421c6ec86fa657fa40
6
+ metadata.gz: b6923156ac95ba428b498b98290ca2e2a03fcee5ddd28da8f90029e6ec4c5fb5c144de04e87929b7923065f43ada6df779cdf2e025e498a3abc063dbcbad358b
7
+ data.tar.gz: 2e09b44afca6169797fe7cc31d9bbdf396b824279112dc59d480019abe245029c7e8c3f1c0c7663551eefe85e7de01218f43716ddb565af4513b5c0a023fa907
data/.gitignore CHANGED
@@ -1,3 +1,3 @@
1
1
  # the test suite writes to this
2
- spec/fixtures/example/docs/epic.html.md
3
- spec/fixtures/example/docs/concept.html.md
2
+ spec/fixtures/example/docs
3
+ spec/fixtures/example/docs
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- brief (1.6.0)
4
+ brief (1.6.2)
5
5
  activemodel
6
6
  activesupport
7
7
  commander
@@ -1,6 +1,9 @@
1
- view(:summary) do |briefcase, params|
1
+ view(:summary) do |*args|
2
+ params = args.extract_options!
3
+ briefcase = args.first
4
+
2
5
  briefcase.present(:default, params).tap do |hash|
3
- if summary = where(title:"Summary",type:"page").first
6
+ if summary = briefcase.pages.find {|p| p.title == "Summary" }
4
7
  hash.merge!(summary: summary.to_model.as_json(:rendered=>true, :content=>true))
5
8
  end
6
9
  end
@@ -35,6 +35,9 @@ As a **User** I would like to **Do this** so that I can **succeed**
35
35
 
36
36
  content do
37
37
  title "h1:first-of-type"
38
+ paragraph "p:first-of-type"
39
+ paragraphs "p"
40
+
38
41
  define_section "User Stories" do
39
42
  each("h2").has(:title => "h2",
40
43
  :paragraph => "p:first-of-type",
@@ -8,9 +8,9 @@ class Brief::Apps::Blueprint::Page
8
8
  end
9
9
 
10
10
  content do
11
- title "h1:first-of-type"
12
- tagline "h2:first-of-type"
13
11
  paragraph "p:first-of-type"
14
- yaml_data "code.yaml:first-of-type", :serialize => :yaml
12
+ title "h1:first-of-type", :hide => true
13
+ tagline "h2:first-of-type", :hide => true
14
+ yaml_data "code.yaml:first-of-type", :serialize => :yaml, :hide => true
15
15
  end
16
16
  end
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "brief-client",
3
+ "version": "0.0.1",
4
+ "description": "a client for the brief markdown as a database service",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "author": "",
10
+ "license": "ISC"
11
+ }
@@ -17,7 +17,7 @@ module Brief
17
17
  Brief.case = self
18
18
  end
19
19
 
20
- Brief.cases[root] ||= self
20
+ Brief.cases[root.basename.to_s] ||= self
21
21
  end
22
22
 
23
23
  def present(style="default", params={})
@@ -0,0 +1,21 @@
1
+ command 'export' do |c|
2
+ c.syntax= 'brief export [OPTIONS]'
3
+ c.description = 'Export a parsed version of the document collection'
4
+
5
+ c.option '--include-content', nil, 'whether to include the unparsed content'
6
+ c.option '--include-rendered', nil, 'whether to include the rendered content'
7
+ c.option '--config-path PATH', String, 'Path to the config file'
8
+
9
+ c.action do |args, options|
10
+ options.default :config_path => Pathname(Dir.pwd).join('brief.rb')
11
+
12
+
13
+ briefcase = Brief::Briefcase.new(config_path: Pathname(options.config_path))
14
+
15
+ json = briefcase.present("default", content: !!options.include_content, rendered: !!options.include_rendered)
16
+
17
+ output = args.first || "briefcase.json"
18
+
19
+ Pathname(Dir.pwd).join(output).open("w+") {|fh| fh.write(json.to_json) }
20
+ end
21
+ end
@@ -4,6 +4,12 @@ class Hash
4
4
  end
5
5
  end
6
6
 
7
+ class Hashie::Mash
8
+ def to_mash
9
+ self
10
+ end
11
+ end
12
+
7
13
  class String
8
14
  def to_pathname
9
15
  Pathname(self)
@@ -12,7 +12,7 @@ module Brief
12
12
  end
13
13
 
14
14
  def content_schema_attributes
15
- model_class.definition.content_schema.attributes
15
+ model_class.definition.content_schema.attributes.symbolize_keys!
16
16
  end
17
17
 
18
18
  def extracted_content_data
@@ -25,39 +25,67 @@ module Brief
25
25
  end
26
26
 
27
27
  def respond_to?(meth)
28
- content_schema_attributes.key?(meth) || super
28
+ supports_extraction?(meth) || super
29
+ end
30
+
31
+ def extraction_rule_for(attribute)
32
+ content_schema_attributes.fetch(attribute.to_sym, nil)
33
+ end
34
+
35
+ def selector_for(attribute)
36
+ extraction_rule_for(attribute).first
37
+ end
38
+
39
+ def supports_extraction?(attribute)
40
+ content_schema_attributes.key?(attribute.to_sym)
29
41
  end
30
42
 
31
43
  def method_missing(meth, *_args, &_block)
32
- if settings = content_schema_attributes.fetch(meth, nil)
33
- if settings.args.length == 1 && settings.args.first.is_a?(String)
34
- selector = settings.args.first
35
- matches = document.css(selector)
36
-
37
- if matches.length > 1
38
- selector.match(/first-of-type/) ? matches.first.text : matches.map(&:text)
39
- else
40
- matches.first.try(:text)
41
- end
42
- elsif settings.args.first.to_s.match(/code/i) && (settings.args.last.serialize rescue nil)
43
- selector = settings.args.first
44
- opts = settings.args.last
45
-
46
- matches = document.css(selector)
47
-
48
- val = if matches.length > 1
49
- selector.match(/first-of-type/) ? matches.first.text : matches.map(&:text)
50
- else
51
- matches.first.try(:text)
52
- end
53
-
54
- if val && opts.serialize == :yaml
55
- return (YAML.load(val) rescue {}).to_mash
56
- end
57
-
58
- if val && opts.serialize == :json
59
- return (JSON.parse(val) rescue {}).to_mash
60
- end
44
+ return super unless supports_extraction?(meth)
45
+ rule = ExtractionRule.new(extraction_rule_for(meth))
46
+ rule.apply_to(document)
47
+ end
48
+
49
+ class ExtractionRule
50
+ attr_reader :rule, :args
51
+
52
+ def initialize(rule)
53
+ @rule = rule
54
+ @args = rule.args
55
+ end
56
+
57
+ def options
58
+ args[1] || {}.to_mash
59
+ end
60
+
61
+ def deserialize?
62
+ !!(options.serialize.present? && options.serialize)
63
+ end
64
+
65
+ def format
66
+ options.serialize.to_sym
67
+ end
68
+
69
+ def selector
70
+ args.first if args.first.is_a?(String)
71
+ end
72
+
73
+ def apply_to(document)
74
+ raise 'Must specify a selector' unless selector
75
+
76
+ extracted = document.css(selector)
77
+
78
+ return nil if extracted.length == 0
79
+
80
+ case
81
+ when deserialize? && format == :json
82
+ (JSON.parse(extracted.text.to_s) rescue {}).to_mash
83
+ when deserialize? && format == :yaml
84
+ (YAML.load(extracted.text.to_s) rescue {}).to_mash
85
+ when selector.match(/first-of-type/) && extracted.length > 0
86
+ extracted.first.text
87
+ else
88
+ extracted.map(&:text)
61
89
  end
62
90
  end
63
91
  end
@@ -10,6 +10,18 @@ module Brief
10
10
  self
11
11
  end
12
12
 
13
+ def to_s
14
+ "#{ model_class }.at_path(#{relative_path})"
15
+ end
16
+
17
+ def inspect
18
+ "#{ model_class }.at_path(#{relative_path})"
19
+ end
20
+
21
+ def relative_path
22
+ briefcase.present? ? path.relative_path_from(briefcase.docs_path) : path
23
+ end
24
+
13
25
  def initialize(path, options = {})
14
26
  if path.respond_to?(:key?) && options.empty?
15
27
  @frontmatter = path.to_mash
@@ -27,6 +39,18 @@ module Brief
27
39
  end
28
40
  end
29
41
 
42
+ def content_hash
43
+ Digest::MD5.hexdigest(@content.to_s)
44
+ end
45
+
46
+ def file_hash
47
+ Digest::MD5.hexdigest(path.read.to_s)
48
+ end
49
+
50
+ def content_stale?
51
+ content_hash != file_hash
52
+ end
53
+
30
54
  def raw= val
31
55
  @raw_set = true
32
56
  @raw_content = val
@@ -55,6 +79,7 @@ module Brief
55
79
  @frontmatter = nil
56
80
  @raw_frontmatter = nil
57
81
  @refreshing = true
82
+ @content_hash = nil
58
83
  load_frontmatter
59
84
  true
60
85
  end
@@ -106,9 +131,10 @@ module Brief
106
131
  end
107
132
 
108
133
  def content
109
- if @content.nil?
110
- generate_content
134
+ if @content.nil? && path && path.exist?
135
+ @content = path.read
111
136
  end
137
+
112
138
  @content || generate_content
113
139
  end
114
140
 
@@ -153,8 +179,14 @@ module Brief
153
179
  path.extname
154
180
  end
155
181
 
182
+ def model_attributes
183
+ (data || {}).to_hash
184
+ .merge(path: path, document: self)
185
+ .reverse_merge(type: document_type)
186
+ end
187
+
156
188
  def to_model
157
- model_class.new((data || {}).to_hash.merge(path: path, document: self).reverse_merge(:type=>document_type)) if model_class
189
+ model_class.try(:new, model_attributes)
158
190
  end
159
191
 
160
192
  def exist?
@@ -7,7 +7,7 @@ module Brief::Model::Serializers
7
7
  doc_path = path.relative_path_from(docs_path).to_s
8
8
 
9
9
  # TEMP
10
- title = data.try(:[], :title) || extracted_content.try(:title) || (send(:title) rescue nil) || path.basename.to_s.gsub(/\.html.md/,'')
10
+ title = data.try(:[], :title) || extracted_content_data.try(:title) || (send(:title) rescue nil) || path.basename.to_s.gsub(/\.html.md/,'')
11
11
  title = title.to_s.gsub(/\.md/,'')
12
12
 
13
13
  {
@@ -16,7 +16,7 @@ module Brief::Model::Serializers
16
16
  path: path.to_s,
17
17
  type: type,
18
18
  group: type.to_s.pluralize,
19
- title: title,
19
+ title: document_title,
20
20
  actions: self.class.defined_actions,
21
21
  urls: {
22
22
  view_content_url: "/view/content/#{ doc_path }",
data/lib/brief/model.rb CHANGED
@@ -29,6 +29,14 @@ module Brief
29
29
  end
30
30
 
31
31
  module AccessorMethods
32
+ def document_title
33
+ data.try(:[], :title) ||
34
+ extracted_content_data.try(:title) ||
35
+ path.basename.to_s
36
+ .gsub(/\.html.md/,'')
37
+ .gsub(/\.md/,'')
38
+ end
39
+
32
40
  def data
33
41
  document.data || {}.to_mash
34
42
  end
@@ -112,6 +120,10 @@ module Brief
112
120
  type_alias && type_alias == other.type_alias
113
121
  end
114
122
 
123
+ def accessor_property_names
124
+ (definition.content_schema.attributes.keys + definition.metadata_schema.keys).uniq
125
+ end
126
+
115
127
  def to_schema
116
128
  {
117
129
  schema: {
@@ -21,8 +21,15 @@ module Brief
21
21
  end
22
22
 
23
23
  def method_missing(meth, *args, &block)
24
- if model_groups.include?(meth.to_s)
24
+ in_model_group = model_groups.include?(meth.to_s)
25
+
26
+ if in_model_group && args.empty?
25
27
  find_models_by_type(meth)
28
+ elsif in_model_group && !args.empty?
29
+ group = find_models_by_type(meth)
30
+ Brief::DocumentMapper::Query.new(group).send(:where, *args)
31
+ else
32
+ super
26
33
  end
27
34
  end
28
35
 
data/lib/brief/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Brief
2
- VERSION = '1.6.1'
2
+ VERSION = '1.7.0'
3
3
  end
@@ -1,8 +1,10 @@
1
1
  ---
2
2
  type: concept
3
- title: Blueprint Concept Example
4
- subheading: A key concept to the domain model
5
- needle: cbw5astzg5gh0ui3op68054s9sm39me6vjvc
3
+
6
4
  ---
7
5
 
8
- # Modified Content oh5lszxtl7w8uyldpjrum1w5uy3pbr33wsh
6
+ # Modified Content 38inom7nxjlo9z3neuhognu1y75csd79i1ec
7
+
8
+ 1
9
+
10
+ 1
@@ -0,0 +1,26 @@
1
+ require "spec_helper"
2
+
3
+ describe "Hashing" do
4
+ let(:doc) { Brief.concept_document }
5
+
6
+ it "has a content hash" do
7
+ expect(doc.content_hash).not_to be_empty
8
+ end
9
+
10
+ it "has a file system hash" do
11
+ expect(doc.file_hash).not_to be_empty
12
+ end
13
+
14
+ it "detects when it is stale" do
15
+ new_content = doc.content += "\n\n1";
16
+ doc.path.open("w+") {|fh| fh.write("---\ntype: concept\n\n---\n\n#{new_content}") }
17
+ expect(doc).to be_content_stale
18
+ end
19
+
20
+ it "refreshes itself if stale" do
21
+ new_content = doc.content += "\n\n1";
22
+ doc.path.open("w+") {|fh| fh.write("---\ntype: concept\n\n---\n\n#{new_content}") }
23
+ expect(doc).to be_content_stale
24
+ end
25
+
26
+ end
@@ -9,11 +9,22 @@ describe "The Brief Document Repository" do
9
9
 
10
10
  it "gives me the document models for paths" do
11
11
  paths = ["./concept.html.md", "wireframe.html.md", "epics/epic.html.md", Brief.example_document.path.realpath]
12
-
13
12
  docs = repository.documents_at(*paths)
13
+ expect(docs).not_to be_empty
14
14
  end
15
15
 
16
16
  context "querying api" do
17
+ it "lets me query the model groups with params" do
18
+ items = Brief.testcase.epics(title:"No Bueno").all
19
+ expect(items).to be_empty
20
+ end
21
+
22
+ it "lets me query the model group with params" do
23
+ items = Brief.testcase.epics(title:"Blueprint Epic Example").all
24
+ expect(items).not_to be_empty
25
+ expect(items.map(&:title).uniq).to include("Blueprint Epic Example")
26
+ end
27
+
17
28
  it "finds the first document matching a query" do
18
29
  query = repository.where(state:"active")
19
30
  expect(query.first.type).to eq("epic")
data/spec/spec_helper.rb CHANGED
@@ -13,14 +13,19 @@ module Brief
13
13
  testcase.root
14
14
  end
15
15
 
16
+ def self.concept_document
17
+ path = Brief.example_path.join("docs","concept.html.md")
18
+ testcase.document_at(path)
19
+ end
20
+
16
21
  def self.page_document
17
22
  path = Brief.example_path.join("docs","page.html.md")
18
- Brief::Document.new(path)
23
+ testcase.document_at(path)
19
24
  end
20
25
 
21
26
  def self.example_document
22
27
  path = Brief.example_path.join("docs","epics","epic.html.md")
23
- Brief::Document.new(path)
28
+ testcase.document_at(path)
24
29
  end
25
30
 
26
31
  def self.testcase
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brief
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Soeder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-25 00:00:00.000000000 Z
11
+ date: 2015-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie
@@ -245,6 +245,7 @@ files:
245
245
  - apps/blueprint/models/wireframe.rb
246
246
  - bin/brief
247
247
  - brief.gemspec
248
+ - clients/package.json
248
249
  - examples/blog/brief.rb
249
250
  - examples/blog/docs/an-intro-to-brief.html.md
250
251
  - lib/.DS_Store
@@ -253,6 +254,7 @@ files:
253
254
  - lib/brief/apps.rb
254
255
  - lib/brief/briefcase.rb
255
256
  - lib/brief/cli/change.rb
257
+ - lib/brief/cli/export.rb
256
258
  - lib/brief/cli/init.rb
257
259
  - lib/brief/cli/write.rb
258
260
  - lib/brief/configuration.rb
@@ -312,6 +314,7 @@ files:
312
314
  - spec/lib/brief/briefcase_spec.rb
313
315
  - spec/lib/brief/document_spec.rb
314
316
  - spec/lib/brief/dsl_spec.rb
317
+ - spec/lib/brief/hashing_spec.rb
315
318
  - spec/lib/brief/model_spec.rb
316
319
  - spec/lib/brief/models/page_spec.rb
317
320
  - spec/lib/brief/persistence_spec.rb
@@ -378,6 +381,7 @@ test_files:
378
381
  - spec/lib/brief/briefcase_spec.rb
379
382
  - spec/lib/brief/document_spec.rb
380
383
  - spec/lib/brief/dsl_spec.rb
384
+ - spec/lib/brief/hashing_spec.rb
381
385
  - spec/lib/brief/model_spec.rb
382
386
  - spec/lib/brief/models/page_spec.rb
383
387
  - spec/lib/brief/persistence_spec.rb