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
@@ -1,150 +1,420 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require 'rubygems'
|
3
2
|
require 'sexp'
|
4
3
|
|
5
4
|
class Sexp
|
5
|
+
# prepare current node.
|
6
|
+
#
|
7
|
+
# @param [RailsBestPractices::Core::CheckingVisitor] visitor the visitor to prepare current node
|
6
8
|
def prepare(visitor)
|
7
9
|
visitor.prepare(self)
|
8
10
|
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
first
|
12
|
+
# prepare current node.
|
13
|
+
#
|
14
|
+
# @param [RailsBestPractices::Core::CheckingVisitor] visitor the visitor to review current node
|
15
|
+
def review(visitor)
|
16
|
+
visitor.review(self)
|
16
17
|
end
|
17
18
|
|
19
|
+
# return child nodes of a sexp node.
|
20
|
+
#
|
21
|
+
# s(:call, nil, :puts,
|
22
|
+
# s(:arglist, s(:str, "hello "), s(:str, "world"))
|
23
|
+
# )
|
24
|
+
# => [s(:arglist, s(:str, "hello "), s(:str, "world"))]
|
25
|
+
#
|
26
|
+
# @return [Array] child nodes.
|
18
27
|
def children
|
19
28
|
find_all { | sexp | Sexp === sexp }
|
20
29
|
end
|
21
30
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
parent = is_language_node? ? sexp_body : self
|
28
|
-
parent.children
|
29
|
-
end
|
30
|
-
|
31
|
-
def recursive_children(&handler)
|
32
|
-
visitable_children.each do |child|
|
33
|
-
handler.call child
|
34
|
-
child.recursive_children(&handler)
|
31
|
+
# recursively find all child nodes, and yeild each child node.
|
32
|
+
def recursive_children
|
33
|
+
children.each do |child|
|
34
|
+
yield child
|
35
|
+
child.recursive_children { |c| yield c }
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
39
|
+
# grep all the recursive child nodes with conditions, and yield each match node.
|
40
|
+
#
|
41
|
+
# @param [Hash] options grep conditions
|
42
|
+
#
|
43
|
+
# options is the grep conditions, like
|
44
|
+
#
|
45
|
+
# :node_type => :call,
|
46
|
+
# :subject => s(:const, Post),
|
47
|
+
# :message => [:find, :new],
|
48
|
+
# :arguments => s(:arglist)
|
49
|
+
#
|
50
|
+
# the condition key is one of :node_type, :subject, :message or :arguments,
|
51
|
+
# the condition value can be Symbol, Array or Sexp.
|
38
52
|
def grep_nodes(options)
|
39
|
-
return self if options.empty?
|
40
53
|
node_type = options[:node_type]
|
41
54
|
subject = options[:subject]
|
42
55
|
message = options[:message]
|
43
56
|
arguments = options[:arguments]
|
44
|
-
nodes = []
|
45
57
|
self.recursive_children do |child|
|
46
|
-
if (!node_type
|
47
|
-
|
58
|
+
if (!node_type || (node_type.is_a?(Array) ? node_type.include?(child.node_type) : node_type == child.node_type)) &&
|
59
|
+
(!subject || (subject.is_a?(Array) ? subject.include?(child.subject) : subject == child.subject)) &&
|
60
|
+
(!message || (message.is_a?(Array) ? message.include?(child.message) : message == child.message)) &&
|
61
|
+
(!arguments || (arguments.is_?(Array) ? arguments.include?(child.arguments) : arguments == child.arguments))
|
62
|
+
yield child
|
48
63
|
end
|
49
64
|
end
|
50
|
-
nodes
|
51
65
|
end
|
52
66
|
|
67
|
+
# grep all the recursive child nodes with conditions, and yield the first match node.
|
68
|
+
#
|
69
|
+
# @param [Hash] options grep conditions
|
70
|
+
#
|
71
|
+
# options is the grep conditions, like
|
72
|
+
#
|
73
|
+
# :node_type => :call,
|
74
|
+
# :subject => s(:const, Post),
|
75
|
+
# :message => [:find, :new],
|
76
|
+
# :arguments => s(:arglist)
|
77
|
+
#
|
78
|
+
# the condition key is one of :node_type, :subject, :message or :arguments,
|
79
|
+
# the condition value can be Symbol, Array or Sexp.
|
80
|
+
def grep_node(options)
|
81
|
+
grep_nodes(options) { |node| return node }
|
82
|
+
end
|
83
|
+
|
84
|
+
# grep all the recursive child nodes with conditions, and get the count of match nodes.
|
85
|
+
#
|
86
|
+
# @param [Hash] options grep conditions
|
87
|
+
# @return [Integer] the count of metch nodes
|
88
|
+
def grep_nodes_count(options)
|
89
|
+
count = 0
|
90
|
+
grep_nodes(options) { |node| count += 1 }
|
91
|
+
count
|
92
|
+
end
|
93
|
+
|
94
|
+
# Get subject of attrasgan, call and iter node.
|
95
|
+
#
|
96
|
+
# s(:attrasgn,
|
97
|
+
# s(:call, nil, :user, s(:arglist)),
|
98
|
+
# :name=,
|
99
|
+
# s(:arglist,
|
100
|
+
# s(:call,
|
101
|
+
# s(:call, nil, :params, s(:arglist)),
|
102
|
+
# :[],
|
103
|
+
# s(:arglist, s(:lit, :name))
|
104
|
+
# )
|
105
|
+
# )
|
106
|
+
# )
|
107
|
+
# => s(:call, nil, :user, s(:arglist))
|
108
|
+
#
|
109
|
+
# s(:call,
|
110
|
+
# s(:call, nil, :user, s(:arglist)),
|
111
|
+
# :name,
|
112
|
+
# s(:arglist)
|
113
|
+
# )
|
114
|
+
# => s(:call, nil, :user, s(:arglist))
|
115
|
+
#
|
116
|
+
# s(:iter,
|
117
|
+
# s(:call, s(:ivar, :@users), :each, s(:arglist)),
|
118
|
+
# s(:lasgn, :user),
|
119
|
+
# s(:call, nil, :p,
|
120
|
+
# s(:arglist, s(:lvar, :user))
|
121
|
+
# )
|
122
|
+
# )
|
123
|
+
# => s(:call, :s(:ivar, ;@users), :each, s(:arglist))
|
124
|
+
#
|
125
|
+
# @return [Sexp] subject of attrasgn, call or iter node
|
53
126
|
def subject
|
54
|
-
if [:attrasgn, :call, :
|
127
|
+
if [:attrasgn, :call, :iter].include? node_type
|
55
128
|
self[1]
|
56
129
|
end
|
57
130
|
end
|
58
131
|
|
132
|
+
# Get the class name of the class node.
|
133
|
+
#
|
134
|
+
# s(:class, :User, nil, s(:scope))
|
135
|
+
# => :User
|
136
|
+
#
|
137
|
+
# @return [Symbol] class name of class node
|
138
|
+
def class_name
|
139
|
+
if :class == node_type
|
140
|
+
self[1]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Get the base class of the class node.
|
145
|
+
#
|
146
|
+
# s(:class, :User, s(:colon2, s(:const, :ActiveRecord), :Base), s(:scope))
|
147
|
+
# => s(:colon2, s(:const, :ActiveRecord), :Base)
|
148
|
+
#
|
149
|
+
# @return [Sexp] base class of class node
|
150
|
+
def base_class
|
151
|
+
if :class == node_type
|
152
|
+
self[2]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Get the left value of the lasgn or iasgn node.
|
157
|
+
#
|
158
|
+
# s(:lasgn,
|
159
|
+
# :user,
|
160
|
+
# s(:call,
|
161
|
+
# s(:call, nil, :params, s(:arglist)),
|
162
|
+
# :[],
|
163
|
+
# s(:arglist, s(:lit, :user))
|
164
|
+
# )
|
165
|
+
# )
|
166
|
+
# => :user
|
167
|
+
#
|
168
|
+
# s(:iasgn,
|
169
|
+
# :@user,
|
170
|
+
# s(:call,
|
171
|
+
# s(:call, nil, :params, s(:arglist)),
|
172
|
+
# :[],
|
173
|
+
# s(:arglist, s(:lit, :user))
|
174
|
+
# )
|
175
|
+
# )
|
176
|
+
# => :@user
|
177
|
+
#
|
178
|
+
# @return [Symbol] left value of lasgn or iasgn node
|
59
179
|
def left_value
|
60
180
|
if [:lasgn, :iasgn].include? node_type
|
61
181
|
self[1]
|
62
182
|
end
|
63
183
|
end
|
64
184
|
|
65
|
-
|
66
|
-
|
185
|
+
# Get the right value of lasgn and iasgn node.
|
186
|
+
#
|
187
|
+
# s(:lasgn,
|
188
|
+
# :user,
|
189
|
+
# s(:call, nil, :current_user, s(:arglist))
|
190
|
+
# )
|
191
|
+
# => s(:call, nil, :current_user, s(:arglist))
|
192
|
+
#
|
193
|
+
# s(:iasgn,
|
194
|
+
# :@user,
|
195
|
+
# s(:call, nil, :current_user, s(:arglist))
|
196
|
+
# )
|
197
|
+
# => s(:call, nil, :current_user, s(:arglist))
|
198
|
+
#
|
199
|
+
# @return [Sexp] right value of lasgn or iasgn node
|
200
|
+
def right_value
|
201
|
+
if [:lasgn, :iasgn].include? node_type
|
67
202
|
self[2]
|
68
203
|
end
|
69
204
|
end
|
70
205
|
|
71
|
-
|
206
|
+
# Get the message of attrasgn and call node.
|
207
|
+
#
|
208
|
+
# s(:attrasgn,
|
209
|
+
# s(:call, nil, :user, s(:arglist)),
|
210
|
+
# :name=,
|
211
|
+
# s(:arglist,
|
212
|
+
# s(:call,
|
213
|
+
# s(:call, nil, :params, s(:arglist)),
|
214
|
+
# :[],
|
215
|
+
# s(:arglist, s(:lit, :name))
|
216
|
+
# )
|
217
|
+
# )
|
218
|
+
# )
|
219
|
+
# => :name=
|
220
|
+
#
|
221
|
+
# s(:call, nil, :has_many, s(:arglist, s(:lit, :projects)))
|
222
|
+
# => :has_many
|
223
|
+
#
|
224
|
+
# @return [Symbol] message of attrasgn or call node
|
225
|
+
def message
|
72
226
|
if [:attrasgn, :call].include? node_type
|
73
|
-
self[
|
227
|
+
self[2]
|
74
228
|
end
|
75
229
|
end
|
76
230
|
|
77
|
-
|
78
|
-
|
79
|
-
|
231
|
+
# Get arguments of call node.
|
232
|
+
#
|
233
|
+
# s(:attrasgn,
|
234
|
+
# s(:call, nil, :post, s(:arglist)),
|
235
|
+
# :user=,
|
236
|
+
# s(:arglist,
|
237
|
+
# s(:call, nil, :current_user, s(:arglist))
|
238
|
+
# )
|
239
|
+
# )
|
240
|
+
# => s(:arglist, s(:call, nil, :current_user, s(:arglist)))
|
241
|
+
#
|
242
|
+
# s(:call,
|
243
|
+
# s(:call, nil, :username, s(:arglist)),
|
244
|
+
# :==,
|
245
|
+
# s(:arglist, s(:str, ""))
|
246
|
+
# )
|
247
|
+
# => s(:arglist, s(:str, ""))
|
248
|
+
#
|
249
|
+
# @return [Sexp] arguments of attrasgn or call node
|
250
|
+
def arguments
|
251
|
+
if [:attrasgn, :call].include? node_type
|
252
|
+
self[3]
|
80
253
|
end
|
81
254
|
end
|
82
255
|
|
256
|
+
# Get the conditional statement of if node.
|
257
|
+
#
|
258
|
+
# s(:if,
|
259
|
+
# s(:call,
|
260
|
+
# s(:call, nil, :current_user, s(:arglist)),
|
261
|
+
# :present?,
|
262
|
+
# s(:arglist)
|
263
|
+
# ),
|
264
|
+
# s(:call, nil, :puts,
|
265
|
+
# s(:arglist,
|
266
|
+
# s(:call,
|
267
|
+
# s(:call, nil, :current_user, s(:arglist)),
|
268
|
+
# :login,
|
269
|
+
# s(:arglist)
|
270
|
+
# )
|
271
|
+
# )
|
272
|
+
# ),
|
273
|
+
# nil
|
274
|
+
# )
|
275
|
+
# => s(:call, s(:call, nil, :current_user, s(:arglist)), :present?, s(:arglist))
|
276
|
+
#
|
277
|
+
# @return [Sexp] conditional statement of if node
|
83
278
|
def conditional_statement
|
84
|
-
if
|
279
|
+
if :if == node_type
|
85
280
|
self[1]
|
86
281
|
end
|
87
282
|
end
|
88
283
|
|
284
|
+
# Get the body node when conditional statement is true.
|
285
|
+
#
|
286
|
+
# s(:if,
|
287
|
+
# s(:call, s(:call, nil, :current_user, s(:arglist)), :login?, s(:arglist)),
|
288
|
+
# s(:call, s(:call, nil, :current_user, s(:arglist)), :login, s(:arglist)),
|
289
|
+
# s(:call, s(:call, nil, :current_user, s(:arglist)), :email, s(:arglist))
|
290
|
+
# )
|
291
|
+
# => s(:call, s(:call, nil, :current_user, s(:arglist)), :login, s(:arglist))
|
292
|
+
#
|
293
|
+
# @return [Sexp] the body node when conditional statement is true
|
89
294
|
def true_node
|
90
295
|
if :if == node_type
|
91
296
|
self[2]
|
92
297
|
end
|
93
298
|
end
|
94
299
|
|
300
|
+
# Get the body node when conditional statement is false.
|
301
|
+
#
|
302
|
+
# s(:if,
|
303
|
+
# s(:call, s(:call, nil, :current_user, s(:arglist)), :login?, s(:arglist)),
|
304
|
+
# s(:call, s(:call, nil, :current_user, s(:arglist)), :login, s(:arglist)),
|
305
|
+
# s(:call, s(:call, nil, :current_user, s(:arglist)), :email, s(:arglist))
|
306
|
+
# )
|
307
|
+
# => s(:call, s(:call, nil, :current_user, s(:arglist)), :email, s(:arglist))
|
308
|
+
#
|
309
|
+
# @return [Sexp] the body node when conditional statement is false
|
95
310
|
def false_node
|
96
311
|
if :if == node_type
|
97
312
|
self[3]
|
98
313
|
end
|
99
314
|
end
|
100
315
|
|
101
|
-
|
316
|
+
# Get the method name of defn node.
|
317
|
+
#
|
318
|
+
# s(:defn, :show, s(:args), s(:scope, s(:block, s(:nil))))
|
319
|
+
# => :show
|
320
|
+
#
|
321
|
+
# @return [Symbol] method name of defn node
|
322
|
+
def method_name
|
102
323
|
if :defn == node_type
|
103
324
|
self[1]
|
104
325
|
end
|
105
326
|
end
|
106
327
|
|
328
|
+
# Get body of iter, class and defn node.
|
329
|
+
#
|
330
|
+
# s(:iter,
|
331
|
+
# s(:call, nil, :resources, s(:arglist, s(:lit, :posts))),
|
332
|
+
# nil,
|
333
|
+
# s(:call, nil, :resources, s(:arglist, s(:lit, :comments)))
|
334
|
+
# )
|
335
|
+
# => s(:call, nil, :resources, s(:arglist, s(:lit, :comments)))
|
336
|
+
#
|
337
|
+
# s(:class, :User, nil,
|
338
|
+
# s(:scope,
|
339
|
+
# s(:block,
|
340
|
+
# s(:defn, :login, s(:args), s(:scope, s(:block, s(:nil)))),
|
341
|
+
# s(:defn, :email, s(:args), s(:scope, s(:block, s(:nil))))
|
342
|
+
# )
|
343
|
+
# )
|
344
|
+
# )
|
345
|
+
# => s(:block,
|
346
|
+
# s(:defn, :login, s(:args), s(:scope, s(:block, s(:nil)))),
|
347
|
+
# s(:defn, :email, s(:args), s(:scope, s(:block, s(:nil))))
|
348
|
+
# )
|
349
|
+
#
|
350
|
+
# s(:defn, :fullname, s(:args),
|
351
|
+
# s(:scope,
|
352
|
+
# s(:block,
|
353
|
+
# s(:call,
|
354
|
+
# s(:call,
|
355
|
+
# s(:call, nil, :first_name, s(:arglist)),
|
356
|
+
# :+,
|
357
|
+
# s(:arglist,
|
358
|
+
# s(:call, nil, :last, s(:arglist))
|
359
|
+
# )
|
360
|
+
# ),
|
361
|
+
# :+,
|
362
|
+
# s(:arglist,
|
363
|
+
# s(:call, nil, :name, s(:arglist))
|
364
|
+
# )
|
365
|
+
# )
|
366
|
+
# )
|
367
|
+
# )
|
368
|
+
# )
|
369
|
+
# => s(:block,
|
370
|
+
# s(:call,
|
371
|
+
# s(:call,
|
372
|
+
# s(:call, nil, :first_name, s(:arglist)),
|
373
|
+
# :+,
|
374
|
+
# s(:arglist,
|
375
|
+
# s(:call, nil, :last, s(:arglist))
|
376
|
+
# )
|
377
|
+
# ),
|
378
|
+
# :+,
|
379
|
+
# s(:arglist,
|
380
|
+
# s(:call, nil, :name, s(:arglist))
|
381
|
+
# )
|
382
|
+
# )
|
383
|
+
# )
|
384
|
+
#
|
385
|
+
# @return [Sexp] body of iter, class or defn node
|
107
386
|
def body
|
108
|
-
if :
|
109
|
-
self[1..-1]
|
110
|
-
elsif :class == node_type
|
387
|
+
if :iter == node_type
|
111
388
|
self[3]
|
389
|
+
elsif :class == node_type
|
390
|
+
self[3][1]
|
112
391
|
elsif :defn == node_type
|
113
392
|
self[3][1]
|
114
|
-
elsif :defs == node_type
|
115
|
-
self[4][1]
|
116
393
|
end
|
117
394
|
end
|
118
395
|
|
396
|
+
# to_s for lvar, ivar, lit, const, array and hash node.
|
397
|
+
#
|
398
|
+
# @return [String] to_s
|
119
399
|
def to_s
|
120
400
|
if [:lvar, :ivar].include? node_type
|
121
401
|
self[1].to_s
|
122
402
|
elsif :str == node_type
|
123
403
|
self[1]
|
124
404
|
elsif :lit == node_type
|
125
|
-
|
405
|
+
self[1].to_s
|
126
406
|
elsif :const == node_type
|
127
|
-
self[1]
|
407
|
+
self[1].to_s
|
128
408
|
elsif :array == node_type
|
129
409
|
"[\"#{self.children.collect(&:to_s).join('", "')}\"]"
|
130
410
|
elsif :hash == node_type
|
131
411
|
key_value = false # false is key, true is value
|
132
|
-
result =
|
412
|
+
result = ['{"']
|
133
413
|
children.each do |child|
|
134
|
-
result
|
414
|
+
result << "#{child.to_s}#{key_value ? '", "' : '" => "'}"
|
135
415
|
key_value = !key_value
|
136
416
|
end
|
137
|
-
result.sub
|
138
|
-
result += "}"
|
417
|
+
result.join("").sub(/, "$/, '') + '}'
|
139
418
|
end
|
140
419
|
end
|
141
|
-
|
142
|
-
#def to_ruby
|
143
|
-
#Ruby2Ruby.new.process(self) unless self.empty?
|
144
|
-
#end
|
145
|
-
|
146
|
-
#def to_ruby_string
|
147
|
-
#return nil if self.empty?
|
148
|
-
#eval(Ruby2Ruby.new.process(self)).to_s
|
149
|
-
#end
|
150
420
|
end
|