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