rails_best_practices 1.19.2 → 1.20.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|