brief 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/Gemfile +4 -0
- data/Gemfile.lock +81 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +11 -0
- data/bin/brief +35 -0
- data/brief-0.0.1.gem +0 -0
- data/brief.gemspec +33 -0
- data/examples/project_overview.md +23 -0
- data/lib/brief/cli/commands/config.rb +40 -0
- data/lib/brief/cli/commands/publish.rb +27 -0
- data/lib/brief/cli/commands/write.rb +26 -0
- data/lib/brief/configuration.rb +134 -0
- data/lib/brief/document.rb +68 -0
- data/lib/brief/dsl.rb +0 -0
- data/lib/brief/formatters/base.rb +12 -0
- data/lib/brief/formatters/github_milestone_rollup.rb +52 -0
- data/lib/brief/git.rb +19 -0
- data/lib/brief/github/wiki.rb +9 -0
- data/lib/brief/github.rb +78 -0
- data/lib/brief/github_client/authentication.rb +32 -0
- data/lib/brief/github_client/client.rb +86 -0
- data/lib/brief/github_client/commands.rb +5 -0
- data/lib/brief/github_client/issue_labels.rb +65 -0
- data/lib/brief/github_client/issues.rb +22 -0
- data/lib/brief/github_client/milestone_issues.rb +13 -0
- data/lib/brief/github_client/organization_activity.rb +9 -0
- data/lib/brief/github_client/organization_issues.rb +13 -0
- data/lib/brief/github_client/organization_repositories.rb +20 -0
- data/lib/brief/github_client/organization_users.rb +9 -0
- data/lib/brief/github_client/repository_events.rb +8 -0
- data/lib/brief/github_client/repository_issue_events.rb +9 -0
- data/lib/brief/github_client/repository_issues.rb +8 -0
- data/lib/brief/github_client/repository_labels.rb +18 -0
- data/lib/brief/github_client/repository_milestones.rb +9 -0
- data/lib/brief/github_client/request.rb +181 -0
- data/lib/brief/github_client/request_wrapper.rb +121 -0
- data/lib/brief/github_client/response_object.rb +50 -0
- data/lib/brief/github_client/single_repository.rb +9 -0
- data/lib/brief/github_client/user_activity.rb +16 -0
- data/lib/brief/github_client/user_gists.rb +9 -0
- data/lib/brief/github_client/user_info.rb +9 -0
- data/lib/brief/github_client/user_issues.rb +13 -0
- data/lib/brief/github_client/user_organizations.rb +9 -0
- data/lib/brief/github_client/user_repositories.rb +9 -0
- data/lib/brief/github_client.rb +43 -0
- data/lib/brief/handlers/base.rb +62 -0
- data/lib/brief/handlers/github_issue.rb +41 -0
- data/lib/brief/handlers/github_milestone.rb +37 -0
- data/lib/brief/handlers/github_wiki.rb +11 -0
- data/lib/brief/line.rb +69 -0
- data/lib/brief/parser.rb +354 -0
- data/lib/brief/publisher/handler_manager.rb +47 -0
- data/lib/brief/publisher.rb +142 -0
- data/lib/brief/tree.rb +42 -0
- data/lib/brief/version.rb +9 -0
- data/lib/brief.rb +56 -0
- data/lib/core_ext.rb +37 -0
- data/spec/fixtures/front_end_tutorial.md +33 -0
- data/spec/fixtures/generated/project_overview.json +0 -0
- data/spec/fixtures/generator_dsl_example.rb +22 -0
- data/spec/fixtures/project_overview.md +48 -0
- data/spec/fixtures/sample.md +19 -0
- data/spec/lib/brief/document_spec.rb +35 -0
- data/spec/lib/brief/dsl_spec.rb +21 -0
- data/spec/lib/brief/line_spec.rb +11 -0
- data/spec/lib/brief/parser_spec.rb +12 -0
- data/spec/spec_helper.rb +25 -0
- metadata +231 -9
@@ -0,0 +1,121 @@
|
|
1
|
+
module Brief::GithubClient
|
2
|
+
|
3
|
+
class RequestWrapper
|
4
|
+
attr_accessor :request,:headers,:params,:type, :method
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :request_cache, :response_cache
|
8
|
+
end
|
9
|
+
|
10
|
+
self.request_cache ||= Brief::GithubClient.request_cache
|
11
|
+
self.response_cache ||= Brief::GithubClient.response_cache
|
12
|
+
|
13
|
+
def initialize(type,params,headers)
|
14
|
+
@type = type
|
15
|
+
@params = params
|
16
|
+
@headers = headers
|
17
|
+
@method = :get
|
18
|
+
|
19
|
+
@cache_key = Digest::MD5.hexdigest([params.to_json, type].to_json)
|
20
|
+
end
|
21
|
+
|
22
|
+
def url
|
23
|
+
"https://api.github.com/#{ type }"
|
24
|
+
end
|
25
|
+
|
26
|
+
def request
|
27
|
+
if method == :get
|
28
|
+
@request = Typhoeus::Request.new url,
|
29
|
+
method: method,
|
30
|
+
headers: request_headers,
|
31
|
+
params: params
|
32
|
+
else
|
33
|
+
@request = Typhoeus::Request.new url,
|
34
|
+
method: method,
|
35
|
+
headers: request_headers,
|
36
|
+
body: JSON.generate(params)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def get
|
41
|
+
@method = :get
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def update
|
46
|
+
@method = :patch
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def post
|
51
|
+
@method = :post
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def create
|
56
|
+
@method = :post
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete
|
61
|
+
@method = :delete
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def request_headers
|
66
|
+
if method == :get && cached_etag.present? && cached_etag.length > 1
|
67
|
+
if Brief::GithubClient.enable_caching
|
68
|
+
@headers["If-None-Match"] = cached_etag.split('|').first
|
69
|
+
@headers["If-Modified-Since"] = cached_etag.split('|').last
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
@headers
|
74
|
+
end
|
75
|
+
|
76
|
+
def cached_etag
|
77
|
+
Brief::GithubClient.enable_caching ? self.class.request_cache[cache_key] : nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def cache_key
|
81
|
+
@cache_key
|
82
|
+
end
|
83
|
+
|
84
|
+
def cached?
|
85
|
+
response.headers.try(:[],"Status") == "304 Not Modified"
|
86
|
+
end
|
87
|
+
|
88
|
+
def response
|
89
|
+
return @response if @response
|
90
|
+
|
91
|
+
@response = request.run
|
92
|
+
|
93
|
+
if Brief::GithubClient.enable_caching && response.headers.try(:[],"Status") == "200 OK"
|
94
|
+
self.class.request_cache[cache_key] = "#{response.headers.try(:[],"Etag")}|#{ response.headers.try(:[],"Last-Modified") }"
|
95
|
+
self.class.response_cache[cache_key] = response.body
|
96
|
+
end
|
97
|
+
|
98
|
+
@response
|
99
|
+
end
|
100
|
+
|
101
|
+
def result
|
102
|
+
@result ||= JSON.parse(response_body)
|
103
|
+
end
|
104
|
+
|
105
|
+
def response_body
|
106
|
+
return response.body unless Brief::GithubClient.enable_caching
|
107
|
+
|
108
|
+
self.class.response_cache.fetch(cache_key) do
|
109
|
+
response.body
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def records
|
114
|
+
result
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_object
|
118
|
+
Hashie::Mash.new(result)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Brief::GithubClient
|
2
|
+
class ResponseObject < Hashie::Mash
|
3
|
+
|
4
|
+
def url_fields
|
5
|
+
keys.grep(/_url/).inject({}) do |memo,field|
|
6
|
+
memo[field] = self.send(field)
|
7
|
+
memo
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_request_class obj
|
12
|
+
@request_class = obj if obj
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def with_request_object obj
|
17
|
+
@request_object = obj if obj
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def request_object
|
22
|
+
@request_object
|
23
|
+
end
|
24
|
+
|
25
|
+
def request_class
|
26
|
+
@request_class || Brief::GithubClient::Request
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch(relationship, params={})
|
30
|
+
if endpoint_for(relationship)
|
31
|
+
endpoint_url = endpoint_for(relationship)
|
32
|
+
tmpl = URITemplate.new(endpoint_url)
|
33
|
+
request_class.new(client: request_object.client, endpoint: tmpl.expand(params))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def endpoint_for(relationship)
|
38
|
+
url = url_for(relationship)
|
39
|
+
url && url.gsub(%r{https://api.github.com/},'')
|
40
|
+
end
|
41
|
+
|
42
|
+
def url_for(relationship)
|
43
|
+
field ="#{ relationship }_url"
|
44
|
+
|
45
|
+
if has_key?(field)
|
46
|
+
send(field)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Brief::GithubClient
|
2
|
+
class UserActivity < Request
|
3
|
+
requires :user
|
4
|
+
|
5
|
+
def endpoint
|
6
|
+
"users/#{user}/events"
|
7
|
+
end
|
8
|
+
|
9
|
+
def organization_repos
|
10
|
+
all.select do |item|
|
11
|
+
item.repo && item.repo.name.try(:match,org) rescue false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Brief::GithubClient
|
2
|
+
def self.default_organization
|
3
|
+
Brief.profile.github_organization
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.default_github_token
|
7
|
+
Brief.profile.github_token
|
8
|
+
end
|
9
|
+
|
10
|
+
# Brief.github_client is useful for backend apps where there is a Brief configuration profile
|
11
|
+
Brief.define_singleton_method(:github_client) do
|
12
|
+
Brief::GithubClient::Client.new(github_token: Brief.profile.github_token)
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_accessor :request_cache,
|
17
|
+
:response_cache,
|
18
|
+
:enable_caching
|
19
|
+
end
|
20
|
+
|
21
|
+
self.request_cache ||= {}
|
22
|
+
self.response_cache ||= {}
|
23
|
+
self.enable_caching = false
|
24
|
+
|
25
|
+
def self.clear_caches!
|
26
|
+
self.request_cache = {}
|
27
|
+
self.response_cache = {}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'brief/github_client/authentication'
|
32
|
+
require 'brief/github_client/client'
|
33
|
+
require 'brief/github_client/request'
|
34
|
+
require 'brief/github_client/request_wrapper'
|
35
|
+
require 'brief/github_client/response_object'
|
36
|
+
require 'brief/github_client/commands'
|
37
|
+
require 'brief/github_client/issues'
|
38
|
+
require 'brief/github_client/issue_labels'
|
39
|
+
require 'brief/github_client/repository_issues'
|
40
|
+
require 'brief/github_client/repository_milestones'
|
41
|
+
require 'brief/github_client/repository_labels'
|
42
|
+
require 'brief/github_client/milestone_issues'
|
43
|
+
require 'brief/github_client/user_issues'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Brief
|
2
|
+
module Handlers
|
3
|
+
class Base
|
4
|
+
attr_accessor :element, :prepared
|
5
|
+
|
6
|
+
def initialize(element)
|
7
|
+
@element = element
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing meth, *args, &block
|
11
|
+
if element.respond_to?(meth)
|
12
|
+
return element.send(meth, *args, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def content
|
19
|
+
element.content
|
20
|
+
end
|
21
|
+
|
22
|
+
def document
|
23
|
+
get_manager.try :document
|
24
|
+
end
|
25
|
+
|
26
|
+
def document_settings
|
27
|
+
document.try(:settings)
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_manager &block
|
31
|
+
if block_given?
|
32
|
+
@get_manager = block
|
33
|
+
end
|
34
|
+
|
35
|
+
@get_manager && @get_manager.call()
|
36
|
+
end
|
37
|
+
|
38
|
+
def parent &block
|
39
|
+
if block_given?
|
40
|
+
@find_parent_block = block
|
41
|
+
end
|
42
|
+
|
43
|
+
@find_parent_block && @find_parent_block.call(element.parent_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Your handler should implement this method, the goal is to transform the
|
47
|
+
# element we are passed into an object that is suitable for whatever integration
|
48
|
+
# you have planned for it. For example, preparing the necessary attributes for
|
49
|
+
# a submission to github issues or milestones
|
50
|
+
def prepare!
|
51
|
+
@prepared ||= begin
|
52
|
+
element
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Do whatever you want to do with the prepared element
|
57
|
+
def handle!
|
58
|
+
prepare!
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Brief
|
2
|
+
module Handlers
|
3
|
+
class GithubIssue < Base
|
4
|
+
def prepare!
|
5
|
+
@prepared ||= begin
|
6
|
+
if github_api_issue.present?
|
7
|
+
element.issue_url = github_api_issue.url
|
8
|
+
element.issue_number = github_api_issue.number
|
9
|
+
else
|
10
|
+
element.issue_url = create_in_github_api.url
|
11
|
+
element.issue_number = github_api_issue.number
|
12
|
+
end
|
13
|
+
|
14
|
+
element
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_in_github_api
|
19
|
+
issues_api.create_object(body: body, title: title, milestone: milestone)
|
20
|
+
end
|
21
|
+
|
22
|
+
def body
|
23
|
+
element.content
|
24
|
+
end
|
25
|
+
|
26
|
+
def milestone
|
27
|
+
parent && parent.milestone_number
|
28
|
+
end
|
29
|
+
|
30
|
+
def issues_api refresh=false
|
31
|
+
Brief.repository_issues.tap do |api|
|
32
|
+
refresh ? api.refresh : api
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def github_api_issue
|
37
|
+
issues_api(true).all.detect {|issue| issue.title == title }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Brief
|
2
|
+
module Handlers
|
3
|
+
class GithubMilestone < Base
|
4
|
+
def prepare!
|
5
|
+
@prepared ||= begin
|
6
|
+
if github_api_milestone.present?
|
7
|
+
element.milestone_url = github_api_milestone.url
|
8
|
+
element.milestone_number = github_api_milestone.number
|
9
|
+
else
|
10
|
+
element.milestone_url = create_in_github_api.url
|
11
|
+
element.milestone_number = github_api_milestone.number
|
12
|
+
end
|
13
|
+
|
14
|
+
element
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_in_github_api
|
19
|
+
@github_api_milestone ||= milestones_api.create_object(body: body, title: title)
|
20
|
+
end
|
21
|
+
|
22
|
+
def body
|
23
|
+
document.parser.content_lines_under(title).reject(&:blank?).join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
def milestones_api refresh=false
|
27
|
+
Brief.repository_milestones.tap do |api|
|
28
|
+
refresh ? api.refresh : api
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def github_api_milestone
|
33
|
+
@github_api_milestone ||= milestones_api(true).all.detect {|milestone| milestone.title == element.title }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/brief/line.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Brief
|
2
|
+
class Line
|
3
|
+
attr_reader :_content,
|
4
|
+
:number,
|
5
|
+
:code
|
6
|
+
|
7
|
+
attr_accessor :raw
|
8
|
+
|
9
|
+
HeadingRegex = /^#+\s*/
|
10
|
+
CodeBlockRegex = /^\s*```/
|
11
|
+
|
12
|
+
def initialize(content, number, code=false)
|
13
|
+
@_content = content
|
14
|
+
@number = number + 1
|
15
|
+
@code = code
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
content
|
20
|
+
end
|
21
|
+
|
22
|
+
def type
|
23
|
+
case
|
24
|
+
when code?
|
25
|
+
"content"
|
26
|
+
when _content.match(HeadingRegex)
|
27
|
+
"heading"
|
28
|
+
when _content.match(CodeBlockRegex)
|
29
|
+
"code_block_marker"
|
30
|
+
else
|
31
|
+
"content"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def content
|
36
|
+
_content.gsub(HeadingRegex, '')
|
37
|
+
end
|
38
|
+
|
39
|
+
def raw
|
40
|
+
@raw || _content
|
41
|
+
end
|
42
|
+
|
43
|
+
def heading?
|
44
|
+
!code? && type == "heading"
|
45
|
+
end
|
46
|
+
|
47
|
+
def level
|
48
|
+
_content.match(HeadingRegex).to_s.count('#')
|
49
|
+
end
|
50
|
+
|
51
|
+
def sort_index
|
52
|
+
[level, number]
|
53
|
+
end
|
54
|
+
|
55
|
+
def line_number
|
56
|
+
number
|
57
|
+
end
|
58
|
+
|
59
|
+
def content?
|
60
|
+
type == "content" || type == "code_block_marker"
|
61
|
+
end
|
62
|
+
|
63
|
+
def code?
|
64
|
+
!!@code
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|