rails_best_practices 1.16.0 → 1.17.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 (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