Skip to content

Commit 1e4f28b

Browse files
authored
Merge pull request #1266 from ccutrer/inspect_change_table_for_not_null_column
Rails/NotNullColumn: Inspect change_table calls for offenses
2 parents 7e691de + 5fcfda4 commit 1e4f28b

File tree

3 files changed

+193
-6
lines changed

3 files changed

+193
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#1266](https://github.com/rubocop/rubocop-rails/pull/1266): Check `change_table` calls for offenses. ([@ccutrer][])

Diff for: lib/rubocop/cop/rails/not_null_column.rb

+72-6
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@ module Rails
1515
# # bad
1616
# add_column :users, :name, :string, null: false
1717
# add_reference :products, :category, null: false
18+
# change_table :users do |t|
19+
# t.string :name, null: false
20+
# end
1821
#
1922
# # good
2023
# add_column :users, :name, :string, null: true
2124
# add_column :users, :name, :string, null: false, default: ''
25+
# change_table :users do |t|
26+
# t.string :name, null: false, default: ''
27+
# end
2228
# add_reference :products, :category
2329
# add_reference :products, :category, null: false, default: 1
2430
class NotNullColumn < Base
@@ -35,6 +41,22 @@ class NotNullColumn < Base
3541
(send nil? :add_reference _ _ (hash $...))
3642
PATTERN
3743

44+
def_node_matcher :change_table?, <<~PATTERN
45+
(block (send nil? :change_table ...) (args (arg $_)) _)
46+
PATTERN
47+
48+
def_node_matcher :add_not_null_column_in_change_table?, <<~PATTERN
49+
(send (lvar $_) :column _ $_ (hash $...))
50+
PATTERN
51+
52+
def_node_matcher :add_not_null_column_via_shortcut_in_change_table?, <<~PATTERN
53+
(send (lvar $_) $_ _ (hash $...))
54+
PATTERN
55+
56+
def_node_matcher :add_not_null_reference_in_change_table?, <<~PATTERN
57+
(send (lvar $_) :add_reference _ _ (hash $...))
58+
PATTERN
59+
3860
def_node_matcher :null_false?, <<~PATTERN
3961
(pair (sym :null) (false))
4062
PATTERN
@@ -48,16 +70,25 @@ def on_send(node)
4870
check_add_reference(node)
4971
end
5072

73+
def on_block(node)
74+
check_change_table(node)
75+
end
76+
alias on_numblock on_block
77+
5178
private
5279

80+
def check_column(type, pairs)
81+
if type.respond_to?(:value)
82+
return if type.value == :virtual || type.value == 'virtual'
83+
return if (type.value == :text || type.value == 'text') && database == MYSQL
84+
end
85+
86+
check_pairs(pairs)
87+
end
88+
5389
def check_add_column(node)
5490
add_not_null_column?(node) do |type, pairs|
55-
if type.respond_to?(:value)
56-
return if type.value == :virtual || type.value == 'virtual'
57-
return if (type.value == :text || type.value == 'text') && database == MYSQL
58-
end
59-
60-
check_pairs(pairs)
91+
check_column(type, pairs)
6192
end
6293
end
6394

@@ -67,6 +98,41 @@ def check_add_reference(node)
6798
end
6899
end
69100

101+
def check_add_column_in_change_table(node, table)
102+
add_not_null_column_in_change_table?(node) do |receiver, type, pairs|
103+
next unless receiver == table
104+
105+
check_column(type, pairs)
106+
end
107+
end
108+
109+
def check_add_column_via_shortcut_in_change_table(node, table)
110+
add_not_null_column_via_shortcut_in_change_table?(node) do |receiver, type, pairs|
111+
next unless receiver == table
112+
113+
check_column(type, pairs)
114+
end
115+
end
116+
117+
def check_add_reference_in_change_table(node, table)
118+
add_not_null_reference_in_change_table?(node) do |receiver, pairs|
119+
next unless receiver == table
120+
121+
check_pairs(pairs)
122+
end
123+
end
124+
125+
def check_change_table(node)
126+
change_table?(node) do |table|
127+
children = node.body.begin_type? ? node.body.children : [node.body]
128+
children.each do |child|
129+
check_add_column_in_change_table(child, table)
130+
check_add_column_via_shortcut_in_change_table(child, table)
131+
check_add_reference_in_change_table(child, table)
132+
end
133+
end
134+
end
135+
70136
def check_pairs(pairs)
71137
return if pairs.any? { |pair| default_option?(pair) }
72138

Diff for: spec/rubocop/cop/rails/not_null_column_spec.rb

+120
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,126 @@ def change
8787
end
8888
end
8989

90+
context 'with change_table call' do
91+
context 'with shortcut column call' do
92+
context 'with null: false' do
93+
it 'reports an offense' do
94+
expect_offense(<<~RUBY)
95+
def change
96+
change_table :users do |t|
97+
t.string :name, null: false
98+
^^^^^^^^^^^ Do not add a NOT NULL column without a default value.
99+
end
100+
end
101+
RUBY
102+
end
103+
104+
it 'reports multiple offenses' do
105+
expect_offense(<<~RUBY)
106+
def change
107+
change_table :users do |t|
108+
t.string :name, null: false
109+
^^^^^^^^^^^ Do not add a NOT NULL column without a default value.
110+
t.string :address, null: false
111+
^^^^^^^^^^^ Do not add a NOT NULL column without a default value.
112+
end
113+
end
114+
RUBY
115+
end
116+
end
117+
118+
context 'with default option' do
119+
it 'does not register an offense' do
120+
expect_no_offenses(<<~RUBY)
121+
def change
122+
change_table :users do |t|
123+
t.string :name, null: false, default: ""
124+
end
125+
end
126+
RUBY
127+
end
128+
end
129+
130+
context 'without any options' do
131+
it 'does not register an offense' do
132+
expect_no_offenses(<<~RUBY)
133+
def change
134+
change_table :users do |t|
135+
t.string :name
136+
end
137+
end
138+
RUBY
139+
end
140+
end
141+
end
142+
143+
context 'with column call' do
144+
context 'with null: false' do
145+
it 'reports an offense' do
146+
expect_offense(<<~RUBY)
147+
def change
148+
change_table :users do |t|
149+
t.column :name, :string, null: false
150+
^^^^^^^^^^^ Do not add a NOT NULL column without a default value.
151+
end
152+
end
153+
RUBY
154+
end
155+
end
156+
157+
context 'with default option' do
158+
it 'does not register an offense' do
159+
expect_no_offenses(<<~RUBY)
160+
def change
161+
change_table :users do |t|
162+
t.column :name, :string, null: false, default: ""
163+
end
164+
end
165+
RUBY
166+
end
167+
end
168+
169+
context 'without any options' do
170+
it 'does not register an offense' do
171+
expect_no_offenses(<<~RUBY)
172+
def change
173+
change_table :users do |t|
174+
t.column :name, :string
175+
end
176+
end
177+
RUBY
178+
end
179+
end
180+
end
181+
182+
context 'with reference call' do
183+
context 'with null: false' do
184+
it 'reports an offense' do
185+
expect_offense(<<~RUBY)
186+
def change
187+
change_table :users do |t|
188+
t.references :address, null: false
189+
^^^^^^^^^^^ Do not add a NOT NULL column without a default value.
190+
end
191+
end
192+
RUBY
193+
end
194+
end
195+
196+
context 'without any options' do
197+
it 'does not register an offense' do
198+
expect_no_offenses(<<~RUBY)
199+
def change
200+
change_table :users do |t|
201+
t.references :address
202+
end
203+
end
204+
RUBY
205+
end
206+
end
207+
end
208+
end
209+
90210
context 'with add_reference call' do
91211
context 'with null: false' do
92212
it 'reports an offense' do

0 commit comments

Comments
 (0)