brief 1.6.1 → 1.7.0

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