rails_best_practices 0.10.1 → 1.0.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.
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
@@ -17,14 +17,14 @@ module RailsBestPractices
17
17
  # then you can use query attribute instead.
18
18
  class UseQueryAttributeReview < Review
19
19
 
20
- QUERY_METHODS = [:nil?, :blank?, :present?]
20
+ QUERY_METHODS = %w(nil? blank? present?)
21
21
 
22
22
  def url
23
23
  "http://rails-bestpractices.com/posts/56-use-query-attribute"
24
24
  end
25
25
 
26
26
  def interesting_nodes
27
- [:if]
27
+ [:if, :unless, :elsif]
28
28
  end
29
29
 
30
30
  # check if node to see whose conditional statement nodes contain nodes that can use query attribute instead.
@@ -40,22 +40,33 @@ module RailsBestPractices
40
40
  #
41
41
  # then the call node can use query attribute instead.
42
42
  def start_if(node)
43
- if node = query_attribute_node(node.conditional_statement)
44
- subject_node = node.subject
45
- add_error "use query attribute (#{subject_node.subject}.#{subject_node.message}?)", node.file, node.line
43
+ all_conditions = node.conditional_statement == node.conditional_statement.all_conditions ? [node.conditional_statement] : node.conditional_statement.all_conditions
44
+ all_conditions.each do |condition_node|
45
+ if query_attribute_node = query_attribute_node(condition_node)
46
+ subject_node = query_attribute_node.subject
47
+ add_error "use query attribute (#{subject_node.subject}.#{subject_node.message}?)", node.file, query_attribute_node.line
48
+ end
46
49
  end
47
50
  end
48
51
 
52
+ alias_method :start_unless, :start_if
53
+ alias_method :start_elsif, :start_if
54
+
49
55
  private
50
56
  # recursively check conditional statement nodes to see if there is a call node that may be possible query attribute.
51
57
  def query_attribute_node(conditional_statement_node)
52
- case conditional_statement_node.node_type
58
+ case conditional_statement_node.sexp_type
53
59
  when :and, :or
54
- return query_attribute_node(conditional_statement_node[1]) || query_attribute_node(conditional_statement_node[2])
60
+ node = query_attribute_node(conditional_statement_node[1]) || query_attribute_node(conditional_statement_node[2])
61
+ node.file = conditional_statement_code.file
62
+ return node
55
63
  when :not
56
- return query_attribute_node(conditional_statement_node[1])
64
+ node = query_attribute_node(conditional_statement_node[1])
65
+ node.file = conditional_statement_node.file
57
66
  when :call
58
67
  return conditional_statement_node if possible_query_attribute?(conditional_statement_node)
68
+ when :binary
69
+ return conditional_statement_node if possible_query_attribute?(conditional_statement_node)
59
70
  end
60
71
  nil
61
72
  end
@@ -70,50 +81,34 @@ module RailsBestPractices
70
81
  # for the second call, the message should be one of nil?, blank? or present? or
71
82
  # it is compared with an empty string.
72
83
  #
73
- # the node that may use query attribute is like
74
- #
75
- # s(:call, s(:call, s(:ivar, :@user), :login, s(:arglist)), :nil?, s(:arglist))
76
- #
77
- #
84
+ # the node that may use query attribute.
78
85
  def possible_query_attribute?(node)
79
- return false unless :call == node.subject.node_type
80
- subject = node.subject.subject
81
- return false unless subject
82
- message = node.subject.message
86
+ return false unless :call == node.subject.sexp_type
87
+ variable_node = variable(node)
88
+ message_node = node.grep_node(:subject => variable_node.to_s).message
83
89
 
84
- [:arglist] == node.subject.arguments && is_model?(subject) && model_attribute?(subject, message) &&
85
- (QUERY_METHODS.include?(node.message) || compare_with_empty_string?(node))
90
+ is_model?(variable_node) && model_attribute?(variable_node, message_node.to_s) &&
91
+ (QUERY_METHODS.include?(node.message.to_s) || compare_with_empty_string?(node))
86
92
  end
87
93
 
88
94
  # check if the subject is one of the models.
89
- #
90
- # subject, subject of call node, like
91
- # s(:ivar, @user)
92
- def is_model?(subject)
93
- return false if :const == subject.node_type
94
- class_name = subject.to_s(:remove_at => true).classify
95
+ def is_model?(variable_node)
96
+ return false if variable_node.const?
97
+ class_name = variable_node.to_s.sub(/^@/, '').classify
95
98
  models.include?(class_name)
96
99
  end
97
100
 
98
101
  # check if the subject and message is one of the model's attribute.
99
102
  # the subject should match one of the class model name, and the message should match one of attribute name.
100
- #
101
- # subject, subject of call node, like
102
- # s(:ivar, @user)
103
- #
104
- # message, message of call node, like
105
- # :login
106
- def model_attribute?(subject, message)
107
- class_name = subject.to_s(:remove_at => true).classify
108
- attribute_type = model_attributes.get_attribute_type(class_name, message.to_s)
109
- attribute_type && ![:integer, :float].include?(attribute_type)
103
+ def model_attribute?(variable_node, message)
104
+ class_name = variable_node.to_s.sub(/^@/, '').classify
105
+ attribute_type = model_attributes.get_attribute_type(class_name, message)
106
+ attribute_type && !["integer", "float"].include?(attribute_type)
110
107
  end
111
108
 
112
- # check if the node is with node type :call, node message :== and node arguments {:arglist, (:str, "")}
113
- #
114
- # @user.login == "" => true
109
+ # check if the node is with node type :binary, node message :== and node argument is empty string.
115
110
  def compare_with_empty_string?(node)
116
- :call == node.node_type && :== == node.message && [:arglist, [:str, ""]] == node.arguments
111
+ :binary == node.sexp_type && ["==", "!="].include?(node.message.to_s) && s(:string_literal, s(:string_content)) == node.argument
117
112
  end
118
113
  end
119
114
  end
@@ -16,8 +16,7 @@ module RailsBestPractices
16
16
  # then the method call should be wrapped by say or say_with_time.
17
17
  class UseSayWithTimeInMigrationsReview < Review
18
18
 
19
- DEFAULT_MIGRATION_METHODS = [:add_column, :add_index, :add_timestamps, :change_column, :change_column_default, :change_table, :create_table, :drop_table, :remove_column, :remove_index, :remove_timestamps, :rename_column, :rename_index, :rename_table]
20
- WITH_SAY_METHODS = [:say, :say_with_time]
19
+ WITH_SAY_METHODS = %w(say say_with_time)
21
20
 
22
21
  def url
23
22
  "http://rails-bestpractices.com/posts/46-use-say-and-say_with_time-in-migrations-to-make-a-useful-migration-log"
@@ -31,47 +30,24 @@ module RailsBestPractices
31
30
  MIGRATION_FILES
32
31
  end
33
32
 
34
- # check a class method define node to see if there are method calls that need to be wrapped by :say or :say_with_time.
33
+ # check a class method define node to see if there are method calls that need to be wrapped by say or say_with_time.
35
34
  #
36
35
  # it will check the first block node,
37
- # if any method call whose message is not default migration methods in the block node, like
38
- #
39
- # s(:defs, s(:self), :up, s(:args),
40
- # s(:scope,
41
- # s(:block,
42
- # s(:iter,
43
- # s(:call, s(:const, :User), :find_each, s(:arglist)),
44
- # s(:lasgn, :user),
45
- # s(:block,
46
- # s(:masgn,
47
- # s(:array,
48
- # s(:attrasgn, s(:lvar, :user), :first_name=, s(:arglist)),
49
- # s(:attrasgn, s(:lvar, :user), :last_name=, s(:arglist))
50
- # ),
51
- # s(:to_ary,
52
- # s(:call,
53
- # s(:call, s(:lvar, :user), :full_name, s(:arglist)),
54
- # :split,
55
- # s(:arglist, s(:str, " "))
56
- # )
57
- # )
58
- # ),
59
- # s(:call, s(:lvar, :user), :save, s(:arglist))
60
- # )
61
- # )
62
- # )
63
- # )
64
- # )
65
- #
36
+ # if any method call whose message is not default migration methods in the block node,
66
37
  # then such method call should be wrapped by say or say_with_time
67
38
  def start_defs(node)
68
- block_node = node.grep_node(:node_type => :block)
69
- block_node.children.each do |child_node|
70
- next if :iter != child_node.node_type || child_node.grep_nodes_count(:node_type => :call, :message => WITH_SAY_METHODS) > 0
39
+ node.body.statements.each do |child_node|
40
+ next if child_node.grep_nodes_count(:sexp_type => [:fcall, :command], :message => WITH_SAY_METHODS) > 0
71
41
 
72
- subject_node = child_node.subject
73
- if :call == subject_node.node_type && !DEFAULT_MIGRATION_METHODS.include?(subject_node.message)
74
- add_error("use say with time in migrations", subject_node.file, subject_node.line)
42
+ subject_node = if :method_add_block == child_node.sexp_type
43
+ child_node[1]
44
+ elsif :method_add_arg == child_node.sexp_type
45
+ child_node[1]
46
+ else
47
+ child_node
48
+ end
49
+ if :call == subject_node.sexp_type
50
+ add_error("use say with time in migrations", node.file, child_node.line)
75
51
  end
76
52
  end
77
53
  end
@@ -21,7 +21,7 @@ module RailsBestPractices
21
21
  end
22
22
 
23
23
  def interesting_nodes
24
- [:if]
24
+ [:if, :unless, :elsif]
25
25
  end
26
26
 
27
27
  def interesting_files
@@ -31,63 +31,32 @@ module RailsBestPractices
31
31
  # check if node.
32
32
  #
33
33
  # if it is a method call compared with current_user or current_user.id,
34
- # and there is a redirect_to method call in the block body, like
35
- #
36
- # unless @post.user == current_user
37
- # falsh[:error] = "Access Denied"
38
- # redirect_to posts_url
39
- # end
40
- #
34
+ # and there is a redirect_to method call in the block body,
41
35
  # then it should be replaced by using scope access.
42
36
  def start_if(node)
43
37
  add_error "use scope access" if current_user_redirect?(node)
44
38
  end
45
39
 
40
+ alias_method :start_unless, :start_if
41
+ alias_method :start_elsif, :start_if
42
+
46
43
  private
47
44
  # check a if node to see
48
45
  #
49
46
  # if the conditional statement is compared with current_user or current_user.id,
50
- # and there is a redirect_to method call in the block body, like
51
- #
52
- # s(:if,
53
- # s(:call,
54
- # s(:call, s(:ivar, :@post), :user, s(:arglist)),
55
- # :==,
56
- # s(:arglist, s(:call, nil, :current_user, s(:arglist)))
57
- # ),
58
- # nil,
59
- # s(:block,
60
- # s(:attrasgn,
61
- # s(:call, nil, :flash, s(:arglist)),
62
- # :[]=,
63
- # s(:arglist, s(:lit, :warning), s(:str, "Access Denied"))
64
- # ),
65
- # s(:call, nil, :redirect_to,
66
- # s(:arglist, s(:call, nil, :posts_url, s(:arglist)))
67
- # )
68
- # )
69
- # )
70
- #
47
+ # and there is a redirect_to method call in the block body,
71
48
  # then it should be replaced by using scope access.
72
49
  def current_user_redirect?(node)
73
- condition_node = node.conditional_statement
74
-
75
- condition_node.message == :== &&
76
- (current_user?(condition_node.arguments[1]) || current_user?(condition_node.subject)) &&
77
- (node.false_node.grep_node(:message => :redirect_to) || node.true_node.grep_node(:message => :redirect_to))
50
+ all_conditions = node.conditional_statement == node.conditional_statement.all_conditions ? [node.conditional_statement] : node.conditional_statement.all_conditions
51
+ results = all_conditions.map do |condition_node|
52
+ ["==", "!="].include?(condition_node.message.to_s) && (current_user?(condition_node.argument) || current_user?(condition_node.subject))
53
+ end
54
+ results.any? { |result| result == true } && node.body.grep_node(:message => "redirect_to")
78
55
  end
79
56
 
80
57
  # check a call node to see if it uses current_user, or current_user.id.
81
- #
82
- # the expected call node may be
83
- #
84
- # s(:call, nil, :current_user, s(:arglist))
85
- #
86
- # or
87
- #
88
- # s(:call, s(:call, nil, :current_user, s(:arglist)), :id, s(:arglist))
89
- def current_user?(call_node)
90
- call_node.message == :current_user || (call_node.subject.message == :current_user && call_node.message == :id)
58
+ def current_user?(node)
59
+ "current_user" == node.to_s || ("current_user" == node.subject.to_s && "id" == node.message.to_s)
91
60
  end
92
61
  end
93
62
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
  module RailsBestPractices
3
- VERSION = "0.10.1"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
 
@@ -4,7 +4,7 @@ describe RailsBestPractices::Core::Check do
4
4
  let(:check) { RailsBestPractices::Core::Check.new }
5
5
 
6
6
  it "should get empty interesting nodes" do
7
- check.interesting_nodes.should == RailsBestPractices::Core::Check::NODE_TYPES
7
+ check.interesting_nodes.should == []
8
8
  end
9
9
 
10
10
  it "should match all files of interesting files" do
@@ -13,13 +13,13 @@ describe RailsBestPractices::Core::Check do
13
13
 
14
14
  context "node_start" do
15
15
  it "should call start_if" do
16
- node = stub(:node_type => :if)
16
+ node = stub(:sexp_type => :if)
17
17
  check.should_receive(:send).with("start_if", node)
18
18
  check.node_start(node)
19
19
  end
20
20
 
21
21
  it "should call start_call" do
22
- node = stub(:node_type => :call)
22
+ node = stub(:sexp_type => :call)
23
23
  check.should_receive(:send).with("start_call", node)
24
24
  check.node_start(node)
25
25
  end
@@ -27,13 +27,13 @@ describe RailsBestPractices::Core::Check do
27
27
 
28
28
  context "node_end" do
29
29
  it "should call end_if" do
30
- node = stub(:node_type => :if)
30
+ node = stub(:sexp_type => :if)
31
31
  check.should_receive(:send).with("end_if", node)
32
32
  check.node_end(node)
33
33
  end
34
34
 
35
35
  it "should call end_call" do
36
- node = stub(:node_type => :call)
36
+ node = stub(:sexp_type => :call)
37
37
  check.should_receive(:send).with("end_call", node)
38
38
  check.node_end(node)
39
39
  end
@@ -69,28 +69,28 @@ describe RailsBestPractices::Core::CheckingVisitor do
69
69
  end
70
70
 
71
71
  it "should prepare model associations" do
72
- node = stub(:node_type => :call, :children => [], :file => "app/models/user.rb")
72
+ node = stub(:sexp_type => :call, :children => [], :file => "app/models/user.rb")
73
73
  prepare1.should_receive(:node_start).with(node)
74
74
  prepare1.should_receive(:node_end).with(node)
75
75
  visitor.prepare(node)
76
76
  end
77
77
 
78
78
  it "should prepare mailer names" do
79
- node = stub(:node_type => :class, :children => [], :file => "app/mailers/user_mailer.rb")
79
+ node = stub(:sexp_type => :class, :children => [], :file => "app/mailers/user_mailer.rb")
80
80
  prepare2.should_receive(:node_start).with(node)
81
81
  prepare2.should_receive(:node_end).with(node)
82
82
  visitor.prepare(node)
83
83
  end
84
84
 
85
85
  it "should review controller method definitions" do
86
- node = stub(:node_type => :defn, :children => [], :file => "app/controllers/users_controller.rb")
86
+ node = stub(:sexp_type => :defn, :children => [], :file => "app/controllers/users_controller.rb")
87
87
  review1.should_receive(:node_start).with(node)
88
88
  review1.should_receive(:node_end).with(node)
89
89
  visitor.review(node)
90
90
  end
91
91
 
92
92
  it "should review view calls" do
93
- node = stub(:node_type => :call, :children => [], :file => "app/views/users/new.html.erb")
93
+ node = stub(:sexp_type => :call, :children => [], :file => "app/views/users/new.html.erb")
94
94
  review2.should_receive(:node_start).with(node)
95
95
  review2.should_receive(:node_end).with(node)
96
96
  visitor.review(node)
@@ -4,13 +4,13 @@ describe RailsBestPractices::Core::ModelAssociations do
4
4
  let(:model_associations) { RailsBestPractices::Core::ModelAssociations.new }
5
5
 
6
6
  before :each do
7
- model_associations.add_association("Project", "project_manager", :belongs_to)
8
- model_associations.add_association("Project", "people", :has_many, "Person")
7
+ model_associations.add_association("Project", "project_manager", "belongs_to")
8
+ model_associations.add_association("Project", "people", "has_many", "Person")
9
9
  end
10
10
 
11
11
  it "should get model associations" do
12
- model_associations.get_association("Project", "project_manager").should == {:meta => :belongs_to, :class_name => "ProjectManager"}
13
- model_associations.get_association("Project", "people").should == {:meta => :has_many, :class_name => "Person"}
12
+ model_associations.get_association("Project", "project_manager").should == {"meta" => "belongs_to", "class_name" => "ProjectManager"}
13
+ model_associations.get_association("Project", "people").should == {"meta" => "has_many", "class_name" => "Person"}
14
14
  model_associations.get_association("Project", "unknown").should be_nil
15
15
  end
16
16
 
@@ -9,9 +9,15 @@ describe RailsBestPractices::Core::Nil do
9
9
  end
10
10
  end
11
11
 
12
+ context "hash_size" do
13
+ it "should return 0" do
14
+ core_nil.hash_size.should == 0
15
+ end
16
+ end
17
+
12
18
  context "method_missing" do
13
19
  it "should return self" do
14
20
  core_nil.undefined.should == core_nil
15
21
  end
16
22
  end
17
- end
23
+ end
@@ -0,0 +1,430 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sexp do
4
+ describe "line" do
5
+ before :each do
6
+ content = <<-EOF
7
+ class Demo
8
+ def test
9
+ end
10
+ end
11
+ EOF
12
+ @node = parse_content(content)
13
+ end
14
+
15
+ it "should return class line" do
16
+ @node.grep_node(:sexp_type => :class).line.should == 1
17
+ end
18
+
19
+ it "should return def line" do
20
+ @node.grep_node(:sexp_type => :def).line.should == 2
21
+ end
22
+ end
23
+
24
+ describe "grep_nodes" do
25
+ before :each do
26
+ content = <<-EOF
27
+ def show
28
+ current_user.posts.find(params[:id])
29
+ end
30
+ EOF
31
+ @node = parse_content(content)
32
+ end
33
+
34
+ it "should get the call nodes with subject current_user" do
35
+ nodes = []
36
+ @node.grep_nodes(:sexp_type => :call, :subject => "current_user") { |node| nodes << node }
37
+ nodes.should == [s(:call, s(:var_ref, s(:@ident, "current_user", s(2, 8))), :".", s(:@ident, "posts", s(2, 21)))]
38
+ end
39
+
40
+ it "should get the call nodes with different messages" do
41
+ nodes = []
42
+ @node.grep_nodes(:sexp_type => :call, :message => ["posts", "find"]) { |node| nodes << node }
43
+ nodes.should == [s(:call, s(:call, s(:var_ref, s(:@ident, "current_user", s(2, 8))), :".", s(:@ident, "posts", s(2, 21))), :".", s(:@ident, "find", s(2, 27))), s(:call, s(:var_ref, s(:@ident, "current_user", s(2, 8))), :".", s(:@ident, "posts", s(2, 21)))]
44
+ end
45
+ end
46
+
47
+ describe "grep_node" do
48
+ before :each do
49
+ content = <<-EOF
50
+ def show
51
+ current_user.posts.find(params[:id])
52
+ end
53
+ EOF
54
+ @node = parse_content(content)
55
+ end
56
+
57
+ it "should get first node with empty argument" do
58
+ node = @node.grep_node(:sexp_type => :call, :subject => "current_user")
59
+ node.should == s(:call, s(:var_ref, s(:@ident, "current_user", s(2, 8))), :".", s(:@ident, "posts", s(2, 21)))
60
+ end
61
+ end
62
+
63
+ describe "grep_nodes_count" do
64
+ before :each do
65
+ content = <<-EOF
66
+ def show
67
+ current_user.posts.find(params[:id])
68
+ end
69
+ EOF
70
+ @node = parse_content(content)
71
+ end
72
+
73
+ it "should get the count of call nodes" do
74
+ @node.grep_nodes_count(:sexp_type => :call).should == 2
75
+ end
76
+ end
77
+
78
+ describe "subject" do
79
+ it "should get subject of assign node" do
80
+ node = parse_content("user.name = params[:name]").grep_node(:sexp_type => :assign)
81
+ subject = node.subject
82
+ subject.sexp_type.should == :field
83
+ subject.subject.to_s.should == "user"
84
+ subject.message.to_s.should == "name"
85
+ end
86
+
87
+ it "should get subject of field node" do
88
+ node = parse_content("user.name = params[:name]").grep_node(:sexp_type => :field)
89
+ node.subject.to_s.should == "user"
90
+ end
91
+
92
+ it "should get subject of call node" do
93
+ node = parse_content("user.name").grep_node(:sexp_type => :call)
94
+ node.subject.to_s.should == "user"
95
+ end
96
+
97
+ it "should get subject of binary" do
98
+ node = parse_content("user == 'user_name'").grep_node(:sexp_type => :binary)
99
+ node.subject.to_s.should == "user"
100
+ end
101
+
102
+ it "should get subject of command_call" do
103
+ content = <<-EOF
104
+ map.resources :posts do
105
+ end
106
+ EOF
107
+ node = parse_content(content).grep_node(:sexp_type => :command_call)
108
+ node.subject.to_s.should == "map"
109
+ end
110
+
111
+ it "should get subject of method_add_arg" do
112
+ node = parse_content("Post.find(:all)").grep_node(:sexp_type => :method_add_arg)
113
+ node.subject.to_s.should == "Post"
114
+ end
115
+
116
+ it "should get subject of method_add_block" do
117
+ node = parse_content("Post.save do; end").grep_node(:sexp_type => :method_add_block)
118
+ node.subject.to_s.should == "Post"
119
+ end
120
+ end
121
+
122
+ describe "class_name" do
123
+ it "should get class name of class node" do
124
+ node = parse_content("class User; end").grep_node(:sexp_type => :class)
125
+ node.class_name.to_s.should == "User"
126
+ end
127
+ end
128
+
129
+ describe "base_class" do
130
+ it "should get base class of class node" do
131
+ node = parse_content("class User < ActiveRecord::Base; end").grep_node(:sexp_type => :class)
132
+ node.base_class.to_s.should == "ActiveRecord::Base"
133
+ end
134
+ end
135
+
136
+ describe "left_value" do
137
+ it "should get the left value of assign" do
138
+ node = parse_content("user = current_user").grep_node(:sexp_type => :assign)
139
+ node.left_value.to_s.should == "user"
140
+ end
141
+ end
142
+
143
+ describe "right_value" do
144
+ it "should get the right value of assign" do
145
+ node = parse_content("user = current_user").grep_node(:sexp_type => :assign)
146
+ node.right_value.to_s.should == "current_user"
147
+ end
148
+ end
149
+
150
+ describe "message" do
151
+ it "should get the message of command" do
152
+ node = parse_content("has_many :projects").grep_node(:sexp_type => :command)
153
+ node.message.to_s.should == "has_many"
154
+ end
155
+
156
+ it "should get the message of command_call" do
157
+ node = parse_content("map.resources :posts do; end").grep_node(:sexp_type => :command_call)
158
+ node.message.to_s.should == "resources"
159
+ end
160
+
161
+ it "should get the message of field" do
162
+ node = parse_content("user.name = 'test'").grep_node(:sexp_type => :field)
163
+ node.message.to_s.should == "name"
164
+ end
165
+
166
+ it "should get the message of call" do
167
+ node = parse_content("user.name").grep_node(:sexp_type => :call)
168
+ node.message.to_s.should == "name"
169
+ end
170
+
171
+ it "should get the message of binary" do
172
+ node = parse_content("user.name == 'test'").grep_node(:sexp_type => :binary)
173
+ node.message.to_s.should == "=="
174
+ end
175
+
176
+ it "should get the message of fcall" do
177
+ node = parse_content("test?('world')").grep_node(:sexp_type => :fcall)
178
+ node.message.to_s.should == "test?"
179
+ end
180
+
181
+ it "should get the message of method_add_arg" do
182
+ node = parse_content("Post.find(:all)").grep_node(:sexp_type => :method_add_arg)
183
+ node.message.to_s.should == "find"
184
+ end
185
+
186
+ it "should get the message of method_add_block" do
187
+ node = parse_content("Post.save do; end").grep_node(:sexp_type => :method_add_block)
188
+ node.message.to_s.should == "save"
189
+ end
190
+ end
191
+
192
+ describe "arguments" do
193
+ it "should get the arguments of command" do
194
+ node = parse_content("resources :posts do; end").grep_node(:sexp_type => :command)
195
+ node.arguments.sexp_type.should == :args_add_block
196
+ end
197
+
198
+ it "should get the arguments of command_call" do
199
+ node = parse_content("map.resources :posts do; end").grep_node(:sexp_type => :command_call)
200
+ node.arguments.sexp_type.should == :args_add_block
201
+ end
202
+
203
+ it "should get the arguments of method_add_args" do
204
+ node = parse_content("User.find(:all)").grep_node(:sexp_type => :method_add_arg)
205
+ node.arguments.sexp_type.should == :args_add_block
206
+ end
207
+ end
208
+
209
+ describe "argument" do
210
+ it "should get the argument of binary" do
211
+ node = parse_content("user == current_user").grep_node(:sexp_type => :binary)
212
+ node.argument.to_s.should == "current_user"
213
+ end
214
+ end
215
+
216
+ describe "all" do
217
+ it "should get all arguments" do
218
+ node = parse_content("puts 'hello', 'world'").grep_node(:sexp_type => :args_add_block)
219
+ node.all.map(&:to_s).should == ["hello", "world"]
220
+ end
221
+ end
222
+
223
+ describe "conditional_statement" do
224
+ it "should get conditional statement of if" do
225
+ node = parse_content("if true; end").grep_node(:sexp_type => :if)
226
+ node.conditional_statement.to_s.should == "true"
227
+ end
228
+
229
+ it "should get conditional statement of unless" do
230
+ node = parse_content("unless true; end").grep_node(:sexp_type => :unless)
231
+ node.conditional_statement.to_s.should == "true"
232
+ end
233
+
234
+ it "should get conditional statement of elsif" do
235
+ content =<<-EOF
236
+ if true
237
+ elsif false
238
+ end
239
+ EOF
240
+ node = parse_content(content).grep_node(:sexp_type => :elsif)
241
+ node.conditional_statement.to_s.should == "false"
242
+ end
243
+ end
244
+
245
+ describe "all_conditions" do
246
+ it "should get all conditions" do
247
+ node = parse_content("user == current_user && user.valid? || user.admin?").grep_node(:sexp_type => :binary)
248
+ node.all_conditions.size.should == 3
249
+ end
250
+ end
251
+
252
+ describe "method_name" do
253
+ it "should get the method name of defn" do
254
+ node = parse_content("def show; end").grep_node(:sexp_type => :def)
255
+ node.method_name.to_s.should == "show"
256
+ end
257
+ end
258
+
259
+ describe "body" do
260
+ it "should get body of class" do
261
+ node = parse_content("class User; end").grep_node(:sexp_type => :class)
262
+ node.body.sexp_type.should == :bodystmt
263
+ end
264
+
265
+ it "should get body of def" do
266
+ node = parse_content("def login; end").grep_node(:sexp_type => :def)
267
+ node.body.sexp_type.should == :bodystmt
268
+ end
269
+
270
+ it "should get body of defs" do
271
+ node = parse_content("def self.login; end").grep_node(:sexp_type => :defs)
272
+ node.body.sexp_type.should == :bodystmt
273
+ end
274
+
275
+ it "should get body of module" do
276
+ node = parse_content("module Enumerable; end").grep_node(:sexp_type => :module)
277
+ node.body.sexp_type.should == :bodystmt
278
+ end
279
+
280
+ it "should get body of if" do
281
+ node = parse_content("if true; puts 'hello world'; end").grep_node(:sexp_type => :if)
282
+ node.body.sexp_type.should == :stmts_add
283
+ end
284
+
285
+ it "should get body of elsif" do
286
+ node = parse_content("if true; elsif true; puts 'hello world'; end").grep_node(:sexp_type => :elsif)
287
+ node.body.sexp_type.should == :stmts_add
288
+ end
289
+
290
+ it "should get body of unless" do
291
+ node = parse_content("unless true; puts 'hello world'; end").grep_node(:sexp_type => :unless)
292
+ node.body.sexp_type.should == :stmts_add
293
+ end
294
+
295
+ it "should get body of else" do
296
+ node = parse_content("if true; else; puts 'hello world'; end").grep_node(:sexp_type => :else)
297
+ node.body.sexp_type.should == :stmts_add
298
+ end
299
+ end
300
+
301
+ describe "block" do
302
+ it "sould get block of method_add_block node" do
303
+ node = parse_content("resources :posts do; resources :comments; end").grep_node(:sexp_type => :method_add_block)
304
+ node.block.sexp_type.should == :do_block
305
+ end
306
+ end
307
+
308
+ describe "statements" do
309
+ it "should get statements of do_block node" do
310
+ node = parse_content("resources :posts do; resources :comments; resources :like; end").grep_node(:sexp_type => :do_block)
311
+ node.statements.size.should == 2
312
+ end
313
+
314
+ it "should get statements of bodystmt node" do
315
+ node = parse_content("class User; def login?; end; def admin?; end; end").grep_node(:sexp_type => :bodystmt)
316
+ node.statements.size.should == 2
317
+ end
318
+ end
319
+
320
+ describe "hash_value" do
321
+ it "should get value for hash node" do
322
+ node = parse_content("{first_name: 'Richard', last_name: 'Huang'}").grep_node(:sexp_type => :hash)
323
+ node.hash_value("first_name").to_s.should == "Richard"
324
+ node.hash_value("last_name").to_s.should == "Huang"
325
+ end
326
+
327
+ it "should get value for bare_assoc_hash" do
328
+ node = parse_content("add_user :user, first_name: 'Richard', last_name: 'Huang'").grep_node(:sexp_type => :bare_assoc_hash)
329
+ node.hash_value("first_name").to_s.should == "Richard"
330
+ node.hash_value("last_name").to_s.should == "Huang"
331
+ end
332
+ end
333
+
334
+ describe "hash_size" do
335
+ it "should get value for hash node" do
336
+ node = parse_content("{first_name: 'Richard', last_name: 'Huang'}").grep_node(:sexp_type => :hash)
337
+ node.hash_size.should == 2
338
+ end
339
+
340
+ it "should get value for bare_assoc_hash" do
341
+ node = parse_content("add_user :user, first_name: 'Richard', last_name: 'Huang'").grep_node(:sexp_type => :bare_assoc_hash)
342
+ node.hash_size.should == 2
343
+ end
344
+ end
345
+
346
+ describe "hash_keys" do
347
+ it "should get value for hash node" do
348
+ node = parse_content("{first_name: 'Richard', last_name: 'Huang'}").grep_node(:sexp_type => :hash)
349
+ node.hash_keys.should == ["first_name", "last_name"]
350
+ end
351
+
352
+ it "should get value for bare_assoc_hash" do
353
+ node = parse_content("add_user :user, first_name: 'Richard', last_name: 'Huang'").grep_node(:sexp_type => :bare_assoc_hash)
354
+ node.hash_keys.should == ["first_name", "last_name"]
355
+ end
356
+ end
357
+
358
+ describe "array_size" do
359
+ it "should get array size" do
360
+ node = parse_content("['first_name', 'last_name']").grep_node(:sexp_type => :array)
361
+ node.array_size.should == 2
362
+ end
363
+ end
364
+
365
+ describe "to_object" do
366
+ it "should to_object" do
367
+ node = parse_content("['first_name', 'last_name']").grep_node(:sexp_type => :array)
368
+ node.to_object.should == ["first_name", "last_name"]
369
+ end
370
+ end
371
+
372
+ describe "to_s" do
373
+ it "should get to_s for string" do
374
+ node = parse_content("'user'").grep_node(:sexp_type => :string_literal)
375
+ node.to_s.should == "user"
376
+ end
377
+
378
+ it "should get to_s for symbol" do
379
+ node = parse_content(":user").grep_node(:sexp_type => :symbol_literal)
380
+ node.to_s.should == "user"
381
+ end
382
+
383
+ it "should get to_s for const" do
384
+ node = parse_content("User").grep_node(:sexp_type => :@const)
385
+ node.to_s.should == "User"
386
+ end
387
+
388
+ it "should get to_s for ivar" do
389
+ node = parse_content("@user").grep_node(:sexp_type => :@ivar)
390
+ node.to_s.should == "@user"
391
+ end
392
+
393
+ it "should get to_s for class with module" do
394
+ node = parse_content("ActiveRecord::Base").grep_node(:sexp_type => :const_path_ref)
395
+ node.to_s.should == "ActiveRecord::Base"
396
+ end
397
+
398
+ it "should get to_s for label" do
399
+ node = parse_content("{first_name: 'Richard'}").grep_node(:sexp_type => :@label)
400
+ node.to_s.should == "first_name"
401
+ end
402
+ end
403
+
404
+ describe "const?" do
405
+ it "should return true for const with var_ref" do
406
+ node = parse_content("User.find").grep_node(:sexp_type => :var_ref)
407
+ node.should be_const
408
+ end
409
+
410
+ it "should return true for const with @const" do
411
+ node = parse_content("User.find").grep_node(:sexp_type => :@const)
412
+ node.should be_const
413
+ end
414
+
415
+ it "should return false for ivar" do
416
+ node = parse_content("@user.find").grep_node(:sexp_type => :@ivar)
417
+ node.should_not be_const
418
+ end
419
+ end
420
+
421
+ describe "remove_line_and_column" do
422
+ it "should remove" do
423
+ s(:@ident, "test", s(2, 12)).remove_line_and_column.should_equal s(:@ident, "test")
424
+ end
425
+
426
+ it "should remove child nodes" do
427
+ s(:const_ref, s(:@const, "Demo", s(1, 12))).remove_line_and_column.should_equal s(:const_def, s(:@const, "Demo"))
428
+ end
429
+ end
430
+ end