rails_best_practices 1.19.2 → 1.20.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.
- checksums.yaml +5 -5
- data/.gitignore +0 -1
- data/.travis.yml +2 -3
- data/CHANGELOG.md +7 -7
- data/Gemfile +3 -5
- data/Gemfile.lock +125 -0
- data/Guardfile +2 -0
- data/README.md +6 -6
- data/Rakefile +2 -17
- data/assets/result.html.erb +2 -0
- data/lib/rails_best_practices.rb +3 -2
- data/lib/rails_best_practices/analyzer.rb +61 -49
- data/lib/rails_best_practices/cli.rb +22 -0
- data/lib/rails_best_practices/command.rb +1 -131
- data/lib/rails_best_practices/core/check.rb +64 -56
- data/lib/rails_best_practices/core/checks_loader.rb +24 -23
- data/lib/rails_best_practices/core/configs.rb +1 -2
- data/lib/rails_best_practices/core/controllers.rb +1 -2
- data/lib/rails_best_practices/core/error.rb +1 -1
- data/lib/rails_best_practices/core/helpers.rb +1 -2
- data/lib/rails_best_practices/core/mailers.rb +1 -2
- data/lib/rails_best_practices/core/methods.rb +27 -21
- data/lib/rails_best_practices/core/model_associations.rb +10 -5
- data/lib/rails_best_practices/core/models.rb +1 -2
- data/lib/rails_best_practices/core/modules.rb +1 -1
- data/lib/rails_best_practices/core/routes.rb +2 -2
- data/lib/rails_best_practices/core/runner.rb +67 -73
- data/lib/rails_best_practices/lexicals/long_line_check.rb +7 -3
- data/lib/rails_best_practices/option_parser.rb +156 -0
- data/lib/rails_best_practices/prepares.rb +1 -1
- data/lib/rails_best_practices/prepares/controller_prepare.rb +24 -17
- data/lib/rails_best_practices/prepares/gemfile_prepare.rb +2 -2
- data/lib/rails_best_practices/prepares/helper_prepare.rb +6 -1
- data/lib/rails_best_practices/prepares/initializer_prepare.rb +3 -3
- data/lib/rails_best_practices/prepares/mailer_prepare.rb +2 -1
- data/lib/rails_best_practices/prepares/model_prepare.rb +63 -23
- data/lib/rails_best_practices/prepares/route_prepare.rb +28 -21
- data/lib/rails_best_practices/prepares/schema_prepare.rb +1 -1
- data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +38 -34
- data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +94 -89
- data/lib/rails_best_practices/reviews/check_destroy_return_value_review.rb +15 -5
- data/lib/rails_best_practices/reviews/check_save_return_value_review.rb +20 -8
- data/lib/rails_best_practices/reviews/default_scope_is_evil_review.rb +1 -1
- data/lib/rails_best_practices/reviews/dry_bundler_in_capistrano_review.rb +1 -1
- data/lib/rails_best_practices/reviews/hash_syntax_review.rb +16 -16
- data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +12 -12
- data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +10 -11
- data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +25 -24
- data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +4 -4
- data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +9 -10
- data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +10 -11
- data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +24 -22
- data/lib/rails_best_practices/reviews/not_rescue_exception_review.rb +1 -1
- data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +1 -2
- data/lib/rails_best_practices/reviews/not_use_time_ago_in_words_review.rb +1 -1
- data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +8 -8
- data/lib/rails_best_practices/reviews/protect_mass_assignment_review.rb +35 -32
- data/lib/rails_best_practices/reviews/remove_empty_helpers_review.rb +4 -4
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +26 -19
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +12 -10
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +38 -18
- data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +11 -11
- data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +77 -74
- data/lib/rails_best_practices/reviews/review.rb +2 -1
- data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +2 -3
- data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +12 -12
- data/lib/rails_best_practices/reviews/use_before_filter_review.rb +18 -15
- data/lib/rails_best_practices/reviews/use_model_association_review.rb +15 -15
- data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +24 -22
- data/lib/rails_best_practices/reviews/use_observer_review.rb +28 -28
- data/lib/rails_best_practices/reviews/use_parentheses_in_method_def_review.rb +6 -6
- data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +68 -66
- data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +9 -8
- data/lib/rails_best_practices/reviews/use_scope_access_review.rb +16 -14
- data/lib/rails_best_practices/reviews/use_turbo_sprockets_rails3_review.rb +2 -1
- data/lib/rails_best_practices/version.rb +1 -1
- data/rails_best_practices.gemspec +38 -43
- data/spec/fixtures/lib/rails_best_practices/plugins/reviews/not_use_rails_root_review.rb +1 -2
- data/spec/rails_best_practices/analyzer_spec.rb +73 -42
- data/spec/rails_best_practices/core/check_spec.rb +5 -5
- data/spec/rails_best_practices/core/checks_loader_spec.rb +3 -3
- data/spec/rails_best_practices/core/configs_spec.rb +1 -1
- data/spec/rails_best_practices/core/controllers_spec.rb +1 -1
- data/spec/rails_best_practices/core/error_spec.rb +21 -18
- data/spec/rails_best_practices/core/except_methods_spec.rb +7 -7
- data/spec/rails_best_practices/core/gems_spec.rb +4 -4
- data/spec/rails_best_practices/core/helpers_spec.rb +1 -1
- data/spec/rails_best_practices/core/klasses_spec.rb +3 -3
- data/spec/rails_best_practices/core/mailers_spec.rb +1 -1
- data/spec/rails_best_practices/core/methods_spec.rb +6 -6
- data/spec/rails_best_practices/core/model_associations_spec.rb +10 -6
- data/spec/rails_best_practices/core/model_attributes_spec.rb +4 -4
- data/spec/rails_best_practices/core/models_spec.rb +1 -1
- data/spec/rails_best_practices/core/modules_spec.rb +5 -5
- data/spec/rails_best_practices/core/routes_spec.rb +5 -5
- data/spec/rails_best_practices/core/runner_spec.rb +9 -7
- data/spec/rails_best_practices/core_ext/erubis_spec.rb +10 -10
- data/spec/rails_best_practices/lexicals/long_line_check_spec.rb +32 -31
- data/spec/rails_best_practices/lexicals/remove_tab_check_spec.rb +6 -6
- data/spec/rails_best_practices/lexicals/remove_trailing_whitespace_check_spec.rb +6 -6
- data/spec/rails_best_practices/prepares/config_prepare_spec.rb +2 -2
- data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +18 -10
- data/spec/rails_best_practices/prepares/gemfile_prepare_spec.rb +17 -17
- data/spec/rails_best_practices/prepares/helper_prepare_spec.rb +3 -3
- data/spec/rails_best_practices/prepares/initializer_prepare_spec.rb +3 -3
- data/spec/rails_best_practices/prepares/mailer_prepare_spec.rb +2 -2
- data/spec/rails_best_practices/prepares/model_prepare_spec.rb +79 -43
- data/spec/rails_best_practices/prepares/route_prepare_spec.rb +141 -76
- data/spec/rails_best_practices/prepares/schema_prepare_spec.rb +2 -2
- data/spec/rails_best_practices/reviews/add_model_virtual_attribute_review_spec.rb +18 -12
- data/spec/rails_best_practices/reviews/always_add_db_index_review_spec.rb +28 -22
- data/spec/rails_best_practices/reviews/check_destroy_return_value_review_spec.rb +15 -13
- data/spec/rails_best_practices/reviews/check_save_return_value_review_spec.rb +31 -21
- data/spec/rails_best_practices/reviews/default_scope_is_evil_review_spec.rb +6 -6
- data/spec/rails_best_practices/reviews/dry_bundler_in_capistrano_review_spec.rb +5 -5
- data/spec/rails_best_practices/reviews/hash_syntax_review_spec.rb +13 -13
- data/spec/rails_best_practices/reviews/isolate_seed_data_review_spec.rb +7 -7
- data/spec/rails_best_practices/reviews/keep_finders_on_their_own_model_review_spec.rb +9 -9
- data/spec/rails_best_practices/reviews/law_of_demeter_review_spec.rb +29 -22
- data/spec/rails_best_practices/reviews/move_code_into_controller_review_spec.rb +6 -6
- data/spec/rails_best_practices/reviews/move_code_into_helper_review_spec.rb +11 -6
- data/spec/rails_best_practices/reviews/move_code_into_model_review_spec.rb +32 -22
- data/spec/rails_best_practices/reviews/move_finder_to_named_scope_review_spec.rb +7 -7
- data/spec/rails_best_practices/reviews/move_model_logic_into_model_review_spec.rb +9 -7
- data/spec/rails_best_practices/reviews/needless_deep_nesting_review_spec.rb +9 -9
- data/spec/rails_best_practices/reviews/not_rescue_exception_review_spec.rb +9 -9
- data/spec/rails_best_practices/reviews/not_use_default_route_review_spec.rb +5 -5
- data/spec/rails_best_practices/reviews/not_use_time_ago_in_words_review_spec.rb +7 -7
- data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +7 -7
- data/spec/rails_best_practices/reviews/protect_mass_assignment_review_spec.rb +24 -17
- data/spec/rails_best_practices/reviews/remove_empty_helpers_review_spec.rb +6 -6
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +64 -31
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_helpers_review_spec.rb +21 -14
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +57 -53
- data/spec/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review_spec.rb +10 -8
- data/spec/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review_spec.rb +20 -14
- data/spec/rails_best_practices/reviews/restrict_auto_generated_routes_review_spec.rb +54 -31
- data/spec/rails_best_practices/reviews/simplify_render_in_controllers_review_spec.rb +9 -9
- data/spec/rails_best_practices/reviews/simplify_render_in_views_review_spec.rb +13 -13
- data/spec/rails_best_practices/reviews/use_before_filter_review_spec.rb +11 -9
- data/spec/rails_best_practices/reviews/use_model_association_review_spec.rb +7 -7
- data/spec/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review_spec.rb +35 -31
- data/spec/rails_best_practices/reviews/use_observer_review_spec.rb +6 -6
- data/spec/rails_best_practices/reviews/use_parentheses_in_method_def_review_spec.rb +10 -8
- data/spec/rails_best_practices/reviews/use_query_attribute_review_spec.rb +31 -24
- data/spec/rails_best_practices/reviews/use_say_with_time_in_migrations_review_spec.rb +15 -11
- data/spec/rails_best_practices/reviews/use_scope_access_review_spec.rb +14 -14
- data/spec/rails_best_practices/reviews/use_turbo_sprockets_rails3_review_spec.rb +61 -59
- metadata +16 -18
@@ -11,7 +11,7 @@ module RailsBestPractices
|
|
11
11
|
# Review process:
|
12
12
|
# only check the command and command_calls nodes and at the end of review process,
|
13
13
|
# if the receiver of command node is "create_table", then remember the table names
|
14
|
-
# if the receiver of command_call node is "integer" or "string" and suffix with _id, then remember it as foreign key
|
14
|
+
# if the receiver of command_call node is "integer" or "string" or "bigint" and suffix with _id, then remember it as foreign key
|
15
15
|
# if the receiver of command_call node is "string", the name of it is _type suffixed and there is an integer or string column _id suffixed, then remember it as polymorphic foreign key
|
16
16
|
# if the receiver of command_call node is remembered as foreign key and it have argument non-false "index", then remember the index columns
|
17
17
|
# if the receiver of command node is "add_index", then remember the index columns
|
@@ -40,9 +40,9 @@ module RailsBestPractices
|
|
40
40
|
# if the message of command_call node is "create_table", then remember the table name.
|
41
41
|
# if the message of command_call node is "add_index", then remember it as index columns.
|
42
42
|
add_callback :start_command_call do |node|
|
43
|
-
if %w[integer string].include? node.message.to_s
|
43
|
+
if %w[integer string bigint].include? node.message.to_s
|
44
44
|
remember_foreign_key_columns(node)
|
45
|
-
elsif
|
45
|
+
elsif node.message.to_s == 'index'
|
46
46
|
remember_index_columns_inside_table(node)
|
47
47
|
end
|
48
48
|
end
|
@@ -75,120 +75,125 @@ module RailsBestPractices
|
|
75
75
|
@foreign_keys.each do |table, foreign_key|
|
76
76
|
table_node = @table_nodes[table]
|
77
77
|
foreign_key.each do |column|
|
78
|
-
|
79
|
-
|
80
|
-
|
78
|
+
next unless not_indexed?(table, column)
|
79
|
+
|
80
|
+
add_error "always add db index (#{table} => [#{Array(column).join(', ')}])",
|
81
|
+
table_node.file,
|
82
|
+
table_node.line_number
|
81
83
|
end
|
82
84
|
end
|
83
85
|
end
|
84
86
|
|
85
87
|
private
|
86
88
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
89
|
+
# remember the node as index columns, when used outside a table
|
90
|
+
# block, i.e.
|
91
|
+
# add_index :table_name, :column_name
|
92
|
+
def remember_index_columns_outside_table(node)
|
93
|
+
table_name = node.arguments.all.first.to_s
|
94
|
+
index_column = node.arguments.all[1].to_object
|
93
95
|
|
94
|
-
|
95
|
-
|
96
|
-
|
96
|
+
@index_columns[table_name] ||= []
|
97
|
+
@index_columns[table_name] << index_column
|
98
|
+
end
|
97
99
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
100
|
+
# remember the node as index columns, when used inside a table
|
101
|
+
# block, i.e.
|
102
|
+
# t.index [:column_name, ...]
|
103
|
+
def remember_index_columns_inside_table(node)
|
104
|
+
table_name = @table_name
|
105
|
+
index_column = node.arguments.all.first.to_object
|
104
106
|
|
105
|
-
|
106
|
-
|
107
|
-
|
107
|
+
@index_columns[table_name] ||= []
|
108
|
+
@index_columns[table_name] << index_column
|
109
|
+
end
|
108
110
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
111
|
+
# remember table nodes
|
112
|
+
def remember_table_nodes(node)
|
113
|
+
@table_name = node.arguments.all.first.to_s
|
114
|
+
@table_nodes[@table_name] = node
|
115
|
+
end
|
114
116
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
117
|
+
# remember foreign key columns
|
118
|
+
def remember_foreign_key_columns(node)
|
119
|
+
table_name = @table_name
|
120
|
+
foreign_key_column = node.arguments.all.first.to_s
|
121
|
+
@foreign_keys[table_name] ||= []
|
122
|
+
if foreign_key_column =~ /(.*?)_id$/
|
123
|
+
@foreign_keys[table_name] <<
|
124
|
+
if @foreign_keys[table_name].delete("#{Regexp.last_match(1)}_type")
|
125
|
+
["#{Regexp.last_match(1)}_id", "#{Regexp.last_match(1)}_type"]
|
123
126
|
else
|
124
|
-
|
127
|
+
foreign_key_column
|
125
128
|
end
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
129
|
+
foreign_id_column = foreign_key_column
|
130
|
+
elsif foreign_key_column =~ /(.*?)_type$/
|
131
|
+
@foreign_keys[table_name] <<
|
132
|
+
if @foreign_keys[table_name].delete("#{Regexp.last_match(1)}_id")
|
133
|
+
["#{Regexp.last_match(1)}_id", "#{Regexp.last_match(1)}_type"]
|
130
134
|
else
|
131
|
-
|
135
|
+
foreign_key_column
|
132
136
|
end
|
133
|
-
|
134
|
-
|
137
|
+
foreign_id_column = "#{Regexp.last_match(1)}_id"
|
138
|
+
end
|
135
139
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
end
|
140
|
+
if foreign_id_column
|
141
|
+
index_node = node.arguments.all.last.hash_value('index')
|
142
|
+
if index_node.present? && (index_node.to_s != 'false')
|
143
|
+
@index_columns[table_name] ||= []
|
144
|
+
@index_columns[table_name] << foreign_id_column
|
142
145
|
end
|
143
146
|
end
|
147
|
+
end
|
144
148
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
149
|
+
# remove the non foreign keys without corresponding tables.
|
150
|
+
def remove_table_not_exist_foreign_keys
|
151
|
+
@foreign_keys.each do |table, foreign_keys|
|
152
|
+
foreign_keys.delete_if do |key|
|
153
|
+
if key.is_a?(String) && key =~ /_id$/
|
154
|
+
class_name = Prepares.model_associations.get_association_class_name(table, key[0..-4])
|
155
|
+
class_name ? !@table_nodes[class_name.gsub('::', '').tableize] : !@table_nodes[key[0..-4].pluralize]
|
153
156
|
end
|
154
157
|
end
|
155
158
|
end
|
159
|
+
end
|
156
160
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
}
|
161
|
+
# remove the non foreign keys with only _type column.
|
162
|
+
def remove_only_type_foreign_keys
|
163
|
+
@foreign_keys.each do |_table, foreign_keys|
|
164
|
+
foreign_keys.delete_if { |key| key.is_a?(String) && key =~ /_type$/ }
|
162
165
|
end
|
166
|
+
end
|
163
167
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
168
|
+
# combine polymorphic foreign keys, e.g.
|
169
|
+
# [tagger_id], [tagger_type] => [tagger_id, tagger_type]
|
170
|
+
def combine_polymorphic_foreign_keys
|
171
|
+
@index_columns.each do |_table, foreign_keys|
|
172
|
+
foreign_id_keys = foreign_keys.select { |key| key.size == 1 && key.first =~ /_id/ }
|
173
|
+
foreign_type_keys = foreign_keys.select { |key| key.size == 1 && key.first =~ /_type/ }
|
174
|
+
foreign_id_keys.each do |id_key|
|
175
|
+
next unless type_key =
|
176
|
+
foreign_type_keys.detect { |type_key| type_key.first == id_key.first.sub(/_id/, '') + '_type' }
|
177
|
+
|
178
|
+
foreign_keys.delete(id_key)
|
179
|
+
foreign_keys.delete(type_key)
|
180
|
+
foreign_keys << id_key + type_key
|
181
|
+
end
|
178
182
|
end
|
183
|
+
end
|
179
184
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
+
# check if the table's column is indexed.
|
186
|
+
def not_indexed?(table, column)
|
187
|
+
index_columns = @index_columns[table]
|
188
|
+
!index_columns || index_columns.none? { |e| greater_than_or_equal(Array(e), Array(column)) }
|
189
|
+
end
|
185
190
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
191
|
+
# check if more_array is greater than less_array or equal to less_array.
|
192
|
+
def greater_than_or_equal(more_array, less_array)
|
193
|
+
more_size = more_array.size
|
194
|
+
less_size = less_array.size
|
195
|
+
(more_array - less_array).size == more_size - less_size
|
196
|
+
end
|
192
197
|
end
|
193
198
|
end
|
194
199
|
end
|
@@ -10,7 +10,17 @@ module RailsBestPractices
|
|
10
10
|
# Check all "save" calls to check the return value is used by a node we have visited.
|
11
11
|
class CheckDestroyReturnValueReview < Review
|
12
12
|
include Classable
|
13
|
-
interesting_nodes :call,
|
13
|
+
interesting_nodes :call,
|
14
|
+
:command_call,
|
15
|
+
:method_add_arg,
|
16
|
+
:if,
|
17
|
+
:ifop,
|
18
|
+
:elsif,
|
19
|
+
:unless,
|
20
|
+
:if_mod,
|
21
|
+
:unless_mod,
|
22
|
+
:assign,
|
23
|
+
:binary
|
14
24
|
interesting_files ALL_FILES
|
15
25
|
|
16
26
|
add_callback :start_if, :start_ifop, :start_elsif, :start_unless, :start_if_mod, :start_unless_mod do |node|
|
@@ -21,14 +31,13 @@ module RailsBestPractices
|
|
21
31
|
@used_return_value_of = node.right_value
|
22
32
|
end
|
23
33
|
|
34
|
+
# Consider anything used in an expression like "A or B" as used
|
24
35
|
add_callback :start_binary do |node|
|
25
|
-
# Consider anything used in an expression like "A or B" as used
|
26
36
|
if %w[&& || and or].include?(node[2].to_s)
|
27
37
|
all_conditions = node.all_conditions
|
28
38
|
# if our current binary is a subset of the @used_return_value_of
|
29
39
|
# then don't overwrite it
|
30
|
-
already_included = @used_return_value_of &&
|
31
|
-
(all_conditions - @used_return_value_of).empty?
|
40
|
+
already_included = @used_return_value_of && (all_conditions - @used_return_value_of).empty?
|
32
41
|
|
33
42
|
@used_return_value_of = node.all_conditions unless already_included
|
34
43
|
end
|
@@ -36,7 +45,8 @@ module RailsBestPractices
|
|
36
45
|
|
37
46
|
def return_value_is_used?(node)
|
38
47
|
return false unless @used_return_value_of
|
39
|
-
|
48
|
+
|
49
|
+
(node == @used_return_value_of) || @used_return_value_of.include?(node)
|
40
50
|
end
|
41
51
|
|
42
52
|
def model_classnames
|
@@ -14,7 +14,17 @@ module RailsBestPractices
|
|
14
14
|
# Check all "save" calls to check the return value is used by a node we have visited.
|
15
15
|
class CheckSaveReturnValueReview < Review
|
16
16
|
include Classable
|
17
|
-
interesting_nodes :call,
|
17
|
+
interesting_nodes :call,
|
18
|
+
:command_call,
|
19
|
+
:method_add_arg,
|
20
|
+
:if,
|
21
|
+
:ifop,
|
22
|
+
:elsif,
|
23
|
+
:unless,
|
24
|
+
:if_mod,
|
25
|
+
:unless_mod,
|
26
|
+
:assign,
|
27
|
+
:binary
|
18
28
|
interesting_files ALL_FILES
|
19
29
|
url 'https://rails-bestpractices.com/posts/2012/11/02/check-the-return-value-of-save-otherwise-use-save/'
|
20
30
|
|
@@ -26,14 +36,13 @@ module RailsBestPractices
|
|
26
36
|
@used_return_value_of = node.right_value
|
27
37
|
end
|
28
38
|
|
39
|
+
# Consider anything used in an expression like "A or B" as used
|
29
40
|
add_callback :start_binary do |node|
|
30
|
-
# Consider anything used in an expression like "A or B" as used
|
31
41
|
if %w[&& || and or].include?(node[2].to_s)
|
32
42
|
all_conditions = node.all_conditions
|
33
43
|
# if our current binary is a subset of the @used_return_value_of
|
34
44
|
# then don't overwrite it
|
35
|
-
already_included = @used_return_value_of &&
|
36
|
-
(all_conditions - @used_return_value_of).empty?
|
45
|
+
already_included = @used_return_value_of && (all_conditions - @used_return_value_of).empty?
|
37
46
|
|
38
47
|
@used_return_value_of = node.all_conditions unless already_included
|
39
48
|
end
|
@@ -41,7 +50,8 @@ module RailsBestPractices
|
|
41
50
|
|
42
51
|
def return_value_is_used?(node)
|
43
52
|
return false unless @used_return_value_of
|
44
|
-
|
53
|
+
|
54
|
+
(node == @used_return_value_of) || @used_return_value_of.include?(node)
|
45
55
|
end
|
46
56
|
|
47
57
|
def model_classnames
|
@@ -57,9 +67,11 @@ module RailsBestPractices
|
|
57
67
|
end
|
58
68
|
elsif message == 'create'
|
59
69
|
# We're only interested in 'create' calls on model classes:
|
60
|
-
possible_receiver_classes =
|
61
|
-
|
62
|
-
|
70
|
+
possible_receiver_classes =
|
71
|
+
[node.receiver.to_s] +
|
72
|
+
classable_modules.map do |mod|
|
73
|
+
"#{mod}::#{node.receiver}"
|
74
|
+
end
|
63
75
|
unless (possible_receiver_classes & model_classnames).empty?
|
64
76
|
add_error "use 'create!' instead of 'create' as the latter may not always save"
|
65
77
|
end
|
@@ -20,7 +20,7 @@ module RailsBestPractices
|
|
20
20
|
|
21
21
|
# check call node to see if it is with message "namespace" and argument "bundler".
|
22
22
|
add_callback :start_command do |node|
|
23
|
-
if
|
23
|
+
if node.message.to_s == 'namespace' && node.arguments.all[0].to_s == 'bundler'
|
24
24
|
add_error 'dry bundler in capistrano'
|
25
25
|
end
|
26
26
|
end
|
@@ -12,7 +12,7 @@ module RailsBestPractices
|
|
12
12
|
interesting_nodes :hash, :bare_assoc_hash
|
13
13
|
interesting_files ALL_FILES
|
14
14
|
|
15
|
-
VALID_SYMBOL_KEY = /\A[@$_A-Za-z]([_\w]*[!_=?\w])?\z
|
15
|
+
VALID_SYMBOL_KEY = /\A[@$_A-Za-z]([_\w]*[!_=?\w])?\z/.freeze
|
16
16
|
|
17
17
|
# check hash node to see if it is ruby 1.8 style.
|
18
18
|
add_callback :start_hash, :start_bare_assoc_hash do |node|
|
@@ -23,24 +23,24 @@ module RailsBestPractices
|
|
23
23
|
|
24
24
|
protected
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
# check if hash node is empty.
|
27
|
+
def empty_hash?(node)
|
28
|
+
s(:hash, nil) == node || s(:bare_assoc_hash, nil) == node
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
# check if hash key/value pairs are ruby 1.8 style.
|
32
|
+
def hash_is_18?(node)
|
33
|
+
pair_nodes = node.sexp_type == :hash ? node[1][1] : node[1]
|
34
|
+
return false if pair_nodes.blank?
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
pair_nodes.any? { |pair_node| pair_node[1].sexp_type == :symbol_literal }
|
37
|
+
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
# check if the hash keys are valid to be converted to ruby 1.9
|
40
|
+
# syntax.
|
41
|
+
def valid_keys?(node)
|
42
|
+
node.hash_keys.all? { |key| key =~ VALID_SYMBOL_KEY }
|
43
|
+
end
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -55,20 +55,20 @@ module RailsBestPractices
|
|
55
55
|
|
56
56
|
private
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
58
|
+
# check assignment node,
|
59
|
+
# if the right vavlue is a method_add_arg node with message "new",
|
60
|
+
# then remember the left value as new variable.
|
61
|
+
def remember_new_variable(node)
|
62
|
+
right_value = node.right_value
|
63
|
+
if right_value.sexp_type == :method_add_arg && right_value.message.to_s == 'new'
|
64
|
+
@new_variables << node.left_value.to_s
|
66
65
|
end
|
66
|
+
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
68
|
+
# see if the receiver of the call node is included in the @new_varaibles.
|
69
|
+
def new_record?(node)
|
70
|
+
@new_variables.include? node.receiver.to_s
|
71
|
+
end
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
@@ -37,17 +37,16 @@ module RailsBestPractices
|
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
40
|
+
# check if the call node is the finder of other model.
|
41
|
+
#
|
42
|
+
# the message of the node should be one of find, all, first or last,
|
43
|
+
# and the receiver of the node should be with message :call (this is the other model),
|
44
|
+
# and any of its arguments is a hash,
|
45
|
+
# then it is the finder of other model.
|
46
|
+
def other_finder?(node)
|
47
|
+
FINDERS.include?(node[1].message.to_s) && node[1].receiver.sexp_type == :call &&
|
48
|
+
node.arguments.grep_nodes_count(sexp_type: :bare_assoc_hash) > 0
|
49
|
+
end
|
51
50
|
end
|
52
51
|
end
|
53
52
|
end
|