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
data/lib/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review.rb
CHANGED
@@ -18,7 +18,7 @@ module RailsBestPractices
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def interesting_nodes
|
21
|
-
[:
|
21
|
+
[:var_ref]
|
22
22
|
end
|
23
23
|
|
24
24
|
def interesting_files
|
@@ -27,8 +27,10 @@ module RailsBestPractices
|
|
27
27
|
|
28
28
|
# check ivar node in partial view file,
|
29
29
|
# it is an instance variable, and should be replaced with local variable.
|
30
|
-
def
|
31
|
-
|
30
|
+
def start_var_ref(node)
|
31
|
+
if node.to_s.start_with?('@')
|
32
|
+
add_error "replace instance variable with local variable"
|
33
|
+
end
|
32
34
|
end
|
33
35
|
end
|
34
36
|
end
|
@@ -10,15 +10,15 @@ module RailsBestPractices
|
|
10
10
|
def url
|
11
11
|
"#"
|
12
12
|
end
|
13
|
-
# remember use count for the
|
13
|
+
# remember use count for the variable in the call or assign node.
|
14
14
|
#
|
15
|
-
# find the
|
15
|
+
# find the variable in the call or assign node,
|
16
16
|
# then save it to as key in @variable_use_count hash, and add the call count (hash value).
|
17
17
|
def remember_variable_use_count(node)
|
18
18
|
variable_node = variable(node)
|
19
19
|
if variable_node
|
20
|
-
variable_use_count[variable_node] ||= 0
|
21
|
-
variable_use_count[variable_node] += 1
|
20
|
+
variable_use_count[variable_node.to_s] ||= 0
|
21
|
+
variable_use_count[variable_node.to_s] += 1
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -32,32 +32,13 @@ module RailsBestPractices
|
|
32
32
|
@variable_use_count = nil
|
33
33
|
end
|
34
34
|
|
35
|
-
# find
|
36
|
-
#
|
37
|
-
# if the call node is
|
38
|
-
#
|
39
|
-
# s(:call, s(:ivar, :@post), :editors, s(:arglist)),
|
40
|
-
#
|
41
|
-
# or it is
|
42
|
-
#
|
43
|
-
# s(:call,
|
44
|
-
# s(:call, s(:ivar, :@post), :editors, s(:arglist)),
|
45
|
-
# :include?,
|
46
|
-
# s(:arglist, s(:call, nil, :current_user, s(:arglist)))
|
47
|
-
# )
|
48
|
-
#
|
49
|
-
# then the variable both are s(:ivar, :@post).
|
50
|
-
#
|
35
|
+
# find variable in the call or field node.
|
51
36
|
def variable(node)
|
52
|
-
while node.subject.
|
37
|
+
while [:call, :field, :method_add_arg, :method_add_block].include?(node.subject.sexp_type)
|
53
38
|
node = node.subject
|
54
39
|
end
|
55
|
-
|
56
|
-
|
57
|
-
subject_node
|
58
|
-
else
|
59
|
-
nil
|
60
|
-
end
|
40
|
+
return if [:fcall, :hash].include?(node.subject.sexp_type)
|
41
|
+
node.subject
|
61
42
|
end
|
62
43
|
|
63
44
|
# get the models from Prepares.
|
@@ -80,16 +61,6 @@ module RailsBestPractices
|
|
80
61
|
def model_attributes
|
81
62
|
@model_attributes ||= Prepares.model_attributes
|
82
63
|
end
|
83
|
-
|
84
|
-
# compare two sexp nodes' to_s.
|
85
|
-
#
|
86
|
-
# equal?(":test", :test) => true
|
87
|
-
# equai?("@test", :test) => true
|
88
|
-
def equal?(node, expected_node)
|
89
|
-
actual = node.to_s.downcase
|
90
|
-
expected = expected_node.to_s.downcase
|
91
|
-
actual == expected || actual == ':' + expected || actual == '@' + expected
|
92
|
-
end
|
93
64
|
end
|
94
65
|
end
|
95
66
|
end
|
@@ -10,7 +10,7 @@ module RailsBestPractices
|
|
10
10
|
# Implementation:
|
11
11
|
#
|
12
12
|
# Review process:
|
13
|
-
# check all render method
|
13
|
+
# check all render method commands in controller files,
|
14
14
|
# if there is a key 'action', 'template' or 'file' in the argument,
|
15
15
|
# then they should be replaced by simplified syntax.
|
16
16
|
class SimplifyRenderInControllersReview < Review
|
@@ -19,19 +19,23 @@ module RailsBestPractices
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def interesting_nodes
|
22
|
-
[:
|
22
|
+
[:command]
|
23
23
|
end
|
24
24
|
|
25
25
|
def interesting_files
|
26
26
|
CONTROLLER_FILES
|
27
27
|
end
|
28
28
|
|
29
|
-
# check
|
29
|
+
# check command node in the controller file,
|
30
30
|
# if its message is render and the arguments contain a key action, template or file,
|
31
31
|
# then it should be replaced by simplified syntax.
|
32
|
-
def
|
33
|
-
if
|
34
|
-
|
32
|
+
def start_command(node)
|
33
|
+
if "render" == node.message.to_s
|
34
|
+
keys = node.arguments.all[0].hash_keys
|
35
|
+
if keys && keys.size == 1 &&
|
36
|
+
(keys.include?("action") || keys.include?("template") || keys.include?("file"))
|
37
|
+
add_error 'simplify render in controllers'
|
38
|
+
end
|
35
39
|
end
|
36
40
|
end
|
37
41
|
end
|
@@ -10,7 +10,7 @@ module RailsBestPractices
|
|
10
10
|
# Implementation:
|
11
11
|
#
|
12
12
|
# Review process:
|
13
|
-
# check all render method
|
13
|
+
# check all render method commands in view files,
|
14
14
|
# if there is a key 'partial' in the argument, then they should be replaced by simplified syntax.
|
15
15
|
class SimplifyRenderInViewsReview < Review
|
16
16
|
def url
|
@@ -18,19 +18,22 @@ module RailsBestPractices
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def interesting_nodes
|
21
|
-
[:
|
21
|
+
[:command]
|
22
22
|
end
|
23
23
|
|
24
24
|
def interesting_files
|
25
25
|
VIEW_FILES
|
26
26
|
end
|
27
27
|
|
28
|
-
# check
|
28
|
+
# check command node in view file,
|
29
29
|
# if its message is render and the arguments contain a key partial,
|
30
30
|
# then it should be replaced by simplified syntax.
|
31
|
-
def
|
32
|
-
if
|
33
|
-
|
31
|
+
def start_command(node)
|
32
|
+
if "render" == node.message.to_s
|
33
|
+
hash_node = node.arguments.all[0]
|
34
|
+
if hash_node && hash_node.hash_keys && hash_node.hash_keys.include?("partial")
|
35
|
+
add_error 'simplify render in views'
|
36
|
+
end
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
@@ -13,9 +13,6 @@ module RailsBestPractices
|
|
13
13
|
# check all first code line in method definitions (actions),
|
14
14
|
# if they are duplicated, then they should be moved to before_filter.
|
15
15
|
class UseBeforeFilterReview < Review
|
16
|
-
|
17
|
-
PROTECTED_PRIVATE_CALLS = [[:call, nil, :protected, [:arglist]], [:call, nil, :private, [:arglist]]]
|
18
|
-
|
19
16
|
def url
|
20
17
|
"http://rails-bestpractices.com/posts/22-use-before_filter"
|
21
18
|
end
|
@@ -35,87 +32,32 @@ module RailsBestPractices
|
|
35
32
|
|
36
33
|
# check class define node to see if there are method define nodes whose first code line are duplicated.
|
37
34
|
#
|
38
|
-
# it will check every
|
39
|
-
# if there are defn nodes who have the same first code line,
|
40
|
-
#
|
41
|
-
# s(:class, :PostsController, s(:const, :ApplicationController),
|
42
|
-
# s(:scope,
|
43
|
-
# s(:block,
|
44
|
-
# s(:defn, :show, s(:args),
|
45
|
-
# s(:scope,
|
46
|
-
# s(:block,
|
47
|
-
# s(:iasgn, :@post,
|
48
|
-
# s(:call,
|
49
|
-
# s(:call, s(:call, nil, :current_user, s(:arglist)), :posts, s(:arglist)),
|
50
|
-
# :find,
|
51
|
-
# s(:arglist,
|
52
|
-
# s(:call, s(:call, nil, :params, s(:arglist)), :[], s(:arglist, s(:lit, :id)))
|
53
|
-
# )
|
54
|
-
# )
|
55
|
-
# )
|
56
|
-
# )
|
57
|
-
# )
|
58
|
-
# ),
|
59
|
-
# s(:defn, :edit, s(:args),
|
60
|
-
# s(:scope,
|
61
|
-
# s(:block,
|
62
|
-
# s(:iasgn, :@post,
|
63
|
-
# s(:call,
|
64
|
-
# s(:call, s(:call, nil, :current_user, s(:arglist)), :posts, s(:arglist)),
|
65
|
-
# :find,
|
66
|
-
# s(:arglist,
|
67
|
-
# s(:call, s(:call, nil, :params, s(:arglist)), :[], s(:arglist, s(:lit, :id)))
|
68
|
-
# )
|
69
|
-
# )
|
70
|
-
# )
|
71
|
-
# )
|
72
|
-
# )
|
73
|
-
# )
|
74
|
-
# )
|
75
|
-
# )
|
76
|
-
# )
|
77
|
-
#
|
35
|
+
# it will check every def nodes in the class node until protected or private identification,
|
36
|
+
# if there are defn nodes who have the same first code line,
|
78
37
|
# then these duplicated first code lines should be moved to before_filter.
|
79
|
-
def start_class(
|
38
|
+
def start_class(node)
|
80
39
|
@first_sentences = {}
|
81
40
|
|
82
|
-
|
83
|
-
break if
|
84
|
-
remember_first_sentence(
|
41
|
+
node.body.statements.each do |statement_node|
|
42
|
+
break if :var_ref == statement_node.sexp_type && ["protected", "private"].include?(statement_node.to_s)
|
43
|
+
remember_first_sentence(statement_node) if :def == statement_node.sexp_type
|
85
44
|
end
|
86
|
-
@first_sentences.each do |first_sentence,
|
87
|
-
if
|
88
|
-
add_error "use before_filter for #{
|
45
|
+
@first_sentences.each do |first_sentence, def_nodes|
|
46
|
+
if def_nodes.size > @customize_count
|
47
|
+
add_error "use before_filter for #{def_nodes.map { |node| node.method_name.to_s }.join(',')}", node.file, def_nodes.map(&:line).join(',')
|
89
48
|
end
|
90
49
|
end
|
91
50
|
end
|
92
51
|
|
93
52
|
private
|
94
53
|
# check method define node, and remember the first sentence.
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
# s(:block,
|
100
|
-
# s(:iasgn, :@post,
|
101
|
-
# s(:call,
|
102
|
-
# s(:call, s(:call, nil, :current_user, s(:arglist)), :posts, s(:arglist)),
|
103
|
-
# :find,
|
104
|
-
# s(:arglist,
|
105
|
-
# s(:call, s(:call, nil, :params, s(:arglist)), :[], s(:arglist, s(:lit, :id)))
|
106
|
-
# )
|
107
|
-
# )
|
108
|
-
# )
|
109
|
-
# )
|
110
|
-
# )
|
111
|
-
# )
|
112
|
-
#
|
113
|
-
# the first sentence of defn node is :iasgn node.
|
114
|
-
def remember_first_sentence(defn_node)
|
115
|
-
first_sentence = defn_node.body[1]
|
54
|
+
def remember_first_sentence(node)
|
55
|
+
first_sentence = node.body.statements.first
|
56
|
+
return unless first_sentence
|
57
|
+
first_sentence = first_sentence.remove_line_and_column
|
116
58
|
unless first_sentence == s(:nil)
|
117
59
|
@first_sentences[first_sentence] ||= []
|
118
|
-
@first_sentences[first_sentence] <<
|
60
|
+
@first_sentences[first_sentence] << node
|
119
61
|
end
|
120
62
|
end
|
121
63
|
end
|
@@ -12,7 +12,7 @@ module RailsBestPractices
|
|
12
12
|
# Review process:
|
13
13
|
# check model define nodes in all controller files,
|
14
14
|
# if there is an attribute assignment node with message xxx_id=,
|
15
|
-
# and after it, there is a call node with message
|
15
|
+
# and after it, there is a call node with message "save" or "save!",
|
16
16
|
# and the subjects of attribute assignment node and call node are the same,
|
17
17
|
# then model association should be used instead of xxx_id assignment.
|
18
18
|
class UseModelAssociationReview < Review
|
@@ -21,7 +21,7 @@ module RailsBestPractices
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def interesting_nodes
|
24
|
-
[:
|
24
|
+
[:def]
|
25
25
|
end
|
26
26
|
|
27
27
|
def interesting_files
|
@@ -30,56 +30,44 @@ module RailsBestPractices
|
|
30
30
|
|
31
31
|
# check method define nodes to see if there are some attribute assignments that can use model association instead.
|
32
32
|
#
|
33
|
-
# it will check attribute assignment node with message xxx_id=, and call node with message
|
33
|
+
# it will check attribute assignment node with message xxx_id=, and call node with message "save" or "save!"
|
34
34
|
#
|
35
35
|
# 1. if there is an attribute assignment node with message xxx_id=,
|
36
36
|
# then remember the subject of attribute assignment node.
|
37
|
-
# 2. after assignment, if there is a call node with message
|
37
|
+
# 2. after assignment, if there is a call node with message "save" or "save!",
|
38
38
|
# and the subject of call node is one of the subject of attribute assignment node,
|
39
39
|
# then the attribute assignment should be replaced by using model association.
|
40
|
-
def
|
41
|
-
@
|
40
|
+
def start_def(node)
|
41
|
+
@assignments = {}
|
42
42
|
node.recursive_children do |child|
|
43
|
-
case child.
|
44
|
-
when :
|
43
|
+
case child.sexp_type
|
44
|
+
when :assign
|
45
45
|
attribute_assignment(child)
|
46
46
|
when :call
|
47
47
|
call_assignment(child)
|
48
48
|
else
|
49
49
|
end
|
50
50
|
end
|
51
|
-
@
|
51
|
+
@assignments = nil
|
52
52
|
end
|
53
53
|
|
54
54
|
private
|
55
|
-
# check an attribute assignment node, if its message is xxx_id,
|
56
|
-
#
|
57
|
-
# s(:attrasgn, s(:ivar, :@post), :user_id=,
|
58
|
-
# s(:arglist,
|
59
|
-
# s(:call, s(:call, nil, :current_user, s(:arglist)), :id, s(:arglist))
|
60
|
-
# )
|
61
|
-
# )
|
62
|
-
#
|
63
|
-
# then remember the subject of the attribute assignment in @attrasgns.
|
64
|
-
#
|
65
|
-
# @attrasgns => { s(:ivar, :@post) => true }
|
55
|
+
# check an attribute assignment node, if its message is xxx_id,
|
56
|
+
# then remember the subject of the attribute assignment in @assignments.
|
66
57
|
def attribute_assignment(node)
|
67
|
-
if node.message.to_s =~ /_id
|
68
|
-
subject = node.subject
|
69
|
-
@
|
58
|
+
if node.left_value.message.to_s =~ /_id$/
|
59
|
+
subject = node.left_value.subject.to_s
|
60
|
+
@assignments[subject] = true
|
70
61
|
end
|
71
62
|
end
|
72
63
|
|
73
|
-
# check a call node with message
|
74
|
-
# if the subject of call node exists in @
|
75
|
-
#
|
76
|
-
# s(:call, s(:ivar, :@post), :save, s(:arglist))
|
77
|
-
#
|
64
|
+
# check a call node with message "save" or "save!",
|
65
|
+
# if the subject of call node exists in @assignments,
|
78
66
|
# then the attribute assignment should be replaced by using model association.
|
79
67
|
def call_assignment(node)
|
80
|
-
if [
|
81
|
-
subject = node.subject
|
82
|
-
add_error "use model association (for #{subject})" if @
|
68
|
+
if ["save", "save!"].include? node.message.to_s
|
69
|
+
subject = node.subject.to_s
|
70
|
+
add_error "use model association (for #{subject})" if @assignments[subject]
|
83
71
|
end
|
84
72
|
end
|
85
73
|
end
|
data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb
CHANGED
@@ -19,7 +19,7 @@ module RailsBestPractices
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def interesting_nodes
|
22
|
-
[:class, :
|
22
|
+
[:class, :def]
|
23
23
|
end
|
24
24
|
|
25
25
|
def interesting_files
|
@@ -28,12 +28,12 @@ module RailsBestPractices
|
|
28
28
|
|
29
29
|
# check class node to remember the ActionMailer class name.
|
30
30
|
def start_class(node)
|
31
|
-
@klazz_name = node.class_name
|
31
|
+
@klazz_name = node.class_name.to_s
|
32
32
|
end
|
33
33
|
|
34
|
-
# check
|
35
|
-
def
|
36
|
-
name = node.method_name
|
34
|
+
# check def node and find if the corresponding views exist or not?
|
35
|
+
def start_def(node)
|
36
|
+
name = node.method_name.to_s
|
37
37
|
return unless deliver_method?(name)
|
38
38
|
if rails2_canonical_mailer_views?(name) || rails3_canonical_mailer_views?(name)
|
39
39
|
add_error("use multipart/alternative as content_type of email")
|
@@ -12,7 +12,7 @@ module RailsBestPractices
|
|
12
12
|
# Implementation:
|
13
13
|
#
|
14
14
|
# Review process:
|
15
|
-
# check all
|
15
|
+
# check all command nodes to see if they are callback definitions, like after_create, before_destroy,
|
16
16
|
# if so, remember the callback methods.
|
17
17
|
#
|
18
18
|
# check all method define nodes to see
|
@@ -25,7 +25,7 @@ module RailsBestPractices
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def interesting_nodes
|
28
|
-
[:
|
28
|
+
[:def, :command]
|
29
29
|
end
|
30
30
|
|
31
31
|
def interesting_files
|
@@ -37,15 +37,11 @@ module RailsBestPractices
|
|
37
37
|
@callbacks = []
|
38
38
|
end
|
39
39
|
|
40
|
-
# check a
|
40
|
+
# check a command node.
|
41
41
|
#
|
42
|
-
# if it is a callback definition,
|
43
|
-
#
|
44
|
-
|
45
|
-
# before_destroy :send_destroy_notification
|
46
|
-
#
|
47
|
-
# then remember its callback methods (:send_create_notification).
|
48
|
-
def start_call(node)
|
42
|
+
# if it is a callback definition,
|
43
|
+
# then remember its callback methods.
|
44
|
+
def start_command(node)
|
49
45
|
remember_callback(node)
|
50
46
|
end
|
51
47
|
|
@@ -54,67 +50,53 @@ module RailsBestPractices
|
|
54
50
|
# if it is callback method,
|
55
51
|
# and there is a actionmailer deliver call in the method define node,
|
56
52
|
# then it should be replaced by using observer.
|
57
|
-
def
|
58
|
-
if callback_method?(node)
|
53
|
+
def start_def(node)
|
54
|
+
if callback_method?(node) && deliver_mailer?(node)
|
59
55
|
add_error "use observer"
|
60
56
|
end
|
61
57
|
end
|
62
58
|
|
63
59
|
private
|
64
|
-
# check a
|
65
|
-
#
|
66
|
-
# s(:call, nil, :after_create,
|
67
|
-
# s(:arglist, s(:lit, :send_create_notification))
|
68
|
-
# )
|
69
|
-
#
|
60
|
+
# check a command node, if it is a callback definition, such as after_create, before_create,
|
70
61
|
# then save the callback methods in @callbacks
|
71
|
-
#
|
72
|
-
# @callbacks => [:send_create_notification]
|
73
62
|
def remember_callback(node)
|
74
63
|
if node.message.to_s =~ /^after_|^before_/
|
75
|
-
node.arguments
|
64
|
+
node.arguments.all.each do |argument|
|
76
65
|
# ignore callback like after_create Comment.new
|
77
|
-
@callbacks << argument.to_s if :
|
66
|
+
@callbacks << argument.to_s if :symbol_literal == argument.sexp_type
|
78
67
|
end
|
79
68
|
end
|
80
69
|
end
|
81
70
|
|
82
71
|
# check a defn node to see if the method name exists in the @callbacks.
|
83
72
|
def callback_method?(node)
|
84
|
-
@callbacks.find { |callback|
|
73
|
+
@callbacks.find { |callback| callback == node.method_name.to_s }
|
85
74
|
end
|
86
75
|
|
87
|
-
# check a
|
76
|
+
# check a def node to see if it contains a actionmailer deliver call.
|
88
77
|
#
|
89
78
|
# for rails2
|
90
79
|
#
|
91
80
|
# if the message of call node is deliver_xxx,
|
92
|
-
# and the subject of the call node exists in @callbacks,
|
93
|
-
#
|
94
|
-
# s(:call, s(:const, :ProjectMailer), :deliver_notification,
|
95
|
-
# s(:arglist, s(:self), s(:lvar, :member))
|
96
|
-
# )
|
81
|
+
# and the subject of the call node exists in @callbacks,
|
97
82
|
#
|
98
83
|
# for rails3
|
99
84
|
#
|
100
85
|
# if the message of call node is deliver,
|
101
|
-
# and the subject of the call node is with subject node who exists in @callbacks,
|
102
|
-
#
|
103
|
-
# s(:call,
|
104
|
-
# s(:call, s(:const, :ProjectMailer), :notification,
|
105
|
-
# s(:arglist, s(:self), s(:lvar, :member))
|
106
|
-
# ),
|
107
|
-
# :deliver,
|
108
|
-
# s(:arglist)
|
109
|
-
# )
|
86
|
+
# and the subject of the call node is with subject node who exists in @callbacks,
|
110
87
|
#
|
111
88
|
# then the call node is actionmailer deliver call.
|
112
89
|
def deliver_mailer?(node)
|
113
|
-
node.grep_nodes(:
|
90
|
+
node.grep_nodes(:sexp_type => :call) do |child_node|
|
114
91
|
# rails2 actionmailer deliver
|
115
92
|
return true if child_node.message.to_s =~ /^deliver_/ && mailers.include?(child_node.subject.to_s)
|
116
93
|
# rails3 actionmailer deliver
|
117
|
-
|
94
|
+
if "deliver" == child_node.message.to_s
|
95
|
+
if :method_add_arg == child_node.subject.sexp_type &&
|
96
|
+
mailers.include?(child_node.subject[1].subject.to_s)
|
97
|
+
return true
|
98
|
+
end
|
99
|
+
end
|
118
100
|
end
|
119
101
|
false
|
120
102
|
end
|