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.
- 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
data/lib/brief/git.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Brief
|
2
|
+
module Git
|
3
|
+
def self.current_github_repository
|
4
|
+
return nil unless Pathname(Dir.pwd).join('.git').exist?
|
5
|
+
|
6
|
+
return @current_github_repository if @current_github_repository
|
7
|
+
|
8
|
+
github_remote = `git remote`.lines.to_a.detect do |remote_name|
|
9
|
+
`git remote show #{ remote_name }`.match(/github\.com/)
|
10
|
+
end
|
11
|
+
|
12
|
+
if github_remote
|
13
|
+
lines = `git remote show #{ github_remote }`.lines.to_a.map(&:strip)
|
14
|
+
line = lines.detect {|l| l.match(/Fetch URL/)}
|
15
|
+
@current_github_repository = line.split(':').last.gsub(/\.git$/,'')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/brief/github.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'brief/formatters/github_milestone_rollup'
|
2
|
+
require 'brief/handlers/github_issue'
|
3
|
+
require 'brief/handlers/github_milestone'
|
4
|
+
require 'brief/handlers/github_wiki'
|
5
|
+
|
6
|
+
module Brief
|
7
|
+
def self.github_nickname
|
8
|
+
(Brief.profile.github_nickname rescue nil) || Brief::Git.current_github_repository.try(:split, '/').try(:first)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.github_repository_name
|
12
|
+
(Brief.profile.github_repository_name rescue nil) || Brief::Git.current_github_repository.try(:split, '/').try(:last)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.repository_issues
|
16
|
+
@repository_issues ||= Brief::GithubClient::RepositoryIssues.new(user: github_nickname, repo: github_repository_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.repository_milestones
|
20
|
+
@repository_milestones ||= Brief::GithubClient::RepositoryMilestones.new(user: github_nickname, repo: github_repository_name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Brief.define "Project Overview" do
|
25
|
+
aliases :overview, :project_overview
|
26
|
+
|
27
|
+
before(:write) do
|
28
|
+
require "brief/github_client"
|
29
|
+
!Brief.config.github_repository.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
before(:publish) do
|
33
|
+
require "brief/github_client"
|
34
|
+
!Brief.config.github_repository.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
sample <<-EOF
|
38
|
+
# This is a project overview
|
39
|
+
|
40
|
+
A body paragraph or two here will be the content for a cover page. You can supply whatever background
|
41
|
+
info, domain knowledge requirements, processes, file dependencies and links, whatever.
|
42
|
+
|
43
|
+
The content for the nested milestones and issues below will be transformed into raw markdown
|
44
|
+
which will generate a status summary of the milestones and issues as they get created in the Github API.
|
45
|
+
|
46
|
+
## This is a milestone heading
|
47
|
+
|
48
|
+
A milestone is some logical way of grouping issues and attaching a date to them. You will write the body
|
49
|
+
of the individual issues and optionally assign them or label them by writing them below. When this brief
|
50
|
+
gets published, the issues will be created and added to the milestone.
|
51
|
+
|
52
|
+
### This is an issue that belongs to the above milestone
|
53
|
+
|
54
|
+
The content of your issue goes here. Normal github flavored markdown is supported.
|
55
|
+
EOF
|
56
|
+
|
57
|
+
levels do
|
58
|
+
order(2,3,1)
|
59
|
+
|
60
|
+
level(1) do
|
61
|
+
desc "Wiki Page"
|
62
|
+
define_handler :publish, "Brief::Handlers::GithubWiki"
|
63
|
+
replaces_items_from_level 2, :with => "Brief::Formatters::GithubMilestoneRollup"
|
64
|
+
end
|
65
|
+
|
66
|
+
level(2) do
|
67
|
+
desc "Github Milestone"
|
68
|
+
define_handler :publish, "Brief::Handlers::GithubMilestone"
|
69
|
+
replaces_items_from_level 3, :with => nil
|
70
|
+
end
|
71
|
+
|
72
|
+
level(3) do
|
73
|
+
desc "Github Issue"
|
74
|
+
define_handler :publish, "Brief::Handlers::GithubIssue"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Brief::GithubClient
|
2
|
+
class Authentication
|
3
|
+
attr_accessor :github_token, :options
|
4
|
+
|
5
|
+
InvalidAuth = Class.new(Exception)
|
6
|
+
|
7
|
+
def initialize(options={})
|
8
|
+
options.symbolize_keys! if options.is_a?(Hash)
|
9
|
+
@options = options
|
10
|
+
fetch_token
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def fetch_token
|
16
|
+
@github_token = case
|
17
|
+
when options.respond_to?(:github_token)
|
18
|
+
options.github_token
|
19
|
+
when options.is_a?(Hash) && options.has_key?(:github_token)
|
20
|
+
options[:github_token]
|
21
|
+
when options.is_a?(Hash) && (options.has_key?(:username) && options.has_key?(:password))
|
22
|
+
raise "Not implemented. use personal access tokens."
|
23
|
+
|
24
|
+
when "#{ENV['GITHUB_TOKEN']}".length > 1
|
25
|
+
ENV['GITHUB_TOKEN']
|
26
|
+
else
|
27
|
+
raise InvalidAuth
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'typhoeus'
|
2
|
+
|
3
|
+
class Brief::GithubClient::Client
|
4
|
+
attr_accessor :options, :cache
|
5
|
+
|
6
|
+
InvalidAuth = Class.new(Exception)
|
7
|
+
|
8
|
+
def initialize options={}
|
9
|
+
@options = options
|
10
|
+
|
11
|
+
if options[:auto]
|
12
|
+
options[:github_token] ||= Brief::GithubClient::Authentication.new.github_token
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def request_wrapper_class
|
17
|
+
Brief::GithubClient::RequestWrapper
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch(request_type,*args)
|
21
|
+
fetch_request_object(request_type, *args).to_object
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch_request_object request_type, *args
|
25
|
+
options = args.extract_options!
|
26
|
+
request_type = request_type.to_s.camelize
|
27
|
+
|
28
|
+
if request_klass = Brief::GithubClient.const_get(request_type) rescue nil
|
29
|
+
request_klass.new(options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def github_token
|
34
|
+
options.fetch(:github_token, impersonate_user.try(:github_token))
|
35
|
+
end
|
36
|
+
|
37
|
+
def impersonate_user
|
38
|
+
@impersonate_user ||= options.fetch(:user, nil)
|
39
|
+
end
|
40
|
+
|
41
|
+
def headers
|
42
|
+
base = {
|
43
|
+
"Authorization" => "token #{ github_token }",
|
44
|
+
"Accepts" => "application/json"
|
45
|
+
}
|
46
|
+
|
47
|
+
base.merge(options[:headers] || {}).stringify_keys
|
48
|
+
end
|
49
|
+
|
50
|
+
def anonymous?
|
51
|
+
!!(options[:public] || options[:anonymous])
|
52
|
+
end
|
53
|
+
|
54
|
+
def delete_request type, params={}
|
55
|
+
if !github_token.present? && !anonymous?
|
56
|
+
raise InvalidAuth
|
57
|
+
end
|
58
|
+
|
59
|
+
request_wrapper_class.new(type,params,headers).delete
|
60
|
+
end
|
61
|
+
|
62
|
+
def post_request type, params={}
|
63
|
+
if !github_token.present? && !anonymous?
|
64
|
+
raise InvalidAuth
|
65
|
+
end
|
66
|
+
|
67
|
+
request_wrapper_class.new(type,params,headers).post
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_request type, params={}
|
71
|
+
if !github_token.present? && !anonymous?
|
72
|
+
raise InvalidAuth
|
73
|
+
end
|
74
|
+
|
75
|
+
request_wrapper_class.new(type,params,headers).get
|
76
|
+
end
|
77
|
+
|
78
|
+
def update_request type, params={}
|
79
|
+
if !github_token.present? && !anonymous?
|
80
|
+
raise InvalidAuth
|
81
|
+
end
|
82
|
+
|
83
|
+
request_wrapper_class.new(type,params,headers).update
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Brief::GithubClient
|
2
|
+
class IssueLabels < Request
|
3
|
+
|
4
|
+
requires :org, :repo
|
5
|
+
|
6
|
+
def endpoint
|
7
|
+
"repos/#{ org }/#{ repo }/labels"
|
8
|
+
end
|
9
|
+
|
10
|
+
Defaults = {
|
11
|
+
# stage labels
|
12
|
+
"s:backlog" => "c7def8",
|
13
|
+
"s:greenlit" => "bfe5bf",
|
14
|
+
"s:review" => "fef2c0",
|
15
|
+
"s:in_progress" => "3ded58",
|
16
|
+
|
17
|
+
# priority labels
|
18
|
+
"p:1" => "e11d21",
|
19
|
+
"p:2" => "eb6420",
|
20
|
+
|
21
|
+
# type labels
|
22
|
+
"t:development" => "bada55",
|
23
|
+
"t:design" => "55adba",
|
24
|
+
"t:ux" => "2234fe",
|
25
|
+
"t:project" => "ae3498",
|
26
|
+
|
27
|
+
# acceptance labels
|
28
|
+
"a:approved" => "339933",
|
29
|
+
"a:rejected" => "993333"
|
30
|
+
}
|
31
|
+
|
32
|
+
def missing_defaults
|
33
|
+
current = all.collect(&:name)
|
34
|
+
@missing_defaults ||= Defaults.keys - current
|
35
|
+
end
|
36
|
+
|
37
|
+
def missing_defaults?
|
38
|
+
missing_defaults.length > 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_status_sort_labels
|
42
|
+
Defaults.each do |name, color|
|
43
|
+
create_or_update(name, color)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete_github_defaults
|
48
|
+
%w{bug duplicate enhancement invalid wontfix question}.each do |name|
|
49
|
+
destroy(name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_or_update name, color
|
54
|
+
existing = show(name)
|
55
|
+
|
56
|
+
unless existing.nil? || (existing.present? && existing.respond_to?(:message))
|
57
|
+
update(name, name: name, color: color)
|
58
|
+
return show(name)
|
59
|
+
end
|
60
|
+
|
61
|
+
create(name: name, color: color)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Brief::GithubClient
|
2
|
+
class Issues < Request
|
3
|
+
requires :org, :repo
|
4
|
+
|
5
|
+
def endpoint
|
6
|
+
case
|
7
|
+
when supplied_org && repo
|
8
|
+
"#{ supplied_org }/#{ repo }/issues"
|
9
|
+
when user && repo
|
10
|
+
"repos/#{ user }/#{ repo }/issues"
|
11
|
+
when supplied_org
|
12
|
+
"orgs/#{ supplied_org }/issues"
|
13
|
+
when user
|
14
|
+
"users/#{ user }/issues"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def params
|
19
|
+
@params.merge(sort:"updated")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Brief::GithubClient
|
2
|
+
class OrganizationRepositories < Request
|
3
|
+
requires :supplied_org
|
4
|
+
|
5
|
+
def endpoint
|
6
|
+
"orgs/#{ org }/repos"
|
7
|
+
end
|
8
|
+
|
9
|
+
def all
|
10
|
+
@all ||= self.result
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_list
|
14
|
+
all.map do |repository|
|
15
|
+
repository.slice("id","name","html_url","description","ssh_url")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Brief::GithubClient
|
2
|
+
class RepositoryLabels < Request
|
3
|
+
requires :org, :repo
|
4
|
+
|
5
|
+
def endpoint
|
6
|
+
"repos/#{ org }/#{ repo }/labels"
|
7
|
+
end
|
8
|
+
|
9
|
+
def stage_labels
|
10
|
+
@stage_labels ||= all.select {|label| label.name && label.name.match(/^s:/) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def priority_labels
|
14
|
+
@priority_labels ||= all.select {|label| label.name && label.name.match(/^p:/) }
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'escape_utils'
|
3
|
+
require 'uri_template'
|
4
|
+
|
5
|
+
class Brief::GithubClient::Request
|
6
|
+
|
7
|
+
MissingArguments = Class.new(Exception)
|
8
|
+
|
9
|
+
attr_accessor :options, :user, :org, :repo, :params, :headers, :github_token
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :_required_arguments
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.requires *args
|
16
|
+
(self._required_arguments = args).uniq!
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.required_arguments
|
20
|
+
Array(self._required_arguments).uniq
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(options={}, &block)
|
24
|
+
@options = options.with_indifferent_access.dup
|
25
|
+
@client, @user, @org, @repo, @github_token = options.values_at(:client, :user,:org,:repo,:github_token)
|
26
|
+
@params = options[:params] || {}
|
27
|
+
@headers = options[:headers] || {}
|
28
|
+
|
29
|
+
instance_eval(&blk) if block_given?
|
30
|
+
|
31
|
+
assert_valid_arguments!
|
32
|
+
end
|
33
|
+
|
34
|
+
def required_arguments
|
35
|
+
Array(self.class._required_arguments)
|
36
|
+
end
|
37
|
+
|
38
|
+
def with_valid_arguments &blk
|
39
|
+
instance_eval(&blk) if block_given?
|
40
|
+
assert_valid_arguments!
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def assert_valid_arguments!
|
45
|
+
return true if required_arguments.length > 0
|
46
|
+
|
47
|
+
valid = required_arguments.all? do |arg|
|
48
|
+
test = false
|
49
|
+
test = true if !!self.send(arg).present?
|
50
|
+
test = true if options.has_key?(arg)
|
51
|
+
|
52
|
+
test
|
53
|
+
end
|
54
|
+
|
55
|
+
raise MissingArguments unless valid
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_object
|
59
|
+
req = self
|
60
|
+
|
61
|
+
response_wrapper = lambda do |r|
|
62
|
+
Brief::GithubClient::ResponseObject.new(r).with_request_object(req)
|
63
|
+
end
|
64
|
+
|
65
|
+
return object.map(&response_wrapper) if object.is_a?(Array)
|
66
|
+
|
67
|
+
response_wrapper.call(object)
|
68
|
+
end
|
69
|
+
|
70
|
+
def object
|
71
|
+
records
|
72
|
+
end
|
73
|
+
|
74
|
+
def refresh
|
75
|
+
@request = nil
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def all
|
80
|
+
to_object
|
81
|
+
end
|
82
|
+
|
83
|
+
def create params={}
|
84
|
+
client.post_request(request_endpoint, params).request.run
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_object params={}
|
88
|
+
resp = create(params)
|
89
|
+
Hashie::Mash.new(JSON.parse(resp.body))
|
90
|
+
end
|
91
|
+
|
92
|
+
def update record_id, params={}
|
93
|
+
client.update_request("#{ request_endpoint }/#{ record_id }", params).request.run
|
94
|
+
end
|
95
|
+
|
96
|
+
def update_object record_id, params={}
|
97
|
+
resp = update(record_id, params)
|
98
|
+
Hashie::Mash.new(JSON.parse(resp.body))
|
99
|
+
end
|
100
|
+
|
101
|
+
def destroy record_id, params={}
|
102
|
+
client.delete_request("#{ request_endpoint }/#{ record_id }").request.run
|
103
|
+
end
|
104
|
+
|
105
|
+
def show record_id, params={}
|
106
|
+
client.get_request("#{ request_endpoint }/#{ record_id }", params).to_object
|
107
|
+
end
|
108
|
+
|
109
|
+
def client
|
110
|
+
return @client if @client
|
111
|
+
|
112
|
+
if impersonate_user.present?
|
113
|
+
@client = Brief::GithubClient::Client.new(user: impersonate_user, headers: headers, github_token: github_token)
|
114
|
+
end
|
115
|
+
|
116
|
+
@client = Brief.github_client
|
117
|
+
end
|
118
|
+
|
119
|
+
def records
|
120
|
+
@records = request.records
|
121
|
+
end
|
122
|
+
|
123
|
+
def results
|
124
|
+
records
|
125
|
+
end
|
126
|
+
|
127
|
+
def result
|
128
|
+
records
|
129
|
+
end
|
130
|
+
|
131
|
+
def request_endpoint
|
132
|
+
options.fetch(:endpoint, endpoint)
|
133
|
+
end
|
134
|
+
|
135
|
+
def request refresh=false
|
136
|
+
@request = nil if refresh
|
137
|
+
@request ||= client.get_request(request_endpoint, params)
|
138
|
+
end
|
139
|
+
|
140
|
+
def organization_or_user
|
141
|
+
supplied_org || user
|
142
|
+
end
|
143
|
+
|
144
|
+
def user_or_organization
|
145
|
+
supplied_user || supplied_org
|
146
|
+
end
|
147
|
+
|
148
|
+
def org
|
149
|
+
organization_or_user
|
150
|
+
end
|
151
|
+
|
152
|
+
# The idea of 'supplied' means it was provided to the object
|
153
|
+
# and not calculated in any way. This is used when determining
|
154
|
+
# the value for the endpoint, in the context of a github user vs github organization
|
155
|
+
def supplied_org
|
156
|
+
@org
|
157
|
+
end
|
158
|
+
|
159
|
+
def supplied_user
|
160
|
+
@user
|
161
|
+
end
|
162
|
+
|
163
|
+
def supplied_repo
|
164
|
+
@repo
|
165
|
+
end
|
166
|
+
|
167
|
+
protected
|
168
|
+
def github_token
|
169
|
+
@github_token || impersonate_user.try(:github_token) || Brief.config.github_token
|
170
|
+
end
|
171
|
+
|
172
|
+
def endpoint
|
173
|
+
"users/#{ user }"
|
174
|
+
end
|
175
|
+
|
176
|
+
def impersonate_user
|
177
|
+
if defined?(::User) && ::User.respond_to?(:find_by_github_nickname)
|
178
|
+
@impersonate_user ||= ::User.find_by_github_nickname(user)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|