rails_best_practices 1.10.1 → 1.11.0

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 (76) hide show
  1. data/.gitignore +1 -0
  2. data/README.md +11 -6
  3. data/assets/result.html.erb +76 -46
  4. data/install_supported_rubies.sh +3 -0
  5. data/lib/rails_best_practices.rb +2 -1
  6. data/lib/rails_best_practices/analyzer.rb +10 -8
  7. data/lib/rails_best_practices/core.rb +0 -4
  8. data/lib/rails_best_practices/core/check.rb +41 -117
  9. data/lib/rails_best_practices/core/error.rb +3 -9
  10. data/lib/rails_best_practices/core/runner.rb +20 -80
  11. data/lib/rails_best_practices/lexicals/long_line_check.rb +2 -1
  12. data/lib/rails_best_practices/lexicals/remove_tab_check.rb +1 -3
  13. data/lib/rails_best_practices/lexicals/remove_trailing_whitespace_check.rb +1 -3
  14. data/lib/rails_best_practices/prepares/config_prepare.rb +1 -1
  15. data/lib/rails_best_practices/prepares/controller_prepare.rb +7 -8
  16. data/lib/rails_best_practices/prepares/helper_prepare.rb +2 -2
  17. data/lib/rails_best_practices/prepares/mailer_prepare.rb +1 -1
  18. data/lib/rails_best_practices/prepares/model_prepare.rb +6 -7
  19. data/lib/rails_best_practices/prepares/route_prepare.rb +12 -13
  20. data/lib/rails_best_practices/prepares/schema_prepare.rb +2 -2
  21. data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +19 -15
  22. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +10 -17
  23. data/lib/rails_best_practices/reviews/dry_bundler_in_capistrano_review.rb +2 -5
  24. data/lib/rails_best_practices/reviews/hash_syntax_review.rb +3 -30
  25. data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +7 -10
  26. data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +5 -9
  27. data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +10 -13
  28. data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +6 -9
  29. data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +2 -5
  30. data/lib/rails_best_practices/reviews/move_code_into_model_review.rb +7 -13
  31. data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +3 -6
  32. data/lib/rails_best_practices/reviews/move_model_logic_into_model_review.rb +6 -9
  33. data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +3 -6
  34. data/lib/rails_best_practices/reviews/not_use_default_route_review.rb +4 -7
  35. data/lib/rails_best_practices/reviews/not_use_time_ago_in_words_review.rb +2 -5
  36. data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +7 -10
  37. data/lib/rails_best_practices/reviews/protect_mass_assignment_review.rb +2 -5
  38. data/lib/rails_best_practices/reviews/remove_empty_helpers_review.rb +2 -5
  39. data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +8 -6
  40. data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +1 -2
  41. data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +4 -5
  42. data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +9 -12
  43. data/lib/rails_best_practices/reviews/replace_instance_variable_with_local_variable_review.rb +2 -13
  44. data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +18 -26
  45. data/lib/rails_best_practices/reviews/review.rb +6 -7
  46. data/lib/rails_best_practices/reviews/simplify_render_in_controllers_review.rb +2 -5
  47. data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +2 -5
  48. data/lib/rails_best_practices/reviews/use_before_filter_review.rb +2 -5
  49. data/lib/rails_best_practices/reviews/use_model_association_review.rb +11 -14
  50. data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +11 -8
  51. data/lib/rails_best_practices/reviews/use_observer_review.rb +8 -11
  52. data/lib/rails_best_practices/reviews/use_parentheses_in_method_def_review.rb +1 -1
  53. data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +12 -18
  54. data/lib/rails_best_practices/reviews/use_say_with_time_in_migrations_review.rb +7 -10
  55. data/lib/rails_best_practices/reviews/use_scope_access_review.rb +4 -10
  56. data/lib/rails_best_practices/version.rb +1 -1
  57. data/rails_best_practices.gemspec +1 -1
  58. data/rails_best_practices.yml +5 -5
  59. data/spec/rails_best_practices/core/check_spec.rb +0 -67
  60. data/spec/rails_best_practices/prepares/controller_prepare_spec.rb +0 -1
  61. data/spec/rails_best_practices/prepares/model_prepare_spec.rb +0 -4
  62. data/spec/rails_best_practices/reviews/hash_syntax_review_spec.rb +3 -30
  63. data/spec/rails_best_practices/reviews/remove_unused_methods_in_controllers_review_spec.rb +22 -0
  64. data/spec/rails_best_practices/reviews/remove_unused_methods_in_models_review_spec.rb +19 -0
  65. data/spec/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review_spec.rb +2 -2
  66. data/spec/spec_helper.rb +0 -4
  67. metadata +28 -41
  68. data/Gemfile.lock +0 -71
  69. data/lib/rails_best_practices/core/checking_visitor.rb +0 -80
  70. data/lib/rails_best_practices/core/nil.rb +0 -37
  71. data/lib/rails_best_practices/core_ext/enumerable.rb +0 -9
  72. data/lib/rails_best_practices/core_ext/sexp.rb +0 -840
  73. data/spec/rails_best_practices/core/checking_visitor_spec.rb +0 -79
  74. data/spec/rails_best_practices/core/nil_spec.rb +0 -37
  75. data/spec/rails_best_practices/core_ext/enumerable_spec.rb +0 -7
  76. data/spec/rails_best_practices/core_ext/sexp_spec.rb +0 -613
data/Gemfile.lock DELETED
@@ -1,71 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- rails_best_practices (1.10.1)
5
- activesupport
6
- awesome_print
7
- colored
8
- erubis
9
- i18n
10
- progressbar
11
- sexp_processor
12
-
13
- GEM
14
- remote: http://rubygems.org/
15
- specs:
16
- activesupport (3.2.3)
17
- i18n (~> 0.6)
18
- multi_json (~> 1.0)
19
- awesome_print (1.0.2)
20
- coderay (1.0.6)
21
- colored (1.2)
22
- diff-lcs (1.1.3)
23
- erubis (2.7.0)
24
- growl (1.0.3)
25
- guard (0.8.8)
26
- thor (~> 0.14.6)
27
- guard-rspec (0.5.7)
28
- guard (>= 0.8.4)
29
- haml (3.1.3)
30
- i18n (0.6.0)
31
- method_source (0.7.1)
32
- multi_json (1.2.0)
33
- progressbar (0.11.0)
34
- pry (0.9.9.6)
35
- coderay (~> 1.0.5)
36
- method_source (~> 0.7.1)
37
- slop (>= 2.4.4, < 3)
38
- rake (0.9.2.2)
39
- rb-fsevent (0.9.1)
40
- rspec (2.7.0)
41
- rspec-core (~> 2.7.0)
42
- rspec-expectations (~> 2.7.0)
43
- rspec-mocks (~> 2.7.0)
44
- rspec-core (2.7.1)
45
- rspec-expectations (2.7.0)
46
- diff-lcs (~> 1.1.2)
47
- rspec-mocks (2.7.0)
48
- sexp_processor (3.2.0)
49
- slim (1.0.4)
50
- temple (~> 0.3.4)
51
- tilt (~> 1.3.2)
52
- slop (2.4.4)
53
- temple (0.3.4)
54
- thor (0.14.6)
55
- tilt (1.3.3)
56
-
57
- PLATFORMS
58
- ruby
59
-
60
- DEPENDENCIES
61
- bundler
62
- growl
63
- guard
64
- guard-rspec
65
- haml
66
- pry
67
- rails_best_practices!
68
- rake
69
- rb-fsevent
70
- rspec
71
- slim
@@ -1,80 +0,0 @@
1
- # encoding: utf-8
2
- module RailsBestPractices
3
- module Core
4
- # CheckingVisitor is a visitor class.
5
- #
6
- # it remembers all the checks for prepare and review processes according to interesting_nodes and interesting_nodes,
7
- # then recursively iterate all sexp nodes,
8
- #
9
- # for prepare process
10
- # if the sexp_type and the node filename match the interesting_prepare_nodes and interesting_files,
11
- # then run the prepare for that node.
12
- #
13
- # for review process
14
- # if the sexp_type and the node filename match the interesting_review_nodes and interesting_files,
15
- # then run the reivew for that node.
16
- class CheckingVisitor
17
- # remember all the checks for prepare and review processes according to interesting_nodes.
18
- #
19
- # @param [Hash] options
20
- # {lexicals: [], prepares: [], reviews: []}
21
- def initialize(options={})
22
- @lexicals = options[:lexicals]
23
- [:prepare, :review].each do |process|
24
- instance_variable_set("@#{process}_checks", {}) # @review_checks = {}
25
- options["#{process}s".to_sym].each do |check| # options[:reviews].each do |check|
26
- check.send("interesting_nodes").each do |node| # check.interesting_nodes.each do |node|
27
- instance_variable_get("@#{process}_checks")[node] ||= [] # @review_checks[node] ||= []
28
- instance_variable_get("@#{process}_checks")[node] << check # @review_checks[node] << check
29
- instance_variable_get("@#{process}_checks")[node].uniq! # @review_checks[node].uniq!
30
- end # end
31
- end # end
32
- end
33
- end
34
-
35
- # for lexical process
36
- # check the content of files one by one.
37
- def lexical(filename, content)
38
- @lexicals.each do |lexical|
39
- lexical.check(filename, content)
40
- end
41
- end
42
-
43
- # for prepare process
44
- # if the sexp_type and the node filename match the interesting_nodes and interesting_files,
45
- # then run the prepare for that node.
46
- #
47
- # for review process
48
- # if the sexp_type and the node filename match the interesting_nodes and interesting_files,
49
- # then run the reivew for that node.
50
- [:prepare, :review].each do |process|
51
- class_eval <<-EOS
52
- def #{process}(node) # def review(node)
53
- checks = @#{process}_checks[node.sexp_type] # checks = @review_checks[node.sexp_type]
54
- if checks # if checks
55
- checks.each { |check| # checks.each { |check|
56
- if check.parse_file?(node.file) # if check.parse_file?(node.file)
57
- check.node_start(node) # check.node_start(node)
58
- end # end
59
- } # }
60
- end # end
61
- node.children.each { |sexp| # node.children.each { |sexp|
62
- sexp.file = node.file # sexp.filename = node.file
63
- sexp.#{process}(self) # sexp.review(self)
64
- } # }
65
- if checks # if checks
66
- checks.each { |check| # checks.each { |check|
67
- if check.parse_file?(node.file) # if check.parse_file?(node.file)
68
- check.node_end(node) # check.node_end(node)
69
- end # end
70
- } # }
71
- end # end
72
- rescue Exception # rescue Exception
73
- puts "find error in file: \#{node.file} line: \#{node.line}" # puts "find error in file: \#{node.file} line: \#{node.line}"
74
- raise $! # raise $!
75
- end # end
76
- EOS
77
- end
78
- end
79
- end
80
- end
@@ -1,37 +0,0 @@
1
- # encoding: utf-8
2
- module RailsBestPractices
3
- module Core
4
- # Fake nil.
5
- class Nil
6
- # hash_size is 0.
7
- def hash_size
8
- 0
9
- end
10
-
11
- # array_size is 0.
12
- def array_size
13
- 0
14
- end
15
-
16
- # return self for to_s.
17
- def to_s
18
- self
19
- end
20
-
21
- # false
22
- def present?
23
- false
24
- end
25
-
26
- # true
27
- def blank?
28
- true
29
- end
30
-
31
- # return self.
32
- def method_missing(method_sym, *arguments, &block)
33
- self
34
- end
35
- end
36
- end
37
- end
@@ -1,9 +0,0 @@
1
- # encoding: utf-8
2
- module Enumerable
3
- # Get the duplicate entries from an Enumerable.
4
- #
5
- # @return [Enumerable] the duplicate entries.
6
- def dups
7
- inject({}) {|h,v| h[v]=h[v].to_i+1; h}.reject{|k,v| v==1}.keys
8
- end
9
- end
@@ -1,840 +0,0 @@
1
- # encoding: utf-8
2
- require 'sexp'
3
-
4
- class Sexp
5
- # prepare current node.
6
- #
7
- # @param [RailsBestPractices::Core::CheckingVisitor] visitor the visitor to prepare current node
8
- def prepare(visitor)
9
- visitor.prepare(self)
10
- end
11
-
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)
17
- end
18
-
19
- # return the line number of a sexp node.
20
- #
21
- # s(:@ident, "test", s(2, 12)
22
- # => 2
23
- def line
24
- case sexp_type
25
- when :def, :defs, :command, :command_call, :call, :fcall, :method_add_arg, :method_add_block,
26
- :var_ref, :vcall, :const_ref, :const_path_ref, :class, :module, :if, :unless, :elsif, :ifop, :binary,
27
- :alias, :symbol_literal, :symbol, :aref, :hash, :assoc_new, :string_literal
28
- self[1].line
29
- when :assoclist_from_args, :bare_assoc_hash
30
- self[1][0].line
31
- when :string_add
32
- self[2].line
33
- when :array
34
- array_values.first.line
35
- else
36
- self.last.first if self.last.is_a? Array
37
- end
38
- end
39
-
40
- # return child nodes of a sexp node.
41
- #
42
- # @return [Array] child nodes.
43
- def children
44
- find_all { | sexp | Sexp === sexp }
45
- end
46
-
47
- # recursively find all child nodes, and yeild each child node.
48
- def recursive_children
49
- children.each do |child|
50
- yield child
51
- child.recursive_children { |c| yield c }
52
- end
53
- end
54
-
55
- # grep all the recursive child nodes with conditions, and yield each match node.
56
- #
57
- # @param [Hash] options grep conditions
58
- #
59
- # options is the grep conditions, like
60
- #
61
- # sexp_type: :call,
62
- # subject: "Post",
63
- # message: ["find", "new"]
64
- # to_s: "devise"
65
- #
66
- # the condition key is one of :sexp_type, :subject, :message, :to_s,
67
- # the condition value can be Symbol, Array or Sexp.
68
- def grep_nodes(options)
69
- sexp_type = options[:sexp_type]
70
- subject = options[:subject]
71
- message = options[:message]
72
- to_s = options[:to_s]
73
- self.recursive_children do |child|
74
- if (!sexp_type || (sexp_type.is_a?(Array) ? sexp_type.include?(child.sexp_type) : sexp_type == child.sexp_type)) &&
75
- (!subject || (subject.is_a?(Array) ? subject.include?(child.subject.to_s) : subject == child.subject.to_s)) &&
76
- (!message || (message.is_a?(Array) ? message.include?(child.message.to_s) : message == child.message.to_s)) &&
77
- (!to_s || (to_s.is_a?(Array) ? to_s.include?(child.to_s) : to_s == child.to_s))
78
- yield child
79
- end
80
- end
81
- end
82
-
83
- # grep all the recursive child nodes with conditions, and yield the first match node.
84
- #
85
- # @param [Hash] options grep conditions
86
- #
87
- # options is the grep conditions, like
88
- #
89
- # sexp_type: :call,
90
- # subject: s(:const, Post),
91
- # message: [:find, :new]
92
- #
93
- # the condition key is one of :sexp_type, :subject, :message, and to_s,
94
- # the condition value can be Symbol, Array or Sexp.
95
- def grep_node(options)
96
- result = RailsBestPractices::Core::Nil.new
97
- grep_nodes(options) { |node| result = node; break; }
98
- result
99
- end
100
-
101
- # grep all the recursive child nodes with conditions, and get the count of match nodes.
102
- #
103
- # @param [Hash] options grep conditions
104
- # @return [Integer] the count of metch nodes
105
- def grep_nodes_count(options)
106
- count = 0
107
- grep_nodes(options) { |node| count += 1 }
108
- count
109
- end
110
-
111
- # Get subject node.
112
- #
113
- # s(:call,
114
- # s(:var_ref,
115
- # s(:@ident, "user", s(1, 0))
116
- # ),
117
- # :".",
118
- # s(:@ident, "name", s(1, 5))
119
- # )
120
- # => s(:var_ref,
121
- # s(:@ident, "user", s(1, 0))
122
- # )
123
- #
124
- # @return [Sexp] subject node
125
- def subject
126
- case sexp_type
127
- when :assign, :field, :call, :binary, :command_call
128
- self[1]
129
- when :method_add_arg, :method_add_block
130
- self[1].subject
131
- end
132
- end
133
-
134
- # Get the module name of the module node.
135
- #
136
- # s(:module,
137
- # s(:const_ref, s(:@const, "Admin", s(1, 7))),
138
- # s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
139
- # )
140
- # => s(:const_ref, s(:@const, "Admin", s(1, 7))),
141
- #
142
- # @return [Sexp] module name node
143
- def module_name
144
- if :module == sexp_type
145
- self[1]
146
- end
147
- end
148
-
149
- # Get the class name of the class node.
150
- #
151
- # s(:class,
152
- # s(:const_ref, s(:@const, "User", s(1, 6))),
153
- # nil,
154
- # s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
155
- # )
156
- # => s(:const_ref, s(:@const, "User", s(1, 6))),
157
- #
158
- # @return [Sexp] class name node
159
- def class_name
160
- if :class == sexp_type
161
- self[1]
162
- end
163
- end
164
-
165
- # Get the base class of the class node.
166
- #
167
- # s(:class,
168
- # s(:const_ref, s(:@const, "User", s(1, 6))),
169
- # s(:const_path_ref, s(:var_ref, s(:@const, "ActiveRecord", s(1, 13))), s(:@const, "Base", s(1, 27))),
170
- # s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
171
- # )
172
- # => s(:const_path_ref, s(:var_ref, s(:@const, "ActiveRecord", s(1, 13))), s(:@const, "Base", s(1, 27))),
173
- #
174
- # @return [Sexp] base class of class node
175
- def base_class
176
- if :class == sexp_type
177
- self[2]
178
- end
179
- end
180
-
181
- # Get the left value of the assign node.
182
- #
183
- # s(:assign,
184
- # s(:var_field, s(:@ident, "user", s(1, 0))),
185
- # s(:var_ref, s(:@ident, "current_user", s(1, 7)))
186
- # )
187
- # => s(:var_field, s(:@ident, "user", s(1, 0))),
188
- #
189
- # @return [Symbol] left value of lasgn or iasgn node
190
- def left_value
191
- if :assign == sexp_type
192
- self[1]
193
- end
194
- end
195
-
196
- # Get the right value of assign node.
197
- #
198
- # s(:assign,
199
- # s(:var_field, s(:@ident, "user", s(1, 0))),
200
- # s(:var_ref, s(:@ident, "current_user", s(1, 7)))
201
- # )
202
- # => s(:var_ref, s(:@ident, "current_user", s(1, 7)))
203
- #
204
- # @return [Sexp] right value of assign node
205
- def right_value
206
- if :assign == sexp_type
207
- self[2]
208
- end
209
- end
210
-
211
- # Get the message node.
212
- #
213
- # s(:command,
214
- # s(:@ident, "has_many", s(1, 0)),
215
- # s(:args_add_block,
216
- # s(:args_add, s(:args_new),
217
- # s(:symbol_literal, s(:symbol, s(:@ident, "projects", s(1, 10))))
218
- # ),
219
- # false
220
- # )
221
- # )
222
- # => s(:@ident, "has_many", s(1, 0)),
223
- #
224
- # @return [Symbol] message node
225
- def message
226
- case sexp_type
227
- when :command, :fcall
228
- self[1]
229
- when :binary
230
- self[2]
231
- when :command_call, :field, :call
232
- self[3]
233
- when :method_add_arg, :method_add_block
234
- self[1].message
235
- end
236
- end
237
-
238
- # Get arguments node.
239
- #
240
- # s(:command,
241
- # s(:@ident, "resources", s(1, 0)),
242
- # s(:args_add_block,
243
- # s(:args_add, s(:args_new),
244
- # s(:symbol_literal, s(:symbol, s(:@ident, "posts", s(1, 11))))
245
- # ), false
246
- # )
247
- # )
248
- # => s(:args_add_block,
249
- # s(:args_add, s(:args_new),
250
- # s(:symbol_literal, s(:symbol, s(:@ident, "posts", s(1, 11))))
251
- # ), false
252
- # )
253
- #
254
- # @return [Sexp] arguments node
255
- def arguments
256
- case sexp_type
257
- when :command
258
- self[2]
259
- when :command_call
260
- self[4]
261
- when :method_add_arg
262
- self[2].arguments
263
- when :method_add_block
264
- self[1].arguments
265
- when :arg_paren
266
- self[1]
267
- when :array
268
- self
269
- end
270
- end
271
-
272
- # Get only argument for binary.
273
- #
274
- # s(:binary,
275
- # s(:var_ref, s(:@ident, "user", s(1, 0))),
276
- # :==,
277
- # s(:var_ref, s(:@ident, "current_user", s(1, 8)))
278
- # )
279
- # => s(:var_ref, s(:@ident, "current_user", s(1, 8)))
280
- #
281
- # @return [Sexp] argument node
282
- def argument
283
- if :binary == sexp_type
284
- self[3]
285
- end
286
- end
287
-
288
- # Get all arguments.
289
- #
290
- # s(:args_add_block,
291
- # s(:args_add,
292
- # s(:args_add, s(:args_new), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "hello", s(1, 6))))),
293
- # s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "world", s(1, 15))))
294
- # ), false
295
- # )
296
- # => [
297
- # s(:args_add, s(:args_new), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "hello", s(1, 6))))),
298
- # s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "world", s(1, 15))))
299
- # ]
300
- #
301
- # @return [Array] all arguments
302
- def all
303
- nodes = []
304
- case sexp_type
305
- when :args_add_block, :array
306
- if self[1].sexp_type == :args_new
307
- nodes << self[2]
308
- else
309
- node = self[1]
310
- while true
311
- if [:args_add, :args_add_star].include? node.sexp_type
312
- nodes.unshift node[2]
313
- node = node[1]
314
- elsif :args_new == node.sexp_type
315
- break
316
- end
317
- end
318
- end
319
- end
320
- nodes
321
- end
322
-
323
- # Get the conditional statement of if node.
324
- #
325
- # s(:if,
326
- # s(:var_ref, s(:@kw, "true", s(1, 3))),
327
- # s(:stmts_add, s(:stmts_new), s(:void_stmt)),
328
- # nil
329
- # )
330
- # => s(:var_ref, s(:@kw, "true", s(1, 3))),
331
- #
332
- # @return [Sexp] conditional statement of if node
333
- def conditional_statement
334
- if [:if, :unless, :elsif, :ifop].include? sexp_type
335
- self[1]
336
- end
337
- end
338
-
339
- # Get all condition nodes.
340
- #
341
- # s(:binary,
342
- # s(:binary,
343
- # s(:var_ref, s(:@ident, "user", s(1, 0))),
344
- # :==,
345
- # s(:var_ref, s(:@ident, "current_user", s(1, 8)))
346
- # ),
347
- # :"&&",
348
- # s(:call,
349
- # s(:var_ref, s(:@ident, "user", s(1, 24))),
350
- # :".",
351
- # s(:@ident, "valid?", s(1, 29))
352
- # )
353
- # )
354
- # => [
355
- # s(:binary,
356
- # s(:var_ref, s(:@ident, "user", s(1, 0))),
357
- # :==,
358
- # s(:var_ref, s(:@ident, "current_user", s(1, 8)))
359
- # ),
360
- # s(:call,
361
- # s(:var_ref, s(:@ident, "user", s(1, 24))),
362
- # :".",
363
- # s(:@ident, "valid?", s(1, 29))
364
- # )
365
- # ]
366
- #
367
- # @return [Array] all condition nodes
368
- def all_conditions
369
- nodes = []
370
- if :binary == sexp_type && %w(&& || and or).include?(self[2].to_s)
371
- if :binary == self[1].sexp_type && %w(&& || and or).include?(self[1][2].to_s)
372
- nodes += self[1].all_conditions
373
- else
374
- nodes << self[1]
375
- end
376
- if :binary == self[3].sexp_type && %w(&& || and or).include?(self[3][2].to_s)
377
- nodes += self[3].all_conditions
378
- else
379
- nodes << self[3]
380
- end
381
- else
382
- self
383
- end
384
- end
385
-
386
- # Get the method name of def node.
387
- #
388
- # s(:def,
389
- # s(:@ident, "show", s(1, 4)),
390
- # s(:params, nil, nil, nil, nil, nil),
391
- # s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
392
- # )
393
- # => s(:@ident, "show", s(1, 4)),
394
- #
395
- # @return [Sexp] method name node
396
- def method_name
397
- case sexp_type
398
- when :def
399
- self[1]
400
- when :defs
401
- self[3]
402
- else
403
- end
404
- end
405
-
406
- # Get body node.
407
- #
408
- # s(:class,
409
- # s(:const_ref, s(:@const, "User", s(1, 6))),
410
- # nil,
411
- # s(:bodystmt,
412
- # s(:stmts_add, s(:stmts_new),
413
- # s(:def,
414
- # s(:@ident, "login", s(1, 16)),
415
- # s(:params, nil, nil, nil, nil, nil),
416
- # s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
417
- # )
418
- # ), nil, nil, nil
419
- # )
420
- # )
421
- # => s(:bodystmt,
422
- # s(:stmts_add, s(:stmts_new),
423
- # s(:def,
424
- # s(:@ident, "login", s(1, 16)),
425
- # s(:params, nil, nil, nil, nil, nil),
426
- # s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
427
- # )
428
- # ), nil, nil, nil
429
- # )
430
- #
431
- # @return [Sexp] body node
432
- def body
433
- case sexp_type
434
- when :else
435
- self[1]
436
- when :module, :if, :elsif, :unless
437
- self[2]
438
- when :class, :def
439
- self[3]
440
- when :defs
441
- self[5]
442
- end
443
- end
444
-
445
- # Get block node.
446
- #
447
- # s(:method_add_block,
448
- # s(:command,
449
- # s(:@ident, "resources", s(1, 0)),
450
- # s(:args_add_block, s(:args_add, s(:args_new), s(:symbol_literal, s(:symbol, s(:@ident, "posts", s(1, 11))))), false)
451
- # ),
452
- # s(:do_block, nil,
453
- # s(:stmts_add, s(:stmts_add, s(:stmts_new), s(:void_stmt)),
454
- # s(:command,
455
- # s(:@ident, "resources", s(1, 21)),
456
- # s(:args_add_block, s(:args_add, s(:args_new), s(:symbol_literal, s(:symbol, s(:@ident, "comments", s(1, 32))))), false))
457
- # )
458
- # )
459
- # )
460
- # => s(:do_block, nil,
461
- # s(:stmts_add, s(:stmts_add, s(:stmts_new), s(:void_stmt)),
462
- # s(:command,
463
- # s(:@ident, "resources", s(1, 21)),
464
- # s(:args_add_block, s(:args_add, s(:args_new), s(:symbol_literal, s(:symbol, s(:@ident, "comments", s(1, 32))))), false))
465
- # )
466
- # )
467
- #
468
- # @return [Sexp] body node
469
- def block
470
- case sexp_type
471
- when :method_add_block
472
- self[2]
473
- end
474
- end
475
-
476
- # Get all statements nodes.
477
- #
478
- # s(:bodystmt,
479
- # s(:stmts_add,
480
- # s(:stmts_add, s(:stmts_new),
481
- # s(:def,
482
- # s(:@ident, "login?", s(1, 16)),
483
- # s(:params, nil, nil, nil, nil, nil),
484
- # s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
485
- # )
486
- # ),
487
- # s(:def,
488
- # s(:@ident, "admin?", s(1, 33)),
489
- # s(:params, nil, nil, nil, nil, nil),
490
- # s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
491
- # )
492
- # ), nil, nil, nil
493
- # )
494
- # => [
495
- # s(:def,
496
- # s(:@ident, "login?", s(1, 16)),
497
- # s(:params, nil, nil, nil, nil, nil),
498
- # s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
499
- # ),
500
- # s(:def,
501
- # s(:@ident, "admin?", s(1, 33)),
502
- # s(:params, nil, nil, nil, nil, nil),
503
- # s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil)
504
- # )
505
- # ]
506
- #
507
- # @return [Array] all statements
508
- def statements
509
- stmts = []
510
- node = case sexp_type
511
- when :do_block, :brace_block
512
- self[2]
513
- when :bodystmt
514
- self[1]
515
- else
516
- end
517
- if node
518
- while true
519
- if :stmts_add == node.sexp_type && s(:void_stmt) != node[2]
520
- stmts.unshift node[2]
521
- node = node[1]
522
- else
523
- break
524
- end
525
- end
526
- end
527
- stmts
528
- end
529
-
530
- # Get hash value node.
531
- #
532
- # s(:hash,
533
- # s(:assoclist_from_args,
534
- # s(
535
- # s(:assoc_new, s(:@label, "first_name:", s(1, 1)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14))))),
536
- # s(:assoc_new, s(:@label, "last_name:", s(1, 24)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Huang", s(1, 36)))))
537
- # )
538
- # )
539
- # )
540
- # => s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14))))
541
- #
542
- # @return [Sexp] hash value node
543
- def hash_value(key)
544
- pair_nodes = case sexp_type
545
- when :bare_assoc_hash
546
- self[1]
547
- when :hash
548
- self[1][1]
549
- else
550
- end
551
- if pair_nodes
552
- pair_nodes.size.times do |i|
553
- if key == pair_nodes[i][1].to_s
554
- return pair_nodes[i][2]
555
- end
556
- end
557
- end
558
- RailsBestPractices::Core::Nil.new
559
- end
560
-
561
- # Get hash size.
562
- #
563
- # s(:hash,
564
- # s(:assoclist_from_args,
565
- # s(
566
- # s(:assoc_new, s(:@label, "first_name:", s(1, 1)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14))))),
567
- # s(:assoc_new, s(:@label, "last_name:", s(1, 24)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Huang", s(1, 36)))))
568
- # )
569
- # )
570
- # )
571
- # => 2
572
- #
573
- # @return [Integer] hash size
574
- def hash_size
575
- case sexp_type
576
- when :hash
577
- self[1].hash_size
578
- when :assoclist_from_args
579
- self[1].size
580
- when :bare_assoc_hash
581
- self[1].size
582
- end
583
- end
584
-
585
- # Get the hash keys.
586
- #
587
- # s(:hash,
588
- # s(:assoclist_from_args,
589
- # s(
590
- # s(:assoc_new, s(:@label, "first_name:", s(1, 1)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14))))),
591
- # s(:assoc_new, s(:@label, "last_name:", s(1, 24)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Huang", s(1, 36)))))
592
- # )
593
- # )
594
- # )
595
- # => ["first_name", "last_name"]
596
- #
597
- # @return [Array] hash keys
598
- def hash_keys
599
- pair_nodes = case sexp_type
600
- when :bare_assoc_hash
601
- self[1]
602
- when :hash
603
- self[1][1]
604
- else
605
- end
606
- if pair_nodes
607
- keys = []
608
- pair_nodes.size.times do |i|
609
- keys << pair_nodes[i][1].to_s
610
- end
611
- keys
612
- end
613
- end
614
-
615
- # Get the hash values.
616
- #
617
- # s(:hash,
618
- # s(:assoclist_from_args,
619
- # s(
620
- # s(:assoc_new, s(:@label, "first_name:", s(1, 1)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14))))),
621
- # s(:assoc_new, s(:@label, "last_name:", s(1, 24)), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Huang", s(1, 36)))))
622
- # )
623
- # )
624
- # )
625
- # => [
626
- # s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Richard", s(1, 14)))),
627
- # s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "Huang", s(1, 36))))
628
- # ]
629
- #
630
- # @return [Array] hash values
631
- def hash_values
632
- pair_nodes = case sexp_type
633
- when :bare_assoc_hash
634
- self[1]
635
- when :hash
636
- self[1][1]
637
- else
638
- end
639
- if pair_nodes
640
- values = []
641
- pair_nodes.size.times do |i|
642
- values << pair_nodes[i][2]
643
- end
644
- values
645
- end
646
- end
647
-
648
- # Get the array size.
649
- #
650
- # s(:array,
651
- # s(:args_add,
652
- # s(:args_add, s(:args_new), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "first_name", s(1, 2))))),
653
- # s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "last_name", s(1, 16))))
654
- # )
655
- # )
656
- # => 2
657
- #
658
- # @return [Integer] array size
659
- def array_size
660
- if :array == sexp_type
661
- first_node = self[1]
662
- array_size = 0
663
- if first_node
664
- while true
665
- array_size += 1
666
- first_node = s(:args_new) == first_node[1] ? first_node[2] : first_node[1]
667
- if :args_add != first_node.sexp_type
668
- if :array == first_node.sexp_type
669
- array_size += first_node.array_size
670
- end
671
- break
672
- end
673
- end
674
- end
675
- array_size
676
- end
677
- end
678
-
679
- # Get the array values.
680
- #
681
- # s(:array,
682
- # s(:args_add,
683
- # s(:args_add, s(:args_new), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "first_name", s(1, 2))))),
684
- # s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "last_name", s(1, 16))))
685
- # )
686
- # )
687
- # => [
688
- # s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "first_name", s(1, 2)))),
689
- # s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "last_name", s(1, 16))))
690
- # ]
691
- #
692
- # @return [Array] array values
693
- def array_values
694
- if :array == sexp_type
695
- if nil == self[1]
696
- []
697
- elsif :qwords_add == self[1].sexp_type
698
- self[1].array_values
699
- else
700
- arguments.all
701
- end
702
- elsif :qwords_add
703
- values = []
704
- node = self
705
- while true
706
- if :qwords_add == node.sexp_type
707
- values.unshift node[2]
708
- node = node[1]
709
- elsif :qwords_new == node.sexp_type
710
- break
711
- end
712
- end
713
- values
714
- end
715
- end
716
-
717
- # old method for alias node.
718
- #
719
- # s(:alias,
720
- # s(:symbol_literal, s(:@ident, "new", s(1, 6))),
721
- # s(:symbol_literal, s(:@ident, "old", s(1, 10)))
722
- # )
723
- # => s(:symbol_literal, s(:@ident, "old", s(1, 10))),
724
- def old_method
725
- self[2]
726
- end
727
-
728
- # new method for alias node.
729
- #
730
- # s(:alias,
731
- # s(:symbol_literal, s(:@ident, "new", s(1, 6))),
732
- # s(:symbol_literal, s(:@ident, "old", s(1, 10)))
733
- # )
734
- # => s(:symbol_literal, s(:@ident, "new", s(1, 6))),
735
- def new_method
736
- self[1]
737
- end
738
-
739
- # To object.
740
- #
741
- # s(:array,
742
- # s(:args_add,
743
- # s(:args_add, s(:args_new), s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "first_name", s(1, 2))))),
744
- # s(:string_literal, s(:string_add, s(:string_content), s(:@tstring_content, "last_name", s(1, 16))))
745
- # )
746
- # )
747
- # => ["first_name", "last_name"]
748
- #
749
- # @return [Object]
750
- def to_object
751
- case sexp_type
752
- when :array
753
- array_values.map(&:to_s)
754
- else
755
- to_s
756
- end
757
- end
758
-
759
- # to_s.
760
- #
761
- # @return [String] to_s
762
- def to_s
763
- case sexp_type
764
- when :string_literal, :xstring_literal, :string_content, :const_ref, :symbol_literal, :symbol,
765
- :args_add_block, :var_ref, :vcall, :var_field,
766
- :@ident, :@tstring_content, :@const, :@ivar, :@kw, :@gvar, :@cvar
767
- self[1].to_s
768
- when :string_add
769
- if s(:string_content) == self[1]
770
- self[2].to_s
771
- else
772
- self[1].to_s
773
- end
774
- when :args_add
775
- if s(:args_new) == self[1]
776
- self[2].to_s
777
- else
778
- self[1].to_s
779
- end
780
- when :qwords_add
781
- if s(:qwords_new) == self[1]
782
- self[2].to_s
783
- else
784
- self[1].to_s
785
- end
786
- when :const_path_ref
787
- "#{self[1]}::#{self[2]}"
788
- when :@label
789
- self[1].to_s[0..-2]
790
- when :aref
791
- "#{self[1]}[#{self[2]}]"
792
- when :call, :field
793
- "#{self.subject}.#{self.message}"
794
- else
795
- ""
796
- end
797
- end
798
-
799
- # check if the self node is a const.
800
- def const?
801
- :@const == self.sexp_type || ([:var_ref, :vcall].include?(self.sexp_type) && :@const == self[1].sexp_type)
802
- end
803
-
804
- # true
805
- def present?
806
- true
807
- end
808
-
809
- # false
810
- def blank?
811
- false
812
- end
813
-
814
- # remove the line and column info from sexp.
815
- def remove_line_and_column
816
- node = self.clone
817
- last_node = node.last
818
- if Sexp === last_node && last_node.size == 2 && last_node.first.is_a?(Integer) && last_node.last.is_a?(Integer)
819
- node.delete_at(-1)
820
- end
821
- node.sexp_body.each_with_index do |child, index|
822
- if Sexp === child
823
- node[index+1] = child.remove_line_and_column
824
- end
825
- end
826
- node
827
- end
828
-
829
- # if the return value of these methods is nil, then return RailsBestPractices::Core::Nil.new instead
830
- [:sexp_type, :subject, :message, :arguments, :argument, :class_name, :base_class, :method_name, :body, :block, :conditional_statement, :left_value, :right_value].each do |method|
831
- class_eval <<-EOS
832
- alias_method :origin_#{method}, :#{method}
833
-
834
- def #{method}
835
- ret = origin_#{method}
836
- ret.nil? ? RailsBestPractices::Core::Nil.new : ret
837
- end
838
- EOS
839
- end
840
- end