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.
- data/README.md +20 -0
- data/assets/result.html.haml +1 -1
- data/lib/rails_best_practices.rb +5 -3
- data/lib/rails_best_practices/core.rb +1 -1
- data/lib/rails_best_practices/core/check.rb +9 -12
- data/lib/rails_best_practices/core/checking_visitor.rb +9 -6
- data/lib/rails_best_practices/core/model_associations.rb +1 -1
- data/lib/rails_best_practices/core/nil.rb +5 -1
- data/lib/rails_best_practices/core/runner.rb +5 -5
- data/lib/rails_best_practices/core_ext/sexp.rb +688 -0
- data/lib/rails_best_practices/prepares/mailer_prepare.rb +4 -5
- data/lib/rails_best_practices/prepares/model_prepare.rb +16 -26
- data/lib/rails_best_practices/prepares/schema_prepare.rb +11 -17
- data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +24 -75
- data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +39 -113
- data/lib/rails_best_practices/reviews/dry_bundler_in_capistrano_review.rb +6 -16
- data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +16 -32
- data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +11 -20
- data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +7 -28
- data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +16 -14
- data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +10 -28
- data/lib/rails_best_practices/reviews/move_code_into_model_review.rb +12 -11
- data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +13 -24
- data/lib/rails_best_practices/reviews/move_model_logic_into_model_review.rb +9 -9
- data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +24 -68
- data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +15 -22
- data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +31 -91
- data/lib/rails_best_practices/reviews/remove_empty_helpers_review.rb +4 -2
- data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +20 -18
- data/lib/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review.rb +5 -3
- data/lib/rails_best_practices/reviews/review.rb +8 -37
- data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +10 -6
- data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +9 -6
- data/lib/rails_best_practices/reviews/use_before_filter_review.rb +14 -72
- data/lib/rails_best_practices/reviews/use_model_association_review.rb +19 -31
- data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +5 -5
- data/lib/rails_best_practices/reviews/use_observer_review.rb +22 -40
- data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +34 -39
- data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +14 -38
- data/lib/rails_best_practices/reviews/use_scope_access_review.rb +13 -44
- data/lib/rails_best_practices/version.rb +1 -1
- data/spec/rails_best_practices/core/check_spec.rb +5 -5
- data/spec/rails_best_practices/core/checking_visitor_spec.rb +4 -4
- data/spec/rails_best_practices/core/model_associations_spec.rb +4 -4
- data/spec/rails_best_practices/core/nil_spec.rb +7 -1
- data/spec/rails_best_practices/core_ext/sexp_spec.rb +430 -0
- data/spec/rails_best_practices/prepares/model_prepare_spec.rb +12 -12
- data/spec/rails_best_practices/prepares/schema_prepare_spec.rb +6 -6
- data/spec/rails_best_practices/reviews/move_code_into_controller_review_spec.rb +14 -2
- data/spec/rails_best_practices/reviews/move_code_into_helper_review_spec.rb +1 -1
- data/spec/rails_best_practices/reviews/needless_deep_nesting_review_spec.rb +3 -3
- data/spec/rails_best_practices/reviews/not_use_default_route_review_spec.rb +1 -1
- data/spec/rails_best_practices/reviews/overuse_route_customizations_review_spec.rb +15 -1
- data/spec/rails_best_practices/reviews/simplify_render_in_controllers_review_spec.rb +3 -3
- data/spec/rails_best_practices/reviews/use_query_attribute_review_spec.rb +1 -1
- data/spec/rails_best_practices/reviews/use_say_with_time_in_migrations_review_spec.rb +1 -1
- data/spec/rails_best_practices/reviews/use_scope_access_review_spec.rb +4 -4
- data/spec/rails_best_practices_spec.rb +1 -3
- data/spec/spec_helper.rb +4 -0
- metadata +6 -8
- data/lib/rails_best_practices/core/visitable_sexp.rb +0 -444
- data/spec/rails_best_practices/core/visitable_sexp_spec.rb +0 -272
- 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 =
|
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
|
-
|
44
|
-
|
45
|
-
|
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.
|
58
|
+
case conditional_statement_node.sexp_type
|
53
59
|
when :and, :or
|
54
|
-
|
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
|
-
|
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
|
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.
|
80
|
-
|
81
|
-
|
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
|
-
|
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
|
-
|
91
|
-
|
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
|
-
|
102
|
-
|
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 :
|
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
|
-
:
|
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
|
-
|
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
|
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,
|
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
|
-
|
69
|
-
|
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.
|
73
|
-
|
74
|
-
|
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,
|
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,
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
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
|
@@ -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 ==
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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(:
|
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",
|
8
|
-
model_associations.add_association("Project", "people",
|
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 == {
|
13
|
-
model_associations.get_association("Project", "people").should == {
|
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
|