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
@@ -0,0 +1,14 @@
|
|
1
|
+
command "change" do |c|
|
2
|
+
c.syntax = "brief change ATTRIBUTE [OPTIONS]"
|
3
|
+
c.description = "change attributes of brief documents"
|
4
|
+
|
5
|
+
c.option "--from", String, "Only apply when the attributes current value matches."
|
6
|
+
c.option "--to", String, "Only apply when the attributes current value matches."
|
7
|
+
c.option "--files", String, "The files you want to change"
|
8
|
+
c.option "--on", String, "alias for --files. The files you want to change"
|
9
|
+
|
10
|
+
c.action do |args, options|
|
11
|
+
puts "Args: #{ args.inspect }"
|
12
|
+
puts "Options: #{ options.inspect }"
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
command "init" do |c|
|
2
|
+
c.syntax = "brief init NAME [OPTIONS]"
|
3
|
+
c.description = "Create a new brief project, aka a briefcase"
|
4
|
+
|
5
|
+
c.option "--root", String, "The root folder for the new project."
|
6
|
+
|
7
|
+
c.action do |args, options|
|
8
|
+
options.default(:root => Dir.pwd())
|
9
|
+
|
10
|
+
if path = args.first
|
11
|
+
root = Pathname(options.root).join(path)
|
12
|
+
end
|
13
|
+
|
14
|
+
[root, root.join("models"), root.join("docs")].each do |p|
|
15
|
+
puts "== folder #{ p.basename } #{ '. exists' if p.exist? }"
|
16
|
+
FileUtils.mkdir_p(p) unless p.exist?
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
if root.join("brief.rb").exist?
|
21
|
+
say "== Briefcase config already exists. Skipping."
|
22
|
+
else
|
23
|
+
say "== Creating config file brief.rb"
|
24
|
+
root.join("brief.rb").open("w+") do |fh|
|
25
|
+
default_config = <<-EOF
|
26
|
+
|
27
|
+
config do
|
28
|
+
set(:models_path => Pathname(__FILE__).parent.join("models"))
|
29
|
+
end
|
30
|
+
|
31
|
+
define("Post") do
|
32
|
+
meta do
|
33
|
+
status
|
34
|
+
date DateTime, :default => lambda {|post, attr| post.document.created_at }
|
35
|
+
end
|
36
|
+
|
37
|
+
content do
|
38
|
+
title "h1"
|
39
|
+
has_many :subheadings, "h2"
|
40
|
+
end
|
41
|
+
|
42
|
+
helpers do
|
43
|
+
def publish(options={})
|
44
|
+
post.set(:status, "published")
|
45
|
+
post.save
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
on_status_change(:from => "draft", :to => "published") do |model|
|
50
|
+
# Do Something
|
51
|
+
# mail_service.send_html_email_campaign(model.to_html)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# brief publish posts /path/to/*.html.md
|
56
|
+
action "publish posts" do |briefcase, models, options|
|
57
|
+
models.each do |post|
|
58
|
+
post.publish(options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
EOF
|
62
|
+
fh.write(default_config)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
File without changes
|
File without changes
|
data/lib/brief/configuration.rb
CHANGED
@@ -1,134 +1,38 @@
|
|
1
|
-
require
|
2
|
-
require "json"
|
1
|
+
require 'singleton'
|
3
2
|
|
4
3
|
module Brief
|
5
4
|
class Configuration
|
6
5
|
include Singleton
|
7
6
|
|
8
|
-
|
9
|
-
github_username: "",
|
10
|
-
github_api_token: ""
|
11
|
-
}
|
12
|
-
|
13
|
-
def self.method_missing meth, *args, &block
|
7
|
+
def self.method_missing(meth, *args, &block)
|
14
8
|
if instance.respond_to?(meth)
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
nil
|
19
|
-
end
|
20
|
-
|
21
|
-
def initialize!
|
22
|
-
FileUtils.mkdir_p home_config_path.dirname
|
23
|
-
end
|
24
|
-
|
25
|
-
def method_missing meth, *args, &block
|
26
|
-
if current.has_key?(meth.to_s)
|
27
|
-
return current.fetch(meth)
|
28
|
-
end
|
29
|
-
|
30
|
-
super
|
31
|
-
end
|
32
|
-
|
33
|
-
def current_github_repository
|
34
|
-
value = current.github_repository
|
35
|
-
|
36
|
-
return value if value
|
37
|
-
|
38
|
-
if value.nil?
|
39
|
-
if guess = Brief::Git.current_github_repository
|
40
|
-
set "github_repository", guess
|
41
|
-
return guess
|
42
|
-
end
|
9
|
+
instance.send(meth, *args, &block)
|
10
|
+
else
|
11
|
+
super
|
43
12
|
end
|
44
|
-
|
45
|
-
current.github_repository
|
46
13
|
end
|
47
14
|
|
48
15
|
def current
|
49
|
-
@current ||=
|
50
|
-
|
51
|
-
|
16
|
+
@current ||= {
|
17
|
+
docs_path: "docs",
|
18
|
+
models_path: "models"
|
19
|
+
}.to_mash
|
52
20
|
end
|
53
21
|
|
54
|
-
def
|
55
|
-
|
22
|
+
def set(attribute, value=nil)
|
23
|
+
current[attribute] = value
|
24
|
+
self
|
56
25
|
end
|
57
26
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
current_config.delete(setting)
|
66
|
-
save! if persist == true
|
67
|
-
end
|
68
|
-
|
69
|
-
def current
|
70
|
-
Hashie::Mash.new(home_config.merge(cwd_config).merge(applied_config))
|
71
|
-
end
|
72
|
-
|
73
|
-
def apply_config_from_path path
|
74
|
-
path = Pathname(path)
|
75
|
-
parsed = JSON.parse(path.read) rescue {}
|
76
|
-
applied_config.merge!(parsed)
|
77
|
-
nil
|
78
|
-
end
|
79
|
-
|
80
|
-
def save!
|
81
|
-
save_home_config
|
82
|
-
save_cwd_config
|
83
|
-
end
|
84
|
-
|
85
|
-
def save_cwd_config
|
86
|
-
return nil unless cwd_config_path.exist?
|
87
|
-
|
88
|
-
File.open(cwd_config_path,'w+') do |fh|
|
89
|
-
fh.write JSON.generate(cwd_config.to_hash)
|
27
|
+
def method_missing(meth, *args, &block)
|
28
|
+
if current.respond_to?(meth) && current.key?(meth)
|
29
|
+
current.send(meth, *args, &block)
|
30
|
+
else
|
31
|
+
# swallow invalid method calls in production
|
32
|
+
super if ENV['BRIEF_DEBUG_MODE']
|
33
|
+
nil
|
90
34
|
end
|
91
35
|
end
|
92
36
|
|
93
|
-
def save_home_config
|
94
|
-
File.open(home_config_path,'w+') do |fh|
|
95
|
-
fh.write JSON.generate(home_config.to_hash)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Applied config is configuration values passed in context
|
100
|
-
# usually from the cli, but also in the unit tests
|
101
|
-
def applied_config
|
102
|
-
@applied_config ||= {}
|
103
|
-
end
|
104
|
-
|
105
|
-
def cwd_config
|
106
|
-
@cwd_config ||= begin
|
107
|
-
(cwd_config_path.exist? rescue false) ? JSON.parse(cwd_config_path.read) : {}
|
108
|
-
rescue
|
109
|
-
{}
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def home_config
|
114
|
-
@home_config ||= begin
|
115
|
-
(home_config_path.exist? rescue false) ? JSON.parse(home_config_path.read) : {}
|
116
|
-
rescue
|
117
|
-
{}
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def home_folder
|
122
|
-
Pathname(ENV['HOME']).join('.brief')
|
123
|
-
end
|
124
|
-
|
125
|
-
def home_config_path
|
126
|
-
Pathname(ENV['HOME']).join('.brief','config.json')
|
127
|
-
end
|
128
|
-
|
129
|
-
def cwd_config_path
|
130
|
-
Pathname(Dir.pwd).join('.briefrc')
|
131
|
-
end
|
132
37
|
end
|
133
38
|
end
|
134
|
-
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Brief
|
2
|
+
class Document::ContentExtractor
|
3
|
+
def initialize(model_type, document)
|
4
|
+
@model_type = model_type
|
5
|
+
@document = document
|
6
|
+
end
|
7
|
+
|
8
|
+
def document
|
9
|
+
@document
|
10
|
+
end
|
11
|
+
|
12
|
+
def model_class
|
13
|
+
Brief::Model.for_type(@model_type)
|
14
|
+
end
|
15
|
+
|
16
|
+
def attribute_set
|
17
|
+
model_class.definition.content_schema.attributes
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_to?(meth)
|
21
|
+
attribute_set.key?(meth) || super
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(meth, *args, &block)
|
25
|
+
if settings = attribute_set.fetch(meth, nil)
|
26
|
+
if settings.args.length == 1 && settings.args.first.is_a?(String)
|
27
|
+
selector = settings.args.first
|
28
|
+
document.css(selector).try(:text)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Brief
|
2
|
+
class Document
|
3
|
+
module FrontMatter
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def frontmatter
|
7
|
+
@frontmatter || load_frontmatter
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def load_frontmatter
|
13
|
+
if content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
14
|
+
self.content = content[($1.size + $2.size)..-1]
|
15
|
+
@frontmatter = YAML.load($1).to_mash
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Brief
|
2
|
+
class Document
|
3
|
+
module Rendering
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def to_html
|
7
|
+
self.class.renderer.render(content)
|
8
|
+
end
|
9
|
+
|
10
|
+
def css(*args, &block)
|
11
|
+
parser.send(:css, *args, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def at(*args, &block)
|
15
|
+
parser.send(:at, *args, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def parser
|
19
|
+
@parser ||= Nokogiri::HTML.fragment(to_html)
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
def renderer
|
24
|
+
@renderer ||= begin
|
25
|
+
r = ::Redcarpet::Render::HTML.new(:tables => true,
|
26
|
+
:autolink => true,
|
27
|
+
:gh_blockcode => true,
|
28
|
+
:fenced_code_blocks => true,
|
29
|
+
:footnotes => true)
|
30
|
+
|
31
|
+
::Redcarpet::Markdown.new(r)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/brief/document.rb
CHANGED
@@ -1,68 +1,73 @@
|
|
1
|
-
|
2
1
|
module Brief
|
3
2
|
class Document
|
4
|
-
|
3
|
+
include Brief::Document::Rendering
|
4
|
+
include Brief::Document::FrontMatter
|
5
|
+
|
6
|
+
attr_accessor :path, :content, :frontmatter
|
5
7
|
|
6
|
-
def initialize(options)
|
8
|
+
def initialize(path, options={})
|
9
|
+
@path = Pathname(path)
|
7
10
|
@options = options
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
when options.is_a?(Pathname)
|
13
|
-
@content = options.read
|
14
|
-
when options.is_a?(Hash)
|
15
|
-
@content = options[:path].read
|
12
|
+
if self.path.exist?
|
13
|
+
content
|
14
|
+
load_frontmatter
|
16
15
|
end
|
16
|
+
|
17
|
+
self.model_class.try(:models).try(:<<, to_model) unless model_instance_registered?
|
17
18
|
end
|
18
19
|
|
19
|
-
def
|
20
|
-
|
21
|
-
write
|
22
|
-
end
|
20
|
+
def content
|
21
|
+
@content ||= path.read
|
23
22
|
end
|
24
23
|
|
25
|
-
def
|
26
|
-
|
24
|
+
def extract_content(*args)
|
25
|
+
options = args.extract_options!
|
26
|
+
args = options[:args] if options.is_a?(Hash) && options.key?(:args)
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
case
|
29
|
+
when args.length == 1 && args.first.is_a?(String)
|
30
|
+
css(args.first).try(:text).to_s
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
35
|
-
|
34
|
+
def data
|
35
|
+
frontmatter
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
|
40
|
-
return parser.send(meth, *args, &block)
|
41
|
-
end
|
42
|
-
|
43
|
-
super
|
38
|
+
def extension
|
39
|
+
path.extname
|
44
40
|
end
|
45
41
|
|
46
|
-
def
|
47
|
-
|
42
|
+
def to_model
|
43
|
+
model_class.new(data.to_hash.merge(path: path, document: self)) if model_class
|
48
44
|
end
|
49
45
|
|
50
|
-
def
|
51
|
-
@
|
46
|
+
def model_class
|
47
|
+
@model_class || ((data && data.type) && Brief::Model.for_type(data.type))
|
52
48
|
end
|
53
49
|
|
54
|
-
|
55
|
-
|
56
|
-
|
50
|
+
# Each model class tracks the instances of the models created
|
51
|
+
# and ensures that there is a 1-1 relationship between a document path
|
52
|
+
# and the model.
|
53
|
+
def model_instance_registered?
|
54
|
+
self.model_class && self.model_class.models.any? do |model|
|
55
|
+
model.path == self.path
|
56
|
+
end
|
57
57
|
end
|
58
58
|
|
59
|
-
def
|
60
|
-
|
59
|
+
def respond_to?(method)
|
60
|
+
super || data.respond_to?(method) || data.key?(method)
|
61
61
|
end
|
62
62
|
|
63
|
-
def
|
64
|
-
|
63
|
+
def method_missing(meth, *args, &block)
|
64
|
+
if data.respond_to?(meth)
|
65
|
+
data.send(meth, *args, &block)
|
66
|
+
else
|
67
|
+
super
|
68
|
+
end
|
65
69
|
end
|
66
70
|
|
67
71
|
end
|
68
72
|
end
|
73
|
+
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module Brief::DocumentMapper
|
2
|
+
OPERATOR_MAPPING = {
|
3
|
+
'equal' => :==,
|
4
|
+
'eq' => :==,
|
5
|
+
'neq' => :!=,
|
6
|
+
'not_equal' => :!=,
|
7
|
+
'gt' => :>,
|
8
|
+
'gte' => :>=,
|
9
|
+
'in' => :in?,
|
10
|
+
'include' => :include?,
|
11
|
+
'lt' => :<,
|
12
|
+
'lte' => :<=
|
13
|
+
}
|
14
|
+
|
15
|
+
VALID_OPERATORS = OPERATOR_MAPPING.keys
|
16
|
+
|
17
|
+
class Selector
|
18
|
+
attr_reader :attribute, :operator
|
19
|
+
|
20
|
+
def initialize(opts = {})
|
21
|
+
unless Brief::DocumentMapper::VALID_OPERATORS.include?(opts[:operator])
|
22
|
+
raise 'Operator not supported'
|
23
|
+
end
|
24
|
+
|
25
|
+
@attribute, @operator = opts[:attribute], opts[:operator]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Query
|
30
|
+
attr_reader :model
|
31
|
+
|
32
|
+
def initialize(model)
|
33
|
+
@model = model
|
34
|
+
@where = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def where(constraints_hash)
|
38
|
+
selector_hash = constraints_hash.reject { |key, value| !key.is_a? Selector }
|
39
|
+
symbol_hash = constraints_hash.reject { |key, value| key.is_a? Selector }
|
40
|
+
symbol_hash.each do |attribute, value|
|
41
|
+
selector = Selector.new(:attribute => attribute, :operator => 'equal')
|
42
|
+
selector_hash.update({ selector => value })
|
43
|
+
end
|
44
|
+
@where.merge! selector_hash
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def order_by(field)
|
49
|
+
@order_by = field.is_a?(Symbol) ? {field => :asc} : field
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def offset(number)
|
54
|
+
@offset = number
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def limit(number)
|
59
|
+
@limit = number
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def first
|
64
|
+
self.all.first
|
65
|
+
end
|
66
|
+
|
67
|
+
def last
|
68
|
+
self.all.last
|
69
|
+
end
|
70
|
+
|
71
|
+
def run_query
|
72
|
+
if query_is_empty?
|
73
|
+
select
|
74
|
+
else
|
75
|
+
model.select do |obj|
|
76
|
+
match = true
|
77
|
+
|
78
|
+
@where.each do |selector, value|
|
79
|
+
if obj.respond_to?(selector.attribute)
|
80
|
+
test_value = obj.send(selector.attribute)
|
81
|
+
operator = OPERATOR_MAPPING[selector.operator]
|
82
|
+
match = false unless test_value.send(operator, value)
|
83
|
+
else
|
84
|
+
match = false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
match
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def all
|
94
|
+
results = run_query
|
95
|
+
|
96
|
+
if @order_by
|
97
|
+
order_by_attr = @order_by.keys.first
|
98
|
+
direction = @order_by.values.first
|
99
|
+
|
100
|
+
results.select! do |result|
|
101
|
+
result.respond_to?(order_by_attr)
|
102
|
+
end
|
103
|
+
|
104
|
+
results.sort_by! do |result|
|
105
|
+
result.send(order_by_attr)
|
106
|
+
end
|
107
|
+
|
108
|
+
results.reverse! if direction == :desc
|
109
|
+
end
|
110
|
+
|
111
|
+
if @offset.present?
|
112
|
+
results = results.last([results.size - @offset, 0].max)
|
113
|
+
end
|
114
|
+
|
115
|
+
if @limit.present?
|
116
|
+
results = results.first(@limit)
|
117
|
+
end
|
118
|
+
|
119
|
+
results
|
120
|
+
end
|
121
|
+
|
122
|
+
def to_a
|
123
|
+
all
|
124
|
+
end
|
125
|
+
|
126
|
+
def query_is_empty?
|
127
|
+
@where.empty? && @limit.nil? && @order_by.nil?
|
128
|
+
end
|
129
|
+
|
130
|
+
def inspect
|
131
|
+
"Query: #{ @where.map {|k,v| "#{k.attribute} #{k.operator} #{v}" }}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def method_missing(meth, *args, &block)
|
135
|
+
if all.respond_to?(meth)
|
136
|
+
all.send(meth, *args, &block)
|
137
|
+
else
|
138
|
+
super
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class Symbol
|
145
|
+
Brief::DocumentMapper::VALID_OPERATORS.each do |operator|
|
146
|
+
class_eval <<-OPERATORS
|
147
|
+
def #{operator}
|
148
|
+
Brief::DocumentMapper::Selector.new(:attribute => self, :operator => '#{operator}')
|
149
|
+
end
|
150
|
+
OPERATORS
|
151
|
+
end
|
152
|
+
|
153
|
+
unless method_defined?(:"<=>")
|
154
|
+
def <=>(other)
|
155
|
+
self.to_s <=> other.to_s
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
data/lib/brief/dsl.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Brief
|
2
|
+
module DSL
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def config(options={}, &block)
|
6
|
+
Brief::Configuration.instance.load_options(options) unless options.nil? || options.empty?
|
7
|
+
Brief::Configuration.instance.instance_eval(&block) if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def define(*args, &block)
|
11
|
+
definition = Brief::Model::Definition.new(args.first, args.extract_options!)
|
12
|
+
definition.instance_eval(&block) if block_given?
|
13
|
+
definition.validate!
|
14
|
+
end
|
15
|
+
|
16
|
+
def action(identifier, options={}, &block)
|
17
|
+
Object.class.class_eval do
|
18
|
+
command "#{identifier}" do |c|
|
19
|
+
c.syntax = "brief #{identifier}"
|
20
|
+
c.description = "run the #{identifier} command"
|
21
|
+
|
22
|
+
c.action do |args, opts|
|
23
|
+
briefcase = Brief.case
|
24
|
+
|
25
|
+
path_args = args.select {|arg| arg.is_a?(String) && arg.match(/\.md$/) }
|
26
|
+
|
27
|
+
path_args.select! do |arg|
|
28
|
+
path = briefcase.repository.root.join(arg)
|
29
|
+
path.exist?
|
30
|
+
end
|
31
|
+
|
32
|
+
path_args.map! {|p| briefcase.repository.root.join(p) }
|
33
|
+
|
34
|
+
models = path_args.map {|path| Brief::Document.new(path) }.map(&:to_model)
|
35
|
+
|
36
|
+
block.call(Brief.case, models, opts)
|
37
|
+
end
|
38
|
+
end rescue nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|