gh-issues-inspector 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.rubocop.yml +120 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +85 -0
- data/Rakefile +52 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/gh-issues-inspector.gemspec +25 -0
- data/lib/evidence.rb +85 -0
- data/lib/exception_hound.rb +45 -0
- data/lib/inspector.rb +73 -0
- data/lib/inspector/version.rb +3 -0
- data/lib/sidekick.rb +101 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3fd9721c6b868db5ec4786787fcc9ffd2e8522a7
|
4
|
+
data.tar.gz: 531b0f5e2b0b5ae52c8597dbe2483f7ceef06011
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e4f9986e0e09d55122852a3eab9104caf82227228ccc97b8693969c19893f2f79c82f3c41e69ac1b4b0bfea14778a9ca8ad80ed8ec1397c193e3db98ec6c412
|
7
|
+
data.tar.gz: e3e04cd2a582ddb4df0f6033a63673757f2c3198335889b47f20749051182fdfcd3ed21cc97d13436f676d1a6fec940328a06262d5b24aa69d6f87a59ff511f9
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
Style/ClassVars:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Style/ClassCheck:
|
5
|
+
EnforcedStyle: kind_of?
|
6
|
+
|
7
|
+
# Cop supports --auto-correct.
|
8
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
9
|
+
Style/BracesAroundHashParameters:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Lint/UselessAssignment:
|
13
|
+
Exclude:
|
14
|
+
- 'spec/**/*'
|
15
|
+
|
16
|
+
# Cop supports --auto-correct.
|
17
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
18
|
+
Style/IndentHash:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/RaiseArgs:
|
22
|
+
EnforcedStyle: exploded
|
23
|
+
|
24
|
+
Style/DoubleNegation:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Lint/HandleExceptions:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
# Cop supports --auto-correct.
|
31
|
+
Lint/UnusedBlockArgument:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
# Needed for $verbose
|
35
|
+
Style/GlobalVars:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
Style/FileName:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
# $? Exit
|
42
|
+
Style/SpecialGlobalVars:
|
43
|
+
Enabled: false
|
44
|
+
|
45
|
+
# the let(:key) { ... } should be allowed in tests
|
46
|
+
Lint/ParenthesesAsGroupedExpression:
|
47
|
+
Exclude:
|
48
|
+
- 'spec/**/*'
|
49
|
+
|
50
|
+
# options.rb might be large, we know that
|
51
|
+
Metrics/MethodLength:
|
52
|
+
Max: 60
|
53
|
+
Exclude:
|
54
|
+
- 'lib/*/options.rb'
|
55
|
+
|
56
|
+
# Both string notations are okay
|
57
|
+
Style/StringLiterals:
|
58
|
+
Enabled: false
|
59
|
+
|
60
|
+
# The %w might be confusing for new users
|
61
|
+
Style/WordArray:
|
62
|
+
MinSize: 19
|
63
|
+
|
64
|
+
# Not a good thing
|
65
|
+
Style/RedundantSelf:
|
66
|
+
Enabled: false
|
67
|
+
|
68
|
+
# raise and fail are both okay
|
69
|
+
Style/SignalException:
|
70
|
+
Enabled: false
|
71
|
+
|
72
|
+
# Better too much 'return' than one missing
|
73
|
+
Style/RedundantReturn:
|
74
|
+
Enabled: false
|
75
|
+
|
76
|
+
# Having if in the same line might not always be good
|
77
|
+
Style/IfUnlessModifier:
|
78
|
+
Enabled: false
|
79
|
+
|
80
|
+
# That looks wrong
|
81
|
+
Style/AlignHash:
|
82
|
+
Enabled: false
|
83
|
+
|
84
|
+
# and and or is okay
|
85
|
+
Style/AndOr:
|
86
|
+
Enabled: false
|
87
|
+
|
88
|
+
# Offense count: 20
|
89
|
+
Metrics/AbcSize:
|
90
|
+
Max: 60
|
91
|
+
|
92
|
+
# Configuration parameters: CountComments.
|
93
|
+
Metrics/ClassLength:
|
94
|
+
Max: 320
|
95
|
+
|
96
|
+
Metrics/CyclomaticComplexity:
|
97
|
+
Max: 17
|
98
|
+
|
99
|
+
# Configuration parameters: AllowURI, URISchemes.
|
100
|
+
Metrics/LineLength:
|
101
|
+
Max: 370
|
102
|
+
|
103
|
+
# Configuration parameters: CountKeywordArgs.
|
104
|
+
Metrics/ParameterLists:
|
105
|
+
Max: 17
|
106
|
+
|
107
|
+
Metrics/PerceivedComplexity:
|
108
|
+
Max: 18
|
109
|
+
|
110
|
+
Style/DotPosition:
|
111
|
+
Enabled: false
|
112
|
+
|
113
|
+
Style/GuardClause:
|
114
|
+
Enabled: false
|
115
|
+
|
116
|
+
Style/Documentation:
|
117
|
+
Enabled: false
|
118
|
+
|
119
|
+
Style/ZeroLengthPredicate:
|
120
|
+
Enabled: false
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# The Issues Inspector
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'gh-issues-inspector'
|
9
|
+
```
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
#### The Inspector
|
18
|
+
|
19
|
+
To get started using The Issues Inspector, you will need to
|
20
|
+
create an inspector instance. This class is main public API for querying issues.
|
21
|
+
|
22
|
+
#### Getting Started
|
23
|
+
|
24
|
+
Create an instance of `Inspector::Inspector`, you can then ask it to search
|
25
|
+
based on your raised exception, or as a direct query yourself.
|
26
|
+
|
27
|
+
``` ruby
|
28
|
+
require 'inspector'
|
29
|
+
inspector = Inspector::Inspector.new "orta", "eigen"
|
30
|
+
inspector.search_query "Someone set us up the bomb"
|
31
|
+
```
|
32
|
+
|
33
|
+
By default this would output:
|
34
|
+
|
35
|
+
```
|
36
|
+
Looking for related issues on CocoaPods/CocoaPods...
|
37
|
+
|
38
|
+
- undefined method `to_ary' for #<Pod::Specification name="iVersion">Did you mean? to_query
|
39
|
+
https://github.com/CocoaPods/CocoaPods/issues/4748 [closed] [1 comment]
|
40
|
+
|
41
|
+
- NoMethodError - undefined method `to_ary' for Pod EAIntroView
|
42
|
+
https://github.com/CocoaPods/CocoaPods/issues/4391 [closed] [15 comments]
|
43
|
+
|
44
|
+
- Do a search on GitHub for issues relating to a crash?
|
45
|
+
https://github.com/CocoaPods/CocoaPods/issues/4391 [open] [3 comments]
|
46
|
+
|
47
|
+
and 10 more at:
|
48
|
+
https://github.com/CocoaPods/CocoaPods/search?q=undefined+method+%60to_ary%27&type=Issues
|
49
|
+
```
|
50
|
+
#### Presenting Your Report
|
51
|
+
|
52
|
+
The default user interface for the inspector, its public API should be
|
53
|
+
considered the protocol for other classes wanting to provide a user interface.
|
54
|
+
|
55
|
+
Your custom objects will be verified at runtime that they conform to the protocol.
|
56
|
+
|
57
|
+
You can see the default implmentation at
|
58
|
+
[lib/evidence.rb](/orta/gh-issues-inspector/tree/master/lib/evidence.rb).
|
59
|
+
|
60
|
+
Both `search_query` and `search_exception` take your custom delegate as a 2nd optional parameter.
|
61
|
+
|
62
|
+
``` ruby
|
63
|
+
require 'inspector'
|
64
|
+
inspector = Inspector::Inspector.new "orta", "eigen"
|
65
|
+
inspector.search_query "Someone set us up the bomb", ArtsyUI.new
|
66
|
+
```
|
67
|
+
Protocol for custom objects:
|
68
|
+
|
69
|
+
- `inspector_started_query(_query, inspector)` - Called just as the investigation has begun.
|
70
|
+
- `inspector_successfully_recieved_report(report, _inspector)` - Called once the inspector has recieved a report with more than one issue.
|
71
|
+
- `inspector_recieved_empty_report(_report, inspector)` - Called once the report has been recieved, but when there are no issues found.
|
72
|
+
- `inspector_could_not_create_report(error, query, inspector)` - Called when there have been networking issues in creating the report.
|
73
|
+
|
74
|
+
|
75
|
+
## Development
|
76
|
+
|
77
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
78
|
+
|
79
|
+
The usage section of this README is generated from inline documentation inside the classes, to update it run `rake readme`.
|
80
|
+
|
81
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
82
|
+
|
83
|
+
## Contributing
|
84
|
+
|
85
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/orta/gh-issues-inspector.
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:specs)
|
6
|
+
|
7
|
+
task default: :spec
|
8
|
+
|
9
|
+
task :spec do
|
10
|
+
Rake::Task['specs'].invoke
|
11
|
+
Rake::Task['rubocop'].invoke
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Run RuboCop on the lib/specs directory'
|
15
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
16
|
+
task.patterns = ['lib/**/*.rb', 'spec/**/*.rb']
|
17
|
+
end
|
18
|
+
|
19
|
+
task :readme do
|
20
|
+
readme = File.open("README.md", 'rb', &:read)
|
21
|
+
|
22
|
+
start_split = "## Usage"
|
23
|
+
end_split = "## Development"
|
24
|
+
|
25
|
+
start = readme.split(start_split)[0]
|
26
|
+
rest = readme.split(start_split)[1]
|
27
|
+
finale = rest.split(end_split)[1]
|
28
|
+
|
29
|
+
require 'yard'
|
30
|
+
files = ["lib/inspector.rb", "lib/sidekick.rb", "lib/evidence.rb"]
|
31
|
+
docs = YARD::Registry.load(files, true)
|
32
|
+
|
33
|
+
usage = "\n\n"
|
34
|
+
usage << "#### The Inspector\n\n"
|
35
|
+
usage << docs.at("Inspector::Inspector").docstring
|
36
|
+
usage << "\n"
|
37
|
+
|
38
|
+
usage << "#### Presenting Your Report \n\n"
|
39
|
+
evidence = docs.at("Inspector::Evidence")
|
40
|
+
usage << evidence.docstring
|
41
|
+
|
42
|
+
usage << "\nProtocol for custom objects:\n\n"
|
43
|
+
evidence.children.each do |method|
|
44
|
+
next unless method.name.to_s.start_with? "inspector"
|
45
|
+
params = method.parameters.flatten.compact
|
46
|
+
usage << " - `#{method.name}(#{params.join ', '})` - #{method.docstring}\n"
|
47
|
+
end
|
48
|
+
usage << "\n\n"
|
49
|
+
|
50
|
+
new_file = start + start_split + usage + end_split + finale
|
51
|
+
File.open("README.md", 'w') { |f| f.write new_file }
|
52
|
+
end
|
data/bin/console
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'inspector/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'gh-issues-inspector'
|
8
|
+
spec.version = Inspector::VERSION
|
9
|
+
spec.authors = ['Orta Therox']
|
10
|
+
spec.email = ['orta.therox@gmail.com']
|
11
|
+
|
12
|
+
spec.summary = 'Search through GitHub issues for your project for existing issues about a Ruby Error.'
|
13
|
+
spec.description = 'Search through GitHub issues for your project for existing issues about a Ruby Error.'
|
14
|
+
spec.homepage = 'https://github.com/orta/gh-issues-inspector'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = 'exe'
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler', '~> 1.11'
|
21
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
22
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
23
|
+
spec.add_development_dependency 'pry'
|
24
|
+
spec.add_development_dependency 'rubocop'
|
25
|
+
end
|
data/lib/evidence.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'inspector/version'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Inspector
|
5
|
+
# The default user interface for the inspector, its public API should be
|
6
|
+
# considered the protocol for other classes wanting to provide a user interface.
|
7
|
+
#
|
8
|
+
# Your custom objects will be verified at runtime that they conform to the protocol.
|
9
|
+
#
|
10
|
+
# You can see the default implmentation at
|
11
|
+
# [lib/evidence.rb](/orta/gh-issues-inspector/tree/master/lib/evidence.rb).
|
12
|
+
#
|
13
|
+
# Both `search_query` and `search_exception` take your custom delegate as a 2nd optional parameter.
|
14
|
+
#
|
15
|
+
# ``` ruby
|
16
|
+
# require 'inspector'
|
17
|
+
# inspector = Inspector::Inspector.new "orta", "eigen"
|
18
|
+
# inspector.search_query "Someone set us up the bomb", ArtsyUI.new
|
19
|
+
# ```
|
20
|
+
#
|
21
|
+
|
22
|
+
class Evidence
|
23
|
+
# Called just as the investigation has begun.
|
24
|
+
def inspector_started_query(_query, inspector)
|
25
|
+
puts "Looking for related issues on #{inspector.repo_owner}/#{inspector.repo_name}..."
|
26
|
+
end
|
27
|
+
|
28
|
+
# Called once the inspector has recieved a report with more than one issue.
|
29
|
+
def inspector_successfully_recieved_report(report, _inspector)
|
30
|
+
report.issues[0..2].each { |issue| print_issue_full(issue) }
|
31
|
+
|
32
|
+
if report.issues.count > 3
|
33
|
+
puts "and #{report.total_results - 3} more at:"
|
34
|
+
puts report.url
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Called once the report has been recieved, but when there are no issues found.
|
39
|
+
def inspector_recieved_empty_report(_report, inspector)
|
40
|
+
puts "Found no similar issues. To create a new issue, please visit:"
|
41
|
+
puts "https://github.com/#{inspector.repo_owner}/#{inspector.repo_name}/issues/new"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Called when there have been networking issues in creating the report.
|
45
|
+
def inspector_could_not_create_report(error, query, inspector)
|
46
|
+
puts "Could not access the GitHub API, you may have better luck via the website."
|
47
|
+
puts "https://github.com/#{inspector.repo_owner}/#{inspector.repo_name}/search?q=#{query}&type=Issues&utf8=✓"
|
48
|
+
puts "Error: #{error.name}"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def print_issue_full(issue)
|
54
|
+
puts " - #{issue.title}"
|
55
|
+
puts " #{issue.html_url} [#{issue.state}] [#{issue.comments} comment#{issue.comments == 1 ? '' : 's'}]"
|
56
|
+
puts " #{Time.parse(issue.updated_at).to_pretty}"
|
57
|
+
puts ""
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Taken from http://stackoverflow.com/questions/195740/how-do-you-do-relative-time-in-rails
|
63
|
+
|
64
|
+
module PrettyDate
|
65
|
+
def to_pretty
|
66
|
+
a = (Time.now - self).to_i
|
67
|
+
|
68
|
+
case a
|
69
|
+
when 0 then 'just now'
|
70
|
+
when 1 then 'a second ago'
|
71
|
+
when 2..59 then a.to_s + ' seconds ago'
|
72
|
+
when 60..119 then 'a minute ago' # 120 = 2 minutes
|
73
|
+
when 120..3540 then (a / 60).to_i.to_s + ' minutes ago'
|
74
|
+
when 3541..7100 then 'an hour ago' # 3600 = 1 hour
|
75
|
+
when 7101..82_800 then ((a + 99) / 3600).to_i.to_s + ' hours ago'
|
76
|
+
when 82_801..172_000 then 'a day ago' # 86400 = 1 day
|
77
|
+
when 172_001..518_400 then ((a + 800) / (60 * 60 * 24)).to_i.to_s + ' days ago'
|
78
|
+
when 518_400..1_036_800 then 'a week ago'
|
79
|
+
when 1_036_801..4_147_204 then ((a + 180_000) / (60 * 60 * 24 * 7)).to_i.to_s + ' weeks ago'
|
80
|
+
else strftime("%d %b %Y")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
Time.send :include, PrettyDate
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Inspector
|
2
|
+
class ExceptionHound
|
3
|
+
attr_accessor :message
|
4
|
+
|
5
|
+
def initialize(error)
|
6
|
+
self.message = find_message error
|
7
|
+
end
|
8
|
+
|
9
|
+
def find_message(error)
|
10
|
+
error.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def query
|
14
|
+
undefined
|
15
|
+
simple_nil
|
16
|
+
demangle_instances
|
17
|
+
|
18
|
+
message
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def undefined
|
24
|
+
self.message = message.gsub "undefined local variable or method", "undefined"
|
25
|
+
end
|
26
|
+
|
27
|
+
def simple_nil
|
28
|
+
self.message = message.gsub "nil:NilClass", "nil"
|
29
|
+
end
|
30
|
+
|
31
|
+
def demangle_instances
|
32
|
+
self.message = regex_replace(message, /(#<.*>)/, /#<(.*):/)
|
33
|
+
end
|
34
|
+
|
35
|
+
def regex_replace(string, find, replace)
|
36
|
+
if string.match find
|
37
|
+
full = string.match(find)[0]
|
38
|
+
simple = string.match(replace)[1]
|
39
|
+
string.gsub full, simple
|
40
|
+
else
|
41
|
+
string
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/inspector.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'inspector/version'
|
2
|
+
require 'sidekick'
|
3
|
+
require 'evidence'
|
4
|
+
require 'evidence'
|
5
|
+
require 'exception_hound'
|
6
|
+
|
7
|
+
# Note that the README is generated from the class comments, so it's a bit
|
8
|
+
# wider scope than your average class comment.
|
9
|
+
|
10
|
+
module Inspector
|
11
|
+
# To get started using The Issues Inspector, you will need to
|
12
|
+
# create an inspector instance. This class is main public API for querying issues.
|
13
|
+
#
|
14
|
+
# #### Getting Started
|
15
|
+
#
|
16
|
+
# Create an instance of `Inspector::Inspector`, you can then ask it to search
|
17
|
+
# based on your raised exception, or as a direct query yourself.
|
18
|
+
#
|
19
|
+
# ``` ruby
|
20
|
+
# require 'inspector'
|
21
|
+
# inspector = Inspector::Inspector.new "orta", "eigen"
|
22
|
+
# inspector.search_query "Someone set us up the bomb"
|
23
|
+
# ```
|
24
|
+
#
|
25
|
+
# By default this would output:
|
26
|
+
#
|
27
|
+
# ```
|
28
|
+
# Looking for related issues on CocoaPods/CocoaPods...
|
29
|
+
#
|
30
|
+
# - undefined method `to_ary' for #<Pod::Specification name="iVersion">Did you mean? to_query
|
31
|
+
# https://github.com/CocoaPods/CocoaPods/issues/4748 [closed] [1 comment]
|
32
|
+
#
|
33
|
+
# - NoMethodError - undefined method `to_ary' for Pod EAIntroView
|
34
|
+
# https://github.com/CocoaPods/CocoaPods/issues/4391 [closed] [15 comments]
|
35
|
+
#
|
36
|
+
# - Do a search on GitHub for issues relating to a crash?
|
37
|
+
# https://github.com/CocoaPods/CocoaPods/issues/4391 [open] [3 comments]
|
38
|
+
#
|
39
|
+
# and 10 more at:
|
40
|
+
# https://github.com/CocoaPods/CocoaPods/search?q=undefined+method+%60to_ary%27&type=Issues
|
41
|
+
# ```
|
42
|
+
#
|
43
|
+
|
44
|
+
class Inspector
|
45
|
+
attr_accessor :repo_owner, :repo_name, :query, :sidekick
|
46
|
+
|
47
|
+
# Class init function with a "orta/project" style string
|
48
|
+
def self.from_slug(slug)
|
49
|
+
details = slug.split '/'
|
50
|
+
Inspector.new details.first, details.last
|
51
|
+
end
|
52
|
+
|
53
|
+
# Init function with "orta", "project"
|
54
|
+
def initialize(repo_owner, repo_name)
|
55
|
+
self.repo_owner = repo_owner
|
56
|
+
self.repo_name = repo_name
|
57
|
+
self.sidekick = Sidekick.new(self, repo_owner, repo_name)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Will do some magic to try and pull out a reasonable search query
|
61
|
+
# for an exception, then searches with that
|
62
|
+
def search_exception(exception, delegate = nil)
|
63
|
+
query = ExceptionHound.new(exception).query
|
64
|
+
search_query(query, delegate)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Queries for an specific search string
|
68
|
+
def search_query(query, _delegate = nil)
|
69
|
+
delegate ||= Evidence.new
|
70
|
+
sidekick.search(query, delegate)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/sidekick.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Inspector
|
5
|
+
# The Sidekick is the one who does all the real work.
|
6
|
+
# They take the query, get the GitHub API results, etc
|
7
|
+
# then pass them back to the inspector who gets the public API credit.
|
8
|
+
|
9
|
+
class Sidekick
|
10
|
+
attr_accessor :repo_owner, :repo_name, :inspector
|
11
|
+
|
12
|
+
def initialize(inspector, repo_owner, repo_name)
|
13
|
+
self.inspector = inspector
|
14
|
+
self.repo_owner = repo_owner
|
15
|
+
self.repo_name = repo_name
|
16
|
+
end
|
17
|
+
|
18
|
+
# Searches for a query, with a UI delegate
|
19
|
+
def search(query, delegate)
|
20
|
+
validate_delegate(delegate)
|
21
|
+
|
22
|
+
delegate.inspector_started_query(query, inspector)
|
23
|
+
url = url_for_request query
|
24
|
+
|
25
|
+
begin
|
26
|
+
results = get_api_results url
|
27
|
+
rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
|
28
|
+
delegate.inspector_could_not_create_report(e, query, inspector)
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
report = parse_results query, results
|
33
|
+
|
34
|
+
# TODO: progress callback
|
35
|
+
|
36
|
+
if report.issues.any?
|
37
|
+
delegate.inspector_successfully_recieved_report(report, inspector)
|
38
|
+
else
|
39
|
+
delegate.inspector_recieved_empty_report(report, inspector)
|
40
|
+
end
|
41
|
+
|
42
|
+
report
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
require 'json'
|
48
|
+
|
49
|
+
# Generates a URL for the request
|
50
|
+
def url_for_request(query)
|
51
|
+
root = 'https://api.github.com/' \
|
52
|
+
"search/issues?q=#{query}%2Brepo%3A#{repo_owner}%2F#{repo_name}&sort=created&order=asc"
|
53
|
+
URI.escape root
|
54
|
+
end
|
55
|
+
|
56
|
+
# Gets the search results
|
57
|
+
def get_api_results(url)
|
58
|
+
uri = URI.parse url
|
59
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
60
|
+
http.use_ssl = true
|
61
|
+
|
62
|
+
request = Net::HTTP::Get.new uri.request_uri
|
63
|
+
response = http.request request
|
64
|
+
|
65
|
+
JSON.parse response.body
|
66
|
+
end
|
67
|
+
|
68
|
+
# Converts a GitHub search JSON into a InspectionReport
|
69
|
+
def parse_results(query, results)
|
70
|
+
report = InspectionReport.new
|
71
|
+
report.url = "https://github.com/#{repo_owner}/#{repo_name}/search?q=#{query}&type=Issues&utf8=✓"
|
72
|
+
report.query = query
|
73
|
+
report.total_results = results['total_count']
|
74
|
+
report.issues = results['items'].map { |item| Issue.new(item) }
|
75
|
+
report
|
76
|
+
end
|
77
|
+
|
78
|
+
def validate_delegate(delegate)
|
79
|
+
e = Evidence.new
|
80
|
+
protocol = e.public_methods false
|
81
|
+
protocol.each do |m|
|
82
|
+
raise "#{delegate} does not handle #{m}" unless delegate.methods.include? m
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class InspectionReport
|
88
|
+
attr_accessor :issues, :url, :query, :total_results
|
89
|
+
end
|
90
|
+
|
91
|
+
class Issue
|
92
|
+
attr_accessor :title, :number, :html_url, :state, :body, :comments, :updated_at
|
93
|
+
|
94
|
+
# Hash -> public attributes
|
95
|
+
def initialize(*h)
|
96
|
+
if h.length == 1 && h.first.kind_of?(Hash)
|
97
|
+
h.first.each { |k, v| send("#{k}=", v) if public_methods.include?("#{k}=".to_sym) }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gh-issues-inspector
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Orta Therox
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Search through GitHub issues for your project for existing issues about
|
84
|
+
a Ruby Error.
|
85
|
+
email:
|
86
|
+
- orta.therox@gmail.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- ".rubocop.yml"
|
94
|
+
- ".travis.yml"
|
95
|
+
- Gemfile
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- bin/console
|
99
|
+
- bin/setup
|
100
|
+
- gh-issues-inspector.gemspec
|
101
|
+
- lib/evidence.rb
|
102
|
+
- lib/exception_hound.rb
|
103
|
+
- lib/inspector.rb
|
104
|
+
- lib/inspector/version.rb
|
105
|
+
- lib/sidekick.rb
|
106
|
+
homepage: https://github.com/orta/gh-issues-inspector
|
107
|
+
licenses: []
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.2.2
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Search through GitHub issues for your project for existing issues about a
|
129
|
+
Ruby Error.
|
130
|
+
test_files: []
|
131
|
+
has_rdoc:
|