gh_inspector 1.0.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 +123 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +89 -0
- data/Rakefile +52 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/gh_inspector.gemspec +24 -0
- data/lib/gh_inspector.rb +8 -0
- data/lib/gh_inspector/evidence.rb +89 -0
- data/lib/gh_inspector/exception_hound.rb +45 -0
- data/lib/gh_inspector/inspector.rb +68 -0
- data/lib/gh_inspector/sidekick.rb +110 -0
- data/lib/gh_inspector/version.rb +3 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3f8de4f9d4d5514333162e28e5720005e56269c9
|
4
|
+
data.tar.gz: e419d38377a555adeb73a106204353df7aef5284
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e47a02594b03d101277e3a24e3f57b06aab704dd521b251bab3ec4971837809a005cbb0328112f4f6730083272491ea3ed7acb5bb038d184357574a8931680a
|
7
|
+
data.tar.gz: b252704668fd6a34cfd5c990e632a41a63d9ef8fa6aca64663f7d54ce7f16f20ed742ba32dfc50d7a931ca613cdb100e82a2c5c3d3bcedfee7e55d349c4ff9a5
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,123 @@
|
|
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
|
121
|
+
|
122
|
+
Lint/UnusedMethodArgument:
|
123
|
+
Enabled: false
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# The Issues Inspector
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'gh_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 `GhInspector::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 'gh_inspector'
|
29
|
+
inspector = GhInspector::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 'gh_inspector'
|
64
|
+
inspector = GhInspector::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
|
+
## Vision
|
84
|
+
|
85
|
+
I don't expect this project to grow too much, there's space around improving the search query for an exception, mainly. Other than that the project is effectively done and just needs some production usage to iron out any kinks. This project is well tested, and has zero dependencies.
|
86
|
+
|
87
|
+
## Contributing
|
88
|
+
|
89
|
+
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/gh_inspector/inspector.rb", "lib/gh_inspector/sidekick.rb", "lib/gh_inspector/evidence.rb"]
|
31
|
+
docs = YARD::Registry.load(files, true)
|
32
|
+
|
33
|
+
usage = "\n\n"
|
34
|
+
usage << "#### The Inspector\n\n"
|
35
|
+
usage << docs.at("GhInspector::Inspector").docstring
|
36
|
+
usage << "\n"
|
37
|
+
|
38
|
+
usage << "#### Presenting Your Report \n\n"
|
39
|
+
evidence = docs.at("GhInspector::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
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'gh_inspector'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
require 'pry'
|
10
|
+
Pry.start
|
data/bin/setup
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gh_inspector/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'gh_inspector'
|
8
|
+
spec.version = GhInspector::VERSION
|
9
|
+
spec.authors = ['Orta Therox', 'Felix Krause']
|
10
|
+
spec.email = ['orta.therox@gmail.com', 'gh_inspector@krausefx.com']
|
11
|
+
|
12
|
+
spec.summary = 'Search through GitHub issues for your project for existing issues about a Ruby Error.'
|
13
|
+
spec.homepage = 'https://github.com/orta/gh_inspector'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = 'exe'
|
17
|
+
spec.require_paths = ['lib']
|
18
|
+
|
19
|
+
spec.add_development_dependency 'bundler', '~> 1.11'
|
20
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
21
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
22
|
+
spec.add_development_dependency 'pry'
|
23
|
+
spec.add_development_dependency 'rubocop'
|
24
|
+
end
|
data/lib/gh_inspector.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'gh_inspector/version'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module GhInspector
|
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 'gh_inspector'
|
17
|
+
# inspector = GhInspector::Inspector.new "orta", "eigen"
|
18
|
+
# inspector.search_query "Someone set us up the bomb", ArtsyUI.new
|
19
|
+
# ```
|
20
|
+
#
|
21
|
+
|
22
|
+
NUMBER_OF_ISSUES_INLINE = 3
|
23
|
+
|
24
|
+
class Evidence
|
25
|
+
# Called just as the investigation has begun.
|
26
|
+
def inspector_started_query(query, inspector)
|
27
|
+
puts "Looking for related GitHub issues on #{inspector.repo_owner}/#{inspector.repo_name}..."
|
28
|
+
puts "Search query: #{query}" if inspector.verbose
|
29
|
+
puts ""
|
30
|
+
end
|
31
|
+
|
32
|
+
# Called once the inspector has recieved a report with more than one issue.
|
33
|
+
def inspector_successfully_recieved_report(report, inspector)
|
34
|
+
report.issues[0..(NUMBER_OF_ISSUES_INLINE - 1)].each { |issue| print_issue_full(issue) }
|
35
|
+
|
36
|
+
if report.issues.count > NUMBER_OF_ISSUES_INLINE
|
37
|
+
puts "and #{report.total_results - NUMBER_OF_ISSUES_INLINE} more at: #{report.url}"
|
38
|
+
puts ""
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Called once the report has been recieved, but when there are no issues found.
|
43
|
+
def inspector_recieved_empty_report(report, inspector)
|
44
|
+
puts "Found no similar issues. To create a new issue, please visit:"
|
45
|
+
puts "https://github.com/#{inspector.repo_owner}/#{inspector.repo_name}/issues/new"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Called when there have been networking issues in creating the report.
|
49
|
+
def inspector_could_not_create_report(error, query, inspector)
|
50
|
+
puts "Could not access the GitHub API, you may have better luck via the website."
|
51
|
+
puts "https://github.com/#{inspector.repo_owner}/#{inspector.repo_name}/search?q=#{query}&type=Issues&utf8=✓"
|
52
|
+
puts "Error: #{error.name}"
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def print_issue_full(issue)
|
58
|
+
puts " - #{issue.title}"
|
59
|
+
puts " #{issue.html_url} [#{issue.state}] [#{issue.comments} comment#{issue.comments == 1 ? '' : 's'}]"
|
60
|
+
puts " #{Time.parse(issue.updated_at).to_pretty}"
|
61
|
+
puts ""
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Taken from https://stackoverflow.com/questions/195740/how-do-you-do-relative-time-in-rails
|
67
|
+
|
68
|
+
module PrettyDate
|
69
|
+
def to_pretty
|
70
|
+
a = (Time.now - self).to_i
|
71
|
+
|
72
|
+
case a
|
73
|
+
when 0 then 'just now'
|
74
|
+
when 1 then 'a second ago'
|
75
|
+
when 2..59 then a.to_s + ' seconds ago'
|
76
|
+
when 60..119 then 'a minute ago' # 120 = 2 minutes
|
77
|
+
when 120..3540 then (a / 60).to_i.to_s + ' minutes ago'
|
78
|
+
when 3541..7100 then 'an hour ago' # 3600 = 1 hour
|
79
|
+
when 7101..82_800 then ((a + 99) / 3600).to_i.to_s + ' hours ago'
|
80
|
+
when 82_801..172_000 then 'a day ago' # 86400 = 1 day
|
81
|
+
when 172_001..518_400 then ((a + 800) / (60 * 60 * 24)).to_i.to_s + ' days ago'
|
82
|
+
when 518_400..1_036_800 then 'a week ago'
|
83
|
+
when 1_036_801..4_147_204 then ((a + 180_000) / (60 * 60 * 24 * 7)).to_i.to_s + ' weeks ago'
|
84
|
+
else strftime("%d %b %Y")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
Time.send :include, PrettyDate
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module GhInspector
|
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
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Note that the README is generated from the class comments, so it's a bit
|
2
|
+
# wider scope than your average class comment.
|
3
|
+
|
4
|
+
module GhInspector
|
5
|
+
# To get started using The Issues Inspector, you will need to
|
6
|
+
# create an inspector instance. This class is main public API for querying issues.
|
7
|
+
#
|
8
|
+
# #### Getting Started
|
9
|
+
#
|
10
|
+
# Create an instance of `GhInspector::Inspector`, you can then ask it to search
|
11
|
+
# based on your raised exception, or as a direct query yourself.
|
12
|
+
#
|
13
|
+
# ``` ruby
|
14
|
+
# require 'gh_inspector'
|
15
|
+
# inspector = GhInspector::Inspector.new "orta", "eigen"
|
16
|
+
# inspector.search_query "Someone set us up the bomb"
|
17
|
+
# ```
|
18
|
+
#
|
19
|
+
# By default this would output:
|
20
|
+
#
|
21
|
+
# ```
|
22
|
+
# Looking for related issues on CocoaPods/CocoaPods...
|
23
|
+
#
|
24
|
+
# - undefined method `to_ary' for #<Pod::Specification name="iVersion">Did you mean? to_query
|
25
|
+
# https://github.com/CocoaPods/CocoaPods/issues/4748 [closed] [1 comment]
|
26
|
+
#
|
27
|
+
# - NoMethodError - undefined method `to_ary' for Pod EAIntroView
|
28
|
+
# https://github.com/CocoaPods/CocoaPods/issues/4391 [closed] [15 comments]
|
29
|
+
#
|
30
|
+
# - Do a search on GitHub for issues relating to a crash?
|
31
|
+
# https://github.com/CocoaPods/CocoaPods/issues/4391 [open] [3 comments]
|
32
|
+
#
|
33
|
+
# and 10 more at:
|
34
|
+
# https://github.com/CocoaPods/CocoaPods/search?q=undefined+method+%60to_ary%27&type=Issues
|
35
|
+
# ```
|
36
|
+
#
|
37
|
+
|
38
|
+
class Inspector
|
39
|
+
attr_accessor :repo_owner, :repo_name, :query, :sidekick, :verbose
|
40
|
+
|
41
|
+
# Class init function with a "orta/project" style string
|
42
|
+
def self.from_slug(slug)
|
43
|
+
details = slug.split '/'
|
44
|
+
Inspector.new details.first, details.last
|
45
|
+
end
|
46
|
+
|
47
|
+
# Init function with "orta", "project"
|
48
|
+
def initialize(repo_owner, repo_name, verbose: false)
|
49
|
+
self.repo_owner = repo_owner
|
50
|
+
self.repo_name = repo_name
|
51
|
+
self.verbose = verbose
|
52
|
+
self.sidekick = Sidekick.new(self, repo_owner, repo_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Will do some magic to try and pull out a reasonable search query
|
56
|
+
# for an exception, then searches with that
|
57
|
+
def search_exception(exception, delegate = nil)
|
58
|
+
query = ExceptionHound.new(exception).query
|
59
|
+
search_query(query, delegate)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Queries for an specific search string
|
63
|
+
def search_query(query, delegate = nil)
|
64
|
+
delegate ||= Evidence.new
|
65
|
+
sidekick.search(query, delegate)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module GhInspector
|
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
|
+
def verbose
|
46
|
+
self.inspector.verbose
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
require 'json'
|
52
|
+
|
53
|
+
# Generates a URL for the request
|
54
|
+
def url_for_request(query, sort_by: nil, order: nil)
|
55
|
+
url = "https://api.github.com/search/issues?q="
|
56
|
+
url += URI.escape(query)
|
57
|
+
url += "+repo:#{repo_owner}/#{repo_name}"
|
58
|
+
url += "&sort=#{sort_by}" if sort_by
|
59
|
+
url += "&order=#{order}" if order
|
60
|
+
|
61
|
+
url
|
62
|
+
end
|
63
|
+
|
64
|
+
# Gets the search results
|
65
|
+
def get_api_results(url)
|
66
|
+
uri = URI.parse(url)
|
67
|
+
puts "URL: #{url}" if self.verbose
|
68
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
69
|
+
http.use_ssl = true
|
70
|
+
|
71
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
72
|
+
response = http.request(request)
|
73
|
+
|
74
|
+
JSON.parse(response.body)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Converts a GitHub search JSON into a InspectionReport
|
78
|
+
def parse_results(query, results)
|
79
|
+
report = InspectionReport.new
|
80
|
+
report.url = "https://github.com/#{repo_owner}/#{repo_name}/search?q=#{query}&type=Issues&utf8=✓"
|
81
|
+
report.query = query
|
82
|
+
report.total_results = results['total_count']
|
83
|
+
report.issues = results['items'].map { |item| Issue.new(item) }
|
84
|
+
report
|
85
|
+
end
|
86
|
+
|
87
|
+
def validate_delegate(delegate)
|
88
|
+
e = Evidence.new
|
89
|
+
protocol = e.public_methods false
|
90
|
+
protocol.each do |m|
|
91
|
+
raise "#{delegate} does not handle #{m}" unless delegate.methods.include? m
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class InspectionReport
|
97
|
+
attr_accessor :issues, :url, :query, :total_results
|
98
|
+
end
|
99
|
+
|
100
|
+
class Issue
|
101
|
+
attr_accessor :title, :number, :html_url, :state, :body, :comments, :updated_at
|
102
|
+
|
103
|
+
# Hash -> public attributes
|
104
|
+
def initialize(*h)
|
105
|
+
if h.length == 1 && h.first.kind_of?(Hash)
|
106
|
+
h.first.each { |k, v| send("#{k}=", v) if public_methods.include?("#{k}=".to_sym) }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gh_inspector
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Orta Therox
|
8
|
+
- Felix Krause
|
9
|
+
autorequire:
|
10
|
+
bindir: exe
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-06-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.11'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.11'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '10.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '10.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: pry
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rubocop
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
description:
|
85
|
+
email:
|
86
|
+
- orta.therox@gmail.com
|
87
|
+
- gh_inspector@krausefx.com
|
88
|
+
executables: []
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- ".gitignore"
|
93
|
+
- ".rspec"
|
94
|
+
- ".rubocop.yml"
|
95
|
+
- ".travis.yml"
|
96
|
+
- Gemfile
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- bin/console
|
100
|
+
- bin/setup
|
101
|
+
- gh_inspector.gemspec
|
102
|
+
- lib/gh_inspector.rb
|
103
|
+
- lib/gh_inspector/evidence.rb
|
104
|
+
- lib/gh_inspector/exception_hound.rb
|
105
|
+
- lib/gh_inspector/inspector.rb
|
106
|
+
- lib/gh_inspector/sidekick.rb
|
107
|
+
- lib/gh_inspector/version.rb
|
108
|
+
homepage: https://github.com/orta/gh_inspector
|
109
|
+
licenses: []
|
110
|
+
metadata: {}
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 2.2.2
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: Search through GitHub issues for your project for existing issues about a
|
131
|
+
Ruby Error.
|
132
|
+
test_files: []
|