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
@@ -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