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