brief 0.0.1 → 0.0.2

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