sabat-guard-rubocop 0.1.1
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/CHANGELOG.md +26 -0
- data/Gemfile +9 -0
- data/Guardfile +14 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +18 -0
- data/guard-rubocop.gemspec +32 -0
- data/lib/guard/rubocop.rb +85 -0
- data/lib/guard/rubocop/runner.rb +93 -0
- data/lib/guard/rubocop/templates/Guardfile +4 -0
- data/lib/guard/rubocop/version.rb +13 -0
- data/spec/guard/rubocop/runner_spec.rb +302 -0
- data/spec/guard/rubocop_spec.rb +202 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/silence_output.rb +18 -0
- metadata +195 -0
data/CHANGELOG.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# CHANGELOG
|
2
|
+
|
3
|
+
## v0.1.0
|
4
|
+
|
5
|
+
* Update RuboCop dependency to 0.9 or later and earlier than 1.0
|
6
|
+
* Rework with JSON formatter
|
7
|
+
* Change the displayed text to "Inspecting ..."
|
8
|
+
* Print relative file paths when they are under current working directory
|
9
|
+
|
10
|
+
## v0.0.4
|
11
|
+
|
12
|
+
* Specify dependency on rubocop gem as under 0.9.0
|
13
|
+
* Force RuboCop to colorize output even though output is not TTY
|
14
|
+
* Revert "Use rubocop 0.6.1 --no-color option instead of uncoloring colored output"
|
15
|
+
|
16
|
+
## v0.0.3
|
17
|
+
|
18
|
+
* Use rubocop 0.6.1 --no-color option instead of uncoloring colored output
|
19
|
+
|
20
|
+
## v0.0.2
|
21
|
+
|
22
|
+
* Fix capitalization of the name RuboCop in notification title
|
23
|
+
|
24
|
+
## v0.0.1
|
25
|
+
|
26
|
+
* Initial release
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :rspec, all_after_pass: true, all_on_start: true, keep_failed: true do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
watch(%r{^spec/support/.+\.rb$}) { "spec" }
|
9
|
+
end
|
10
|
+
|
11
|
+
guard :rubocop do
|
12
|
+
watch(%r{.+\.rb$})
|
13
|
+
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
14
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Yuji Nakayama
|
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/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
[](http://badge.fury.io/rb/guard-rubocop) [](https://gemnasium.com/yujinakayama/guard-rubocop) [](https://travis-ci.org/yujinakayama/guard-rubocop) [](https://coveralls.io/r/yujinakayama/guard-rubocop) [](https://codeclimate.com/github/yujinakayama/guard-rubocop)
|
2
|
+
|
3
|
+
# Guard::Rubocop
|
4
|
+
|
5
|
+
Guard::Rubocop allows you to automatically check Ruby code style with [RuboCop](https://github.com/bbatsov/rubocop) when files are modified.
|
6
|
+
|
7
|
+
Tested on MRI 1.9 and MRI 2.0, according to RuboCop.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Please make sure to have [Guard](https://github.com/guard/guard) installed before continue.
|
12
|
+
|
13
|
+
Add `guard-rubocop` to your `Gemfile`:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
group :development do
|
17
|
+
gem 'guard-rubocop'
|
18
|
+
end
|
19
|
+
```
|
20
|
+
|
21
|
+
and then execute:
|
22
|
+
|
23
|
+
```sh
|
24
|
+
$ bundle install
|
25
|
+
```
|
26
|
+
|
27
|
+
or install it yourself as:
|
28
|
+
|
29
|
+
```sh
|
30
|
+
$ gem install guard-rubocop
|
31
|
+
```
|
32
|
+
|
33
|
+
Add the default Guard::Rubocop definition to your `Guardfile` by running:
|
34
|
+
|
35
|
+
```sh
|
36
|
+
$ guard init rubocop
|
37
|
+
```
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
Please read the [Guard usage documentation](https://github.com/guard/guard#readme).
|
42
|
+
|
43
|
+
## Options
|
44
|
+
|
45
|
+
You can pass some options in `Guardfile`:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
guard :rubocop, all_on_start: false, notification: true do
|
49
|
+
# ...
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
### Available Options
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
all_on_start: true # Check all files at Guard startup, default: true
|
57
|
+
keep_failed: true # Keep failed files until they pass, default: true
|
58
|
+
notification: :failed # Display Growl notification after each run
|
59
|
+
# true - Always notify
|
60
|
+
# false - Never notify
|
61
|
+
# :failed - Notify only when failed
|
62
|
+
# default: :failed
|
63
|
+
```
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
1. Fork it
|
68
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
69
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
70
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
71
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
namespace :ci do
|
7
|
+
task :spec do
|
8
|
+
ENV['CI'] = 'true'
|
9
|
+
|
10
|
+
ENV['CI_REPORTS'] = 'spec/reports'
|
11
|
+
require 'ci/reporter/rake/rspec'
|
12
|
+
Rake::Task['ci:setup:rspec'].invoke
|
13
|
+
|
14
|
+
Rake::Task['spec'].invoke
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
task default: :spec
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'guard/rubocop/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'sabat-guard-rubocop'
|
10
|
+
spec.version = Guard::RubocopVersion::VERSION
|
11
|
+
spec.authors = ['Yuji Nakayama']
|
12
|
+
spec.email = ['nkymyj@gmail.com']
|
13
|
+
spec.summary = 'Guard plugin for RuboCop'
|
14
|
+
spec.description = 'Guard::Rubocop automatically checks Ruby code style with RuboCop when files are modified.'
|
15
|
+
spec.homepage = 'https://github.com/sabat/guard-rubocop'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($/).reject { |f| File.basename(f).start_with?('.') }
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_runtime_dependency 'guard', '~> 1.8'
|
24
|
+
spec.add_runtime_dependency 'sabat-rubocop', '~> 0.9'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 2.13'
|
29
|
+
spec.add_development_dependency 'simplecov', '~> 0.7'
|
30
|
+
spec.add_development_dependency 'guard-rspec', '~> 3.0'
|
31
|
+
spec.add_development_dependency 'ruby_gntp', '~> 0.3'
|
32
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'guard'
|
4
|
+
require 'guard/guard'
|
5
|
+
|
6
|
+
module Guard
|
7
|
+
class Rubocop < Guard
|
8
|
+
autoload :Runner, 'guard/rubocop/runner'
|
9
|
+
|
10
|
+
attr_reader :options, :failed_paths
|
11
|
+
|
12
|
+
def initialize(watchers = [], options = {})
|
13
|
+
super
|
14
|
+
|
15
|
+
@options = {
|
16
|
+
all_on_start: true,
|
17
|
+
keep_failed: true,
|
18
|
+
notification: :failed
|
19
|
+
}.merge(options)
|
20
|
+
|
21
|
+
@failed_paths = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def start
|
25
|
+
run_all if @options[:all_on_start]
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_all
|
29
|
+
UI.info 'Inspecting Ruby code style of all files'
|
30
|
+
run
|
31
|
+
end
|
32
|
+
|
33
|
+
def run_on_changes(paths)
|
34
|
+
paths += @failed_paths if @options[:keep_failed]
|
35
|
+
paths = clean_paths(paths)
|
36
|
+
|
37
|
+
displayed_paths = paths.map { |path| smart_path(path) }
|
38
|
+
UI.info "Inspecting Ruby code style: #{displayed_paths.join(' ')}"
|
39
|
+
|
40
|
+
run(paths)
|
41
|
+
end
|
42
|
+
|
43
|
+
def reload
|
44
|
+
@failed_paths = []
|
45
|
+
end
|
46
|
+
|
47
|
+
def clean_paths(paths)
|
48
|
+
paths = paths.dup
|
49
|
+
paths.map! { |path| File.expand_path(path) }
|
50
|
+
paths.uniq!
|
51
|
+
paths.reject! do |path|
|
52
|
+
included_in_other_path?(path, paths)
|
53
|
+
end
|
54
|
+
paths
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def run(paths = [])
|
60
|
+
runner = Runner.new(@options)
|
61
|
+
passed = runner.run(paths)
|
62
|
+
@failed_paths = runner.failed_paths
|
63
|
+
throw :task_has_failed unless passed
|
64
|
+
rescue => error
|
65
|
+
UI.error 'The following exception occurred while running guard-rubocop: ' +
|
66
|
+
"#{error.backtrace.first} #{error.message} (#{error.class.name})"
|
67
|
+
end
|
68
|
+
|
69
|
+
def included_in_other_path?(target_path, other_paths)
|
70
|
+
dir_paths = other_paths.select { |path| File.directory?(path) }
|
71
|
+
dir_paths.delete(target_path)
|
72
|
+
dir_paths.any? do |dir_path|
|
73
|
+
target_path.start_with?(dir_path)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def smart_path(path)
|
78
|
+
if path.start_with?(Dir.pwd)
|
79
|
+
Pathname.new(path).relative_path_from(Pathname.getwd).to_s
|
80
|
+
else
|
81
|
+
path
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Guard
|
6
|
+
class Rubocop
|
7
|
+
class Runner
|
8
|
+
def initialize(options)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(paths = [])
|
13
|
+
command = build_command(paths)
|
14
|
+
passed = system(*command)
|
15
|
+
|
16
|
+
case @options[:notification]
|
17
|
+
when :failed
|
18
|
+
notify(passed) unless passed
|
19
|
+
when true
|
20
|
+
notify(passed)
|
21
|
+
end
|
22
|
+
|
23
|
+
passed
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_command(paths)
|
27
|
+
command = ['rubocop']
|
28
|
+
command.concat(%w(--format progress)) # Keep default formatter for console.
|
29
|
+
command.concat(['--format', 'json', '--out', json_file_path])
|
30
|
+
command.concat(['--list-cops']) if @options[:cops]
|
31
|
+
command.concat(paths)
|
32
|
+
end
|
33
|
+
|
34
|
+
def json_file_path
|
35
|
+
@tempfile_path ||= begin
|
36
|
+
# Just generate random tempfile path.
|
37
|
+
basename = self.class.name.downcase.gsub('::', '_')
|
38
|
+
tempfile = Tempfile.new(basename)
|
39
|
+
tempfile.close
|
40
|
+
tempfile.path
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def result
|
45
|
+
@result ||= begin
|
46
|
+
File.open(json_file_path) do |file|
|
47
|
+
# Rubinius 2.0.0.rc1 does not support `JSON.load` with 3 args.
|
48
|
+
JSON.parse(file.read, symbolize_names: true)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def notify(passed)
|
54
|
+
image = passed ? :success : :failed
|
55
|
+
Notifier.notify(summary_text, title: 'RuboCop results', image: image)
|
56
|
+
end
|
57
|
+
|
58
|
+
def summary_text
|
59
|
+
summary = result[:summary]
|
60
|
+
|
61
|
+
text = pluralize(summary[:inspected_file_count], 'file')
|
62
|
+
text << ' inspected, '
|
63
|
+
|
64
|
+
text << pluralize(summary[:offence_count], 'offence', no_for_zero: true)
|
65
|
+
text << ' detected'
|
66
|
+
end
|
67
|
+
|
68
|
+
def failed_paths
|
69
|
+
failed_files = result[:files].reject do |file|
|
70
|
+
file[:offences].empty?
|
71
|
+
end
|
72
|
+
failed_files.map do |file|
|
73
|
+
file[:path]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def pluralize(number, thing, options = {})
|
78
|
+
text = ''
|
79
|
+
|
80
|
+
if number == 0 && options[:no_for_zero]
|
81
|
+
text = 'no'
|
82
|
+
else
|
83
|
+
text << number.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
text << " #{thing}"
|
87
|
+
text << 's' unless number == 1
|
88
|
+
|
89
|
+
text
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
# A workaround for declaring `class Rubocop`
|
5
|
+
# before `class Rubocop < Guard` in rubocop.rb
|
6
|
+
module RubocopVersion
|
7
|
+
# http://semver.org/
|
8
|
+
MAJOR = 0
|
9
|
+
MINOR = 1
|
10
|
+
PATCH = 1
|
11
|
+
VERSION = [MAJOR, MINOR, PATCH].join('.')
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper.rb'
|
4
|
+
|
5
|
+
describe Guard::Rubocop::Runner do
|
6
|
+
subject(:runner) { Guard::Rubocop::Runner.new(options) }
|
7
|
+
let(:options) { {} }
|
8
|
+
|
9
|
+
describe '#run' do
|
10
|
+
subject { super().run(paths) }
|
11
|
+
let(:paths) { ['spec/spec_helper.rb'] }
|
12
|
+
|
13
|
+
before do
|
14
|
+
runner.stub(:system)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'executes rubocop' do
|
18
|
+
runner.should_receive(:system) do |*args|
|
19
|
+
args.first.should == 'rubocop'
|
20
|
+
end
|
21
|
+
runner.run
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when RuboCop exited with 0 status' do
|
25
|
+
before do
|
26
|
+
runner.stub(:system).and_return(true)
|
27
|
+
end
|
28
|
+
it { should be_true }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when RuboCop exited with non 0 status' do
|
32
|
+
before do
|
33
|
+
runner.stub(:system).and_return(false)
|
34
|
+
end
|
35
|
+
it { should be_false }
|
36
|
+
end
|
37
|
+
|
38
|
+
shared_examples 'notifies', :notifies do
|
39
|
+
it 'notifies' do
|
40
|
+
runner.should_receive(:notify)
|
41
|
+
runner.run
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
shared_examples 'does not notify', :does_not_notify do
|
46
|
+
it 'does not notify' do
|
47
|
+
runner.should_not_receive(:notify)
|
48
|
+
runner.run
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
shared_examples 'notification' do |expectations|
|
53
|
+
context 'when passed' do
|
54
|
+
before do
|
55
|
+
runner.stub(:system).and_return(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
if expectations[:passed]
|
59
|
+
include_examples 'notifies'
|
60
|
+
else
|
61
|
+
include_examples 'does not notify'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when failed' do
|
66
|
+
before do
|
67
|
+
runner.stub(:system).and_return(false)
|
68
|
+
end
|
69
|
+
|
70
|
+
if expectations[:failed]
|
71
|
+
include_examples 'notifies'
|
72
|
+
else
|
73
|
+
include_examples 'does not notify'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when :notification option is true' do
|
79
|
+
let(:options) { { notification: true } }
|
80
|
+
include_examples 'notification', { passed: true, failed: true }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when :notification option is :failed' do
|
84
|
+
let(:options) { { notification: :failed } }
|
85
|
+
include_examples 'notification', { passed: false, failed: true }
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when :notification option is false' do
|
89
|
+
let(:options) { { notification: false } }
|
90
|
+
include_examples 'notification', { passed: false, failed: false }
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when :cops option is true' do
|
94
|
+
let(:options) { { cops: true } }
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#build_command' do
|
100
|
+
let(:paths) { %w(file1.rb file2.rb) }
|
101
|
+
|
102
|
+
it 'adds args for the default formatter for console' do
|
103
|
+
runner.build_command(paths)[0..2].should == %w(rubocop --format progress)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'adds args for JSON formatter ' do
|
107
|
+
runner.build_command(paths)[3..4].should == %w(--format json)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'adds args for output file path of JSON formatter ' do
|
111
|
+
command = runner.build_command(paths)
|
112
|
+
command[5].should == '--out'
|
113
|
+
command[6].should_not be_empty
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'cops option is set' do
|
117
|
+
let(:options) { { cops: true } }
|
118
|
+
|
119
|
+
it 'adds the --list-cops flag' do
|
120
|
+
command = runner.build_command(paths)
|
121
|
+
command[7].should == '--list-cops'
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'adds the passed paths' do
|
126
|
+
runner.build_command(paths)[7..-1].should == %w(file1.rb file2.rb)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe '#json_file_path' do
|
131
|
+
it 'is not world readable' do
|
132
|
+
File.world_readable?(runner.json_file_path).should be_false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
shared_context 'JSON file', :json_file do
|
137
|
+
before do
|
138
|
+
json = <<-END
|
139
|
+
{
|
140
|
+
"metadata": {
|
141
|
+
"rubocop_version": "0.9.0",
|
142
|
+
"ruby_engine": "ruby",
|
143
|
+
"ruby_version": "2.0.0",
|
144
|
+
"ruby_patchlevel": "195",
|
145
|
+
"ruby_platform": "x86_64-darwin12.3.0"
|
146
|
+
},
|
147
|
+
"files": [{
|
148
|
+
"path": "lib/foo.rb",
|
149
|
+
"offences": []
|
150
|
+
}, {
|
151
|
+
"path": "lib/bar.rb",
|
152
|
+
"offences": [{
|
153
|
+
"severity": "convention",
|
154
|
+
"message": "Line is too long. [81/79]",
|
155
|
+
"cop_name": "LineLength",
|
156
|
+
"location": {
|
157
|
+
"line": 546,
|
158
|
+
"column": 80
|
159
|
+
}
|
160
|
+
}, {
|
161
|
+
"severity": "warning",
|
162
|
+
"message": "Unreachable code detected.",
|
163
|
+
"cop_name": "UnreachableCode",
|
164
|
+
"location": {
|
165
|
+
"line": 15,
|
166
|
+
"column": 9
|
167
|
+
}
|
168
|
+
}
|
169
|
+
]
|
170
|
+
}
|
171
|
+
],
|
172
|
+
"summary": {
|
173
|
+
"offence_count": 2,
|
174
|
+
"target_file_count": 2,
|
175
|
+
"inspected_file_count": 2
|
176
|
+
}
|
177
|
+
}
|
178
|
+
END
|
179
|
+
File.write(runner.json_file_path, json)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#result', :json_file do
|
184
|
+
it 'parses JSON file' do
|
185
|
+
runner.result[:summary][:offence_count].should == 2
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe '#notify' do
|
190
|
+
before do
|
191
|
+
runner.stub(:result).and_return(
|
192
|
+
{
|
193
|
+
summary: {
|
194
|
+
offence_count: 4,
|
195
|
+
target_file_count: 3,
|
196
|
+
inspected_file_count: 2
|
197
|
+
}
|
198
|
+
}
|
199
|
+
)
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'notifies summary' do
|
203
|
+
Guard::Notifier.should_receive(:notify) do |message, options|
|
204
|
+
message.should == '2 files inspected, 4 offences detected'
|
205
|
+
end
|
206
|
+
runner.notify(true)
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'notifies with title "RuboCop results"' do
|
210
|
+
Guard::Notifier.should_receive(:notify) do |message, options|
|
211
|
+
options[:title].should == 'RuboCop results'
|
212
|
+
end
|
213
|
+
runner.notify(true)
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'when passed' do
|
217
|
+
it 'shows success image' do
|
218
|
+
Guard::Notifier.should_receive(:notify) do |message, options|
|
219
|
+
options[:image].should == :success
|
220
|
+
end
|
221
|
+
runner.notify(true)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context 'when failed' do
|
226
|
+
it 'shows failed image' do
|
227
|
+
Guard::Notifier.should_receive(:notify) do |message, options|
|
228
|
+
options[:image].should == :failed
|
229
|
+
end
|
230
|
+
runner.notify(false)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
describe '#summary_text' do
|
236
|
+
before do
|
237
|
+
runner.stub(:result).and_return(
|
238
|
+
{
|
239
|
+
summary: {
|
240
|
+
offence_count: offence_count,
|
241
|
+
target_file_count: target_file_count,
|
242
|
+
inspected_file_count: inspected_file_count
|
243
|
+
}
|
244
|
+
}
|
245
|
+
)
|
246
|
+
end
|
247
|
+
|
248
|
+
subject(:summary_text) { runner.summary_text }
|
249
|
+
|
250
|
+
let(:offence_count) { 0 }
|
251
|
+
let(:target_file_count) { 0 }
|
252
|
+
let(:inspected_file_count) { 0 }
|
253
|
+
|
254
|
+
context 'when no files are inspected' do
|
255
|
+
let(:inspected_file_count) { 0 }
|
256
|
+
it 'includes "0 files"' do
|
257
|
+
summary_text.should include '0 files'
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
context 'when a file is inspected' do
|
262
|
+
let(:inspected_file_count) { 1 }
|
263
|
+
it 'includes "1 file"' do
|
264
|
+
summary_text.should include '1 file'
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
context 'when 2 files are inspected' do
|
269
|
+
let(:inspected_file_count) { 2 }
|
270
|
+
it 'includes "2 files"' do
|
271
|
+
summary_text.should include '2 files'
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
context 'when no offences are detected' do
|
276
|
+
let(:offence_count) { 0 }
|
277
|
+
it 'includes "no offences"' do
|
278
|
+
summary_text.should include 'no offences'
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
context 'when an offence is detected' do
|
283
|
+
let(:offence_count) { 1 }
|
284
|
+
it 'includes "1 offence"' do
|
285
|
+
summary_text.should include '1 offence'
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context 'when 2 offences are detected' do
|
290
|
+
let(:offence_count) { 2 }
|
291
|
+
it 'includes "2 offences"' do
|
292
|
+
summary_text.should include '2 offences'
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe '#failed_paths', :json_file do
|
298
|
+
it 'returns file paths which have offences' do
|
299
|
+
runner.failed_paths.should == ['lib/bar.rb']
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper.rb'
|
4
|
+
|
5
|
+
describe Guard::Rubocop, :silence_output do
|
6
|
+
subject(:guard) { Guard::Rubocop.new(watchers, options) }
|
7
|
+
let(:watchers) { [] }
|
8
|
+
let(:options) { {} }
|
9
|
+
|
10
|
+
let(:runner) { Guard::Rubocop::Runner.any_instance }
|
11
|
+
|
12
|
+
describe '#options' do
|
13
|
+
subject { super().options }
|
14
|
+
|
15
|
+
context 'by default' do
|
16
|
+
let(:options) { {} }
|
17
|
+
its([:all_on_start]) { should be_true }
|
18
|
+
its([:keep_failed]) { should be_true }
|
19
|
+
its([:notification]) { should == :failed }
|
20
|
+
its([:cops]) { should be_false }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#start' do
|
25
|
+
context 'when :all_on_start option is enabled' do
|
26
|
+
let(:options) { { all_on_start: true } }
|
27
|
+
|
28
|
+
it 'runs all' do
|
29
|
+
guard.should_receive(:run_all)
|
30
|
+
guard.start
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when :all_on_start option is disabled' do
|
35
|
+
let(:options) { { all_on_start: false } }
|
36
|
+
|
37
|
+
it 'does nothing' do
|
38
|
+
guard.should_not_receive(:run_all)
|
39
|
+
guard.start
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
shared_examples 'processes after running', :processes_after_running do
|
45
|
+
context 'when passed' do
|
46
|
+
it 'throws nothing' do
|
47
|
+
runner.stub(:run).and_return(true)
|
48
|
+
expect { subject }.not_to throw_symbol
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'clears failed paths' do
|
52
|
+
runner.stub(:run).and_return(true)
|
53
|
+
runner.stub(:failed_paths).and_return([])
|
54
|
+
subject
|
55
|
+
guard.failed_paths.should be_empty
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when failed' do
|
60
|
+
it 'throws symbol :task_has_failed' do
|
61
|
+
runner.stub(:run).and_return(false)
|
62
|
+
expect { subject }.to throw_symbol(:task_has_failed)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'keeps failed paths' do
|
66
|
+
guard.stub(:throw)
|
67
|
+
failed_paths = [
|
68
|
+
'some_failed_file.rb',
|
69
|
+
'dir/another_failed_file.rb'
|
70
|
+
]
|
71
|
+
runner.stub(:run).and_return(false)
|
72
|
+
runner.stub(:failed_paths).and_return(failed_paths)
|
73
|
+
subject
|
74
|
+
guard.failed_paths.should == failed_paths
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when an exception is raised' do
|
79
|
+
it 'prevents itself from firing' do
|
80
|
+
runner.stub(:run).and_raise(RuntimeError)
|
81
|
+
expect { subject }.not_to raise_error
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#run_all', :processes_after_running do
|
87
|
+
subject { super().run_all }
|
88
|
+
|
89
|
+
before do
|
90
|
+
runner.stub(:run).and_return(true)
|
91
|
+
runner.stub(:failed_paths).and_return([])
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'inspects all files with rubocop' do
|
95
|
+
runner.should_receive(:run).with([])
|
96
|
+
guard.run_all
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '#run_on_changes', :processes_after_running do
|
101
|
+
subject { super().run_on_changes(changed_paths) }
|
102
|
+
let(:changed_paths) { ['some.rb', 'dir/another.rb', 'dir/../some.rb'] }
|
103
|
+
|
104
|
+
before do
|
105
|
+
runner.stub(:run).and_return(true)
|
106
|
+
runner.stub(:failed_paths).and_return([])
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'inspects changed files with rubocop' do
|
110
|
+
runner.should_receive(:run)
|
111
|
+
guard.run_on_changes(changed_paths)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'passes cleaned paths to rubocop' do
|
115
|
+
runner.should_receive(:run) do |paths|
|
116
|
+
paths.should == [
|
117
|
+
File.expand_path('some.rb'),
|
118
|
+
File.expand_path('dir/another.rb')
|
119
|
+
]
|
120
|
+
end
|
121
|
+
guard.run_on_changes(changed_paths)
|
122
|
+
end
|
123
|
+
|
124
|
+
let(:failed_path) { File.expand_path('failed_file_last_time.rb') }
|
125
|
+
|
126
|
+
context 'when :keep_failed option is enabled' do
|
127
|
+
let(:options) { { keep_failed: true } }
|
128
|
+
|
129
|
+
it 'also inspects paths which are failed last time' do
|
130
|
+
guard.failed_paths << failed_path
|
131
|
+
runner.should_receive(:run) do |paths|
|
132
|
+
paths.should include failed_path
|
133
|
+
end
|
134
|
+
guard.run_on_changes(changed_paths)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'when :keep_failed option is disabled' do
|
139
|
+
let(:options) { { keep_failed: false } }
|
140
|
+
let(:changed_paths) do
|
141
|
+
[
|
142
|
+
File.expand_path('some.rb'),
|
143
|
+
File.expand_path('dir/another.rb')
|
144
|
+
]
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'inspects just changed paths' do
|
148
|
+
guard.failed_paths << failed_path
|
149
|
+
runner.should_receive(:run) do |paths|
|
150
|
+
paths.should == changed_paths
|
151
|
+
end
|
152
|
+
guard.run_on_changes(changed_paths)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#reload' do
|
158
|
+
it 'clears failed paths' do
|
159
|
+
guard.failed_paths << 'failed.rb'
|
160
|
+
guard.reload
|
161
|
+
guard.failed_paths.should be_empty
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#clean_paths' do
|
166
|
+
it 'converts to absolute paths' do
|
167
|
+
paths = [
|
168
|
+
'lib/guard/rubocop.rb',
|
169
|
+
'spec/spec_helper.rb'
|
170
|
+
]
|
171
|
+
guard.clean_paths(paths).should == [
|
172
|
+
File.expand_path('lib/guard/rubocop.rb'),
|
173
|
+
File.expand_path('spec/spec_helper.rb')
|
174
|
+
]
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'removes duplicated paths' do
|
178
|
+
paths = [
|
179
|
+
'lib/guard/rubocop.rb',
|
180
|
+
'spec/spec_helper.rb',
|
181
|
+
'lib/guard/../guard/rubocop.rb'
|
182
|
+
]
|
183
|
+
guard.clean_paths(paths).should == [
|
184
|
+
File.expand_path('lib/guard/rubocop.rb'),
|
185
|
+
File.expand_path('spec/spec_helper.rb')
|
186
|
+
]
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'removes paths which are included in another path' do
|
190
|
+
paths = [
|
191
|
+
'lib/guard/rubocop.rb',
|
192
|
+
'spec/spec_helper.rb',
|
193
|
+
'spec'
|
194
|
+
]
|
195
|
+
guard.clean_paths(paths).should == [
|
196
|
+
File.expand_path('lib/guard/rubocop.rb'),
|
197
|
+
File.expand_path('spec')
|
198
|
+
]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
5
|
+
end
|
6
|
+
|
7
|
+
Dir[File.join(File.dirname(__FILE__), 'support', '*')].each do |path|
|
8
|
+
require path
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'simplecov'
|
12
|
+
SimpleCov.coverage_dir(File.join('spec', 'coverage'))
|
13
|
+
|
14
|
+
if ENV['TRAVIS']
|
15
|
+
require 'coveralls'
|
16
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
17
|
+
elsif ENV['CI'] # rubocop:disable IfUnlessModifier
|
18
|
+
require 'simplecov-rcov'
|
19
|
+
SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
|
20
|
+
end
|
21
|
+
|
22
|
+
SimpleCov.start do
|
23
|
+
add_filter '/spec/'
|
24
|
+
add_filter '/vendor/bundle/'
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'guard/rubocop'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
shared_context 'silence output', silence_output: true do
|
4
|
+
before do
|
5
|
+
null_output = double('output').as_null_object
|
6
|
+
|
7
|
+
@original_stdout = $stdout
|
8
|
+
@original_stderr = $stderr
|
9
|
+
|
10
|
+
$stdout = null_output
|
11
|
+
$stderr = null_output
|
12
|
+
end
|
13
|
+
|
14
|
+
after do
|
15
|
+
$stdout = @original_stdout
|
16
|
+
$stderr = @original_stderr
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sabat-guard-rubocop
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Yuji Nakayama
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: guard
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.8'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.8'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sabat-rubocop
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0.9'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0.9'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.3'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.3'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '10.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '10.0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '2.13'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '2.13'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: simplecov
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0.7'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.7'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: guard-rspec
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '3.0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: ruby_gntp
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0.3'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0.3'
|
142
|
+
description: Guard::Rubocop automatically checks Ruby code style with RuboCop when
|
143
|
+
files are modified.
|
144
|
+
email:
|
145
|
+
- nkymyj@gmail.com
|
146
|
+
executables: []
|
147
|
+
extensions: []
|
148
|
+
extra_rdoc_files: []
|
149
|
+
files:
|
150
|
+
- CHANGELOG.md
|
151
|
+
- Gemfile
|
152
|
+
- Guardfile
|
153
|
+
- LICENSE.txt
|
154
|
+
- README.md
|
155
|
+
- Rakefile
|
156
|
+
- guard-rubocop.gemspec
|
157
|
+
- lib/guard/rubocop.rb
|
158
|
+
- lib/guard/rubocop/runner.rb
|
159
|
+
- lib/guard/rubocop/templates/Guardfile
|
160
|
+
- lib/guard/rubocop/version.rb
|
161
|
+
- spec/guard/rubocop/runner_spec.rb
|
162
|
+
- spec/guard/rubocop_spec.rb
|
163
|
+
- spec/spec_helper.rb
|
164
|
+
- spec/support/silence_output.rb
|
165
|
+
homepage: https://github.com/sabat/guard-rubocop
|
166
|
+
licenses:
|
167
|
+
- MIT
|
168
|
+
post_install_message:
|
169
|
+
rdoc_options: []
|
170
|
+
require_paths:
|
171
|
+
- lib
|
172
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
173
|
+
none: false
|
174
|
+
requirements:
|
175
|
+
- - ! '>='
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
version: '0'
|
178
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
179
|
+
none: false
|
180
|
+
requirements:
|
181
|
+
- - ! '>='
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: '0'
|
184
|
+
requirements: []
|
185
|
+
rubyforge_project:
|
186
|
+
rubygems_version: 1.8.25
|
187
|
+
signing_key:
|
188
|
+
specification_version: 3
|
189
|
+
summary: Guard plugin for RuboCop
|
190
|
+
test_files:
|
191
|
+
- spec/guard/rubocop/runner_spec.rb
|
192
|
+
- spec/guard/rubocop_spec.rb
|
193
|
+
- spec/spec_helper.rb
|
194
|
+
- spec/support/silence_output.rb
|
195
|
+
has_rdoc:
|