rubocop-airbnb 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/CHANGELOG.md +2 -0
- data/Gemfile +7 -0
- data/LICENSE.md +9 -0
- data/README.md +68 -0
- data/config/default.yml +39 -0
- data/config/rubocop-airbnb.yml +96 -0
- data/config/rubocop-bundler.yml +8 -0
- data/config/rubocop-gemspec.yml +9 -0
- data/config/rubocop-layout.yml +514 -0
- data/config/rubocop-lint.yml +315 -0
- data/config/rubocop-metrics.yml +47 -0
- data/config/rubocop-naming.yml +68 -0
- data/config/rubocop-performance.yml +143 -0
- data/config/rubocop-rails.yml +193 -0
- data/config/rubocop-rspec.yml +281 -0
- data/config/rubocop-security.yml +13 -0
- data/config/rubocop-style.yml +953 -0
- data/lib/rubocop-airbnb.rb +11 -0
- data/lib/rubocop/airbnb.rb +16 -0
- data/lib/rubocop/airbnb/inflections.rb +14 -0
- data/lib/rubocop/airbnb/inject.rb +20 -0
- data/lib/rubocop/airbnb/rails_autoloading.rb +55 -0
- data/lib/rubocop/airbnb/version.rb +8 -0
- data/lib/rubocop/cop/airbnb/class_name.rb +47 -0
- data/lib/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file.rb +138 -0
- data/lib/rubocop/cop/airbnb/const_assigned_in_wrong_file.rb +74 -0
- data/lib/rubocop/cop/airbnb/continuation_slash.rb +25 -0
- data/lib/rubocop/cop/airbnb/default_scope.rb +20 -0
- data/lib/rubocop/cop/airbnb/factory_attr_references_class.rb +74 -0
- data/lib/rubocop/cop/airbnb/factory_class_use_string.rb +39 -0
- data/lib/rubocop/cop/airbnb/mass_assignment_accessible_modifier.rb +18 -0
- data/lib/rubocop/cop/airbnb/module_method_in_wrong_file.rb +104 -0
- data/lib/rubocop/cop/airbnb/no_timeout.rb +19 -0
- data/lib/rubocop/cop/airbnb/opt_arg_parameters.rb +38 -0
- data/lib/rubocop/cop/airbnb/phrase_bundle_keys.rb +67 -0
- data/lib/rubocop/cop/airbnb/risky_activerecord_invocation.rb +63 -0
- data/lib/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace.rb +114 -0
- data/lib/rubocop/cop/airbnb/rspec_environment_modification.rb +58 -0
- data/lib/rubocop/cop/airbnb/simple_modifier_conditional.rb +23 -0
- data/lib/rubocop/cop/airbnb/spec_constant_assignment.rb +55 -0
- data/lib/rubocop/cop/airbnb/unsafe_yaml_marshal.rb +47 -0
- data/rubocop-airbnb.gemspec +32 -0
- data/spec/rubocop/cop/airbnb/class_name_spec.rb +78 -0
- data/spec/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file_spec.rb +174 -0
- data/spec/rubocop/cop/airbnb/const_assigned_in_wrong_file_spec.rb +178 -0
- data/spec/rubocop/cop/airbnb/continuation_slash_spec.rb +162 -0
- data/spec/rubocop/cop/airbnb/default_scope_spec.rb +38 -0
- data/spec/rubocop/cop/airbnb/factory_attr_references_class_spec.rb +160 -0
- data/spec/rubocop/cop/airbnb/factory_class_use_string_spec.rb +26 -0
- data/spec/rubocop/cop/airbnb/mass_assignment_accessible_modifier_spec.rb +28 -0
- data/spec/rubocop/cop/airbnb/module_method_in_wrong_file_spec.rb +181 -0
- data/spec/rubocop/cop/airbnb/no_timeout_spec.rb +30 -0
- data/spec/rubocop/cop/airbnb/opt_arg_parameter_spec.rb +103 -0
- data/spec/rubocop/cop/airbnb/phrase_bundle_keys_spec.rb +74 -0
- data/spec/rubocop/cop/airbnb/risky_activerecord_invocation_spec.rb +54 -0
- data/spec/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace_spec.rb +284 -0
- data/spec/rubocop/cop/airbnb/rspec_environment_modification_spec.rb +64 -0
- data/spec/rubocop/cop/airbnb/simple_modifier_conditional_spec.rb +122 -0
- data/spec/rubocop/cop/airbnb/spec_constant_assignment_spec.rb +80 -0
- data/spec/rubocop/cop/airbnb/unsafe_yaml_marshal_spec.rb +50 -0
- data/spec/spec_helper.rb +35 -0
- metadata +150 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module Airbnb
|
4
|
+
# This cop checks how the Rails environment is modified in specs. If an individual method on
|
5
|
+
# Rails.env is modified multiple environment related branchs could be run down. Rather than
|
6
|
+
# modifying a single path or setting Rails.env in a way that could bleed into other specs,
|
7
|
+
# use `stub_env`
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
#
|
12
|
+
# # spec/foo/bar_spec.rb
|
13
|
+
# before(:each) do
|
14
|
+
# allow(Rails.env).to receive(:production).and_return(true)
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# before(:each) do
|
18
|
+
# expect(Rails.env).to receive(:production).and_return(true)
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# before(:each) do
|
22
|
+
# Rails.env = :production
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
#
|
27
|
+
# # spec/foo/bar_spec.rb do
|
28
|
+
# before(:each) do
|
29
|
+
# stub_env(:production)
|
30
|
+
# end
|
31
|
+
class RspecEnvironmentModification < Cop
|
32
|
+
def_node_matcher :allow_or_expect_rails_env, <<-PATTERN
|
33
|
+
(send (send nil? {:expect :allow} (send (const nil? :Rails) :env)) :to ...)
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def_node_matcher :stub_rails_env, <<-PATTERN
|
37
|
+
(send (send (const nil? :Rails) :env) :stub _)
|
38
|
+
PATTERN
|
39
|
+
|
40
|
+
def_node_matcher :rails_env_assignment, '(send (const nil? :Rails) :env= ...)'
|
41
|
+
|
42
|
+
MESSAGE = "Do not stub or set Rails.env in specs. Use the `stub_env` method instead".freeze
|
43
|
+
|
44
|
+
def on_send(node)
|
45
|
+
path = node.source_range.source_buffer.name
|
46
|
+
return unless is_spec_file?(path)
|
47
|
+
if rails_env_assignment(node) || allow_or_expect_rails_env(node) || stub_rails_env(node)
|
48
|
+
add_offense(node, message: MESSAGE)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def is_spec_file?(path)
|
53
|
+
path.end_with?('_spec.rb')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module Airbnb
|
4
|
+
# Cop to tackle prevent more complicated modifier if/unless statements
|
5
|
+
# https://github.com/airbnb/ruby#only-simple-if-unless
|
6
|
+
class SimpleModifierConditional < Cop
|
7
|
+
MSG = 'Modifier if/unless usage is okay when the body is simple, ' \
|
8
|
+
'the condition is simple, and the whole thing fits on one line. ' \
|
9
|
+
'Otherwise, avoid modifier if/unless.'.freeze
|
10
|
+
|
11
|
+
def_node_matcher :multiple_conditionals?, '(if ({and or :^} ...) ...)'
|
12
|
+
|
13
|
+
def on_if(node)
|
14
|
+
return unless node.modifier_form?
|
15
|
+
|
16
|
+
if multiple_conditionals?(node) || node.multiline?
|
17
|
+
add_offense(node)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubocop-rspec'
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Airbnb
|
6
|
+
# This cop checks for constant assignment inside of specs
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# describe Something do
|
11
|
+
# PAYLOAD = [1, 2, 3]
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# describe Something do
|
16
|
+
# let(:payload) { [1, 2, 3] }
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # bad
|
20
|
+
# describe Something do
|
21
|
+
# MyClass::PAYLOAD = [1, 2, 3]
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # good
|
25
|
+
# describe Something do
|
26
|
+
# before { stub_const('MyClass::PAYLOAD', [1, 2, 3])
|
27
|
+
# end
|
28
|
+
class SpecConstantAssignment < Cop
|
29
|
+
include RuboCop::RSpec::TopLevelDescribe
|
30
|
+
MESSAGE = "Defining constants inside of specs can cause spurious behavior. " \
|
31
|
+
"It is almost always preferable to use `let` statements, "\
|
32
|
+
"anonymous class/module definitions, or stub_const".freeze
|
33
|
+
|
34
|
+
def on_casgn(node)
|
35
|
+
return unless in_spec_file?(node)
|
36
|
+
parent_module_name = node.parent_module_name
|
37
|
+
if node.parent_module_name && parent_module_name != 'Object'
|
38
|
+
return
|
39
|
+
end
|
40
|
+
add_offense(node, message: MESSAGE)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def in_spec_file?(node)
|
46
|
+
filename = node.location.expression.source_buffer.name
|
47
|
+
|
48
|
+
# For tests, the input is a string
|
49
|
+
return true if filename == "(string)"
|
50
|
+
filename.include?("/spec/")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module Airbnb
|
4
|
+
# Disallow use of YAML/Marshal methods that can trigger RCE on untrusted input
|
5
|
+
class UnsafeYamlMarshal < Cop
|
6
|
+
MSG = 'Using unsafe YAML parsing methods on untrusted input can lead ' \
|
7
|
+
'to remote code execution. Use `safe_load`, `parse`, `parse_file`, or ' \
|
8
|
+
'`parse_stream` instead'.freeze
|
9
|
+
|
10
|
+
def on_send(node)
|
11
|
+
receiver, method_name, *_args = *node
|
12
|
+
|
13
|
+
return if receiver.nil?
|
14
|
+
return unless receiver.const_type?
|
15
|
+
|
16
|
+
check_yaml(node, receiver, method_name, *_args)
|
17
|
+
check_marshal(node, receiver, method_name, *_args)
|
18
|
+
rescue => e
|
19
|
+
puts e
|
20
|
+
puts e.backtrace
|
21
|
+
raise
|
22
|
+
end
|
23
|
+
|
24
|
+
def check_yaml(node, receiver, method_name, *_args)
|
25
|
+
return unless ['YAML', 'Psych'].include?(receiver.const_name)
|
26
|
+
return unless [:load, :load_documents, :load_file, :load_stream].include?(method_name)
|
27
|
+
|
28
|
+
message = "Using `#{receiver.const_name}.#{method_name}` on untrusted input can lead " \
|
29
|
+
"to remote code execution. Use `safe_load`, `parse`, `parse_file`, or " \
|
30
|
+
"`parse_stream` instead"
|
31
|
+
|
32
|
+
add_offense(node, message: message)
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_marshal(node, receiver, method_name, *_args)
|
36
|
+
return unless receiver.const_name == 'Marshal'
|
37
|
+
return unless method_name == :load
|
38
|
+
|
39
|
+
message = 'Using `Marshal.load` on untrusted input can lead to remote code execution. ' \
|
40
|
+
'Restructure your code to not use Marshal'
|
41
|
+
|
42
|
+
add_offense(node, message: message)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
3
|
+
require 'rubocop/airbnb/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'rubocop-airbnb'
|
7
|
+
spec.summary = 'Custom code style checking for Airbnb.'
|
8
|
+
spec.description = <<-EOF
|
9
|
+
A plugin for RuboCop code style enforcing & linting tool. It includes Rubocop configuration
|
10
|
+
used at Airbnb and a few custom rules that have cause internal issues at Airbnb but are not
|
11
|
+
supported by core Rubocop.
|
12
|
+
EOF
|
13
|
+
spec.authors = ['Airbnb Engineering']
|
14
|
+
spec.email = ['rubocop@airbnb.com']
|
15
|
+
spec.homepage = 'https://github.com/airbnb/ruby'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
spec.version = RuboCop::Airbnb::VERSION
|
18
|
+
spec.platform = Gem::Platform::RUBY
|
19
|
+
spec.required_ruby_version = '>= 2.1'
|
20
|
+
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
spec.files = Dir[
|
23
|
+
'{config,lib,spec}/**/*',
|
24
|
+
'*.md',
|
25
|
+
'*.gemspec',
|
26
|
+
'Gemfile',
|
27
|
+
]
|
28
|
+
|
29
|
+
spec.add_dependency('rubocop', '0.52.1')
|
30
|
+
spec.add_dependency('rubocop-rspec', '1.22.1')
|
31
|
+
spec.add_development_dependency('rspec', '~> 3.5')
|
32
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
describe RuboCop::Cop::Airbnb::ClassName do
|
2
|
+
subject(:cop) { described_class.new }
|
3
|
+
|
4
|
+
describe "belongs_to" do
|
5
|
+
it 'rejects with Model.name' do
|
6
|
+
source = [
|
7
|
+
'class Coupon',
|
8
|
+
' belongs_to :user, :class_name => User.name',
|
9
|
+
'end',
|
10
|
+
].join("\n")
|
11
|
+
inspect_source(source)
|
12
|
+
|
13
|
+
expect(cop.offenses.size).to eq(1)
|
14
|
+
expect(cop.offenses.map(&:line).sort).to eq([2])
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'passes with "Model"' do
|
18
|
+
source = [
|
19
|
+
'class Coupon',
|
20
|
+
' belongs_to :user, :class_name => "User"',
|
21
|
+
'end',
|
22
|
+
].join("\n")
|
23
|
+
inspect_source(source)
|
24
|
+
|
25
|
+
expect(cop.offenses).to be_empty
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "has_many" do
|
30
|
+
it 'rejects with Model.name' do
|
31
|
+
source = [
|
32
|
+
'class Coupon',
|
33
|
+
' has_many :reservations, :class_name => Reservation2.name',
|
34
|
+
'end',
|
35
|
+
].join("\n")
|
36
|
+
inspect_source(source)
|
37
|
+
|
38
|
+
expect(cop.offenses.size).to eq(1)
|
39
|
+
expect(cop.offenses.map(&:line)).to eq([2])
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'passes with "Model"' do
|
43
|
+
source = [
|
44
|
+
'class Coupon',
|
45
|
+
' has_many :reservations, :class_name => "Reservation2"',
|
46
|
+
'end',
|
47
|
+
].join("\n")
|
48
|
+
inspect_source(source)
|
49
|
+
|
50
|
+
expect(cop.offenses).to be_empty
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "has_one" do
|
55
|
+
it 'rejects with Model.name' do
|
56
|
+
source = [
|
57
|
+
'class Coupon',
|
58
|
+
' has_one :loss, :class_name => Payments::Loss.name',
|
59
|
+
'end',
|
60
|
+
].join("\n")
|
61
|
+
inspect_source(source)
|
62
|
+
|
63
|
+
expect(cop.offenses.size).to eq(1)
|
64
|
+
expect(cop.offenses.map(&:line)).to eq([2])
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'passes with "Model"' do
|
68
|
+
source = [
|
69
|
+
'class Coupon',
|
70
|
+
' has_one :loss, :class_name => "Payments::Loss"',
|
71
|
+
'end',
|
72
|
+
].join("\n")
|
73
|
+
inspect_source(source)
|
74
|
+
|
75
|
+
expect(cop.offenses).to be_empty
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
describe RuboCop::Cop::Airbnb::ClassOrModuleDeclaredInWrongFile do
|
2
|
+
subject(:cop) { described_class.new(config) }
|
3
|
+
|
4
|
+
let(:config) do
|
5
|
+
RuboCop::Config.new(
|
6
|
+
{
|
7
|
+
"Rails" => {
|
8
|
+
"Enabled" => true,
|
9
|
+
},
|
10
|
+
}
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Put source in a directory under /tmp because this cop cares about the filename
|
15
|
+
# but not the parent dir name.
|
16
|
+
let(:tmpdir) { Dir.mktmpdir }
|
17
|
+
let(:models_dir) do
|
18
|
+
gemfile = File.new("#{tmpdir}/Gemfile", "w")
|
19
|
+
gemfile.close
|
20
|
+
FileUtils.mkdir_p("#{tmpdir}/app/models").first
|
21
|
+
end
|
22
|
+
|
23
|
+
after do
|
24
|
+
FileUtils.rm_rf tmpdir
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'rejects if class declaration is in a file with non-matching name' do
|
28
|
+
source = [
|
29
|
+
'module Foo',
|
30
|
+
' module Bar',
|
31
|
+
' class Baz',
|
32
|
+
' end',
|
33
|
+
' end',
|
34
|
+
'end',
|
35
|
+
].join("\n")
|
36
|
+
|
37
|
+
File.open "#{models_dir}/qux.rb", "w" do |file|
|
38
|
+
inspect_source(source, file)
|
39
|
+
end
|
40
|
+
|
41
|
+
expect(cop.offenses.size).to eq(3)
|
42
|
+
expect(cop.offenses.map(&:line).sort).to eq([1, 2, 3])
|
43
|
+
expect(cop.offenses.first.message).to include('Module Foo should be defined in foo.rb.')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'rejects if class declaration is in a file with matching name but wrong parent dir' do
|
47
|
+
source = [
|
48
|
+
'module Foo',
|
49
|
+
' module Bar',
|
50
|
+
' class Baz',
|
51
|
+
' end',
|
52
|
+
' end',
|
53
|
+
'end',
|
54
|
+
].join("\n")
|
55
|
+
|
56
|
+
File.open "#{models_dir}/baz.rb", "w" do |file|
|
57
|
+
inspect_source(source, file)
|
58
|
+
end
|
59
|
+
|
60
|
+
expect(cop.offenses.size).to eq(3)
|
61
|
+
expect(cop.offenses.map(&:line).sort).to eq([1, 2, 3])
|
62
|
+
expect(cop.offenses.last.message).to include('Class Baz should be defined in foo/bar/baz.rb.')
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'accepts if class declaration is in a file with matching name and right parent dir' do
|
66
|
+
source = [
|
67
|
+
'module Foo',
|
68
|
+
' module Bar',
|
69
|
+
' class Baz',
|
70
|
+
' end',
|
71
|
+
' end',
|
72
|
+
'end',
|
73
|
+
].join("\n")
|
74
|
+
|
75
|
+
FileUtils.mkdir_p "#{models_dir}/foo/bar"
|
76
|
+
File.open "#{models_dir}/foo/bar/baz.rb", "w" do |file|
|
77
|
+
inspect_source(source, file)
|
78
|
+
end
|
79
|
+
|
80
|
+
expect(cop.offenses).to be_empty
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'rejects if class declaration is in wrong dir and parent module uses ::' do
|
84
|
+
source = [
|
85
|
+
'module Foo::Bar',
|
86
|
+
' class Baz',
|
87
|
+
' end',
|
88
|
+
'end',
|
89
|
+
].join("\n")
|
90
|
+
|
91
|
+
FileUtils.mkdir_p "#{models_dir}/bar"
|
92
|
+
File.open "#{models_dir}/bar/baz.rb", "w" do |file|
|
93
|
+
inspect_source(source, file)
|
94
|
+
end
|
95
|
+
|
96
|
+
expect(cop.offenses.map(&:line).sort).to eq([1, 2])
|
97
|
+
expect(cop.offenses.last.message).to include('Class Baz should be defined in foo/bar/baz.rb.')
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'accepts if class declaration is in a file with matching name and parent module uses ::' do
|
101
|
+
source = [
|
102
|
+
'module Foo::Bar',
|
103
|
+
' class Baz',
|
104
|
+
' end',
|
105
|
+
'end',
|
106
|
+
].join("\n")
|
107
|
+
|
108
|
+
FileUtils.mkdir_p "#{models_dir}/foo/bar"
|
109
|
+
File.open "#{models_dir}/foo/bar/baz.rb", "w" do |file|
|
110
|
+
inspect_source(source, file)
|
111
|
+
end
|
112
|
+
|
113
|
+
expect(cop.offenses).to be_empty
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'accepts class declaration where the containing class uses an acronym' do
|
117
|
+
source = [
|
118
|
+
'module CSVFoo',
|
119
|
+
' class Baz',
|
120
|
+
' end',
|
121
|
+
'end',
|
122
|
+
].join("\n")
|
123
|
+
|
124
|
+
FileUtils.mkdir_p "#{models_dir}/csv_foo"
|
125
|
+
File.open "#{models_dir}/csv_foo/baz.rb", "w" do |file|
|
126
|
+
inspect_source(source, file)
|
127
|
+
end
|
128
|
+
|
129
|
+
expect(cop.offenses).to be_empty
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'ignores class/module declaration in a rake task' do
|
133
|
+
source = [
|
134
|
+
'class Baz',
|
135
|
+
'end',
|
136
|
+
].join("\n")
|
137
|
+
|
138
|
+
File.open "#{models_dir}/foo.rake", "w" do |file|
|
139
|
+
inspect_source(source, file)
|
140
|
+
end
|
141
|
+
|
142
|
+
expect(cop.offenses).to be_empty
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'suggests moving error classes into the file that defines the owning scope' do
|
146
|
+
source = [
|
147
|
+
'module Foo',
|
148
|
+
' class BarError < StandardError; end',
|
149
|
+
'end',
|
150
|
+
].join("\n")
|
151
|
+
|
152
|
+
File.open "#{models_dir}/bar.rb", "w" do |file|
|
153
|
+
inspect_source(source, file)
|
154
|
+
end
|
155
|
+
|
156
|
+
expect(cop.offenses.map(&:line)).to include(2)
|
157
|
+
expect(cop.offenses.map(&:message)).to include(%r{Class BarError should be defined in foo\.rb.})
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'recognizes error class based on the superclass name' do
|
161
|
+
source = [
|
162
|
+
'module Foo',
|
163
|
+
' class Bar < StandardError; end',
|
164
|
+
'end',
|
165
|
+
].join("\n")
|
166
|
+
|
167
|
+
File.open "#{models_dir}/bar.rb", "w" do |file|
|
168
|
+
inspect_source(source, file)
|
169
|
+
end
|
170
|
+
|
171
|
+
expect(cop.offenses.map(&:line)).to include(2)
|
172
|
+
expect(cop.offenses.map(&:message)).to include(%r{Class Bar should be defined in foo\.rb.})
|
173
|
+
end
|
174
|
+
end
|