rails_best_practices 1.19.3 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +0 -1
- data/.travis.yml +2 -3
- data/CHANGELOG.md +10 -11
- data/Gemfile +3 -5
- data/Gemfile.lock +125 -0
- data/Guardfile +2 -0
- data/README.md +5 -1
- data/Rakefile +2 -17
- data/assets/result.html.erb +2 -0
- data/lib/rails_best_practices.rb +4 -2
- data/lib/rails_best_practices/analyzer.rb +63 -51
- 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 +63 -55
- 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 +87 -72
- data/lib/rails_best_practices/inline_disables.rb +3 -0
- 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/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 +23 -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 -88
- 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 +20 -17
- 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 +14 -10
- 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 +63 -60
- 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 +48 -49
- 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/inline_disables/inline_disable_spec.rb +62 -0
- 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 +47 -32
- 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 +21 -14
@@ -13,7 +13,7 @@ module RailsBestPractices
|
|
13
13
|
# then save it to as key in @variable_use_count hash, and add the call count (hash value).
|
14
14
|
def remember_variable_use_count(node)
|
15
15
|
variable_node = variable(node)
|
16
|
-
if variable_node && 'self'
|
16
|
+
if variable_node && variable_node.to_s != 'self' && @last_variable_node != variable_node
|
17
17
|
@last_variable_node = variable_node
|
18
18
|
variable_use_count[variable_node.to_s] ||= 0
|
19
19
|
variable_use_count[variable_node.to_s] += 1
|
@@ -36,6 +36,7 @@ module RailsBestPractices
|
|
36
36
|
node = node.receiver
|
37
37
|
end
|
38
38
|
return if %i[fcall hash].include?(node.receiver.sexp_type)
|
39
|
+
|
39
40
|
node.receiver
|
40
41
|
end
|
41
42
|
|
@@ -21,10 +21,9 @@ module RailsBestPractices
|
|
21
21
|
# if its message is render and the arguments contain a key action, template or file,
|
22
22
|
# then it should be replaced by simplified syntax.
|
23
23
|
add_callback :start_command do |node|
|
24
|
-
if
|
24
|
+
if node.message.to_s == 'render'
|
25
25
|
keys = node.arguments.all.first.hash_keys
|
26
|
-
if keys && keys.size == 1 &&
|
27
|
-
(keys.include?('action') || keys.include?('template') || keys.include?('file'))
|
26
|
+
if keys && keys.size == 1 && (keys.include?('action') || keys.include?('template') || keys.include?('file'))
|
28
27
|
add_error 'simplify render in controllers'
|
29
28
|
end
|
30
29
|
end
|
@@ -22,10 +22,10 @@ module RailsBestPractices
|
|
22
22
|
# if its message is render and the arguments contain a key partial,
|
23
23
|
# then it should be replaced by simplified syntax.
|
24
24
|
add_callback :start_command do |node|
|
25
|
-
if
|
26
|
-
hash_node =
|
27
|
-
if hash_node && :bare_assoc_hash
|
28
|
-
|
25
|
+
if node.message.to_s == 'render'
|
26
|
+
hash_node = node.arguments.all.first
|
27
|
+
if hash_node && hash_node.sexp_type == :bare_assoc_hash && include_partial?(hash_node) &&
|
28
|
+
valid_hash?(hash_node)
|
29
29
|
add_error 'simplify render in views'
|
30
30
|
end
|
31
31
|
end
|
@@ -33,15 +33,15 @@ module RailsBestPractices
|
|
33
33
|
|
34
34
|
protected
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
def include_partial?(hash_node)
|
37
|
+
hash_node.hash_keys.include?('partial') && !hash_node.hash_value('partial').to_s.include?('/')
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
def valid_hash?(hash_node)
|
41
|
+
keys = hash_node.hash_keys
|
42
|
+
keys.delete('partial')
|
43
|
+
(keys - VALID_KEYS).empty?
|
44
|
+
end
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -12,6 +12,7 @@ module RailsBestPractices
|
|
12
12
|
# Review process:
|
13
13
|
# check all first code line_number in method definitions (actions),
|
14
14
|
# if they are duplicated, then they should be moved to before_filter.
|
15
|
+
|
15
16
|
class UseBeforeFilterReview < Review
|
16
17
|
interesting_nodes :class
|
17
18
|
interesting_files CONTROLLER_FILES
|
@@ -34,10 +35,12 @@ module RailsBestPractices
|
|
34
35
|
var_ref_or_vcall_included = %i[var_ref vcall].include?(statement_node.sexp_type)
|
35
36
|
private_or_protected_included = %w[protected private].include?(statement_node.to_s)
|
36
37
|
break if var_ref_or_vcall_included && private_or_protected_included
|
37
|
-
|
38
|
+
|
39
|
+
remember_first_sentence(statement_node) if statement_node.sexp_type == :def
|
38
40
|
end
|
39
41
|
@first_sentences.each do |_first_sentence, def_nodes|
|
40
42
|
next unless def_nodes.size > @customize_count
|
43
|
+
|
41
44
|
add_error "use before_filter for #{def_nodes.map { |node| node.method_name.to_s }.join(',')}",
|
42
45
|
node.file,
|
43
46
|
def_nodes.map(&:line_number).join(',')
|
@@ -46,16 +49,17 @@ module RailsBestPractices
|
|
46
49
|
|
47
50
|
private
|
48
51
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
52
|
+
# check method define node, and remember the first sentence.
|
53
|
+
def remember_first_sentence(node)
|
54
|
+
first_sentence = node.body.statements.first
|
55
|
+
return unless first_sentence
|
56
|
+
|
57
|
+
first_sentence = first_sentence.remove_line_and_column
|
58
|
+
unless first_sentence == s(:nil)
|
59
|
+
@first_sentences[first_sentence] ||= []
|
60
|
+
@first_sentences[first_sentence] << node
|
58
61
|
end
|
62
|
+
end
|
59
63
|
end
|
60
64
|
end
|
61
65
|
end
|
@@ -43,24 +43,24 @@ module RailsBestPractices
|
|
43
43
|
|
44
44
|
private
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
46
|
+
# check an attribute assignment node, if its message is xxx_id,
|
47
|
+
# then remember the receiver of the attribute assignment in @assignments.
|
48
|
+
def attribute_assignment(node)
|
49
|
+
if node.left_value.message.is_a?(Sexp) && node.left_value.message.to_s =~ /_id$/
|
50
|
+
receiver = node.left_value.receiver.to_s
|
51
|
+
@assignments[receiver] = true
|
53
52
|
end
|
53
|
+
end
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
55
|
+
# check a call node with message "save" or "save!",
|
56
|
+
# if the receiver of call node exists in @assignments,
|
57
|
+
# then the attribute assignment should be replaced by using model association.
|
58
|
+
def call_assignment(node)
|
59
|
+
if ['save', 'save!'].include? node.message.to_s
|
60
|
+
receiver = node.receiver.to_s
|
61
|
+
add_error "use model association (for #{receiver})" if @assignments[receiver]
|
63
62
|
end
|
63
|
+
end
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb
CHANGED
@@ -11,6 +11,7 @@ module RailsBestPractices
|
|
11
11
|
# Review process:
|
12
12
|
# check class node to remember the class name,
|
13
13
|
# and check the method definition nodes to see if the corresponding mailer views exist or not.
|
14
|
+
|
14
15
|
class UseMultipartAlternativeAsContentTypeOfEmailReview < Review
|
15
16
|
interesting_nodes :class, :def
|
16
17
|
interesting_files MAILER_FILES
|
@@ -31,30 +32,31 @@ module RailsBestPractices
|
|
31
32
|
|
32
33
|
private
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
# check if rails3's syntax mailer views are canonical.
|
40
|
-
#
|
41
|
-
# @param [String] name method name in action_mailer
|
42
|
-
def rails3_canonical_mailer_views?(name)
|
43
|
-
return true if mailer_files(name).empty?
|
44
|
-
return true if mailer_files(name).none? { |filename| filename.index 'html' }
|
45
|
-
mailer_files(name).any? { |filename| filename.index 'html' } &&
|
46
|
-
mailer_files(name).any? { |filename| filename.index 'text' }
|
47
|
-
end
|
35
|
+
# check if rails's syntax mailer views are canonical.
|
36
|
+
#
|
37
|
+
# @param [String] name method name in action_mailer
|
38
|
+
def rails_canonical_mailer_views?(name); end
|
48
39
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
40
|
+
# check if rails3's syntax mailer views are canonical.
|
41
|
+
#
|
42
|
+
# @param [String] name method name in action_mailer
|
43
|
+
def rails3_canonical_mailer_views?(name)
|
44
|
+
return true if mailer_files(name).empty?
|
45
|
+
return true if mailer_files(name).none? { |filename| filename.index 'html' }
|
53
46
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
47
|
+
mailer_files(name).any? { |filename| filename.index 'html' } &&
|
48
|
+
mailer_files(name).any? { |filename| filename.index 'text' }
|
49
|
+
end
|
50
|
+
|
51
|
+
# all mail view files for a method name.
|
52
|
+
def mailer_files(name)
|
53
|
+
Dir.entries(mailer_directory) { |filename| filename.index name.to_s }
|
54
|
+
end
|
55
|
+
|
56
|
+
# the view directory of mailer.
|
57
|
+
def mailer_directory
|
58
|
+
File.join(Core::Runner.base_path, "app/views/#{@klazz_name.to_s.underscore}")
|
59
|
+
end
|
58
60
|
end
|
59
61
|
end
|
60
62
|
end
|
@@ -49,42 +49,42 @@ module RailsBestPractices
|
|
49
49
|
|
50
50
|
private
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
52
|
+
# check a command node, if it is a callback definition, such as after_create, before_create,
|
53
|
+
# then save the callback methods in @callbacks
|
54
|
+
def remember_callback(node)
|
55
|
+
if node.message.to_s =~ /^after_|^before_/
|
56
|
+
node.arguments.all.each do |argument|
|
57
|
+
# ignore callback like after_create Comment.new
|
58
|
+
@callbacks << argument.to_s if argument.sexp_type == :symbol_literal
|
60
59
|
end
|
61
60
|
end
|
61
|
+
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
# check a defn node to see if the method name exists in the @callbacks.
|
64
|
+
def callback_method?(node)
|
65
|
+
@callbacks.find { |callback| callback == node.method_name.to_s }
|
66
|
+
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
end
|
68
|
+
# check a def node to see if it contains a actionmailer deliver call.
|
69
|
+
#
|
70
|
+
# if the message of call node is deliver,
|
71
|
+
# and the receiver of the call node is with receiver node who exists in @callbacks,
|
72
|
+
# then the call node is actionmailer deliver call.
|
73
|
+
def deliver_mailer?(node)
|
74
|
+
node.grep_nodes(sexp_type: :call) do |child_node|
|
75
|
+
if child_node.message.to_s == 'deliver'
|
76
|
+
if child_node.receiver.sexp_type == :method_add_arg &&
|
77
|
+
mailers.include?(child_node.receiver[1].receiver.to_s)
|
78
|
+
return true
|
80
79
|
end
|
81
80
|
end
|
82
|
-
false
|
83
81
|
end
|
82
|
+
false
|
83
|
+
end
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
85
|
+
def mailers
|
86
|
+
@mailers ||= Prepares.mailers
|
87
|
+
end
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
@@ -21,13 +21,13 @@ module RailsBestPractices
|
|
21
21
|
|
22
22
|
protected
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
def no_parentheses_around_parameters?(def_node)
|
25
|
+
def_node[2][0] != :parent
|
26
|
+
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
def has_parameters?(def_node)
|
29
|
+
def_node[2][0] == :params && !def_node[2][1..-1].compact.empty?
|
30
|
+
end
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
@@ -34,13 +34,15 @@ module RailsBestPractices
|
|
34
34
|
#
|
35
35
|
# then the call node can use query attribute instead.
|
36
36
|
add_callback :start_if, :start_unless, :start_elsif, :start_ifop, :start_if_mod, :start_unless_mod do |node|
|
37
|
-
all_conditions =
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
all_conditions =
|
38
|
+
if node.conditional_statement == node.conditional_statement.all_conditions
|
39
|
+
[node.conditional_statement]
|
40
|
+
else
|
41
|
+
node.conditional_statement.all_conditions
|
42
|
+
end
|
42
43
|
all_conditions.each do |condition_node|
|
43
44
|
next unless query_attribute_node = query_attribute_node(condition_node)
|
45
|
+
|
44
46
|
receiver_node = query_attribute_node.receiver
|
45
47
|
add_error "use query attribute (#{receiver_node.receiver}.#{receiver_node.message}?)",
|
46
48
|
node.file,
|
@@ -50,67 +52,68 @@ module RailsBestPractices
|
|
50
52
|
|
51
53
|
private
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
nil
|
55
|
+
# recursively check conditional statement nodes to see if there is a call node that may be
|
56
|
+
# possible query attribute.
|
57
|
+
def query_attribute_node(conditional_statement_node)
|
58
|
+
case conditional_statement_node.sexp_type
|
59
|
+
when :and, :or
|
60
|
+
node =
|
61
|
+
query_attribute_node(conditional_statement_node[1]) || query_attribute_node(conditional_statement_node[2])
|
62
|
+
node.file = conditional_statement_code.file
|
63
|
+
return node
|
64
|
+
when :not
|
65
|
+
node = query_attribute_node(conditional_statement_node[1])
|
66
|
+
node.file = conditional_statement_node.file
|
67
|
+
when :call
|
68
|
+
return conditional_statement_node if possible_query_attribute?(conditional_statement_node)
|
69
|
+
when :binary
|
70
|
+
return conditional_statement_node if possible_query_attribute?(conditional_statement_node)
|
71
71
|
end
|
72
|
+
nil
|
73
|
+
end
|
72
74
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
variable_node = variable(node)
|
87
|
-
message_node = node.grep_node(receiver: variable_node.to_s).message
|
75
|
+
# check if the node may use query attribute instead.
|
76
|
+
#
|
77
|
+
# if the node contains two method calls, e.g. @user.login.nil?
|
78
|
+
#
|
79
|
+
# for the first call, the receiver should be one of the class names and
|
80
|
+
# the message should be one of the attribute name.
|
81
|
+
#
|
82
|
+
# for the second call, the message should be one of nil?, blank? or present? or
|
83
|
+
# it is compared with an empty string.
|
84
|
+
#
|
85
|
+
# the node that may use query attribute.
|
86
|
+
def possible_query_attribute?(node)
|
87
|
+
return false unless node.receiver.sexp_type == :call
|
88
88
|
|
89
|
-
|
90
|
-
|
91
|
-
end
|
89
|
+
variable_node = variable(node)
|
90
|
+
message_node = node.grep_node(receiver: variable_node.to_s).message
|
92
91
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
class_name = variable_node.to_s.sub(/^@/, '').classify
|
97
|
-
models.include?(class_name)
|
98
|
-
end
|
92
|
+
is_model?(variable_node) && model_attribute?(variable_node, message_node.to_s) &&
|
93
|
+
(QUERY_METHODS.include?(node.message.to_s) || compare_with_empty_string?(node))
|
94
|
+
end
|
99
95
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
class_name = variable_node.to_s.sub(/^@/, '').classify
|
104
|
-
attribute_type = model_attributes.get_attribute_type(class_name, message)
|
105
|
-
attribute_type && !%w[integer float].include?(attribute_type)
|
106
|
-
end
|
96
|
+
# check if the receiver is one of the models.
|
97
|
+
def is_model?(variable_node)
|
98
|
+
return false if variable_node.const?
|
107
99
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
100
|
+
class_name = variable_node.to_s.sub(/^@/, '').classify
|
101
|
+
models.include?(class_name)
|
102
|
+
end
|
103
|
+
|
104
|
+
# check if the receiver and message is one of the model's attribute.
|
105
|
+
# the receiver should match one of the class model name, and the message should match one of attribute name.
|
106
|
+
def model_attribute?(variable_node, message)
|
107
|
+
class_name = variable_node.to_s.sub(/^@/, '').classify
|
108
|
+
attribute_type = model_attributes.get_attribute_type(class_name, message)
|
109
|
+
attribute_type && !%w[integer float].include?(attribute_type)
|
110
|
+
end
|
111
|
+
|
112
|
+
# check if the node is with node type :binary, node message :== and node argument is empty string.
|
113
|
+
def compare_with_empty_string?(node)
|
114
|
+
node.sexp_type == :binary && ['==', '!='].include?(node.message.to_s) &&
|
115
|
+
s(:string_literal, s(:string_content)) == node.argument
|
116
|
+
end
|
114
117
|
end
|
115
118
|
end
|
116
119
|
end
|