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