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.
Files changed (37) hide show
  1. data/README.md +161 -0
  2. data/lib/rails_best_practices.rb +158 -20
  3. data/lib/rails_best_practices/checks/add_model_virtual_attribute_check.rb +108 -34
  4. data/lib/rails_best_practices/checks/always_add_db_index_check.rb +148 -29
  5. data/lib/rails_best_practices/checks/check.rb +178 -75
  6. data/lib/rails_best_practices/checks/dry_bundler_in_capistrano_check.rb +26 -5
  7. data/lib/rails_best_practices/checks/isolate_seed_data_check.rb +66 -15
  8. data/lib/rails_best_practices/checks/keep_finders_on_their_own_model_check.rb +53 -12
  9. data/lib/rails_best_practices/checks/law_of_demeter_check.rb +59 -30
  10. data/lib/rails_best_practices/checks/move_code_into_controller_check.rb +35 -15
  11. data/lib/rails_best_practices/checks/move_code_into_helper_check.rb +56 -12
  12. data/lib/rails_best_practices/checks/move_code_into_model_check.rb +30 -32
  13. data/lib/rails_best_practices/checks/move_finder_to_named_scope_check.rb +45 -15
  14. data/lib/rails_best_practices/checks/move_model_logic_into_model_check.rb +31 -27
  15. data/lib/rails_best_practices/checks/needless_deep_nesting_check.rb +99 -38
  16. data/lib/rails_best_practices/checks/not_use_default_route_check.rb +43 -12
  17. data/lib/rails_best_practices/checks/overuse_route_customizations_check.rb +140 -28
  18. data/lib/rails_best_practices/checks/replace_complex_creation_with_factory_method_check.rb +44 -30
  19. data/lib/rails_best_practices/checks/replace_instance_variable_with_local_variable_check.rb +18 -7
  20. data/lib/rails_best_practices/checks/use_before_filter_check.rb +88 -18
  21. data/lib/rails_best_practices/checks/use_model_association_check.rb +61 -22
  22. data/lib/rails_best_practices/checks/use_observer_check.rb +125 -23
  23. data/lib/rails_best_practices/checks/use_query_attribute_check.rb +75 -47
  24. data/lib/rails_best_practices/checks/use_say_with_time_in_migrations_check.rb +59 -10
  25. data/lib/rails_best_practices/checks/use_scope_access_check.rb +78 -23
  26. data/lib/rails_best_practices/command.rb +19 -34
  27. data/lib/rails_best_practices/core.rb +4 -2
  28. data/lib/rails_best_practices/core/checking_visitor.rb +49 -19
  29. data/lib/rails_best_practices/core/error.rb +5 -2
  30. data/lib/rails_best_practices/core/runner.rb +79 -55
  31. data/lib/rails_best_practices/core/visitable_sexp.rb +325 -55
  32. data/lib/rails_best_practices/{core/core_ext.rb → core_ext/enumerable.rb} +3 -6
  33. data/lib/rails_best_practices/core_ext/nil_class.rb +8 -0
  34. data/lib/rails_best_practices/version.rb +1 -1
  35. data/rails_best_practices.yml +2 -2
  36. metadata +8 -7
  37. 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
- def accept(visitor)
11
- visitor.visit(self)
12
- end
13
-
14
- def node_type
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
- def is_language_node?
23
- first.class == Symbol
24
- end
25
-
26
- def visitable_children
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 or node_type == child.node_type) and (!subject or subject == child.subject) and (!message or message == child.message) and (!arguments or arguments == child.arguments)
47
- nodes << child
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, :class, :iter].include? node_type
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
- def message
66
- if [:attrasgn, :call, :defs, :iter].include? node_type
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
- def arguments
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[3]
227
+ self[2]
74
228
  end
75
229
  end
76
230
 
77
- def call
78
- if [:if, :arglist].include? node_type
79
- self[1]
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 node_type == :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
- def message_name
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 :block == node_type
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
- ":#{self[1]}"
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 += "#{child.to_s}#{key_value ? ', ' : ' => '}"
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