rubocop-airbnb 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.
- 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
|