rails_best_practices 0.5.6 → 0.6.1
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 +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
|