rails_best_practices 0.2.4 → 0.2.5
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.
- data/README.textile +2 -1
- data/VERSION +1 -1
- data/lib/rails_best_practices/checks.rb +1 -0
- data/lib/rails_best_practices/checks/always_add_db_index_check.rb +83 -0
- data/lib/rails_best_practices/checks/check.rb +1 -1
- data/lib/rails_best_practices/checks/law_of_demeter_check.rb +1 -1
- data/lib/rails_best_practices/checks/use_scope_access_check.rb +2 -2
- data/lib/rails_best_practices/command.rb +17 -2
- data/lib/rails_best_practices/core/visitable_sexp.rb +5 -7
- data/rails_best_practices.gemspec +4 -1
- data/rails_best_practices.yml +1 -0
- data/spec/rails_best_practices/checks/always_add_db_index_check_spec.rb +153 -0
- metadata +4 -1
data/README.textile
CHANGED
@@ -45,6 +45,7 @@ KeepFindersOnTheirOwnModelCheck: { }
|
|
45
45
|
LawOfDemeterCheck: { }
|
46
46
|
UseObserverCheck: { }
|
47
47
|
IsolateSeedDataCheck: { }
|
48
|
+
AlwaysAddDbIndexCheck: { }
|
48
49
|
</code></pre>
|
49
50
|
|
50
51
|
*************************************************
|
@@ -79,7 +80,7 @@ h2. Progress
|
|
79
80
|
|
80
81
|
* Lesson 4. Migration
|
81
82
|
## [-Isolating Seed Data-]
|
82
|
-
## Always add DB index
|
83
|
+
## [-Always add DB index-]
|
83
84
|
|
84
85
|
* Lesson 5. Controller
|
85
86
|
## Use before_filter
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.5
|
@@ -14,3 +14,4 @@ require 'rails_best_practices/checks/keep_finders_on_their_own_model_check'
|
|
14
14
|
require 'rails_best_practices/checks/law_of_demeter_check'
|
15
15
|
require 'rails_best_practices/checks/use_observer_check'
|
16
16
|
require 'rails_best_practices/checks/isolate_seed_data_check'
|
17
|
+
require 'rails_best_practices/checks/always_add_db_index_check'
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rails_best_practices/checks/check'
|
2
|
+
|
3
|
+
module RailsBestPractices
|
4
|
+
module Checks
|
5
|
+
class AlwaysAddDbIndexCheck < Check
|
6
|
+
|
7
|
+
def interesting_nodes
|
8
|
+
[:defs]
|
9
|
+
end
|
10
|
+
|
11
|
+
def interesting_files
|
12
|
+
/db\/migrate\/.*rb/
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
super
|
17
|
+
@files = []
|
18
|
+
@references = {}
|
19
|
+
@indexes = {}
|
20
|
+
@parse = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def evaluate_start(node)
|
24
|
+
if @files.include? node.file
|
25
|
+
@parse = true if :up == node.message
|
26
|
+
else
|
27
|
+
@files << node.file
|
28
|
+
end
|
29
|
+
|
30
|
+
if @parse and :up == node.message
|
31
|
+
@references.each do |table_name, column_names|
|
32
|
+
differences = column_names - (@indexes[table_name] || [])
|
33
|
+
hint = differences.collect {|column_name| "#{table_name} => #{column_name}"}.join(', ')
|
34
|
+
add_error "always add db index (#{hint})" unless differences.empty?
|
35
|
+
end
|
36
|
+
else
|
37
|
+
remember_references(node.body)
|
38
|
+
remember_indexes(node.body)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def remember_references(node)
|
45
|
+
create_table_node = node.grep_nodes({:node_type => :call, :message => :create_table}).first
|
46
|
+
if create_table_node
|
47
|
+
table_name = eval(create_table_node.arguments[1].to_ruby).to_s
|
48
|
+
node.grep_nodes({:node_type => :call, :message => :integer}).each do |integer_node|
|
49
|
+
column_name = eval(integer_node.arguments[1].to_ruby).to_s
|
50
|
+
if column_name =~ /_id$/
|
51
|
+
@references[table_name] ||= []
|
52
|
+
@references[table_name] << column_name
|
53
|
+
end
|
54
|
+
end
|
55
|
+
node.grep_nodes({:node_type => :call, :message => :references}).each do |references_node|
|
56
|
+
column_name = eval(references_node.arguments[1].to_ruby).to_s + "_id"
|
57
|
+
@references[table_name] ||= []
|
58
|
+
@references[table_name] << column_name
|
59
|
+
end
|
60
|
+
node.grep_nodes({:node_type => :call, :message => :column}).each do |column_node|
|
61
|
+
if 'integer' == eval(column_node.arguments[2].to_ruby).to_s
|
62
|
+
column_name = eval(column_node.arguments[1].to_ruby).to_s
|
63
|
+
if column_name =~ /_id$/
|
64
|
+
@references[table_name] ||= []
|
65
|
+
@references[table_name] << column_name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def remember_indexes(node)
|
73
|
+
add_index_nodes = node.grep_nodes({:node_type => :call, :message => :add_index})
|
74
|
+
add_index_nodes.each do |add_index_node|
|
75
|
+
table_name = eval(add_index_node.arguments[1].to_ruby).to_s
|
76
|
+
column_name = eval(add_index_node.arguments[2].to_ruby).to_s
|
77
|
+
@indexes[table_name] ||= []
|
78
|
+
@indexes[table_name] << column_name
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -29,7 +29,7 @@ module RailsBestPractices
|
|
29
29
|
private
|
30
30
|
|
31
31
|
def remember_belongs_to(node)
|
32
|
-
node.
|
32
|
+
node.body.grep_nodes(:message => :belongs_to).collect do |body_node|
|
33
33
|
class_name = node.subject.to_s.underscore
|
34
34
|
@associations[class_name] ||= []
|
35
35
|
@associations[class_name] << eval(body_node.arguments[1].to_ruby).to_s
|
@@ -26,7 +26,7 @@ module RailsBestPractices
|
|
26
26
|
|
27
27
|
condition_node.message == :== and
|
28
28
|
(current_user?(condition_node.arguments.call) or current_user?(condition_node.subject)) and
|
29
|
-
(node.false_node.
|
29
|
+
(node.false_node.body.any? {|n| n.message == :redirect_to} or node.true_node.method_body.any? {|n| n.message == :redirect_to})
|
30
30
|
end
|
31
31
|
|
32
32
|
def current_user?(call_node)
|
@@ -35,4 +35,4 @@ module RailsBestPractices
|
|
35
35
|
|
36
36
|
end
|
37
37
|
end
|
38
|
-
end
|
38
|
+
end
|
@@ -9,7 +9,22 @@ def expand_dirs_to_files *dirs
|
|
9
9
|
else
|
10
10
|
p
|
11
11
|
end
|
12
|
-
}.flatten.sort
|
12
|
+
}.flatten.sort { |a, b|
|
13
|
+
# for law_of_demeter_check
|
14
|
+
if a =~ /models\/.*rb/
|
15
|
+
-1
|
16
|
+
elsif b =~ /models\/.*rb/
|
17
|
+
1
|
18
|
+
else
|
19
|
+
a <=> b
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
# for always_add_db_index_check
|
25
|
+
def add_duplicate_migration_files files
|
26
|
+
migration_files = files.select { |file| file.index("db/migrate") }
|
27
|
+
(files << migration_files).flatten
|
13
28
|
end
|
14
29
|
|
15
30
|
options = {}
|
@@ -25,7 +40,7 @@ OptionParser.new do |opts|
|
|
25
40
|
end
|
26
41
|
|
27
42
|
runner = RailsBestPractices::Core::Runner.new
|
28
|
-
expand_dirs_to_files(ARGV).each
|
43
|
+
add_duplicate_migration_files(expand_dirs_to_files(ARGV)).each { |file| runner.check_file(file) }
|
29
44
|
runner.errors.each {|error| puts error}
|
30
45
|
puts "\nFound #{runner.errors.size} errors."
|
31
46
|
|
@@ -53,7 +53,7 @@ class Sexp
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def message
|
56
|
-
if [:attrasgn, :call].include? node_type
|
56
|
+
if [:attrasgn, :call, :defs].include? node_type
|
57
57
|
self[2]
|
58
58
|
end
|
59
59
|
end
|
@@ -88,15 +88,13 @@ class Sexp
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
-
def
|
91
|
+
def body
|
92
92
|
if :block == node_type
|
93
93
|
self[1..-1]
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
def class_body
|
98
|
-
if :class == node_type
|
94
|
+
elsif :class == node_type
|
99
95
|
self[3]
|
96
|
+
elsif :defs == node_type
|
97
|
+
self[4]
|
100
98
|
end
|
101
99
|
end
|
102
100
|
|
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rails_best_practices}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Richard Huang"]
|
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
|
|
28
28
|
"lib/rails_best_practices.rb",
|
29
29
|
"lib/rails_best_practices/checks.rb",
|
30
30
|
"lib/rails_best_practices/checks/add_model_virtual_attribute_check.rb",
|
31
|
+
"lib/rails_best_practices/checks/always_add_db_index_check.rb",
|
31
32
|
"lib/rails_best_practices/checks/check.rb",
|
32
33
|
"lib/rails_best_practices/checks/isolate_seed_data_check.rb",
|
33
34
|
"lib/rails_best_practices/checks/keep_finders_on_their_own_model_check.rb",
|
@@ -54,6 +55,7 @@ Gem::Specification.new do |s|
|
|
54
55
|
"rails_best_practices.gemspec",
|
55
56
|
"rails_best_practices.yml",
|
56
57
|
"spec/rails_best_practices/checks/add_model_virtual_attribute_check_spec.rb",
|
58
|
+
"spec/rails_best_practices/checks/always_add_db_index_check_spec.rb",
|
57
59
|
"spec/rails_best_practices/checks/isolate_seed_data_check_spec.rb",
|
58
60
|
"spec/rails_best_practices/checks/keep_finders_on_their_own_model_check_spec.rb",
|
59
61
|
"spec/rails_best_practices/checks/law_of_demeter_check_spec.rb",
|
@@ -94,6 +96,7 @@ Gem::Specification.new do |s|
|
|
94
96
|
"spec/rails_best_practices/checks/move_finder_to_named_scope_check_spec.rb",
|
95
97
|
"spec/rails_best_practices/checks/needless_deep_nesting_check_spec.rb",
|
96
98
|
"spec/rails_best_practices/checks/overuse_route_customizations_check_spec.rb",
|
99
|
+
"spec/rails_best_practices/checks/always_add_db_index_check_spec.rb",
|
97
100
|
"spec/spec_helper.rb"
|
98
101
|
]
|
99
102
|
|
data/rails_best_practices.yml
CHANGED
@@ -0,0 +1,153 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe RailsBestPractices::Checks::AlwaysAddDbIndexCheck do
|
4
|
+
before(:each) do
|
5
|
+
@runner = RailsBestPractices::Core::Runner.new(RailsBestPractices::Checks::AlwaysAddDbIndexCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should always add db index" do
|
9
|
+
content = <<-EOF
|
10
|
+
class CreateComments < ActiveRecord::Migration
|
11
|
+
def self.up
|
12
|
+
create_table "comments", :force => true do |t|
|
13
|
+
t.string :content
|
14
|
+
t.integer :post_id
|
15
|
+
t.integer :user_id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.down
|
20
|
+
drop_table "comments"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
EOF
|
24
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
25
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
26
|
+
errors = @runner.errors
|
27
|
+
errors.should_not be_empty
|
28
|
+
errors[0].to_s.should == "db/migrate/20090918130258_create_comments.rb:2 - always add db index (comments => post_id, comments => user_id)"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should always add db index with column has no id" do
|
32
|
+
content = <<-EOF
|
33
|
+
class CreateComments < ActiveRecord::Migration
|
34
|
+
def self.up
|
35
|
+
create_table "comments", :force => true do |t|
|
36
|
+
t.string :content
|
37
|
+
t.integer :position
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.down
|
42
|
+
drop_table "comments"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
EOF
|
46
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
47
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
48
|
+
errors = @runner.errors
|
49
|
+
errors.should be_empty
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should always add db index with references" do
|
53
|
+
content = <<-EOF
|
54
|
+
class CreateComments < ActiveRecord::Migration
|
55
|
+
def self.up
|
56
|
+
create_table "comments", :force => true do |t|
|
57
|
+
t.string :content
|
58
|
+
t.references :post
|
59
|
+
t.references :user
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.down
|
64
|
+
drop_table "comments"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
EOF
|
68
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
69
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
70
|
+
errors = @runner.errors
|
71
|
+
errors.should_not be_empty
|
72
|
+
errors[0].to_s.should == "db/migrate/20090918130258_create_comments.rb:2 - always add db index (comments => post_id, comments => user_id)"
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should always add db index with column" do
|
76
|
+
content = <<-EOF
|
77
|
+
class CreateComments < ActiveRecord::Migration
|
78
|
+
def self.up
|
79
|
+
create_table "comments", :force => true do |t|
|
80
|
+
t.string :content
|
81
|
+
t.column :post_id, :integer
|
82
|
+
t.column :user_id, :integer
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.down
|
87
|
+
drop_table "comments"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
EOF
|
91
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
92
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
93
|
+
errors = @runner.errors
|
94
|
+
errors.should_not be_empty
|
95
|
+
errors[0].to_s.should == "db/migrate/20090918130258_create_comments.rb:2 - always add db index (comments => post_id, comments => user_id)"
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should not always add db index with add_index" do
|
99
|
+
content = <<-EOF
|
100
|
+
class CreateComments < ActiveRecord::Migration
|
101
|
+
def self.up
|
102
|
+
create_table "comments", :force => true do |t|
|
103
|
+
t.string :content
|
104
|
+
t.integer :post_id
|
105
|
+
t.integer :user_id
|
106
|
+
end
|
107
|
+
|
108
|
+
add_index :comments, :post_id
|
109
|
+
add_index :comments, :user_id
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.down
|
113
|
+
drop_table "comments"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
EOF
|
117
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
118
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
119
|
+
errors = @runner.errors
|
120
|
+
errors.should be_empty
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should not always add db index with add_index in another migration file" do
|
124
|
+
content = <<-EOF
|
125
|
+
class CreateComments < ActiveRecord::Migration
|
126
|
+
def self.up
|
127
|
+
create_table "comments", :force => true do |t|
|
128
|
+
t.string :content
|
129
|
+
t.integer :post_id
|
130
|
+
t.integer :user_id
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.down
|
135
|
+
drop_table "comments"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
EOF
|
139
|
+
add_index_content = <<-EOF
|
140
|
+
class AddIndexesToComments < ActiveRecord::Migration
|
141
|
+
def self.up
|
142
|
+
add_index :comments, :post_id
|
143
|
+
add_index :comments, :user_id
|
144
|
+
end
|
145
|
+
end
|
146
|
+
EOF
|
147
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
148
|
+
@runner.check('db/migrate/20090919130258_add_indexes_to_comments.rb', content)
|
149
|
+
@runner.check('db/migrate/20090918130258_create_comments.rb', content)
|
150
|
+
@runner.check('db/migrate/20090919130258_add_indexes_to_comments.rb', content)
|
151
|
+
errors = @runner.errors
|
152
|
+
end
|
153
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_best_practices
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
@@ -51,6 +51,7 @@ files:
|
|
51
51
|
- lib/rails_best_practices.rb
|
52
52
|
- lib/rails_best_practices/checks.rb
|
53
53
|
- lib/rails_best_practices/checks/add_model_virtual_attribute_check.rb
|
54
|
+
- lib/rails_best_practices/checks/always_add_db_index_check.rb
|
54
55
|
- lib/rails_best_practices/checks/check.rb
|
55
56
|
- lib/rails_best_practices/checks/isolate_seed_data_check.rb
|
56
57
|
- lib/rails_best_practices/checks/keep_finders_on_their_own_model_check.rb
|
@@ -77,6 +78,7 @@ files:
|
|
77
78
|
- rails_best_practices.gemspec
|
78
79
|
- rails_best_practices.yml
|
79
80
|
- spec/rails_best_practices/checks/add_model_virtual_attribute_check_spec.rb
|
81
|
+
- spec/rails_best_practices/checks/always_add_db_index_check_spec.rb
|
80
82
|
- spec/rails_best_practices/checks/isolate_seed_data_check_spec.rb
|
81
83
|
- spec/rails_best_practices/checks/keep_finders_on_their_own_model_check_spec.rb
|
82
84
|
- spec/rails_best_practices/checks/law_of_demeter_check_spec.rb
|
@@ -139,4 +141,5 @@ test_files:
|
|
139
141
|
- spec/rails_best_practices/checks/move_finder_to_named_scope_check_spec.rb
|
140
142
|
- spec/rails_best_practices/checks/needless_deep_nesting_check_spec.rb
|
141
143
|
- spec/rails_best_practices/checks/overuse_route_customizations_check_spec.rb
|
144
|
+
- spec/rails_best_practices/checks/always_add_db_index_check_spec.rb
|
142
145
|
- spec/spec_helper.rb
|