rails_best_practices 1.16.0 → 1.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -4
  3. data/CHANGELOG.md +6 -0
  4. data/lib/rails_best_practices/analyzer.rb +0 -1
  5. data/lib/rails_best_practices/core/check.rb +2 -1
  6. data/lib/rails_best_practices/core/checks_loader.rb +1 -0
  7. data/lib/rails_best_practices/core/klasses.rb +6 -11
  8. data/lib/rails_best_practices/core/methods.rb +1 -0
  9. data/lib/rails_best_practices/core/runner.rb +1 -0
  10. data/lib/rails_best_practices/prepares/model_prepare.rb +1 -0
  11. data/lib/rails_best_practices/reviews/add_model_virtual_attribute_review.rb +1 -0
  12. data/lib/rails_best_practices/reviews/always_add_db_index_review.rb +14 -3
  13. data/lib/rails_best_practices/reviews/hash_syntax_review.rb +1 -0
  14. data/lib/rails_best_practices/reviews/isolate_seed_data_review.rb +1 -0
  15. data/lib/rails_best_practices/reviews/keep_finders_on_their_own_model_review.rb +1 -0
  16. data/lib/rails_best_practices/reviews/law_of_demeter_review.rb +1 -0
  17. data/lib/rails_best_practices/reviews/move_code_into_controller_review.rb +1 -0
  18. data/lib/rails_best_practices/reviews/move_code_into_helper_review.rb +1 -0
  19. data/lib/rails_best_practices/reviews/move_finder_to_named_scope_review.rb +1 -0
  20. data/lib/rails_best_practices/reviews/needless_deep_nesting_review.rb +1 -0
  21. data/lib/rails_best_practices/reviews/overuse_route_customizations_review.rb +1 -0
  22. data/lib/rails_best_practices/reviews/protect_mass_assignment_review.rb +4 -3
  23. data/lib/rails_best_practices/reviews/remove_empty_helpers_review.rb +1 -0
  24. data/lib/rails_best_practices/reviews/remove_unused_methods_in_controllers_review.rb +1 -0
  25. data/lib/rails_best_practices/reviews/remove_unused_methods_in_helpers_review.rb +1 -0
  26. data/lib/rails_best_practices/reviews/remove_unused_methods_in_models_review.rb +1 -0
  27. data/lib/rails_best_practices/reviews/replace_complex_creation_with_factory_method_review.rb +1 -0
  28. data/lib/rails_best_practices/reviews/restrict_auto_generated_routes_review.rb +1 -0
  29. data/lib/rails_best_practices/reviews/simplify_render_in_views_review.rb +1 -0
  30. data/lib/rails_best_practices/reviews/use_before_filter_review.rb +3 -2
  31. data/lib/rails_best_practices/reviews/use_model_association_review.rb +1 -0
  32. data/lib/rails_best_practices/reviews/use_multipart_alternative_as_content_type_of_email_review.rb +1 -0
  33. data/lib/rails_best_practices/reviews/use_observer_review.rb +1 -0
  34. data/lib/rails_best_practices/reviews/use_parentheses_in_method_def_review.rb +1 -0
  35. data/lib/rails_best_practices/reviews/use_query_attribute_review.rb +3 -2
  36. data/lib/rails_best_practices/reviews/use_scope_access_review.rb +1 -0
  37. data/lib/rails_best_practices/version.rb +1 -1
  38. data/spec/rails_best_practices/reviews/always_add_db_index_review_spec.rb +27 -0
  39. data/spec/rails_best_practices/reviews/protect_mass_assignment_review_spec.rb +5 -6
  40. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6148fbef051b64b71999a14775871a94ed3bd984
4
- data.tar.gz: 62ec9d8506b481f0a1ae715e9524f0bffdf00590
3
+ metadata.gz: c0b67ef3e8abe665b14bf2dd398755f5b1d84e05
4
+ data.tar.gz: 4ea8cfbcb9a26b10b070e86b2deac14e2f9ea751
5
5
  SHA512:
6
- metadata.gz: b3252c4c6a5a54919b419965d77e4baf7d68c7bfe21142ad09586ff333502211e7da1df85e15eb01a911c2ee96302255476658977251fc2aeeb08ab19aa27984
7
- data.tar.gz: b0c595abc0835de3d4afad189d7531ed9807c261405d2a8d911c018562cbb8df8b1a69027a60df3dc6d22f073cdf86e6fc6abe00ce44381411ffffe766b972b9
6
+ metadata.gz: c032c3904c6f27437c7b8be4a728c74ab543d6f14d1c2d620d8dac8dda029de46aa15f52c31b86d07e45dee2544f7165d991c7dc652731c7227e76970574eefe
7
+ data.tar.gz: dada2172b41c479b90ec443f8a99a9a04bf68833ceea23b5f39ba5e9c572b8686797079c1c15eead93c2a7ea066fe40970f4257342055215574b804e6049e0a9
data/.travis.yml CHANGED
@@ -2,7 +2,4 @@ sudo: false
2
2
  language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
- - 1.9.3
6
- - 2.0.0
7
- - 2.1
8
- - 2.2
5
+ - 2.3.0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Next Release
2
2
 
3
+ ## 1.17.0 (2016-07-14)
4
+
5
+ * Detect index option in column creation
6
+ * Check against activerecord not rails
7
+ * Optimize methods of Klass for performance
8
+
3
9
  ## 1.16.0 (2016-02-22)
4
10
 
5
11
  * Add check destroy return value review
@@ -145,7 +145,6 @@ module RailsBestPractices
145
145
  }.flatten
146
146
  end
147
147
 
148
-
149
148
  # sort files, models first, mailers, helpers, and then sort other files by characters.
150
149
  #
151
150
  # models and mailers first as for prepare process.
@@ -130,7 +130,8 @@ module RailsBestPractices
130
130
 
131
131
  # remember the class name
132
132
  add_callback :start_class do |node|
133
- @klass = Core::Klass.new(node.class_name.to_s, node.base_class.to_s, classable_modules)
133
+ base_class_name = node.base_class.is_a?(CodeAnalyzer::Nil) ? nil : node.base_class.to_s
134
+ @klass = Core::Klass.new(node.class_name.to_s, base_class_name, classable_modules)
134
135
  klasses << @klass
135
136
  end
136
137
 
@@ -17,6 +17,7 @@ module RailsBestPractices
17
17
  end
18
18
 
19
19
  private
20
+
20
21
  # read the checks from yaml config.
21
22
  def checks_from_config
22
23
  @checks ||= YAML.load_file @config
@@ -14,19 +14,14 @@ module RailsBestPractices
14
14
 
15
15
  # Class info includes class name, extend class name and module names.
16
16
  class Klass
17
+ attr_reader :extend_class_name, :class_name
18
+
17
19
  def initialize(class_name, extend_class_name, modules)
18
- @class_name = class_name
19
- @extend_class_name = extend_class_name
20
20
  @modules = modules.dup
21
- end
22
-
23
- def class_name
24
- @modules.map { |modu| "#{modu}::" }.join("") + @class_name
25
- end
26
-
27
- def extend_class_name
28
- if @extend_class_name.present?
29
- @modules.map { |modu| "#{modu}::" }.join("") + @extend_class_name
21
+ base = @modules.map { |modu| "#{modu}::" }.join("")
22
+ @class_name = base + class_name
23
+ if extend_class_name
24
+ @extend_class_name = base + extend_class_name
30
25
  end
31
26
  end
32
27
 
@@ -132,6 +132,7 @@ module RailsBestPractices
132
132
  end
133
133
 
134
134
  private
135
+
135
136
  # Methods of a class.
136
137
  #
137
138
  # @param [String] class name
@@ -103,6 +103,7 @@ module RailsBestPractices
103
103
  end
104
104
 
105
105
  private
106
+
106
107
  # parse html template code, erb, haml and slim.
107
108
  #
108
109
  # @param [String] filename is the filename of the erb, haml or slim code.
@@ -121,6 +121,7 @@ module RailsBestPractices
121
121
  end
122
122
 
123
123
  private
124
+
124
125
  # remember associations, with class to association names.
125
126
  def remember_association(node)
126
127
  association_meta = node.message.to_s
@@ -42,6 +42,7 @@ module RailsBestPractices
42
42
  end
43
43
 
44
44
  private
45
+
45
46
  # check an attribute assignment node, if there is a array reference node in the right value of assignment node,
46
47
  # then remember this attribute assignment.
47
48
  def assign(node)
@@ -10,8 +10,9 @@ module RailsBestPractices
10
10
  # Review process:
11
11
  # only check the command and command_calls nodes and at the end of review process,
12
12
  # if the receiver of command node is "create_table", then remember the table names
13
- # if the receiver of command_call node is "integer" and suffix with id, then remember it as foreign key
14
- # if the sujbect of command_call node is "string", the name of it is _type suffixed and there is an integer column _id suffixed, then remember it as polymorphic foreign key
13
+ # if the receiver of command_call node is "integer" or "string" and suffix with _id, then remember it as foreign key
14
+ # if the receiver of command_call node is "string", the name of it is _type suffixed and there is an integer or string column _id suffixed, then remember it as polymorphic foreign key
15
+ # if the receiver of command_call node is remembered as foreign key and it have argument non-false "index", then remember the index columns
15
16
  # if the receiver of command node is "add_index", then remember the index columns
16
17
  # after all of these, at the end of review process
17
18
  #
@@ -81,6 +82,7 @@ module RailsBestPractices
81
82
  end
82
83
 
83
84
  private
85
+
84
86
  # remember the node as index columns, when used outside a table
85
87
  # block, i.e.
86
88
  # add_index :table_name, :column_name
@@ -109,7 +111,6 @@ module RailsBestPractices
109
111
  @table_nodes[@table_name] = node
110
112
  end
111
113
 
112
-
113
114
  # remember foreign key columns
114
115
  def remember_foreign_key_columns(node)
115
116
  table_name = @table_name
@@ -121,12 +122,22 @@ module RailsBestPractices
121
122
  else
122
123
  @foreign_keys[table_name] << foreign_key_column
123
124
  end
125
+ foreign_id_column = foreign_key_column
124
126
  elsif foreign_key_column =~ /(.*?)_type$/
125
127
  if @foreign_keys[table_name].delete("#{$1}_id")
126
128
  @foreign_keys[table_name] << ["#{$1}_id", "#{$1}_type"]
127
129
  else
128
130
  @foreign_keys[table_name] << foreign_key_column
129
131
  end
132
+ foreign_id_column = "#{$1}_id"
133
+ end
134
+
135
+ if foreign_id_column
136
+ index_node = node.arguments.all.last.hash_value('index')
137
+ if index_node.present? and "false" != index_node.to_s
138
+ @index_columns[table_name] ||= []
139
+ @index_columns[table_name] << foreign_id_column
140
+ end
130
141
  end
131
142
  end
132
143
 
@@ -21,6 +21,7 @@ module RailsBestPractices
21
21
  end
22
22
 
23
23
  protected
24
+
24
25
  # check if hash node is empty.
25
26
  def empty_hash?(node)
26
27
  s(:hash, nil) == node || s(:bare_assoc_hash, nil) == node
@@ -53,6 +53,7 @@ module RailsBestPractices
53
53
  end
54
54
 
55
55
  private
56
+
56
57
  # check assignment node,
57
58
  # if the right vavlue is a method_add_arg node with message "new",
58
59
  # then remember the left value as new variable.
@@ -35,6 +35,7 @@ module RailsBestPractices
35
35
  end
36
36
 
37
37
  private
38
+
38
39
  # check if the call node is the finder of other model.
39
40
  #
40
41
  # the message of the node should be one of find, all, first or last,
@@ -33,6 +33,7 @@ module RailsBestPractices
33
33
  end
34
34
 
35
35
  private
36
+
36
37
  # check if the call node can use delegate to avoid violating law of demeter.
37
38
  #
38
39
  # if the receiver of receiver of the call node matchs any in model names,
@@ -35,6 +35,7 @@ module RailsBestPractices
35
35
  end
36
36
 
37
37
  private
38
+
38
39
  # check if the node is a finder call node.
39
40
  def finder?(node)
40
41
  node.receiver.const? && FINDERS.include?(node.message.to_s)
@@ -36,6 +36,7 @@ module RailsBestPractices
36
36
  end
37
37
 
38
38
  private
39
+
39
40
  # check if the arguments of options_for_select are complex.
40
41
  #
41
42
  # if the first argument is an array,
@@ -27,6 +27,7 @@ module RailsBestPractices
27
27
  end
28
28
 
29
29
  private
30
+
30
31
  # check if the method_add_arg node is a finder.
31
32
  #
32
33
  # if the receiver of method_add_arg node is a constant,
@@ -49,6 +49,7 @@ module RailsBestPractices
49
49
  end
50
50
 
51
51
  private
52
+
52
53
  # check nested route.
53
54
  #
54
55
  # if the receiver of the method_add_block is with message "resources" or "resource",
@@ -64,6 +64,7 @@ module RailsBestPractices
64
64
  end
65
65
 
66
66
  private
67
+
67
68
  # check command_call node to calculate the count of member and collection custom routes.
68
69
  # this is for rails2 syntax.
69
70
  #
@@ -18,7 +18,7 @@ module RailsBestPractices
18
18
  # we treat it as mass assignment by default.
19
19
  add_callback :start_class do |node|
20
20
  @mass_assignement = true
21
- check_rails_version
21
+ check_activerecord_version
22
22
  check_whitelist_attributes_config
23
23
  check_include_forbidden_attributes_protection_config
24
24
  end
@@ -52,8 +52,9 @@ module RailsBestPractices
52
52
  end
53
53
 
54
54
  private
55
- def check_rails_version
56
- if Prepares.gems.gem_version("rails").to_i > 3
55
+
56
+ def check_activerecord_version
57
+ if Prepares.gems.gem_version("activerecord").to_i > 3
57
58
  @mass_assignement = false
58
59
  end
59
60
  end
@@ -22,6 +22,7 @@ module RailsBestPractices
22
22
  end
23
23
 
24
24
  protected
25
+
25
26
  def empty_body?(module_node)
26
27
  s(:bodystmt, s(:stmts_add, s(:stmts_new), s(:void_stmt)), nil, nil, nil) == module_node.body
27
28
  end
@@ -100,6 +100,7 @@ module RailsBestPractices
100
100
  end
101
101
 
102
102
  protected
103
+
103
104
  def methods
104
105
  @controller_methods
105
106
  end
@@ -32,6 +32,7 @@ module RailsBestPractices
32
32
  end
33
33
 
34
34
  protected
35
+
35
36
  def methods
36
37
  @helper_methods
37
38
  end
@@ -86,6 +86,7 @@ module RailsBestPractices
86
86
  end
87
87
 
88
88
  protected
89
+
89
90
  def methods
90
91
  @model_methods
91
92
  end
@@ -48,6 +48,7 @@ module RailsBestPractices
48
48
  end
49
49
 
50
50
  private
51
+
51
52
  # check the call node to see if it is with message "save" or "save!",
52
53
  # and the count attribute assignment on the receiver of the call node is greater than @assign_count defined,
53
54
  # then it is a complex creation, should be replaced with factory method.
@@ -85,6 +85,7 @@ module RailsBestPractices
85
85
  end
86
86
 
87
87
  private
88
+
88
89
  # check resources call, if the routes generated by resources does not exist in the controller.
89
90
  def check_resources(node)
90
91
  _check(node, RESOURCES_METHODS)
@@ -31,6 +31,7 @@ module RailsBestPractices
31
31
  end
32
32
 
33
33
  protected
34
+
34
35
  def include_partial?(hash_node)
35
36
  hash_node.hash_keys.include?("partial") && !hash_node.hash_value("partial").to_s.include?('/')
36
37
  end
@@ -38,13 +38,14 @@ module RailsBestPractices
38
38
  @first_sentences.each do |first_sentence, def_nodes|
39
39
  if def_nodes.size > @customize_count
40
40
  add_error "use before_filter for #{def_nodes.map { |node| node.method_name.to_s }.join(',')}",
41
- node.file,
42
- def_nodes.map(&:line_number).join(',')
41
+ node.file,
42
+ def_nodes.map(&:line_number).join(',')
43
43
  end
44
44
  end
45
45
  end
46
46
 
47
47
  private
48
+
48
49
  # check method define node, and remember the first sentence.
49
50
  def remember_first_sentence(node)
50
51
  first_sentence = node.body.statements.first
@@ -42,6 +42,7 @@ module RailsBestPractices
42
42
  end
43
43
 
44
44
  private
45
+
45
46
  # check an attribute assignment node, if its message is xxx_id,
46
47
  # then remember the receiver of the attribute assignment in @assignments.
47
48
  def attribute_assignment(node)
@@ -29,6 +29,7 @@ module RailsBestPractices
29
29
  end
30
30
 
31
31
  private
32
+
32
33
  # check if rails's syntax mailer views are canonical.
33
34
  #
34
35
  # @param [String] name method name in action_mailer
@@ -47,6 +47,7 @@ module RailsBestPractices
47
47
  end
48
48
 
49
49
  private
50
+
50
51
  # check a command node, if it is a callback definition, such as after_create, before_create,
51
52
  # then save the callback methods in @callbacks
52
53
  def remember_callback(node)
@@ -19,6 +19,7 @@ module RailsBestPractices
19
19
  end
20
20
 
21
21
  protected
22
+
22
23
  def no_parentheses_around_parameters?(def_node)
23
24
  :parent != def_node[2][0]
24
25
  end
@@ -42,13 +42,14 @@ module RailsBestPractices
42
42
  if query_attribute_node = query_attribute_node(condition_node)
43
43
  receiver_node = query_attribute_node.receiver
44
44
  add_error "use query attribute (#{receiver_node.receiver}.#{receiver_node.message}?)",
45
- node.file,
46
- query_attribute_node.line_number
45
+ node.file,
46
+ query_attribute_node.line_number
47
47
  end
48
48
  end
49
49
  end
50
50
 
51
51
  private
52
+
52
53
  # recursively check conditional statement nodes to see if there is a call node that may be
53
54
  # possible query attribute.
54
55
  def query_attribute_node(conditional_statement_node)
@@ -28,6 +28,7 @@ module RailsBestPractices
28
28
  end
29
29
 
30
30
  private
31
+
31
32
  # check a if node to see
32
33
  #
33
34
  # if the conditional statement is compared with current_user or current_user.id,
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module RailsBestPractices
3
- VERSION = "1.16.0"
3
+ VERSION = "1.17.0"
4
4
  end
@@ -295,6 +295,33 @@ module RailsBestPractices
295
295
  runner.after_review
296
296
  expect(runner.errors.size).to eq(0)
297
297
  end
298
+
299
+ it "should detect index option in column creation" do
300
+ content = <<-EOF
301
+ ActiveRecord::Schema.define(version: 20100603080629) do
302
+ create_table "comments", force: true do |t|
303
+ t.string "content"
304
+ t.integer "post_id", index: true
305
+ t.string "user_id", index: { unique: true }
306
+ t.integer "image_id", index: false
307
+ t.integer "link_id"
308
+ end
309
+ create_table "posts", force: true do |t|
310
+ end
311
+ create_table "users", id: :string, force: true do |t|
312
+ end
313
+ create_table "images", force: true do |t|
314
+ end
315
+ create_table "links", force: true do |t|
316
+ end
317
+ end
318
+ EOF
319
+ runner.review('db/schema.rb', content)
320
+ runner.after_review
321
+ expect(runner.errors.size).to eq(2)
322
+ expect(runner.errors[0].to_s).to eq("db/schema.rb:2 - always add db index (comments => [image_id])")
323
+ expect(runner.errors[1].to_s).to eq("db/schema.rb:2 - always add db index (comments => [link_id])")
324
+ end
298
325
  end
299
326
  end
300
327
  end
@@ -138,13 +138,13 @@ module RailsBestPractices
138
138
  end
139
139
  end
140
140
 
141
- context "rails 4" do
142
- it "should not protect mass assignment for rails 4" do
141
+ context "activerecord 4" do
142
+ it "should not protect mass assignment for activerecord 4" do
143
143
  content =<<-EOF
144
144
  GEM
145
145
  remote: http://rubygems.org
146
146
  specs:
147
- rails (4.0.0)
147
+ activerecord (4.0.0)
148
148
  EOF
149
149
  runner.prepare('Gemfile.lock', content)
150
150
  content =<<-EOF
@@ -155,12 +155,12 @@ module RailsBestPractices
155
155
  expect(runner.errors.size).to eq(0)
156
156
  end
157
157
 
158
- it "should protect mass assignment for rails 3" do
158
+ it "should protect mass assignment for activerecord 3" do
159
159
  content =<<-EOF
160
160
  GEM
161
161
  remote: http://rubygems.org
162
162
  specs:
163
- rails (3.2.13)
163
+ activerecord (3.2.13)
164
164
  EOF
165
165
  runner.prepare('Gemfile.lock', content)
166
166
  content =<<-EOF
@@ -172,7 +172,6 @@ module RailsBestPractices
172
172
  end
173
173
  end
174
174
 
175
-
176
175
  it "should not check ignored files" do
177
176
  runner = Core::Runner.new(prepares: [Prepares::GemfilePrepare.new, Prepares::ConfigPrepare.new, Prepares::InitializerPrepare.new],
178
177
  reviews: ProtectMassAssignmentReview.new(ignored_files: /app\/models\/user\.rb/))
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_best_practices
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.16.0
4
+ version: 1.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-22 00:00:00.000000000 Z
11
+ date: 2016-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport