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,26 @@
|
|
1
|
+
describe RuboCop::Cop::Airbnb::FactoryClassUseString do
|
2
|
+
subject(:cop) { described_class.new }
|
3
|
+
|
4
|
+
it 'rejects with :class => Model' do
|
5
|
+
source = [
|
6
|
+
'factory :help_answer, :class => Help::Answer do',
|
7
|
+
' text { Faker::Company.name }',
|
8
|
+
'end',
|
9
|
+
].join("\n")
|
10
|
+
inspect_source(source)
|
11
|
+
|
12
|
+
expect(cop.offenses.size).to eq(1)
|
13
|
+
expect(cop.offenses.map(&:line)).to eq([1])
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'passes with :class => "Model"' do
|
17
|
+
source = [
|
18
|
+
'factory :help_answer, :class => "Help::Answer" do',
|
19
|
+
' text { Faker::Company.name }',
|
20
|
+
'end',
|
21
|
+
].join("\n")
|
22
|
+
inspect_source(source)
|
23
|
+
|
24
|
+
expect(cop.offenses).to be_empty
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
describe RuboCop::Cop::Airbnb::MassAssignmentAccessibleModifier do
|
2
|
+
subject(:cop) { described_class.new }
|
3
|
+
|
4
|
+
it 'rejects when accessible= is called' do
|
5
|
+
source = [
|
6
|
+
'def some_method',
|
7
|
+
' user = User.new',
|
8
|
+
' user.accessible = [:first_name, :last_name]',
|
9
|
+
' user.update_attributes(:first_name => "Walter", :last_name => "Udoing")',
|
10
|
+
'end',
|
11
|
+
].join("\n")
|
12
|
+
inspect_source(source)
|
13
|
+
expect(cop.offenses.size).to eq(1)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'accepts when accessible= is not called' do
|
17
|
+
source = [
|
18
|
+
'def some_method',
|
19
|
+
' user = User.new',
|
20
|
+
' user.first_name = "Walter"',
|
21
|
+
' user.last_name = "Udoing"',
|
22
|
+
' user.save!',
|
23
|
+
'end',
|
24
|
+
].join("\n")
|
25
|
+
inspect_source(source)
|
26
|
+
expect(cop.offenses).to be_empty
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
describe RuboCop::Cop::Airbnb::ModuleMethodInWrongFile 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 module method is in file with non-matching name' do
|
28
|
+
source = [
|
29
|
+
'module Hello',
|
30
|
+
' module Foo',
|
31
|
+
' def baz', # error is here if this file is named bar.rb
|
32
|
+
' 42',
|
33
|
+
' end',
|
34
|
+
' end',
|
35
|
+
'end',
|
36
|
+
].join("\n")
|
37
|
+
|
38
|
+
File.open "#{models_dir}/bar.rb", "w" do |file|
|
39
|
+
inspect_source(source, file)
|
40
|
+
end
|
41
|
+
|
42
|
+
expect(cop.offenses.size).to eq(1)
|
43
|
+
expect(cop.offenses.map(&:line).sort).to eq([3])
|
44
|
+
expect(cop.offenses.first.message).to include("Method baz should be defined in hello/foo.rb.")
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'accepts if module method is in file with matching name' do
|
48
|
+
source = [
|
49
|
+
'module Foo',
|
50
|
+
' def baz',
|
51
|
+
' 42',
|
52
|
+
' end',
|
53
|
+
'end',
|
54
|
+
].join("\n")
|
55
|
+
|
56
|
+
File.open "#{models_dir}/foo.rb", "w" do |file|
|
57
|
+
inspect_source(source, file)
|
58
|
+
end
|
59
|
+
|
60
|
+
expect(cop.offenses).to be_empty
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'rejects with "self." static methods and a non-matching name' do
|
64
|
+
source = [
|
65
|
+
'module Foo',
|
66
|
+
' def self.baz',
|
67
|
+
' 42',
|
68
|
+
' end',
|
69
|
+
'end',
|
70
|
+
].join("\n")
|
71
|
+
|
72
|
+
File.open "#{models_dir}/bar.rb", "w" do |file|
|
73
|
+
inspect_source(source, file)
|
74
|
+
end
|
75
|
+
|
76
|
+
expect(cop.offenses.size).to eq(1)
|
77
|
+
expect(cop.offenses.map(&:line).sort).to eq([2])
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'accepts with "self." static methods and a matching name' do
|
81
|
+
source = [
|
82
|
+
'module Foo',
|
83
|
+
' def self.baz',
|
84
|
+
' 42',
|
85
|
+
' end',
|
86
|
+
'end',
|
87
|
+
].join("\n")
|
88
|
+
|
89
|
+
File.open "#{models_dir}/foo.rb", "w" do |file|
|
90
|
+
inspect_source(source, file)
|
91
|
+
end
|
92
|
+
|
93
|
+
expect(cop.offenses).to be_empty
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'rejects with "<<" static methods and a non-matching name' do
|
97
|
+
source = [
|
98
|
+
'module Foo',
|
99
|
+
' class << self',
|
100
|
+
' def baz',
|
101
|
+
' 42',
|
102
|
+
' end',
|
103
|
+
' end',
|
104
|
+
'end',
|
105
|
+
].join("\n")
|
106
|
+
|
107
|
+
File.open "#{models_dir}/bar.rb", "w" do |file|
|
108
|
+
inspect_source(source, file)
|
109
|
+
end
|
110
|
+
|
111
|
+
expect(cop.offenses.size).to eq(1)
|
112
|
+
expect(cop.offenses.map(&:line).sort).to eq([3])
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'accepts with "<<" static methods and a matching name' do
|
116
|
+
source = [
|
117
|
+
'module Foo',
|
118
|
+
' class << self',
|
119
|
+
' def baz',
|
120
|
+
' 42',
|
121
|
+
' end',
|
122
|
+
' end',
|
123
|
+
'end',
|
124
|
+
].join("\n")
|
125
|
+
|
126
|
+
File.open "#{models_dir}/foo.rb", "w" do |file|
|
127
|
+
inspect_source(source, file)
|
128
|
+
end
|
129
|
+
|
130
|
+
expect(cop.offenses).to be_empty
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'accepts methods defined in a nested class' do
|
134
|
+
source = [
|
135
|
+
'module Foo',
|
136
|
+
' class Bar',
|
137
|
+
' def qux',
|
138
|
+
' end',
|
139
|
+
' end',
|
140
|
+
'end',
|
141
|
+
].join("\n")
|
142
|
+
|
143
|
+
File.open "#{models_dir}/foo.rb", "w" do |file|
|
144
|
+
inspect_source(source, file)
|
145
|
+
end
|
146
|
+
|
147
|
+
expect(cop.offenses).to be_empty
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'accepts methods where the containing class uses an acronym' do
|
151
|
+
source = [
|
152
|
+
'module CSVFoo',
|
153
|
+
' def baz',
|
154
|
+
' 42',
|
155
|
+
' end',
|
156
|
+
'end',
|
157
|
+
].join("\n")
|
158
|
+
|
159
|
+
File.open "#{models_dir}/csv_foo.rb", "w" do |file|
|
160
|
+
inspect_source(source, file)
|
161
|
+
end
|
162
|
+
|
163
|
+
expect(cop.offenses).to be_empty
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'ignores rake tasks' do
|
167
|
+
source = [
|
168
|
+
'module Hello',
|
169
|
+
' def baz', # error is here if this file is named bar.rb
|
170
|
+
' 42',
|
171
|
+
' end',
|
172
|
+
'end',
|
173
|
+
].join("\n")
|
174
|
+
|
175
|
+
File.open "#{models_dir}/bar.rake", "w" do |file|
|
176
|
+
inspect_source(source, file)
|
177
|
+
end
|
178
|
+
|
179
|
+
expect(cop.offenses).to be_empty
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
describe RuboCop::Cop::Airbnb::NoTimeout do
|
2
|
+
subject(:cop) { described_class.new }
|
3
|
+
|
4
|
+
context 'send' do
|
5
|
+
it 'rejects Timeout.timeout' do
|
6
|
+
source = [
|
7
|
+
'def some_method(a)',
|
8
|
+
' Timeout.timeout(5) do',
|
9
|
+
' some_other_method(a)',
|
10
|
+
' end',
|
11
|
+
'end',
|
12
|
+
].join("\n")
|
13
|
+
inspect_source(source)
|
14
|
+
expect(cop.offenses.size).to eql(1)
|
15
|
+
expect(cop.offenses.first.message).to start_with('Do not use Timeout.timeout')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'accepts foo.timeout' do
|
19
|
+
source = [
|
20
|
+
'def some_method(a)',
|
21
|
+
' foo.timeout do',
|
22
|
+
' some_other_method(a)',
|
23
|
+
' end',
|
24
|
+
'end',
|
25
|
+
].join("\n")
|
26
|
+
inspect_source(source)
|
27
|
+
expect(cop.offenses).to be_empty
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
describe RuboCop::Cop::Airbnb::OptArgParameters do
|
2
|
+
subject(:cop) { described_class.new }
|
3
|
+
|
4
|
+
it 'allows method with no parameters' do
|
5
|
+
source = [
|
6
|
+
'def my_method',
|
7
|
+
'end',
|
8
|
+
].join("\n")
|
9
|
+
|
10
|
+
inspect_source(source)
|
11
|
+
expect(cop.offenses).to be_empty
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'allows method with one parameter' do
|
15
|
+
source = [
|
16
|
+
'def my_method(params)',
|
17
|
+
'end',
|
18
|
+
].join("\n")
|
19
|
+
|
20
|
+
inspect_source(source)
|
21
|
+
expect(cop.offenses).to be_empty
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'allows method with one parameter with a default hash value' do
|
25
|
+
source = [
|
26
|
+
'def my_method(params = {})',
|
27
|
+
'end',
|
28
|
+
].join("\n")
|
29
|
+
|
30
|
+
inspect_source(source)
|
31
|
+
expect(cop.offenses).to be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'allows method named default parameters' do
|
35
|
+
source = [
|
36
|
+
'def my_method(a, b, c: 5, d: 6)',
|
37
|
+
'end',
|
38
|
+
].join("\n")
|
39
|
+
|
40
|
+
inspect_source(source)
|
41
|
+
expect(cop.offenses).to be_empty
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'allows method with multiple parameter with a default hash value' do
|
45
|
+
source = [
|
46
|
+
'def my_method(a, b, c, params = {})',
|
47
|
+
'end',
|
48
|
+
].join("\n")
|
49
|
+
|
50
|
+
inspect_source(source)
|
51
|
+
expect(cop.offenses).to be_empty
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'allows method with default parameter before block parameter' do
|
55
|
+
source = [
|
56
|
+
'def my_method(a, b, c, params = {}, &block)',
|
57
|
+
'end',
|
58
|
+
].join("\n")
|
59
|
+
|
60
|
+
inspect_source(source)
|
61
|
+
expect(cop.offenses).to be_empty
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'rejects method with a default parameter other than the last non-block parameter' do
|
65
|
+
source = [
|
66
|
+
'def my_method(a, b = nil, c, &block)',
|
67
|
+
'end',
|
68
|
+
].join("\n")
|
69
|
+
|
70
|
+
inspect_source(source)
|
71
|
+
expect(cop.offenses.size).to eq(1)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'rejects method with a default parameter other than the last parameter' do
|
75
|
+
source = [
|
76
|
+
'def my_method(a, b = 5, c = nil, params = {})',
|
77
|
+
'end',
|
78
|
+
].join("\n")
|
79
|
+
|
80
|
+
inspect_source(source)
|
81
|
+
expect(cop.offenses.size).to eq(2)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'rejects method where last parameter has a default value of nil' do
|
85
|
+
source = [
|
86
|
+
'def my_method(a, b, c, params = nil)',
|
87
|
+
'end',
|
88
|
+
].join("\n")
|
89
|
+
|
90
|
+
inspect_source(source)
|
91
|
+
expect(cop.offenses.size).to eq(1)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'rejects method where last parameter has a default value of a constant' do
|
95
|
+
source = [
|
96
|
+
'def my_method(a, b, c, params = Constants::A_CONSTANT)',
|
97
|
+
'end',
|
98
|
+
].join("\n")
|
99
|
+
|
100
|
+
inspect_source(source)
|
101
|
+
expect(cop.offenses.size).to eq(1)
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
describe RuboCop::Cop::Airbnb::PhraseBundleKeys do
|
2
|
+
subject(:cop) { described_class.new }
|
3
|
+
|
4
|
+
it 'generates offenses for mismatched keys in PhraseBundle classes' do
|
5
|
+
source = <<EOS
|
6
|
+
# encoding: UTF-8
|
7
|
+
module PhraseBundles
|
8
|
+
class Host < PhraseBundle
|
9
|
+
|
10
|
+
def phrases
|
11
|
+
{
|
12
|
+
"shortened_key" => t(
|
13
|
+
"my_real_translation_key",
|
14
|
+
default: 'Does not matter',
|
15
|
+
),
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
EOS
|
21
|
+
|
22
|
+
inspect_source(source)
|
23
|
+
expect(cop.offenses.size).to eq(1)
|
24
|
+
expect(cop.offenses.map(&:line).sort).to eq([7])
|
25
|
+
expect(cop.messages).to eq(
|
26
|
+
['Phrase bundle keys should match their translation keys.']
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'does not generate offenses for matching keys in PhraseBundle classes' do
|
31
|
+
source = <<EOS
|
32
|
+
# encoding: UTF-8
|
33
|
+
module PhraseBundles
|
34
|
+
class Host < PhraseBundle
|
35
|
+
|
36
|
+
def phrases
|
37
|
+
{
|
38
|
+
"my_real_translation_key" => t(
|
39
|
+
"my_real_translation_key",
|
40
|
+
default: 'Does not matter',
|
41
|
+
),
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
EOS
|
47
|
+
|
48
|
+
inspect_source(source)
|
49
|
+
inspect_source(source)
|
50
|
+
expect(cop.offenses).to be_empty
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'passes on t calls that are not in PhraseBundle classes' do
|
54
|
+
source = <<EOS
|
55
|
+
# encoding: UTF-8
|
56
|
+
module PhraseBundles
|
57
|
+
class Host
|
58
|
+
|
59
|
+
def phrases
|
60
|
+
{
|
61
|
+
"shortened_key" => t(
|
62
|
+
"my_real_translation_key",
|
63
|
+
default: 'Does not matter',
|
64
|
+
),
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
EOS
|
70
|
+
|
71
|
+
inspect_source(source)
|
72
|
+
expect(cop.offenses).to be_empty
|
73
|
+
end
|
74
|
+
end
|