rails_best_practices 0.5.6 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +161 -0
- data/lib/rails_best_practices.rb +158 -20
- data/lib/rails_best_practices/checks/add_model_virtual_attribute_check.rb +108 -34
- data/lib/rails_best_practices/checks/always_add_db_index_check.rb +148 -29
- data/lib/rails_best_practices/checks/check.rb +178 -75
- data/lib/rails_best_practices/checks/dry_bundler_in_capistrano_check.rb +26 -5
- data/lib/rails_best_practices/checks/isolate_seed_data_check.rb +66 -15
- data/lib/rails_best_practices/checks/keep_finders_on_their_own_model_check.rb +53 -12
- data/lib/rails_best_practices/checks/law_of_demeter_check.rb +59 -30
- data/lib/rails_best_practices/checks/move_code_into_controller_check.rb +35 -15
- data/lib/rails_best_practices/checks/move_code_into_helper_check.rb +56 -12
- data/lib/rails_best_practices/checks/move_code_into_model_check.rb +30 -32
- data/lib/rails_best_practices/checks/move_finder_to_named_scope_check.rb +45 -15
- data/lib/rails_best_practices/checks/move_model_logic_into_model_check.rb +31 -27
- data/lib/rails_best_practices/checks/needless_deep_nesting_check.rb +99 -38
- data/lib/rails_best_practices/checks/not_use_default_route_check.rb +43 -12
- data/lib/rails_best_practices/checks/overuse_route_customizations_check.rb +140 -28
- data/lib/rails_best_practices/checks/replace_complex_creation_with_factory_method_check.rb +44 -30
- data/lib/rails_best_practices/checks/replace_instance_variable_with_local_variable_check.rb +18 -7
- data/lib/rails_best_practices/checks/use_before_filter_check.rb +88 -18
- data/lib/rails_best_practices/checks/use_model_association_check.rb +61 -22
- data/lib/rails_best_practices/checks/use_observer_check.rb +125 -23
- data/lib/rails_best_practices/checks/use_query_attribute_check.rb +75 -47
- data/lib/rails_best_practices/checks/use_say_with_time_in_migrations_check.rb +59 -10
- data/lib/rails_best_practices/checks/use_scope_access_check.rb +78 -23
- data/lib/rails_best_practices/command.rb +19 -34
- data/lib/rails_best_practices/core.rb +4 -2
- data/lib/rails_best_practices/core/checking_visitor.rb +49 -19
- data/lib/rails_best_practices/core/error.rb +5 -2
- data/lib/rails_best_practices/core/runner.rb +79 -55
- data/lib/rails_best_practices/core/visitable_sexp.rb +325 -55
- data/lib/rails_best_practices/{core/core_ext.rb → core_ext/enumerable.rb} +3 -6
- data/lib/rails_best_practices/core_ext/nil_class.rb +8 -0
- data/lib/rails_best_practices/version.rb +1 -1
- data/rails_best_practices.yml +2 -2
- metadata +8 -7
- data/README.textile +0 -150
@@ -3,19 +3,42 @@ require 'rails_best_practices/checks/check'
|
|
3
3
|
|
4
4
|
module RailsBestPractices
|
5
5
|
module Checks
|
6
|
-
# Check a
|
6
|
+
# Check a controller file to make sure to use model association instead of foreign key id assignment.
|
7
7
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
8
|
+
# See the best practice details here http://rails-bestpractices.com/posts/2-use-model-association.
|
9
|
+
#
|
10
|
+
# Implementation:
|
11
|
+
#
|
12
|
+
# Prepare process:
|
13
|
+
# none
|
14
|
+
#
|
15
|
+
# Review process:
|
16
|
+
# check model define nodes in all controller files,
|
17
|
+
# if there is an attribute assignment node with message xxx_id=,
|
18
|
+
# and after it, there is a call node with message :save or :save!,
|
19
|
+
# and the subjects of attribute assignment node and call node are the same,
|
20
|
+
# then model association should be used instead of xxx_id assignment.
|
11
21
|
class UseModelAssociationCheck < Check
|
12
|
-
|
13
|
-
def
|
22
|
+
|
23
|
+
def interesting_review_nodes
|
14
24
|
[:defn]
|
15
25
|
end
|
16
26
|
|
17
|
-
def
|
18
|
-
|
27
|
+
def interesting_review_files
|
28
|
+
CONTROLLER_FILES
|
29
|
+
end
|
30
|
+
|
31
|
+
# check method define nodes to see if there are some attribute assignments that can use model association instead in review process.
|
32
|
+
#
|
33
|
+
# it will check attribute assignment node with message xxx_id=, and call node with message :save or :save!
|
34
|
+
#
|
35
|
+
# 1. if there is an attribute assignment node with message xxx_id=,
|
36
|
+
# then remember the subject of attribute assignment node.
|
37
|
+
# 2. after assignment, if there is a call node with message :save or :save!,
|
38
|
+
# and the subject of call node is one of the subject of attribute assignment node,
|
39
|
+
# then the attribute assignment should be replaced by using model association.
|
40
|
+
def review_start_defn(node)
|
41
|
+
@attrasgns = {}
|
19
42
|
node.recursive_children do |child|
|
20
43
|
case child.node_type
|
21
44
|
when :attrasgn
|
@@ -25,24 +48,40 @@ module RailsBestPractices
|
|
25
48
|
else
|
26
49
|
end
|
27
50
|
end
|
28
|
-
@
|
51
|
+
@attrasgns = nil
|
29
52
|
end
|
30
|
-
|
53
|
+
|
31
54
|
private
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
55
|
+
# check an attribute assignment node, if its message is xxx_id, like
|
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 }
|
66
|
+
def attribute_assignment(node)
|
67
|
+
if node.message.to_s =~ /_id=$/
|
68
|
+
subject = node.subject
|
69
|
+
@attrasgns[subject] = true
|
70
|
+
end
|
37
71
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
72
|
+
|
73
|
+
# check a call node with message :save or :save!,
|
74
|
+
# if the subject of call node exists in @attrasgns, like
|
75
|
+
#
|
76
|
+
# s(:call, s(:ivar, :@post), :save, s(:arglist))
|
77
|
+
#
|
78
|
+
# then the attribute assignment should be replaced by using model association.
|
79
|
+
def call_assignment(node)
|
80
|
+
if [:save, :save!].include? node.message
|
81
|
+
subject = node.subject
|
82
|
+
add_error "use model association (for #{subject})" if @attrasgns[subject]
|
83
|
+
end
|
44
84
|
end
|
45
|
-
end
|
46
85
|
end
|
47
86
|
end
|
48
87
|
end
|
@@ -3,51 +3,153 @@ require 'rails_best_practices/checks/check'
|
|
3
3
|
|
4
4
|
module RailsBestPractices
|
5
5
|
module Checks
|
6
|
-
#
|
6
|
+
# Make sure to use observer (sorry we only check the mailer deliver now).
|
7
7
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
8
|
+
# See the best practice details here http://rails-bestpractices.com/posts/19-use-observer.
|
9
|
+
#
|
10
|
+
# TODO: we need a better solution, any suggestion?
|
11
|
+
#
|
12
|
+
# Implementation:
|
13
|
+
#
|
14
|
+
# Prepare process:
|
15
|
+
# check all class nodes to see if they are the subclass of ActionMailer::Base,
|
16
|
+
# if so, remember the class name.
|
17
|
+
#
|
18
|
+
# Review process:
|
19
|
+
# check all call nodes to see if they are callback definitions, like after_create, before_destroy,
|
20
|
+
# if so, remember the callback methods.
|
21
|
+
#
|
22
|
+
# check all method define nodes to see
|
23
|
+
# if the method is a callback method,
|
24
|
+
# and there is a mailer deliver call,
|
25
|
+
# then the method should be replaced by using observer.
|
11
26
|
class UseObserverCheck < Check
|
12
27
|
|
13
|
-
def
|
28
|
+
def interesting_prepare_nodes
|
29
|
+
[:class]
|
30
|
+
end
|
31
|
+
|
32
|
+
def interesting_review_nodes
|
14
33
|
[:defn, :call]
|
15
34
|
end
|
16
35
|
|
17
|
-
def
|
18
|
-
|
36
|
+
def interesting_prepare_files
|
37
|
+
/#{MAILER_FILES}|#{MODEL_FILES}/
|
38
|
+
end
|
39
|
+
|
40
|
+
def interesting_review_files
|
41
|
+
MODEL_FILES
|
19
42
|
end
|
20
43
|
|
21
44
|
def initialize
|
22
45
|
super
|
23
46
|
@callbacks = []
|
47
|
+
@mailer_names = []
|
48
|
+
end
|
49
|
+
|
50
|
+
# check class node in prepare process.
|
51
|
+
#
|
52
|
+
# if it is a subclass of ActionMailer::Base,
|
53
|
+
# then remember its class name.
|
54
|
+
def prepare_start_class(node)
|
55
|
+
remember_mailer_names(node)
|
56
|
+
end
|
57
|
+
|
58
|
+
# check a call node in review process.
|
59
|
+
#
|
60
|
+
# if it is a callback definition, like
|
61
|
+
#
|
62
|
+
# after_create :send_create_notification
|
63
|
+
# before_destroy :send_destroy_notification
|
64
|
+
#
|
65
|
+
# then remember its callback methods (:send_create_notification).
|
66
|
+
def review_start_call(node)
|
67
|
+
remember_callback(node)
|
24
68
|
end
|
25
69
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
70
|
+
# check a method define node in prepare process.
|
71
|
+
#
|
72
|
+
# if it is callback method,
|
73
|
+
# and there is a actionmailer deliver call in the method define node,
|
74
|
+
# then it should be replaced by using observer.
|
75
|
+
def review_start_defn(node)
|
76
|
+
if callback_method?(node) and deliver_mailer?(node)
|
77
|
+
add_error "use observer"
|
31
78
|
end
|
32
79
|
end
|
33
80
|
|
34
81
|
private
|
82
|
+
# check a class node, if its base class is ActionMailer::Base, like
|
83
|
+
#
|
84
|
+
# s(:class, :ProjectMailer,
|
85
|
+
# s(:colon2, s(:const, :ActionMailer), :Base),
|
86
|
+
# s(:scope)
|
87
|
+
# )
|
88
|
+
#
|
89
|
+
# then save the class name in @mailer_names
|
90
|
+
def remember_mailer_names(node)
|
91
|
+
if s(:colon2, s(:const, :ActionMailer), :Base) == node.base_class
|
92
|
+
@mailer_names << node.class_name.to_s
|
93
|
+
end
|
94
|
+
end
|
35
95
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
96
|
+
# check a call node, if it is a callback definition, such as after_create, before_create, like
|
97
|
+
#
|
98
|
+
# s(:call, nil, :after_create,
|
99
|
+
# s(:arglist, s(:lit, :send_create_notification))
|
100
|
+
# )
|
101
|
+
#
|
102
|
+
# then save the callback methods in @callbacks
|
103
|
+
#
|
104
|
+
# @callbacks => [:send_create_notification]
|
105
|
+
def remember_callback(node)
|
106
|
+
if node.message.to_s =~ /^after_|^before_/
|
107
|
+
node.arguments[1..-1].each do |argument|
|
108
|
+
# ignore callback like after_create Comment.new
|
109
|
+
@callbacks << argument.to_s if :lit == argument.node_type
|
110
|
+
end
|
41
111
|
end
|
42
112
|
end
|
43
|
-
end
|
44
113
|
|
45
|
-
|
46
|
-
node
|
47
|
-
|
114
|
+
# check a defn node to see if the method name exists in the @callbacks.
|
115
|
+
def callback_method?(node)
|
116
|
+
@callbacks.find { |callback| equal?(callback, node.method_name) }
|
117
|
+
end
|
118
|
+
|
119
|
+
# check a defn node to see if it contains a actionmailer deliver call.
|
120
|
+
#
|
121
|
+
# for rails2
|
122
|
+
#
|
123
|
+
# if the message of call node is deliver_xxx,
|
124
|
+
# and the subject of the call node exists in @callbacks, like
|
125
|
+
#
|
126
|
+
# s(:call, s(:const, :ProjectMailer), :deliver_notification,
|
127
|
+
# s(:arglist, s(:self), s(:lvar, :member))
|
128
|
+
# )
|
129
|
+
#
|
130
|
+
# for rails3
|
131
|
+
#
|
132
|
+
# if the message of call node is deliver,
|
133
|
+
# and the subject of the call node is with subject node who exists in @callbacks, like
|
134
|
+
#
|
135
|
+
# s(:call,
|
136
|
+
# s(:call, s(:const, :ProjectMailer), :notification,
|
137
|
+
# s(:arglist, s(:self), s(:lvar, :member))
|
138
|
+
# ),
|
139
|
+
# :deliver,
|
140
|
+
# s(:arglist)
|
141
|
+
# )
|
142
|
+
#
|
143
|
+
# then the call node is actionmailer deliver call.
|
144
|
+
def deliver_mailer?(node)
|
145
|
+
node.grep_nodes(:node_type => :call) do |child_node|
|
146
|
+
# rails2 actionmailer deliver
|
147
|
+
return true if child_node.message.to_s =~ /^deliver_/ && @mailer_names.include?(child_node.subject.to_s)
|
148
|
+
# rails3 actionmailer deliver
|
149
|
+
return true if :deliver == child_node.message && @mailer_names.include?(child_node.subject.subject.to_s)
|
150
|
+
end
|
151
|
+
false
|
48
152
|
end
|
49
|
-
false
|
50
|
-
end
|
51
153
|
end
|
52
154
|
end
|
53
155
|
end
|
@@ -3,43 +3,46 @@ require 'rails_best_practices/checks/check'
|
|
3
3
|
|
4
4
|
module RailsBestPractices
|
5
5
|
module Checks
|
6
|
-
#
|
6
|
+
# Make sure to use query attribute instead of nil?, blank? and present?.
|
7
|
+
#
|
8
|
+
# See the best practice details here http://rails-bestpractices.com/posts/56-use-query-attribute.
|
7
9
|
#
|
8
10
|
# Implementation:
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
11
|
+
#
|
12
|
+
# Prepare process:
|
13
|
+
# only check all model files to save model names and association names,
|
14
|
+
# model names are saved as only when subject of call method is equal to one of the model name, then the call method may use query attribute instead,
|
15
|
+
# association names are saved as association attributes should not be detected as query attributes.
|
16
|
+
#
|
17
|
+
# Review process:
|
18
|
+
# check all method calls within conditional statements, like @user.login.nil?
|
19
|
+
# if their subjects are one of the model names
|
20
|
+
# and their messages of first call are not pluralize and not in any of the association names
|
21
|
+
# and their messages of second call are one of nil?, blank?, present?, or they are == ""
|
22
|
+
# then you can use query attribute instead.
|
15
23
|
class UseQueryAttributeCheck < Check
|
16
24
|
|
17
25
|
QUERY_METHODS = [:nil?, :blank?, :present?]
|
18
|
-
ASSOCIATION_METHODS = [:belongs_to, :has_one, :has_many, :has_and_belongs_to_many]
|
19
26
|
|
20
|
-
|
21
|
-
[:if, :class, :call]
|
22
|
-
end
|
23
|
-
|
24
|
-
def interesting_prepare_files
|
25
|
-
MODLE_FILES
|
26
|
-
end
|
27
|
+
prepare_model_associations
|
27
28
|
|
28
|
-
def
|
29
|
-
|
30
|
-
@klazzes = []
|
31
|
-
@associations = {}
|
29
|
+
def interesting_review_nodes
|
30
|
+
[:if]
|
32
31
|
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
33
|
+
# check if node to see whose conditional statement nodes contain nodes that can use query attribute instead in review process.
|
34
|
+
#
|
35
|
+
# it will check every call nodes in the if nodes. If the call node is
|
36
|
+
#
|
37
|
+
# 1. two method calls, like @user.login.nil?
|
38
|
+
# 2. the subject is one of the model names
|
39
|
+
# 3. the message of first call is the model's attribute,
|
40
|
+
# the message is not in any of associations name and is not pluralize
|
41
|
+
# 4. the message of second call is one of nil?, blank? or present? or
|
42
|
+
# the message is == and the argument is ""
|
43
|
+
#
|
44
|
+
# then the call node can use query attribute instead.
|
45
|
+
def review_start_if(node)
|
43
46
|
if node = query_attribute_node(node.conditional_statement)
|
44
47
|
subject_node = node.subject
|
45
48
|
add_error "use query attribute (#{subject_node.subject}.#{subject_node.message}?)", node.file, node.line
|
@@ -47,17 +50,7 @@ module RailsBestPractices
|
|
47
50
|
end
|
48
51
|
|
49
52
|
private
|
50
|
-
|
51
|
-
if class_node.file =~ MODLE_FILES
|
52
|
-
@klazzes << class_node.subject
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def remember_association(association_node)
|
57
|
-
@associations[@klazzes.last] ||= []
|
58
|
-
@associations[@klazzes.last] << association_node.arguments[1].to_s
|
59
|
-
end
|
60
|
-
|
53
|
+
# recursively check conditional statement nodes to see if there is a call node that may be possible query attribute.
|
61
54
|
def query_attribute_node(conditional_statement_node)
|
62
55
|
case conditional_statement_node.node_type
|
63
56
|
when :and, :or
|
@@ -65,25 +58,60 @@ module RailsBestPractices
|
|
65
58
|
when :not
|
66
59
|
return query_attribute_node(conditional_statement_node[1])
|
67
60
|
when :call
|
68
|
-
return conditional_statement_node if
|
61
|
+
return conditional_statement_node if possible_query_attribute?(conditional_statement_node)
|
69
62
|
end
|
70
63
|
nil
|
71
64
|
end
|
72
65
|
|
73
|
-
|
66
|
+
# check if the node may use query attribute instead.
|
67
|
+
#
|
68
|
+
# if the node contains two method calls, e.g. @user.login.nil?
|
69
|
+
#
|
70
|
+
# for the first call, the subject should be one of the class names and
|
71
|
+
# the message should not be one of the association name and the message should not be pluralize.
|
72
|
+
#
|
73
|
+
# for the second call, the message should be one of nil?, blank? or present? or
|
74
|
+
# it is compared with an empty string.
|
75
|
+
#
|
76
|
+
# the node that may use query attribute is like
|
77
|
+
#
|
78
|
+
# s(:call, s(:call, s(:ivar, :@user), :login, s(:arglist)), :nil?, s(:arglist))
|
79
|
+
#
|
80
|
+
#
|
81
|
+
def possible_query_attribute?(node)
|
74
82
|
return false unless :call == node.subject.node_type
|
75
83
|
subject = node.subject.subject
|
76
84
|
message = node.subject.message
|
77
|
-
subject_ruby = subject.to_s
|
78
85
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
86
|
+
[:arglist] == node.subject.arguments && class_attribute?(subject, message) && !pluralize?(message.to_s) &&
|
87
|
+
(QUERY_METHODS.include?(node.message) || compare_with_empty_string?(node))
|
88
|
+
end
|
89
|
+
|
90
|
+
# check if the subject and message is one of the model's attribute.
|
91
|
+
# the subject should match one of the class model name, and the message should not match any of association name.
|
92
|
+
#
|
93
|
+
# subject, subject of call node, like
|
94
|
+
# s(:ivar, @user)
|
95
|
+
#
|
96
|
+
# message, message of call node, like
|
97
|
+
# :login
|
98
|
+
def class_attribute?(subject, message)
|
99
|
+
@klazzes.find do |klazz|
|
100
|
+
subject.to_s =~ %r|#{klazz.to_s.underscore}| && !@associations[klazz].find { |association| equal?(association, message) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# check if the str is a pluralize string.
|
106
|
+
def pluralize?(str)
|
107
|
+
str.pluralize == str
|
83
108
|
end
|
84
109
|
|
110
|
+
# check if the node is with node type :call, node message :== and node arguments {:arglist, (:str, "")}
|
111
|
+
#
|
112
|
+
# @user.login == "" => true
|
85
113
|
def compare_with_empty_string?(node)
|
86
|
-
:== == node.message
|
114
|
+
:call == node.node_type && :== == node.message && [:arglist, [:str, ""]] == node.arguments
|
87
115
|
end
|
88
116
|
end
|
89
117
|
end
|
@@ -3,26 +3,75 @@ require 'rails_best_practices/checks/check'
|
|
3
3
|
|
4
4
|
module RailsBestPractices
|
5
5
|
module Checks
|
6
|
-
# Check a migration file to make sure to use say_with_time for customized data changes to produce a more readable output.
|
6
|
+
# Check a migration file to make sure to use say or say_with_time for customized data changes to produce a more readable output.
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# See the best practice detials here http://rails-bestpractices.com/posts/46-use-say-and-say_with_time-in-migrations-to-make-a-useful-migration-log.
|
9
|
+
#
|
10
|
+
# Implementation:
|
11
|
+
#
|
12
|
+
# Prepare process:
|
13
|
+
# none
|
14
|
+
#
|
15
|
+
# Review process:
|
16
|
+
# check class method define nodes (self.up or self.down).
|
17
|
+
# if there is a method call in the class method definition,
|
18
|
+
# and the message of method call is not say, say_with_time and default migration methods (such as add_column and create_table),
|
19
|
+
# then the method call should be wrapped by say or say_with_time.
|
9
20
|
class UseSayWithTimeInMigrationsCheck < Check
|
10
21
|
|
11
|
-
|
22
|
+
DEFAULT_MIGRATION_METHODS = [:add_column, :add_index, :add_timestamps, :change_column, :change_column_default, :change_table, :create_table, :drop_table, :remove_column, :remove_index, :remove_timestamps, :rename_column, :rename_index, :rename_table]
|
23
|
+
WITH_SAY_METHODS = DEFAULT_MIGRATION_METHODS + [:say, :say_with_time]
|
24
|
+
|
12
25
|
|
13
|
-
def
|
26
|
+
def interesting_review_nodes
|
14
27
|
[:defs]
|
15
28
|
end
|
16
29
|
|
17
|
-
def
|
30
|
+
def interesting_review_files
|
18
31
|
MIGRATION_FILES
|
19
32
|
end
|
20
33
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
34
|
+
# check a class method define node to see if there are method calls that need to be wrapped by :say or :say_with_time in review process.
|
35
|
+
#
|
36
|
+
# it will check the first block node,
|
37
|
+
# if any method call whose message is not default migration methods in the block node, like
|
38
|
+
#
|
39
|
+
# s(:defs, s(:self), :up, s(:args),
|
40
|
+
# s(:scope,
|
41
|
+
# s(:block,
|
42
|
+
# s(:iter,
|
43
|
+
# s(:call, s(:const, :User), :find_each, s(:arglist)),
|
44
|
+
# s(:lasgn, :user),
|
45
|
+
# s(:block,
|
46
|
+
# s(:masgn,
|
47
|
+
# s(:array,
|
48
|
+
# s(:attrasgn, s(:lvar, :user), :first_name=, s(:arglist)),
|
49
|
+
# s(:attrasgn, s(:lvar, :user), :last_name=, s(:arglist))
|
50
|
+
# ),
|
51
|
+
# s(:to_ary,
|
52
|
+
# s(:call,
|
53
|
+
# s(:call, s(:lvar, :user), :full_name, s(:arglist)),
|
54
|
+
# :split,
|
55
|
+
# s(:arglist, s(:str, " "))
|
56
|
+
# )
|
57
|
+
# )
|
58
|
+
# ),
|
59
|
+
# s(:call, s(:lvar, :user), :save, s(:arglist))
|
60
|
+
# )
|
61
|
+
# )
|
62
|
+
# )
|
63
|
+
# )
|
64
|
+
# )
|
65
|
+
#
|
66
|
+
# then such method call should be wrapped by say or say_with_time
|
67
|
+
def review_start_defs(node)
|
68
|
+
block_node = node.grep_node(:node_type => :block)
|
69
|
+
block_node.children.each do |child_node|
|
70
|
+
if :iter == child_node.node_type
|
71
|
+
subject_node = child_node.subject
|
72
|
+
if :call == subject_node.node_type && !WITH_SAY_METHODS.include?(subject_node.message)
|
73
|
+
add_error("use say with time in migrations", subject_node.file, subject_node.line)
|
74
|
+
end
|
26
75
|
end
|
27
76
|
end
|
28
77
|
end
|