ruby-appraiser 1.0.0
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.
- data/.gitignore +17 -0
- data/Gemfile +5 -0
- data/LICENSE-APACHE.md +13 -0
- data/LICENSE-MIT.md +22 -0
- data/LICENSE.md +4 -0
- data/README.md +77 -0
- data/Rakefile +2 -0
- data/bin/ruby-appraiser +17 -0
- data/lib/ruby-appraiser/adapter/line-length.rb +19 -0
- data/lib/ruby-appraiser/adapter.rb +88 -0
- data/lib/ruby-appraiser/appraisal.rb +126 -0
- data/lib/ruby-appraiser/cli.rb +98 -0
- data/lib/ruby-appraiser/defect.rb +33 -0
- data/lib/ruby-appraiser/git.rb +41 -0
- data/lib/ruby-appraiser/version.rb +5 -0
- data/lib/ruby-appraiser.rb +64 -0
- data/ruby-appraiser.gemspec +19 -0
- metadata +64 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE-APACHE.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2013 Simply Measured
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/LICENSE-MIT.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Simply Measured
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/LICENSE.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
RubyAppraiser
|
2
|
+
=============
|
3
|
+
|
4
|
+
So you have a big project and you want to improve the code quality? Sweet. Too
|
5
|
+
bad you'll get a million errors when you run [rubocop][], [reek][], or
|
6
|
+
[flog][], so it'll annoy you with information overload until you get fed up &
|
7
|
+
turn it off.
|
8
|
+
|
9
|
+
Enter: RubyAppraiser, a generic interface for attaching code-quality tools
|
10
|
+
that limits their output to the lines you're changing, which allows you to use
|
11
|
+
these tools to gradually heal projects. Add a pre-commit hook that rejects
|
12
|
+
defective contributions, level up to require entire touched files to be fixed,
|
13
|
+
or run several code-quality tools in a single command.
|
14
|
+
|
15
|
+
The filters currently provided are:
|
16
|
+
|
17
|
+
- all - (default) show all defects
|
18
|
+
- authored - all uncommitted defects
|
19
|
+
- staged - all staged defects
|
20
|
+
- touched - all defects in files that have been touched
|
21
|
+
|
22
|
+
Usage:
|
23
|
+
------
|
24
|
+
|
25
|
+
1. Include one or more adapters in your `Gemfile` or as development
|
26
|
+
dependencies of your gem. They'll make sure their dependencies (including
|
27
|
+
`ruby-appraiser` itself) are taken care of.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
gem 'ruby-appraiser-rubocop'
|
31
|
+
gem 'ruby-appraiser-reek'
|
32
|
+
```
|
33
|
+
|
34
|
+
2. Execute the appraiser:
|
35
|
+
|
36
|
+
```sh
|
37
|
+
bundle exec ruby-appraiser --mode=authored reek rubocop
|
38
|
+
```
|
39
|
+
|
40
|
+
The script will exit 0 IFF there are no matching defects from any of your
|
41
|
+
coverage tools. The tools themselves will respect any project-wide settings or
|
42
|
+
config files.
|
43
|
+
|
44
|
+
```
|
45
|
+
$ bundle exec ruby-appraiser --help
|
46
|
+
Usage: ruby-appraiser [inspector...] [options]
|
47
|
+
-v, --[no-]verbose Run verbosely
|
48
|
+
--list List available adapters
|
49
|
+
--silent Silence output
|
50
|
+
--mode=MODE Set the mode. [staged,authored,touched,all]
|
51
|
+
--git-hook Output a git hook with current comand to STDOUT
|
52
|
+
--all Run all available adapters.
|
53
|
+
```
|
54
|
+
|
55
|
+
Contributing:
|
56
|
+
-------------
|
57
|
+
|
58
|
+
1. Write an adapter! Take a look at the existing adapters for help.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
class Foo < RubyAppraiser::Adapter
|
62
|
+
def appraise
|
63
|
+
# ...
|
64
|
+
add_defect( file, line, description )
|
65
|
+
# ...
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
License
|
71
|
+
-------
|
72
|
+
See [LICENSE][]
|
73
|
+
|
74
|
+
[LICENSE]: LICENSE.md
|
75
|
+
[rubocop]: https://github.com/bbatslov/rubocop
|
76
|
+
[reek]: https://github.com/troessner/reek
|
77
|
+
[flog]: https://github.com/seattlerb/flog
|
data/Rakefile
ADDED
data/bin/ruby-appraiser
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
|
5
|
+
|
6
|
+
require 'ruby-appraiser'
|
7
|
+
require 'benchmark'
|
8
|
+
|
9
|
+
cli = RubyAppraiser::CLI.new
|
10
|
+
success = nil
|
11
|
+
|
12
|
+
time = Benchmark::realtime do
|
13
|
+
success = cli.run
|
14
|
+
end
|
15
|
+
|
16
|
+
puts "Finished in #{time} seconds" if cli.options[:verbose]
|
17
|
+
exit(success ? 0 : 1)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'ruby-appraiser'
|
4
|
+
|
5
|
+
class RubyAppraiser::Adapter::LineLength < RubyAppraiser::Adapter
|
6
|
+
def appraise
|
7
|
+
source_files.each do |source_file|
|
8
|
+
File.open(source_file) do |source|
|
9
|
+
source.lines.each_with_index do |line, number|
|
10
|
+
line_length = line.chomp.length
|
11
|
+
if line_length > 80
|
12
|
+
add_defect(source_file, number,
|
13
|
+
"Line too long [#{line_length}/80]")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'forwardable'
|
5
|
+
|
6
|
+
class RubyAppraiser::Adapter
|
7
|
+
class << self
|
8
|
+
def inherited(base)
|
9
|
+
registry << base
|
10
|
+
end
|
11
|
+
|
12
|
+
def registry
|
13
|
+
@registry ||= Set.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def adapter_type(type = nil)
|
17
|
+
@adapter_type = type.to_s unless type.nil?
|
18
|
+
|
19
|
+
@adapter_type or
|
20
|
+
self.name.split('::').last.
|
21
|
+
sub(/Adapter$/, '').
|
22
|
+
gsub(/[A-Z]+/, '-\0').
|
23
|
+
sub(/^-/, '').
|
24
|
+
downcase
|
25
|
+
end
|
26
|
+
|
27
|
+
def find(query)
|
28
|
+
registry.detect do |adapter|
|
29
|
+
adapter.adapter_type == query
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(query)
|
34
|
+
find(query) or
|
35
|
+
attempt_require_adapter(query, "ruby-appraiser/adapter/#{query}") or
|
36
|
+
attempt_require_adapter(query, "ruby-appraiser/#{query}") or
|
37
|
+
attempt_require_adapter(query, "ruby-appraiser-#{query}")
|
38
|
+
end
|
39
|
+
|
40
|
+
def all
|
41
|
+
# load relevant gems
|
42
|
+
scanner_pattern = /^(ruby-appraiser-([a-zA-Z0-9_\-]+))/
|
43
|
+
(`gem list --local`).scan(scanner_pattern) do |gem_name, adapter_type|
|
44
|
+
attempt_require_adapter(adapter_type, gem_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
# look in the adapter directory
|
48
|
+
Dir::glob(File::expand_path('../adapter/*.rb', __FILE__)) do |filepath|
|
49
|
+
require filepath
|
50
|
+
end
|
51
|
+
|
52
|
+
# return the registry
|
53
|
+
registry
|
54
|
+
end
|
55
|
+
|
56
|
+
def attempt_require_adapter(name, path)
|
57
|
+
require path and find(name)
|
58
|
+
rescue LoadError
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
def find!(query)
|
63
|
+
find(query) or raise ArgumentError, "Adapter '#{query}' not found."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(appraisal, options)
|
68
|
+
@appraisal = appraisal
|
69
|
+
@options = options.dup
|
70
|
+
end
|
71
|
+
|
72
|
+
def appraise
|
73
|
+
raise NotImplementedError,
|
74
|
+
"#{self.class.name} does not implement appraise."
|
75
|
+
end
|
76
|
+
|
77
|
+
extend Forwardable
|
78
|
+
|
79
|
+
def_delegators :@appraisal, :source_files,
|
80
|
+
:project_root,
|
81
|
+
:relative_path,
|
82
|
+
:add_defect,
|
83
|
+
:authored_lines,
|
84
|
+
:touched_files,
|
85
|
+
:relevant_files,
|
86
|
+
:relevant_lines
|
87
|
+
end
|
88
|
+
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class RubyAppraiser
|
4
|
+
class Appraisal
|
5
|
+
def initialize(options = {})
|
6
|
+
@options = options.dup.freeze
|
7
|
+
end
|
8
|
+
|
9
|
+
def mode
|
10
|
+
@options.fetch(:mode) { 'all' }
|
11
|
+
end
|
12
|
+
|
13
|
+
def options
|
14
|
+
@options.dup
|
15
|
+
end
|
16
|
+
|
17
|
+
def run!
|
18
|
+
appraisers.each do |appraiser|
|
19
|
+
appraiser.appraise
|
20
|
+
end unless relevant_files.empty?
|
21
|
+
|
22
|
+
@has_run = true
|
23
|
+
end
|
24
|
+
|
25
|
+
def success?
|
26
|
+
run! unless @has_run
|
27
|
+
|
28
|
+
defects.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
def defects
|
32
|
+
@defects ||= Set.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def adapters
|
36
|
+
@adapters ||= Set.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def appraisers
|
40
|
+
adapters.map do |adapter|
|
41
|
+
adapter.new(self, options)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_adapter(name)
|
46
|
+
Adapter::get(name).tap do |adapter|
|
47
|
+
adapter and self.adapters << adapter
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def source_files
|
52
|
+
Dir::glob(File::expand_path('**/*', project_root)).select do |filepath|
|
53
|
+
File::file? filepath and RubyAppraiser::rubytype? filepath
|
54
|
+
end.map { |path| relative_path path }
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_defect(defect, *args)
|
58
|
+
unless defect.kind_of? Defect
|
59
|
+
defect = Defect.new(defect, *args)
|
60
|
+
end
|
61
|
+
defects << defect if match?(defect.location)
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
defects.to_a.sort.map(&:to_s).join($/)
|
66
|
+
end
|
67
|
+
|
68
|
+
def summary
|
69
|
+
"#{defects.count} defects detected."
|
70
|
+
end
|
71
|
+
|
72
|
+
def project_root
|
73
|
+
@project_root ||= (`git rev-parse --show-toplevel`).chomp
|
74
|
+
end
|
75
|
+
|
76
|
+
def relative_path(path)
|
77
|
+
full_path = File::expand_path(path, project_root)
|
78
|
+
full_path[(project_root.length + 1)..-1]
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
def match?(location)
|
84
|
+
file, line = *location
|
85
|
+
relevant_lines[file].include? line
|
86
|
+
end
|
87
|
+
|
88
|
+
def relevant_files
|
89
|
+
relevant_lines.keys
|
90
|
+
end
|
91
|
+
|
92
|
+
def relevant_lines
|
93
|
+
case mode
|
94
|
+
when 'staged' then staged_authored_lines
|
95
|
+
when 'authored' then authored_lines
|
96
|
+
when 'touched' then all_lines_in touched_files
|
97
|
+
when 'all' then all_lines_in source_files
|
98
|
+
else raise ArgumentError, "Unsupported mode #{mode}."
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# return a hash.
|
103
|
+
# key is a filename
|
104
|
+
# value is a truebot
|
105
|
+
def all_lines_in(files)
|
106
|
+
infinite_set = (0 .. (1.0 / 0))
|
107
|
+
files.reduce(Hash.new { [] }) do |memo, file|
|
108
|
+
memo.merge!(file => infinite_set)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def touched_files
|
113
|
+
@touched_files ||= authored_lines.keys
|
114
|
+
end
|
115
|
+
|
116
|
+
def authored_lines
|
117
|
+
@authored_lines ||=
|
118
|
+
RubyAppraiser::Git::authored_lines
|
119
|
+
end
|
120
|
+
|
121
|
+
def staged_authored_lines
|
122
|
+
@staged_authored_lines ||=
|
123
|
+
RubyAppraiser::Git::authored_lines(staged: true)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'ruby-appraiser'
|
4
|
+
require 'optparse'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
class RubyAppraiser
|
8
|
+
class CLI
|
9
|
+
def initialize(args = ARGV)
|
10
|
+
@argv = ARGV.dup
|
11
|
+
args = @argv.dup # needed for --git-hook
|
12
|
+
@options = {}
|
13
|
+
adapters = Set.new
|
14
|
+
|
15
|
+
OptionParser.new do |opts|
|
16
|
+
opts.banner = "Usage: #{File::basename($0)} [inspector...] [options]"
|
17
|
+
opts.on('-v', '--[no-]verbose', 'Run verbosely') do |verbose|
|
18
|
+
@options[:verbose] = verbose
|
19
|
+
end
|
20
|
+
opts.on('--list', 'List available adapters') do |list|
|
21
|
+
puts available_adapters
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
opts.on('--silent', 'Silence output') do |silent|
|
25
|
+
@options[:silent] = true
|
26
|
+
end
|
27
|
+
opts.on('--mode=MODE',
|
28
|
+
'Set the mode. ' +
|
29
|
+
'[staged,authored,touched,all]') do |mode|
|
30
|
+
@options[:mode] = mode
|
31
|
+
end
|
32
|
+
opts.on('--git-hook',
|
33
|
+
'Output a git hook with current comand to STDOUT') do
|
34
|
+
command = $0
|
35
|
+
if (`which #{File::basename(command)}`).chomp == command
|
36
|
+
command = File::basename(command)
|
37
|
+
end
|
38
|
+
be = 'bundle exec'
|
39
|
+
hook_args = @argv.select { |arg| arg != '--git-hook' }
|
40
|
+
|
41
|
+
indented_git_hook = <<-EOGITHOOK
|
42
|
+
#!/bin/bash
|
43
|
+
echo -e "\\033[0;36mRuby Appraiser: running\\033[0m"
|
44
|
+
|
45
|
+
bundle exec #{command} #{hook_args.join(' ')}
|
46
|
+
|
47
|
+
result_code=$?
|
48
|
+
if [ $result_code -gt "0" ]; then
|
49
|
+
echo -en "\\033[0;31m" # RED
|
50
|
+
echo "[✘] Ruby Appraiser found newly-created defects and "
|
51
|
+
echo " has blocked your commit."
|
52
|
+
echo " Fix the defects and commit again."
|
53
|
+
echo " To bypass, commit again with --no-verify."
|
54
|
+
echo -en "\\033[0m" # RESET
|
55
|
+
exit $result_code
|
56
|
+
else
|
57
|
+
echo -en "\\033[0;32m" # GREEN
|
58
|
+
echo "[✔] Ruby Appraiser ok"
|
59
|
+
echo -en "\\033[0m" #RESET
|
60
|
+
fi
|
61
|
+
EOGITHOOK
|
62
|
+
|
63
|
+
indent = indented_git_hook.scan(/^\s*/).map(&:length).min
|
64
|
+
puts indented_git_hook.lines.map {|l| l[indent..-1]}.join
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
opts.on('--all', 'Run all available adapters.') do
|
68
|
+
adapters += available_adapters
|
69
|
+
end
|
70
|
+
end.parse!(args)
|
71
|
+
|
72
|
+
@appraisal = RubyAppraiser::Appraisal.new(options)
|
73
|
+
adapters += args
|
74
|
+
adapters.each do |adapter|
|
75
|
+
@appraisal.add_adapter(adapter) or
|
76
|
+
raise "Unknown adapter '#{adapter}'"
|
77
|
+
end
|
78
|
+
Dir::chdir((`git rev-parse --show-toplevel`).chomp)
|
79
|
+
end
|
80
|
+
|
81
|
+
def options
|
82
|
+
@options.dup
|
83
|
+
end
|
84
|
+
|
85
|
+
def run
|
86
|
+
@appraisal.run!
|
87
|
+
puts @appraisal unless @options[:silent]
|
88
|
+
puts @appraisal.summary if @options[:verbose]
|
89
|
+
@appraisal.success?
|
90
|
+
rescue Object
|
91
|
+
puts "#{@appraisal.class.name} caught #{$!} at #{$!.backtrace.first}."
|
92
|
+
end
|
93
|
+
|
94
|
+
def available_adapters
|
95
|
+
@available_adapters ||= RubyAppraiser::Adapter::all.map(&:adapter_type)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class RubyAppraiser
|
4
|
+
class Defect
|
5
|
+
def initialize(file, line, description)
|
6
|
+
@location = [file, line.to_i].freeze
|
7
|
+
@description = description.dup.freeze
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :location
|
11
|
+
attr_reader :description
|
12
|
+
|
13
|
+
def file
|
14
|
+
@location[0]
|
15
|
+
end
|
16
|
+
|
17
|
+
def line
|
18
|
+
@location[1]
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
"#{file}:#{line} #{description}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
self.to_s == other.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def <=>(other)
|
30
|
+
self.to_s <=> other.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class RubyAppraiser
|
4
|
+
module Git
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def authored_lines(options = {})
|
8
|
+
diff_command = ['git diff']
|
9
|
+
if options[:range]
|
10
|
+
diff_command << options[:range]
|
11
|
+
else
|
12
|
+
diff_command << (options[:staged] ? '--cached' : 'HEAD')
|
13
|
+
end
|
14
|
+
|
15
|
+
diff_output = IO.popen(diff_command.join(' ')) { |io| io.read }
|
16
|
+
|
17
|
+
current_path, current_line = nil, nil
|
18
|
+
authored_lines = Hash.new { |hash, key| hash[key] = [] }
|
19
|
+
|
20
|
+
diff_output.lines do |line|
|
21
|
+
case line
|
22
|
+
when /^---/ then next
|
23
|
+
when /^\+\+\+ (?:b\/)?(.*)/
|
24
|
+
current_path = Regexp::last_match(1)
|
25
|
+
when /-[0-9]+(?:,[0-9]+)? \+([0-9]+)((,[0-9]+)?)/
|
26
|
+
current_line = Regexp::last_match(1).to_i
|
27
|
+
else
|
28
|
+
next if line.start_with? '-'
|
29
|
+
authored_lines[current_path] << current_line if line[0] == '+'
|
30
|
+
current_line += 1 unless current_line.nil?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
authored_lines.default_proc = Proc.new { [] }
|
35
|
+
authored_lines.reject do |filepath, lines|
|
36
|
+
not File::file? filepath or
|
37
|
+
not RubyAppraiser::rubytype? filepath
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'ruby-appraiser/version'
|
4
|
+
|
5
|
+
class RubyAppraiser
|
6
|
+
|
7
|
+
autoload :Adapter, 'ruby-appraiser/adapter'
|
8
|
+
autoload :Appraisal, 'ruby-appraiser/appraisal'
|
9
|
+
autoload :CLI, 'ruby-appraiser/cli'
|
10
|
+
autoload :Defect, 'ruby-appraiser/defect'
|
11
|
+
autoload :Git, 'ruby-appraiser/git'
|
12
|
+
|
13
|
+
def initialize(options)
|
14
|
+
@options = options.dup
|
15
|
+
end
|
16
|
+
|
17
|
+
def options
|
18
|
+
@options.dup
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def appraisal
|
23
|
+
unless @appraisal
|
24
|
+
@appraisal = Appraisal.new(options)
|
25
|
+
|
26
|
+
unless @appraisal.relevant_files.empty?
|
27
|
+
appraisers(@appraisal).each(&:appraise)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@appraisal
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def rubytypes
|
36
|
+
%w(
|
37
|
+
*.rb
|
38
|
+
*.gemspec
|
39
|
+
Capfile
|
40
|
+
Gemfile
|
41
|
+
Rakefile
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def rubytype?(filepath)
|
46
|
+
# true if the extension matches
|
47
|
+
filename = File::basename(filepath)
|
48
|
+
return true if rubytypes.any? do |rubytype|
|
49
|
+
File::fnmatch(rubytype, filename)
|
50
|
+
end
|
51
|
+
|
52
|
+
# true if file has a ruby shebang
|
53
|
+
begin
|
54
|
+
return true if File.open(filepath) do |file|
|
55
|
+
file.read(20).chomp =~ /\A#\!.+ruby/
|
56
|
+
end
|
57
|
+
rescue Errno::ENOENT
|
58
|
+
rescue ArgumentError # invalid byte sequence
|
59
|
+
end
|
60
|
+
|
61
|
+
false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ruby-appraiser/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'ruby-appraiser'
|
8
|
+
gem.version = RubyAppraiser::VERSION
|
9
|
+
gem.authors = ['Ryan Biesemeyer']
|
10
|
+
gem.email = ['ryan@simplymeasured.com']
|
11
|
+
gem.description = 'Run multiple code-quality tools against staged changes'
|
12
|
+
gem.summary = 'A Common interface/executor for code quality tools'
|
13
|
+
gem.homepage = 'https://github.com/simplymeasured'
|
14
|
+
|
15
|
+
gem.files = `git ls-files | grep -v '^ruby-appraiser-'`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-appraiser
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ryan Biesemeyer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-01 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Run multiple code-quality tools against staged changes
|
15
|
+
email:
|
16
|
+
- ryan@simplymeasured.com
|
17
|
+
executables:
|
18
|
+
- ruby-appraiser
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- .gitignore
|
23
|
+
- Gemfile
|
24
|
+
- LICENSE-APACHE.md
|
25
|
+
- LICENSE-MIT.md
|
26
|
+
- LICENSE.md
|
27
|
+
- README.md
|
28
|
+
- Rakefile
|
29
|
+
- bin/ruby-appraiser
|
30
|
+
- lib/ruby-appraiser.rb
|
31
|
+
- lib/ruby-appraiser/adapter.rb
|
32
|
+
- lib/ruby-appraiser/adapter/line-length.rb
|
33
|
+
- lib/ruby-appraiser/appraisal.rb
|
34
|
+
- lib/ruby-appraiser/cli.rb
|
35
|
+
- lib/ruby-appraiser/defect.rb
|
36
|
+
- lib/ruby-appraiser/git.rb
|
37
|
+
- lib/ruby-appraiser/version.rb
|
38
|
+
- ruby-appraiser.gemspec
|
39
|
+
homepage: https://github.com/simplymeasured
|
40
|
+
licenses: []
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.8.24
|
60
|
+
signing_key:
|
61
|
+
specification_version: 3
|
62
|
+
summary: A Common interface/executor for code quality tools
|
63
|
+
test_files: []
|
64
|
+
has_rdoc:
|