rails_best_practices 0.10.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/README.md +20 -0
  2. data/assets/result.html.haml +1 -1
  3. data/lib/rails_best_practices.rb +5 -3
  4. data/lib/rails_best_practices/core.rb +1 -1
  5. data/lib/rails_best_practices/core/check.rb +9 -12
  6. data/lib/rails_best_practices/core/checking_visitor.rb +9 -6
  7. data/lib/rails_best_practices/core/model_associations.rb +1 -1
  8. data/lib/rails_best_practices/core/nil.rb +5 -1
  9. data/lib/rails_best_practices/core/runner.rb +5 -5
  10. data/lib/rails_best_practices/core_ext/sexp.rb +688 -0
  11. data/lib/rails_best_practices/prepares/mailer_prepare.rb +4 -5
  12. data/lib/rails_best_practices/prepares/model_prepare.rb +16 -26
  13. data/lib/rails_best_practices/prepares/schema_prepare.rb +11 -17
  14. data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +24 -75
  15. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +39 -113
  16. data/lib/rails_best_practices/reviews/dry_bundler_in_capistrano_review.rb +6 -16
  17. data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +16 -32
  18. data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +11 -20
  19. data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +7 -28
  20. data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +16 -14
  21. data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +10 -28
  22. data/lib/rails_best_practices/reviews/move_code_into_model_review.rb +12 -11
  23. data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +13 -24
  24. data/lib/rails_best_practices/reviews/move_model_logic_into_model_review.rb +9 -9
  25. data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +24 -68
  26. data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +15 -22
  27. data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +31 -91
  28. data/lib/rails_best_practices/reviews/remove_empty_helpers_review.rb +4 -2
  29. data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +20 -18
  30. data/lib/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review.rb +5 -3
  31. data/lib/rails_best_practices/reviews/review.rb +8 -37
  32. data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +10 -6
  33. data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +9 -6
  34. data/lib/rails_best_practices/reviews/use_before_filter_review.rb +14 -72
  35. data/lib/rails_best_practices/reviews/use_model_association_review.rb +19 -31
  36. data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +5 -5
  37. data/lib/rails_best_practices/reviews/use_observer_review.rb +22 -40
  38. data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +34 -39
  39. data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +14 -38
  40. data/lib/rails_best_practices/reviews/use_scope_access_review.rb +13 -44
  41. data/lib/rails_best_practices/version.rb +1 -1
  42. data/spec/rails_best_practices/core/check_spec.rb +5 -5
  43. data/spec/rails_best_practices/core/checking_visitor_spec.rb +4 -4
  44. data/spec/rails_best_practices/core/model_associations_spec.rb +4 -4
  45. data/spec/rails_best_practices/core/nil_spec.rb +7 -1
  46. data/spec/rails_best_practices/core_ext/sexp_spec.rb +430 -0
  47. data/spec/rails_best_practices/prepares/model_prepare_spec.rb +12 -12
  48. data/spec/rails_best_practices/prepares/schema_prepare_spec.rb +6 -6
  49. data/spec/rails_best_practices/reviews/move_code_into_controller_review_spec.rb +14 -2
  50. data/spec/rails_best_practices/reviews/move_code_into_helper_review_spec.rb +1 -1
  51. data/spec/rails_best_practices/reviews/needless_deep_nesting_review_spec.rb +3 -3
  52. data/spec/rails_best_practices/reviews/not_use_default_route_review_spec.rb +1 -1
  53. data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +15 -1
  54. data/spec/rails_best_practices/reviews/simplify_render_in_controllers_review_spec.rb +3 -3
  55. data/spec/rails_best_practices/reviews/use_query_attribute_review_spec.rb +1 -1
  56. data/spec/rails_best_practices/reviews/use_say_with_time_in_migrations_review_spec.rb +1 -1
  57. data/spec/rails_best_practices/reviews/use_scope_access_review_spec.rb +4 -4
  58. data/spec/rails_best_practices_spec.rb +1 -3
  59. data/spec/spec_helper.rb +4 -0
  60. metadata +6 -8
  61. data/lib/rails_best_practices/core/visitable_sexp.rb +0 -444
  62. data/spec/rails_best_practices/core/visitable_sexp_spec.rb +0 -272
  63. data/spec/rails_best_practices/reviews/review_spec.rb +0 -11
@@ -10,9 +10,9 @@ module RailsBestPractices
10
10
  # Implementation:
11
11
  #
12
12
  # Review process:
13
- # only check the call nodes to see if there is bundler namespace in config/deploy.rb file,
13
+ # only check the command nodes to see if there is bundler namespace in config/deploy.rb file,
14
14
  #
15
- # if the message of call node is :namespace and the arguments of the call node is :bundler,
15
+ # if the message of command node is "namespace" and the first argument is "bundler",
16
16
  # then it should use bundler's capistrano recipe.
17
17
  class DryBundlerInCapistranoReview < Review
18
18
  def url
@@ -20,26 +20,16 @@ module RailsBestPractices
20
20
  end
21
21
 
22
22
  def interesting_nodes
23
- [:call]
23
+ [:command]
24
24
  end
25
25
 
26
26
  def interesting_files
27
27
  /config\/deploy.rb/
28
28
  end
29
29
 
30
- # check call node to see if it is with message :namespace and arguments :bundler.
31
- #
32
- # the ruby code is
33
- #
34
- # namespace :bundler do
35
- # ...
36
- # end
37
- #
38
- # then the call node is as follows
39
- #
40
- # s(:call, nil, :namespace, s(:arglist, s(:lit, :bundler)))
41
- def start_call(node)
42
- if :namespace == node.message and equal?(node.arguments[1], "bundler")
30
+ # check call node to see if it is with message "namespace" and argument "bundler".
31
+ def start_command(node)
32
+ if "namespace" == node.message.to_s && "bundler" == node.arguments.all[0].to_s
43
33
  add_error "dry bundler in capistrano"
44
34
  end
45
35
  end
@@ -10,14 +10,14 @@ module RailsBestPractices
10
10
  # Implementation:
11
11
  #
12
12
  # Review process:
13
- # 1. check all local assignment and instance assignment nodes,
14
- # if the right value is a call node with message :new,
13
+ # 1. check all assignment nodes,
14
+ # if the right value is a call node with message "new",
15
15
  # then remember their left value as new variables.
16
16
  #
17
17
  # 2. check all call nodes,
18
- # if the message is :create or :create!,
18
+ # if the message is "create" or "create!",
19
19
  # then it should be isolated to db seed.
20
- # if the message is :save or :save!,
20
+ # if the message is "save" or "save!",
21
21
  # and the subject is included in new variables,
22
22
  # then it should be isolated to db seed.
23
23
  class IsolateSeedDataReview < Review
@@ -26,7 +26,7 @@ module RailsBestPractices
26
26
  end
27
27
 
28
28
  def interesting_nodes
29
- [:call, :lasgn, :iasgn]
29
+ [:call, :assign]
30
30
  end
31
31
 
32
32
  def interesting_files
@@ -38,53 +38,37 @@ module RailsBestPractices
38
38
  @new_variables = []
39
39
  end
40
40
 
41
- # check local assignment node.
41
+ # check assignment node.
42
42
  #
43
- # if the right value of the node is a call node with :new message,
44
- # then remember it as new variables (@new_variables).
45
- def start_lasgn(node)
46
- remember_new_variable(node)
47
- end
48
-
49
- # check instance assignment node.
50
- #
51
- # if the right value of the node is a call node with :new message,
52
- # then remember it as new variables (@new_variables).
53
- def start_iasgn(node)
43
+ # if the right value of the node is a call node with "new" message,
44
+ # then remember it as new variables.
45
+ def start_assign(node)
54
46
  remember_new_variable(node)
55
47
  end
56
48
 
57
49
  # check the call node.
58
50
  #
59
- # if the message of the call node is :create or :create!,
51
+ # if the message of the call node is "create" or "create!",
60
52
  # then you should isolate it to seed data.
61
53
  #
62
- # if the message of the call node is :save or :save!,
54
+ # if the message of the call node is "save" or "save!",
63
55
  # and the subject of the call node is included in @new_variables,
64
56
  # then you should isolate it to seed data.
65
57
  def start_call(node)
66
- if [:create, :create!].include? node.message
58
+ if ["create", "create!"].include? node.message.to_s
67
59
  add_error("isolate seed data")
68
- elsif [:save, :save!].include? node.message
60
+ elsif ["save", "save!"].include? node.message.to_s
69
61
  add_error("isolate seed data") if new_record?(node)
70
62
  end
71
63
  end
72
64
 
73
65
  private
74
- # check local assignment or instance assignment node,
75
- # if the right vavlue is a call node with message :new,
66
+ # check assignment node,
67
+ # if the right vavlue is a method_add_arg node with message "new",
76
68
  # then remember the left value as new variable.
77
- #
78
- # if the local variable node is
79
- #
80
- # s(:lasgn, :role, s(:call, s(:const, :Role), :new, s(:arglist, s(:hash, s(:lit, :name), s(:lvar, :name)))))
81
- #
82
- # then the new variables (@new_variables) is
83
- #
84
- # ["role"]
85
69
  def remember_new_variable(node)
86
70
  right_value = node.right_value
87
- if :call == right_value.node_type && :new == right_value.message
71
+ if :method_add_arg == right_value.sexp_type && "new" == right_value.message.to_s
88
72
  @new_variables << node.left_value.to_s
89
73
  end
90
74
  end
@@ -18,14 +18,14 @@ module RailsBestPractices
18
18
  # then it should keep finders on its own model.
19
19
  class KeepFindersOnTheirOwnModelReview < Review
20
20
 
21
- FINDERS = [:find, :all, :first, :last]
21
+ FINDERS = %w(find all first last)
22
22
 
23
23
  def url
24
24
  "http://rails-bestpractices.com/posts/13-keep-finders-on-their-own-model"
25
25
  end
26
26
 
27
27
  def interesting_nodes
28
- [:call]
28
+ [:method_add_arg]
29
29
  end
30
30
 
31
31
  def interesting_files
@@ -36,36 +36,27 @@ module RailsBestPractices
36
36
  #
37
37
  # if the call node is
38
38
  #
39
- # 1. the message of call node is one of the :find, :all, :first or :last
39
+ # 1. the message of call node is one of the find, all, first or last
40
40
  # 2. the subject of call node is also a call node (it's the other model)
41
41
  # 3. the any of its arguments is a hash (complex finder)
42
42
  #
43
43
  # then it should keep finders on its own model.
44
- def start_call(node)
44
+
45
+ def start_method_add_arg(node)
45
46
  add_error "keep finders on their own model" if other_finder?(node)
46
47
  end
47
48
 
48
49
  private
49
50
  # check if the call node is the finder of other model.
50
51
  #
51
- # the message of the node should be one of :find, :all, :first or :last,
52
+ # the message of the node should be one of find, all, first or last,
52
53
  # and the subject of the node should be with message :call (this is the other model),
53
- # and any of its arguments is a hash, like
54
- #
55
- # s(:call,
56
- # s(:call, s(:self), :comment, s(:arglist)),
57
- # :find,
58
- # s(:arglist, s(:lit, :all),
59
- # s(:hash,
60
- # s(:lit, :conditions),
61
- # s(:hash, s(:lit, :is_spam), s(:false)),
62
- # s(:lit, :limit),
63
- # s(:lit, 10)
64
- # )
65
- # )
66
- # )
54
+ # and any of its arguments is a hash,
55
+ # then it is the finder of other model.
67
56
  def other_finder?(node)
68
- FINDERS.include?(node.message) && :call == node.subject.node_type && node.arguments.children.any? { |node| :hash == node.node_type }
57
+ FINDERS.include?(node[1].message.to_s) &&
58
+ :call == node[1].subject.sexp_type &&
59
+ node.arguments.grep_nodes_count(:sexp_type => :bare_assoc_hash) > 0
69
60
  end
70
61
  end
71
62
  end
@@ -16,6 +16,7 @@ module RailsBestPractices
16
16
  # and outer the call node, it is also a call node,
17
17
  # then it violate the law of demeter.
18
18
  class LawOfDemeterReview < Review
19
+ ASSOCIATION_METHODS = %w(belongs_to has_one)
19
20
 
20
21
  def url
21
22
  "http://rails-bestpractices.com/posts/15-the-law-of-demeter"
@@ -29,17 +30,10 @@ module RailsBestPractices
29
30
  #
30
31
  # if the subject of the call node is also a call node,
31
32
  # and the subject of the subject call node matchs one of the class names,
32
- # and the message of the subject call node matchs one of the association name with the class name, like
33
- #
34
- # s(:call,
35
- # s(:call, s(:ivar, :@invoice), :user, s(:arglist)),
36
- # :name,
37
- # s(:arglist)
38
- # )
39
- #
33
+ # and the message of the subject call node matchs one of the association name with the class name,
40
34
  # then it violates the law of demeter.
41
35
  def start_call(node)
42
- if [:lvar, :ivar].include?(node.subject.subject.node_type) && need_delegate?(node)
36
+ if :call == node.subject.sexp_type && need_delegate?(node)
43
37
  add_error "law of demeter"
44
38
  end
45
39
  end
@@ -50,29 +44,14 @@ module RailsBestPractices
50
44
  # if the subject of subject of the call node matchs any in model names,
51
45
  # and the message of subject of the call node matchs any in association names,
52
46
  # then it needs delegate.
53
- #
54
- # e.g. the source code is
55
- #
56
- # @invoic.user.name
57
- #
58
- # then the call node is
59
- #
60
- # s(:call, s(:call, s(:ivar, :@invoice), :user, s(:arglist)), :name, s(:arglist))
61
- #
62
- # as you see the subject of subject of the call node is [:ivar, @invoice],
63
- # and the message of subject of the call node is :user
64
47
  def need_delegate?(node)
65
- class_name = node.subject.subject.to_s(:remove_at => true).classify
48
+ return unless variable(node)
49
+ class_name = variable(node).to_s.sub('@', '').classify
66
50
  association_name = node.subject.message.to_s
67
51
  association = model_associations.get_association(class_name, association_name)
68
52
  attribute_name = node.message.to_s
69
- association && association_methods.include?(association[:meta]) &&
70
- is_association_attribute?(association[:class_name], association_name, attribute_name)
71
- end
72
-
73
- # only check belongs_to and has_one association.
74
- def association_methods
75
- [:belongs_to, :has_one]
53
+ association && ASSOCIATION_METHODS.include?(association["meta"]) &&
54
+ is_association_attribute?(association["class_name"], association_name, attribute_name)
76
55
  end
77
56
 
78
57
  def is_association_attribute?(association_class, association_name, attribute_name)
@@ -13,40 +13,42 @@ module RailsBestPractices
13
13
  # only check all view files to see if there are finders, then the finders should be moved to controller.
14
14
  class MoveCodeIntoControllerReview < Review
15
15
 
16
- FINDERS = [:find, :all, :first, :last]
16
+ FINDERS = %w(find all first last)
17
17
 
18
18
  def url
19
19
  "http://rails-bestpractices.com/posts/24-move-code-into-controller"
20
20
  end
21
21
 
22
22
  def interesting_nodes
23
- [:call]
23
+ [:method_add_arg, :assign]
24
24
  end
25
25
 
26
26
  def interesting_files
27
27
  VIEW_FILES
28
28
  end
29
29
 
30
- # check call nodes.
30
+ # check method_add_arg nodes.
31
31
  #
32
- # if the subject of the call node is a constant,
33
- # and the message of the call node is one of the :find, :all, :first and :last,
32
+ # if the subject of the method_add_arg node is a constant,
33
+ # and the message of the method_add_arg node is one of the find, all, first and last,
34
34
  # then it is a finder and should be moved to controller.
35
- def start_call(node)
35
+ def start_method_add_arg(node)
36
36
  add_error "move code into controller" if finder?(node)
37
37
  end
38
38
 
39
+ # check assign nodes.
40
+ #
41
+ # if the subject of the right value node is a constant,
42
+ # and the message of the right value node is one of the find, all, first and last,
43
+ # then it is a finder and should be moved to controller.
44
+ def start_assign(node)
45
+ add_error "move code into controller", node.file, node.right_value.line if finder?(node.right_value)
46
+ end
47
+
39
48
  private
40
49
  # check if the node is a finder call node.
41
- # e.g. the following call node is a finder
42
- #
43
- # s(:call,
44
- # s(:const, :Post),
45
- # :find,
46
- # s(:arglist, s(:lit, :all))
47
- # )
48
50
  def finder?(node)
49
- :const == node.subject.node_type && FINDERS.include?(node.message)
51
+ node.subject.const? && FINDERS.include?(node.message.to_s)
50
52
  end
51
53
  end
52
54
  end
@@ -12,10 +12,10 @@ module RailsBestPractices
12
12
  # Implementation:
13
13
  #
14
14
  # Review process:
15
- # check al method calls to see if there is a complex options_for_select helper.
15
+ # check al method method_add_arg nodes to see if there is a complex options_for_select helper.
16
16
  #
17
- # if the message of the call node is options_for_select,
18
- # and the first argument of the call node is array,
17
+ # if the message of the method_add_arg node is options_for_select,
18
+ # and the first argument of the method_add_arg node is array,
19
19
  # and the size of the array is greater than array_count defined,
20
20
  # then the options_for_select method should be moved into helper.
21
21
  class MoveCodeIntoHelperReview < Review
@@ -24,7 +24,7 @@ module RailsBestPractices
24
24
  end
25
25
 
26
26
  def interesting_nodes
27
- [:call]
27
+ [:method_add_arg]
28
28
  end
29
29
 
30
30
  def interesting_files
@@ -36,12 +36,12 @@ module RailsBestPractices
36
36
  @array_count = options['array_count'] || 3
37
37
  end
38
38
 
39
- # check call node with message options_for_select (sorry we only check options_for_select helper now).
39
+ # check method_add_arg node with message options_for_select (sorry we only check options_for_select helper now).
40
40
  #
41
41
  # if the first argument of options_for_select method call is an array,
42
42
  # and the size of the array is more than @array_count defined,
43
43
  # then the options_for_select helper should be moved into helper.
44
- def start_call(node)
44
+ def start_method_add_arg(node)
45
45
  add_error "move code into helper (array_count >= #{@array_count})" if complex_select_options?(node)
46
46
  end
47
47
 
@@ -50,29 +50,11 @@ module RailsBestPractices
50
50
  #
51
51
  # if the first argument is an array,
52
52
  # and the size of array is greater than @array_count you defined,
53
- # then it is complext, e.g.
54
- #
55
- # s(:call, nil, :options_for_select,
56
- # s(:arglist,
57
- # s(:array,
58
- # s(:array,
59
- # s(:call, nil, :t, s(:arglist, s(:lit, :draft))),
60
- # s(:str, "draft")
61
- # ),
62
- # s(:array,
63
- # s(:call, nil, :t, s(:arglist, s(:lit, :published))),
64
- # s(:str, "published")
65
- # )
66
- # ),
67
- # s(:call,
68
- # s(:call, nil, :params, s(:arglist)),
69
- # :[],
70
- # s(:arglist, s(:lit, :default_state))
71
- # )
72
- # )
73
- # )
53
+ # then it is complext.
74
54
  def complex_select_options?(node)
75
- :options_for_select == node.message && :array == node.arguments[1].node_type && node.arguments[1].size > @array_count
55
+ "options_for_select" == node[1].message.to_s &&
56
+ :array == node.arguments.all[0].sexp_type &&
57
+ node.arguments.all[0].array_size > @array_count
76
58
  end
77
59
  end
78
60
  end
@@ -10,16 +10,15 @@ module RailsBestPractices
10
10
  # Implementation:
11
11
  #
12
12
  # Review process:
13
- # check if there are multiple method calls or attribute assignments apply to one subject,
14
- # and the subject is a local variable or instance variable,
15
- # then they should be moved into model.
13
+ # check if, unless, elsif there are multiple method calls or attribute assignments apply to one subject,
14
+ # and the subject is a variable, then they should be moved into model.
16
15
  class MoveCodeIntoModelReview < Review
17
16
  def url
18
17
  "http://rails-bestpractices.com/posts/25-move-code-into-model"
19
18
  end
20
19
 
21
20
  def interesting_nodes
22
- [:if]
21
+ [:if, :unless, :elsif]
23
22
  end
24
23
 
25
24
  def interesting_files
@@ -31,22 +30,24 @@ module RailsBestPractices
31
30
  @use_count = options['use_count'] || 2
32
31
  end
33
32
 
34
- # check if node to see whose conditional statementnodes contain multiple call nodes with same subject who is a local variable or instance variable.
33
+ # check if node to see whose conditional statementnodes contain multiple call nodes with same subject who is a variable.
35
34
  #
36
- # it will check every call and attrasgn nodes in the conditional statement nodes.
35
+ # it will check every call and assignment nodes in the conditional statement nodes.
37
36
  #
38
- # if there are multiple call and attrasgn nodes who have the same subject,
39
- # and the subject is a local variable or an instance variable,
40
- # then the conditional statement nodes should be moved into model.
37
+ # if there are multiple call and assignment nodes who have the same subject,
38
+ # and the subject is a variable, then the conditional statement nodes should be moved into model.
41
39
  def start_if(node)
42
- node.conditional_statement.grep_nodes(:node_type => [:call, :attrasgn]) { |child_node| remember_variable_use_count(child_node) }
40
+ node.conditional_statement.grep_nodes(:sexp_type => :call) { |child_node| remember_variable_use_count(child_node) }
43
41
 
44
42
  variable_use_count.each do |variable_node, count|
45
- add_error "move code into model (#{variable_node} use_count > #{@use_count})", variable_node.file, variable_node.line if count > @use_count
43
+ add_error "move code into model (#{variable_node} use_count > #{@use_count})" if count > @use_count
46
44
  end
47
45
 
48
46
  reset_variable_use_count
49
47
  end
48
+
49
+ alias_method :start_unless, :start_if
50
+ alias_method :start_elsif, :start_if
50
51
  end
51
52
  end
52
53
  end
@@ -10,55 +10,44 @@ module RailsBestPractices
10
10
  # Implementation:
11
11
  #
12
12
  # Review process:
13
- # check all method calls in controller files.
13
+ # check all method method_add_arg nodes in controller files.
14
14
  # if there is any call node with message find, all, first or last,
15
15
  # and it has a hash argument,
16
16
  # then it is a complex finder, and should be moved to model's named scope.
17
17
  class MoveFinderToNamedScopeReview < Review
18
18
 
19
- FINDER = [:find, :all, :first, :last]
19
+ FINDERS = %w(find all first last)
20
20
 
21
21
  def url
22
22
  "http://rails-bestpractices.com/posts/1-move-finder-to-named_scope"
23
23
  end
24
24
 
25
25
  def interesting_nodes
26
- [:call]
26
+ [:method_add_arg]
27
27
  end
28
28
 
29
29
  def interesting_files
30
30
  CONTROLLER_FILES
31
31
  end
32
32
 
33
- # check call node if its message is one of :find, :all, :first or :last,
33
+ # check method_add_ag node if its message is one of find, all, first or last,
34
34
  # and it has a hash argument,
35
35
  # then the call node is the finder that should be moved to model's named_scope.
36
- def start_call(node)
36
+ def start_method_add_arg(node)
37
37
  add_error "move finder to named_scope" if finder?(node)
38
38
  end
39
39
 
40
40
  private
41
- # check if the call node is a finder.
41
+ # check if the method_add_arg node is a finder.
42
42
  #
43
- # if the subject of call node is a constant,
44
- # and the message of call node is one of find, all, first or last,
43
+ # if the subject of method_add_arg node is a constant,
44
+ # and the message of call method_add_arg is one of find, all, first or last,
45
45
  # and any of its arguments is a hash,
46
- # then it is a finder. e.g.
47
- #
48
- # s(:call, s(:const, :Post), :find,
49
- # s(:arglist, s(:lit, :all),
50
- # s(:hash,
51
- # s(:lit, :conditions),
52
- # s(:hash, s(:lit, :state), s(:str, "public")),
53
- # s(:lit, :limit),
54
- # s(:lit, 10),
55
- # s(:lit, :order),
56
- # s(:str, "created_at desc")
57
- # )
58
- # )
59
- # )
60
- def finder?(call_node)
61
- :const == call_node.subject.node_type && FINDER.include?(call_node.message) && call_node.arguments.children.any? { |node| :hash == node.node_type }
46
+ # then it is a finder.
47
+ def finder?(node)
48
+ FINDERS.include?(node[1].message.to_s) &&
49
+ :call == node[1].sexp_type &&
50
+ node.arguments.grep_nodes_count(:sexp_type => :bare_assoc_hash) > 0
62
51
  end
63
52
  end
64
53
  end