brief 0.0.1 → 0.0.2

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.
Files changed (71) hide show
  1. checksums.yaml +5 -13
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +81 -0
  4. data/Guardfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +71 -0
  7. data/Rakefile +11 -0
  8. data/bin/brief +35 -0
  9. data/brief-0.0.1.gem +0 -0
  10. data/brief.gemspec +33 -0
  11. data/examples/project_overview.md +23 -0
  12. data/lib/brief/cli/commands/config.rb +40 -0
  13. data/lib/brief/cli/commands/publish.rb +27 -0
  14. data/lib/brief/cli/commands/write.rb +26 -0
  15. data/lib/brief/configuration.rb +134 -0
  16. data/lib/brief/document.rb +68 -0
  17. data/lib/brief/dsl.rb +0 -0
  18. data/lib/brief/formatters/base.rb +12 -0
  19. data/lib/brief/formatters/github_milestone_rollup.rb +52 -0
  20. data/lib/brief/git.rb +19 -0
  21. data/lib/brief/github/wiki.rb +9 -0
  22. data/lib/brief/github.rb +78 -0
  23. data/lib/brief/github_client/authentication.rb +32 -0
  24. data/lib/brief/github_client/client.rb +86 -0
  25. data/lib/brief/github_client/commands.rb +5 -0
  26. data/lib/brief/github_client/issue_labels.rb +65 -0
  27. data/lib/brief/github_client/issues.rb +22 -0
  28. data/lib/brief/github_client/milestone_issues.rb +13 -0
  29. data/lib/brief/github_client/organization_activity.rb +9 -0
  30. data/lib/brief/github_client/organization_issues.rb +13 -0
  31. data/lib/brief/github_client/organization_repositories.rb +20 -0
  32. data/lib/brief/github_client/organization_users.rb +9 -0
  33. data/lib/brief/github_client/repository_events.rb +8 -0
  34. data/lib/brief/github_client/repository_issue_events.rb +9 -0
  35. data/lib/brief/github_client/repository_issues.rb +8 -0
  36. data/lib/brief/github_client/repository_labels.rb +18 -0
  37. data/lib/brief/github_client/repository_milestones.rb +9 -0
  38. data/lib/brief/github_client/request.rb +181 -0
  39. data/lib/brief/github_client/request_wrapper.rb +121 -0
  40. data/lib/brief/github_client/response_object.rb +50 -0
  41. data/lib/brief/github_client/single_repository.rb +9 -0
  42. data/lib/brief/github_client/user_activity.rb +16 -0
  43. data/lib/brief/github_client/user_gists.rb +9 -0
  44. data/lib/brief/github_client/user_info.rb +9 -0
  45. data/lib/brief/github_client/user_issues.rb +13 -0
  46. data/lib/brief/github_client/user_organizations.rb +9 -0
  47. data/lib/brief/github_client/user_repositories.rb +9 -0
  48. data/lib/brief/github_client.rb +43 -0
  49. data/lib/brief/handlers/base.rb +62 -0
  50. data/lib/brief/handlers/github_issue.rb +41 -0
  51. data/lib/brief/handlers/github_milestone.rb +37 -0
  52. data/lib/brief/handlers/github_wiki.rb +11 -0
  53. data/lib/brief/line.rb +69 -0
  54. data/lib/brief/parser.rb +354 -0
  55. data/lib/brief/publisher/handler_manager.rb +47 -0
  56. data/lib/brief/publisher.rb +142 -0
  57. data/lib/brief/tree.rb +42 -0
  58. data/lib/brief/version.rb +9 -0
  59. data/lib/brief.rb +56 -0
  60. data/lib/core_ext.rb +37 -0
  61. data/spec/fixtures/front_end_tutorial.md +33 -0
  62. data/spec/fixtures/generated/project_overview.json +0 -0
  63. data/spec/fixtures/generator_dsl_example.rb +22 -0
  64. data/spec/fixtures/project_overview.md +48 -0
  65. data/spec/fixtures/sample.md +19 -0
  66. data/spec/lib/brief/document_spec.rb +35 -0
  67. data/spec/lib/brief/dsl_spec.rb +21 -0
  68. data/spec/lib/brief/line_spec.rb +11 -0
  69. data/spec/lib/brief/parser_spec.rb +12 -0
  70. data/spec/spec_helper.rb +25 -0
  71. 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,9 @@
1
+ module Brief::GithubClient
2
+ class SingleRepository < Request
3
+ requires :org, :repo
4
+
5
+ def endpoint
6
+ "repos/#{ org }/#{ repo }"
7
+ end
8
+ end
9
+ 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,9 @@
1
+ module Brief::GithubClient
2
+ class UserGists < Request
3
+ requires :user
4
+
5
+ def endpoint
6
+ "users/#{ user }/gists"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Brief::GithubClient
2
+ class UserInfo < Request
3
+ requires :user
4
+
5
+ def endpoint
6
+ "users/#{ user }"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Brief::GithubClient
2
+ class UserIssues < Request
3
+ requires :user
4
+
5
+ def endpoint
6
+ "users/#{ user }/issues"
7
+ end
8
+
9
+ def params
10
+ @params.merge(sort:"updated")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Brief::GithubClient
2
+ class UserOrganizations < Request
3
+ requires :user
4
+
5
+ def endpoint
6
+ "users/#{ user }/orgs"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Brief::GithubClient
2
+ class UserRepositories < Request
3
+ requires :user
4
+
5
+ def endpoint
6
+ "users/#{ user }/repos"
7
+ end
8
+ end
9
+ 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
@@ -0,0 +1,11 @@
1
+ module Brief
2
+ module Handlers
3
+ class GithubWiki < Base
4
+ def prepare!
5
+ @prepared ||= begin
6
+ element
7
+ end
8
+ end
9
+ end
10
+ end
11
+ 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
+