rails_best_practices 1.20.0 → 1.22.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 +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +49 -43
- data/Guardfile +2 -0
- data/Rakefile +2 -0
- data/assets/result.html.erb +2 -0
- data/lib/rails_best_practices/analyzer.rb +59 -48
- data/lib/rails_best_practices/core/check.rb +39 -32
- data/lib/rails_best_practices/core/checks_loader.rb +8 -6
- 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 +21 -16
- data/lib/rails_best_practices/core/model_associations.rb +9 -4
- 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 +49 -34
- data/lib/rails_best_practices/inline_disables/comment_ripper.rb +19 -0
- data/lib/rails_best_practices/inline_disables/inline_disable.rb +50 -0
- data/lib/rails_best_practices/inline_disables.rb +3 -0
- data/lib/rails_best_practices/lexicals/long_line_check.rb +7 -3
- data/lib/rails_best_practices/option_parser.rb +22 -6
- data/lib/rails_best_practices/prepares/controller_prepare.rb +15 -3
- data/lib/rails_best_practices/prepares/gemfile_prepare.rb +1 -1
- data/lib/rails_best_practices/prepares/helper_prepare.rb +6 -1
- data/lib/rails_best_practices/prepares/initializer_prepare.rb +2 -2
- data/lib/rails_best_practices/prepares/mailer_prepare.rb +1 -0
- data/lib/rails_best_practices/prepares/model_prepare.rb +52 -12
- data/lib/rails_best_practices/prepares/route_prepare.rb +16 -10
- data/lib/rails_best_practices/prepares.rb +1 -1
- data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +15 -13
- data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +34 -29
- data/lib/rails_best_practices/reviews/check_destroy_return_value_review.rb +14 -5
- data/lib/rails_best_practices/reviews/check_save_return_value_review.rb +19 -8
- data/lib/rails_best_practices/reviews/hash_syntax_review.rb +5 -5
- data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +4 -4
- data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +7 -8
- data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +6 -6
- data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +1 -1
- data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +6 -7
- data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +7 -8
- data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +12 -10
- data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +1 -2
- data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +5 -5
- data/lib/rails_best_practices/reviews/protect_mass_assignment_review.rb +5 -2
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +6 -3
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +6 -4
- data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +29 -9
- data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +3 -3
- data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +17 -15
- data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +1 -2
- data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +3 -3
- data/lib/rails_best_practices/reviews/use_before_filter_review.rb +2 -1
- data/lib/rails_best_practices/reviews/use_model_association_review.rb +5 -5
- data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +9 -8
- data/lib/rails_best_practices/reviews/use_observer_review.rb +9 -9
- data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +26 -26
- data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +8 -7
- data/lib/rails_best_practices/reviews/use_scope_access_review.rb +17 -15
- data/lib/rails_best_practices/reviews/use_turbo_sprockets_rails3_review.rb +2 -1
- data/lib/rails_best_practices/version.rb +1 -1
- data/lib/rails_best_practices.rb +2 -2
- data/rails_best_practices.gemspec +39 -38
- 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 -21
- 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/inline_disables/inline_disable_spec.rb +62 -0
- data/spec/rails_best_practices/lexicals/long_line_check_spec.rb +11 -10
- 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 +2 -2
- 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 +138 -77
- 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 +9 -9
- 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 +21 -14
- 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 +26 -16
- 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 -19
- 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 +44 -31
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_helpers_review_spec.rb +17 -12
- data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +46 -44
- 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 +16 -10
- 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 +21 -17
- 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 +9 -7
- 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 +10 -8
- metadata +12 -7
@@ -38,10 +38,9 @@ module RailsBestPractices
|
|
38
38
|
else
|
39
39
|
action_names = [second_argument.hash_value('to').to_s]
|
40
40
|
end
|
41
|
-
elsif first_argument.sexp_type == :symbol_literal && second_argument.try(:sexp_type) &&
|
41
|
+
elsif first_argument.sexp_type == :symbol_literal && second_argument.try(:sexp_type) &&
|
42
42
|
second_argument.sexp_type == :symbol_literal
|
43
|
-
action_names = node.arguments.all.select
|
44
|
-
{ |arg| arg.sexp_type == :symbol_literal }.map(&:to_s)
|
43
|
+
action_names = node.arguments.all.select { |arg| arg.sexp_type == :symbol_literal }.map(&:to_s)
|
45
44
|
else
|
46
45
|
action_names = [first_argument.to_s]
|
47
46
|
end
|
@@ -84,7 +83,10 @@ module RailsBestPractices
|
|
84
83
|
action_name = options.hash_value('action').present? ? options.hash_value('action').to_s : '*'
|
85
84
|
@routes.add_route(current_namespaces, controller_name, action_name)
|
86
85
|
else
|
87
|
-
route_node =
|
86
|
+
route_node =
|
87
|
+
options.hash_values.find do |value_node|
|
88
|
+
value_node.sexp_type == :string_literal && value_node.to_s.include?('#')
|
89
|
+
end
|
88
90
|
if route_node.present?
|
89
91
|
controller_name, action_name = route_node.to_s.split('#')
|
90
92
|
@routes.add_route(current_namespaces, controller_name.underscore, action_name)
|
@@ -101,7 +103,10 @@ module RailsBestPractices
|
|
101
103
|
options = node.arguments.all.last
|
102
104
|
case options.sexp_type
|
103
105
|
when :bare_assoc_hash
|
104
|
-
route_node =
|
106
|
+
route_node =
|
107
|
+
options.hash_values.find do |value_node|
|
108
|
+
value_node.sexp_type == :string_literal && value_node.to_s.include?('#')
|
109
|
+
end
|
105
110
|
if route_node.present?
|
106
111
|
controller_name, action_name = route_node.to_s.split('#')
|
107
112
|
@routes.add_route(current_namespaces, controller_name.underscore, action_name)
|
@@ -145,11 +150,12 @@ module RailsBestPractices
|
|
145
150
|
if node.arguments.all.last.hash_value('module').present?
|
146
151
|
@namespaces << node.arguments.all.last.hash_value('module').to_s
|
147
152
|
end
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
+
@controller_name =
|
154
|
+
if node.arguments.all.last.hash_value('controller').present?
|
155
|
+
[:scope, node.arguments.all.last.hash_value('controller').to_s]
|
156
|
+
else
|
157
|
+
@controller_name.try(:first) == :scope ? @controller_name : nil
|
158
|
+
end
|
153
159
|
when 'with_options'
|
154
160
|
argument = node.arguments.all.last
|
155
161
|
if argument.sexp_type == :bare_assoc_hash && argument.hash_value('controller').present?
|
@@ -46,8 +46,8 @@ module RailsBestPractices
|
|
46
46
|
|
47
47
|
private
|
48
48
|
|
49
|
-
|
50
|
-
|
49
|
+
# check an attribute assignment node, if there is a array reference node in the right value of assignment node,
|
50
|
+
# then remember this attribute assignment.
|
51
51
|
def assign(node)
|
52
52
|
left_value = node.left_value
|
53
53
|
right_value = node.right_value
|
@@ -59,34 +59,36 @@ module RailsBestPractices
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
# check a call node with message "save" or "save!",
|
63
|
+
# if there exists an attribute assignment for the receiver of this call node,
|
64
|
+
# and if the arguments of this attribute assignments has duplicated entries (different message and same arguments),
|
65
|
+
# then this node needs to add a virtual attribute.
|
66
66
|
def call_assignment(node)
|
67
67
|
if ['save', 'save!'].include? node.message.to_s
|
68
68
|
receiver = node.receiver.to_s
|
69
|
-
add_error "add model virtual attribute (for #{receiver})" if params_dup?(
|
69
|
+
add_error "add model virtual attribute (for #{receiver})" if params_dup?(
|
70
|
+
assignments(receiver).collect { |h| h[:arguments] }
|
71
|
+
)
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
73
|
-
|
75
|
+
# if the nodes are duplicated.
|
74
76
|
def params_dup?(nodes)
|
75
77
|
return false if nodes.nil?
|
76
78
|
|
77
79
|
!dups(nodes).empty?
|
78
80
|
end
|
79
81
|
|
80
|
-
|
82
|
+
# get the assignments of receiver.
|
81
83
|
def assignments(receiver)
|
82
84
|
@assignments[receiver] ||= []
|
83
85
|
end
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
-
|
87
|
+
# Get the duplicate entries from an Enumerable.
|
88
|
+
#
|
89
|
+
# @return [Enumerable] the duplicate entries.
|
88
90
|
def dups(nodes)
|
89
|
-
nodes.each_with_object({}) { |v, h| h[v] = h[v].to_i + 1
|
91
|
+
nodes.each_with_object({}) { |v, h| h[v] = h[v].to_i + 1 }.reject { |_k, v| v == 1 }.keys
|
90
92
|
end
|
91
93
|
end
|
92
94
|
end
|
@@ -75,18 +75,20 @@ 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
|
-
|
89
|
+
# remember the node as index columns, when used outside a table
|
90
|
+
# block, i.e.
|
91
|
+
# add_index :table_name, :column_name
|
90
92
|
def remember_index_columns_outside_table(node)
|
91
93
|
table_name = node.arguments.all.first.to_s
|
92
94
|
index_column = node.arguments.all[1].to_object
|
@@ -95,9 +97,9 @@ module RailsBestPractices
|
|
95
97
|
@index_columns[table_name] << index_column
|
96
98
|
end
|
97
99
|
|
98
|
-
|
99
|
-
|
100
|
-
|
100
|
+
# remember the node as index columns, when used inside a table
|
101
|
+
# block, i.e.
|
102
|
+
# t.index [:column_name, ...]
|
101
103
|
def remember_index_columns_inside_table(node)
|
102
104
|
table_name = @table_name
|
103
105
|
index_column = node.arguments.all.first.to_object
|
@@ -106,31 +108,33 @@ module RailsBestPractices
|
|
106
108
|
@index_columns[table_name] << index_column
|
107
109
|
end
|
108
110
|
|
109
|
-
|
111
|
+
# remember table nodes
|
110
112
|
def remember_table_nodes(node)
|
111
113
|
@table_name = node.arguments.all.first.to_s
|
112
114
|
@table_nodes[@table_name] = node
|
113
115
|
end
|
114
116
|
|
115
|
-
|
117
|
+
# remember foreign key columns
|
116
118
|
def remember_foreign_key_columns(node)
|
117
119
|
table_name = @table_name
|
118
120
|
foreign_key_column = node.arguments.all.first.to_s
|
119
121
|
@foreign_keys[table_name] ||= []
|
120
122
|
if foreign_key_column =~ /(.*?)_id$/
|
121
|
-
|
122
|
-
@foreign_keys[table_name]
|
123
|
-
|
124
|
-
|
125
|
-
|
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"]
|
126
|
+
else
|
127
|
+
foreign_key_column
|
128
|
+
end
|
126
129
|
foreign_id_column = foreign_key_column
|
127
130
|
elsif foreign_key_column =~ /(.*?)_type$/
|
128
|
-
|
129
|
-
@foreign_keys[table_name]
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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"]
|
134
|
+
else
|
135
|
+
foreign_key_column
|
136
|
+
end
|
137
|
+
foreign_id_column = "#{Regexp.last_match(1)}_id"
|
134
138
|
end
|
135
139
|
|
136
140
|
if foreign_id_column
|
@@ -142,7 +146,7 @@ module RailsBestPractices
|
|
142
146
|
end
|
143
147
|
end
|
144
148
|
|
145
|
-
|
149
|
+
# remove the non foreign keys without corresponding tables.
|
146
150
|
def remove_table_not_exist_foreign_keys
|
147
151
|
@foreign_keys.each do |table, foreign_keys|
|
148
152
|
foreign_keys.delete_if do |key|
|
@@ -154,21 +158,22 @@ module RailsBestPractices
|
|
154
158
|
end
|
155
159
|
end
|
156
160
|
|
157
|
-
|
161
|
+
# remove the non foreign keys with only _type column.
|
158
162
|
def remove_only_type_foreign_keys
|
159
163
|
@foreign_keys.each do |_table, foreign_keys|
|
160
164
|
foreign_keys.delete_if { |key| key.is_a?(String) && key =~ /_type$/ }
|
161
165
|
end
|
162
166
|
end
|
163
167
|
|
164
|
-
|
165
|
-
|
168
|
+
# combine polymorphic foreign keys, e.g.
|
169
|
+
# [tagger_id], [tagger_type] => [tagger_id, tagger_type]
|
166
170
|
def combine_polymorphic_foreign_keys
|
167
171
|
@index_columns.each do |_table, foreign_keys|
|
168
172
|
foreign_id_keys = foreign_keys.select { |key| key.size == 1 && key.first =~ /_id/ }
|
169
173
|
foreign_type_keys = foreign_keys.select { |key| key.size == 1 && key.first =~ /_type/ }
|
170
174
|
foreign_id_keys.each do |id_key|
|
171
|
-
next unless type_key =
|
175
|
+
next unless type_key =
|
176
|
+
foreign_type_keys.detect { |type_key| type_key.first == id_key.first.sub(/_id/, '') + '_type' }
|
172
177
|
|
173
178
|
foreign_keys.delete(id_key)
|
174
179
|
foreign_keys.delete(type_key)
|
@@ -177,13 +182,13 @@ module RailsBestPractices
|
|
177
182
|
end
|
178
183
|
end
|
179
184
|
|
180
|
-
|
185
|
+
# check if the table's column is indexed.
|
181
186
|
def not_indexed?(table, column)
|
182
187
|
index_columns = @index_columns[table]
|
183
188
|
!index_columns || index_columns.none? { |e| greater_than_or_equal(Array(e), Array(column)) }
|
184
189
|
end
|
185
190
|
|
186
|
-
|
191
|
+
# check if more_array is greater than less_array or equal to less_array.
|
187
192
|
def greater_than_or_equal(more_array, less_array)
|
188
193
|
more_size = more_array.size
|
189
194
|
less_size = less_array.size
|
@@ -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
|
@@ -37,7 +46,7 @@ module RailsBestPractices
|
|
37
46
|
def return_value_is_used?(node)
|
38
47
|
return false unless @used_return_value_of
|
39
48
|
|
40
|
-
node == @used_return_value_of
|
49
|
+
(node == @used_return_value_of) || @used_return_value_of.include?(node)
|
41
50
|
end
|
42
51
|
|
43
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
|
@@ -42,7 +51,7 @@ module RailsBestPractices
|
|
42
51
|
def return_value_is_used?(node)
|
43
52
|
return false unless @used_return_value_of
|
44
53
|
|
45
|
-
node == @used_return_value_of
|
54
|
+
(node == @used_return_value_of) || @used_return_value_of.include?(node)
|
46
55
|
end
|
47
56
|
|
48
57
|
def model_classnames
|
@@ -58,9 +67,11 @@ module RailsBestPractices
|
|
58
67
|
end
|
59
68
|
elsif message == 'create'
|
60
69
|
# We're only interested in 'create' calls on model classes:
|
61
|
-
possible_receiver_classes =
|
62
|
-
|
63
|
-
|
70
|
+
possible_receiver_classes =
|
71
|
+
[node.receiver.to_s] +
|
72
|
+
classable_modules.map do |mod|
|
73
|
+
"#{mod}::#{node.receiver}"
|
74
|
+
end
|
64
75
|
unless (possible_receiver_classes & model_classnames).empty?
|
65
76
|
add_error "use 'create!' instead of 'create' as the latter may not always save"
|
66
77
|
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,12 +23,12 @@ module RailsBestPractices
|
|
23
23
|
|
24
24
|
protected
|
25
25
|
|
26
|
-
|
26
|
+
# check if hash node is empty.
|
27
27
|
def empty_hash?(node)
|
28
28
|
s(:hash, nil) == node || s(:bare_assoc_hash, nil) == node
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
# check if hash key/value pairs are ruby 1.8 style.
|
32
32
|
def hash_is_18?(node)
|
33
33
|
pair_nodes = node.sexp_type == :hash ? node[1][1] : node[1]
|
34
34
|
return false if pair_nodes.blank?
|
@@ -36,8 +36,8 @@ module RailsBestPractices
|
|
36
36
|
pair_nodes.any? { |pair_node| pair_node[1].sexp_type == :symbol_literal }
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
# check if the hash keys are valid to be converted to ruby 1.9
|
40
|
+
# syntax.
|
41
41
|
def valid_keys?(node)
|
42
42
|
node.hash_keys.all? { |key| key =~ VALID_SYMBOL_KEY }
|
43
43
|
end
|
@@ -55,9 +55,9 @@ module RailsBestPractices
|
|
55
55
|
|
56
56
|
private
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
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
61
|
def remember_new_variable(node)
|
62
62
|
right_value = node.right_value
|
63
63
|
if right_value.sexp_type == :method_add_arg && right_value.message.to_s == 'new'
|
@@ -65,7 +65,7 @@ module RailsBestPractices
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
|
68
|
+
# see if the receiver of the call node is included in the @new_varaibles.
|
69
69
|
def new_record?(node)
|
70
70
|
@new_variables.include? node.receiver.to_s
|
71
71
|
end
|
@@ -37,15 +37,14 @@ module RailsBestPractices
|
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
46
|
def other_finder?(node)
|
47
|
-
FINDERS.include?(node[1].message.to_s) &&
|
48
|
-
node[1].receiver.sexp_type == :call &&
|
47
|
+
FINDERS.include?(node[1].message.to_s) && node[1].receiver.sexp_type == :call &&
|
49
48
|
node.arguments.grep_nodes_count(sexp_type: :bare_assoc_hash) > 0
|
50
49
|
end
|
51
50
|
end
|
@@ -35,11 +35,11 @@ module RailsBestPractices
|
|
35
35
|
|
36
36
|
private
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
# check if the call node can use delegate to avoid violating law of demeter.
|
39
|
+
#
|
40
|
+
# if the receiver of receiver of the call node matchs any in model names,
|
41
|
+
# and the message of receiver of the call node matchs any in association names,
|
42
|
+
# then it needs delegate.
|
43
43
|
def need_delegate?(node)
|
44
44
|
return unless variable(node)
|
45
45
|
|
@@ -55,7 +55,7 @@ module RailsBestPractices
|
|
55
55
|
if association_name =~ /able$/
|
56
56
|
models.each do |class_name|
|
57
57
|
if model_associations.is_association?(class_name, association_name.sub(/able$/, '')) ||
|
58
|
-
|
58
|
+
model_associations.is_association?(class_name, association_name.sub(/able$/, 's'))
|
59
59
|
return true if model_attributes.is_attribute?(class_name, attribute_name)
|
60
60
|
end
|
61
61
|
end
|
@@ -38,14 +38,13 @@ module RailsBestPractices
|
|
38
38
|
|
39
39
|
private
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
# check if the arguments of options_for_select are complex.
|
42
|
+
#
|
43
|
+
# if the first argument is an array,
|
44
|
+
# and the size of array is greater than @array_count you defined,
|
45
|
+
# then it is complext.
|
46
46
|
def complex_select_options?(node)
|
47
|
-
node[1].message.to_s == 'options_for_select' &&
|
48
|
-
node.arguments.all.first.sexp_type == :array &&
|
47
|
+
node[1].message.to_s == 'options_for_select' && node.arguments.all.first.sexp_type == :array &&
|
49
48
|
node.arguments.all.first.array_size > @array_count
|
50
49
|
end
|
51
50
|
end
|
@@ -29,15 +29,14 @@ module RailsBestPractices
|
|
29
29
|
|
30
30
|
private
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
# check if the method_add_arg node is a finder.
|
33
|
+
#
|
34
|
+
# if the receiver of method_add_arg node is a constant,
|
35
|
+
# and the message of call method_add_arg is one of find, all, first or last,
|
36
|
+
# and any of its arguments is a hash,
|
37
|
+
# then it is a finder.
|
38
38
|
def finder?(node)
|
39
|
-
FINDERS.include?(node[1].message.to_s) &&
|
40
|
-
node[1].sexp_type == :call &&
|
39
|
+
FINDERS.include?(node[1].message.to_s) && node[1].sexp_type == :call &&
|
41
40
|
node.arguments.grep_nodes_count(sexp_type: :bare_assoc_hash) > 0
|
42
41
|
end
|
43
42
|
end
|
@@ -51,17 +51,18 @@ module RailsBestPractices
|
|
51
51
|
|
52
52
|
private
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
54
|
+
# check nested route.
|
55
|
+
#
|
56
|
+
# if the receiver of the method_add_block is with message "resources" or "resource",
|
57
|
+
# then increment the @counter, recursively check the block body, and decrement the @counter.
|
58
|
+
#
|
59
|
+
# if the node type is command_call or command,
|
60
|
+
# and its message is resources or resource,
|
61
|
+
# then check if @counter is greater than or equal to @nested_count,
|
62
|
+
# if so, it is the needless deep nesting.
|
63
63
|
def recursively_check(node)
|
64
64
|
shallow = @shallow_nodes.include? node
|
65
|
+
|
65
66
|
if %i[command_call command].include?(node[1].sexp_type) && %w[resources resource].include?(node[1].message.to_s)
|
66
67
|
hash_node = node[1].arguments.grep_node(sexp_type: :bare_assoc_hash)
|
67
68
|
shallow ||= (hash_node && hash_node.hash_value('shallow').to_s == 'true')
|
@@ -72,7 +73,8 @@ module RailsBestPractices
|
|
72
73
|
end
|
73
74
|
@counter -= 1
|
74
75
|
elsif %i[command_call command].include?(node.sexp_type) && %w[resources resource].include?(node.message.to_s)
|
75
|
-
add_error "needless deep nesting (nested_count > #{@nested_count})", @file, node.line_number if @counter >=
|
76
|
+
add_error "needless deep nesting (nested_count > #{@nested_count})", @file, node.line_number if @counter >=
|
77
|
+
@nested_count && !@shallow_nodes.include?(node)
|
76
78
|
end
|
77
79
|
end
|
78
80
|
end
|
@@ -24,8 +24,7 @@ module RailsBestPractices
|
|
24
24
|
|
25
25
|
# check all command nodes
|
26
26
|
add_callback :start_command do |node|
|
27
|
-
if node.message.to_s == 'match' &&
|
28
|
-
node.arguments.all.first.to_s == ':controller(/:action(/:id(.:format)))'
|
27
|
+
if node.message.to_s == 'match' && node.arguments.all.first.to_s == ':controller(/:action(/:id(.:format)))'
|
29
28
|
add_error 'not use default route'
|
30
29
|
end
|
31
30
|
end
|
@@ -41,11 +41,11 @@ module RailsBestPractices
|
|
41
41
|
|
42
42
|
private
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
# check method_add_block node to calculate the count of member and collection custom routes.
|
45
|
+
#
|
46
|
+
# if its receiver is with message "resources",
|
47
|
+
# then calculate the count of call nodes, whose message is get, post, update or delete,
|
48
|
+
# it is just the count of member and collection custom routes.
|
49
49
|
def member_and_collection_count_for_rails3(node)
|
50
50
|
node[1].message.to_s == 'resources' ? node.grep_nodes_count(sexp_type: :command, message: VERBS) : 0
|
51
51
|
end
|
@@ -11,6 +11,7 @@ module RailsBestPractices
|
|
11
11
|
# Review process:
|
12
12
|
# check nodes to see if there is a command with message attr_accessible or attr_protected,
|
13
13
|
# or include ActiveModel::ForbiddenAttributesProtection.
|
14
|
+
|
14
15
|
class ProtectMassAssignmentReview < Review
|
15
16
|
interesting_files MODEL_FILES
|
16
17
|
interesting_nodes :class, :command, :var_ref, :vcall, :fcall
|
@@ -73,13 +74,15 @@ module RailsBestPractices
|
|
73
74
|
end
|
74
75
|
|
75
76
|
def check_rails_builtin(node)
|
76
|
-
if @whitelist_attributes ||
|
77
|
+
if @whitelist_attributes ||
|
78
|
+
[node.to_s, node.message.to_s].any? { |str| %w[attr_accessible attr_protected].include? str }
|
77
79
|
@mass_assignement = false
|
78
80
|
end
|
79
81
|
end
|
80
82
|
|
81
83
|
def check_strong_parameters(command_node)
|
82
|
-
if command_node.message.to_s == 'include' &&
|
84
|
+
if command_node.message.to_s == 'include' &&
|
85
|
+
command_node.arguments.all.first.to_s == 'ActiveModel::ForbiddenAttributesProtection'
|
83
86
|
@mass_assignement = false
|
84
87
|
end
|
85
88
|
end
|
@@ -11,6 +11,7 @@ module RailsBestPractices
|
|
11
11
|
# if they are not defined in routes,
|
12
12
|
# and they are not called in controllers,
|
13
13
|
# then they are the unused methods in controllers.
|
14
|
+
|
14
15
|
class RemoveUnusedMethodsInControllersReview < Review
|
15
16
|
include Classable
|
16
17
|
include Moduleable
|
@@ -98,9 +99,11 @@ module RailsBestPractices
|
|
98
99
|
end
|
99
100
|
end
|
100
101
|
@controller_methods.get_all_unused_methods.each do |method|
|
101
|
-
|
102
|
-
|
103
|
-
|
102
|
+
next if excepted?(method)
|
103
|
+
|
104
|
+
add_error "remove unused methods (#{method.class_name}##{method.method_name})",
|
105
|
+
method.file,
|
106
|
+
method.line_number
|
104
107
|
end
|
105
108
|
end
|
106
109
|
|