consistency_fail 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/.rspec +1 -0
- data/bin/consistency_fail +1 -31
- data/consistency_fail.gemspec +2 -1
- data/lib/consistency_fail.rb +38 -0
- data/lib/consistency_fail/index.rb +18 -0
- data/lib/consistency_fail/introspectors/has_one.rb +1 -1
- data/lib/consistency_fail/introspectors/polymorphic.rb +1 -2
- data/lib/consistency_fail/introspectors/validates_uniqueness_of.rb +1 -0
- data/lib/consistency_fail/models.rb +0 -1
- data/lib/consistency_fail/version.rb +1 -1
- data/spec/index_spec.rb +37 -18
- data/spec/introspectors/has_one_spec.rb +14 -89
- data/spec/introspectors/polymorphic_spec.rb +15 -66
- data/spec/introspectors/table_data_spec.rb +47 -36
- data/spec/introspectors/validates_uniqueness_of_spec.rb +47 -72
- data/spec/models_spec.rb +2 -3
- data/spec/reporter_spec.rb +88 -49
- data/spec/spec_helper.rb +11 -10
- data/spec/support/active_record.rb +1 -0
- data/spec/support/models/correct_account.rb +3 -0
- data/spec/support/models/correct_address.rb +3 -0
- data/spec/support/models/correct_attachment.rb +5 -0
- data/spec/support/models/correct_person.rb +3 -0
- data/spec/support/models/correct_post.rb +3 -0
- data/spec/support/models/correct_user.rb +7 -0
- data/spec/support/models/correct_user/credential.rb +3 -0
- data/spec/support/models/correct_user/phone.rb +4 -0
- data/spec/support/models/new_correct_person.rb +7 -0
- data/spec/support/models/nonexistent.rb +2 -0
- data/spec/support/models/wrong_account.rb +3 -0
- data/spec/support/models/wrong_address.rb +5 -0
- data/spec/support/models/wrong_attachment.rb +3 -0
- data/spec/support/models/wrong_business.rb +3 -0
- data/spec/support/models/wrong_person.rb +3 -0
- data/spec/support/models/wrong_post.rb +3 -0
- data/spec/support/models/wrong_user.rb +3 -0
- data/spec/support/schema.rb +121 -0
- metadata +68 -24
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
YzQzNDRjODdhN2RhN2UzZmVhMjIwYzkxMDQ5NTY3NjEyMDM2NWM5Mw==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e39eaf9c01eac716941d478876cccf815cac17df
|
4
|
+
data.tar.gz: ea14788fa3224a69441d52803d69992d910b360c
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
ZDgwZGM1YmE1MjhlMzA1MzVlZDU2YzJlNGJmNmQ2ZjMxNWQ0YzE4OTY5YjEx
|
11
|
-
Y2ZiNWFjMThkODRjMGNiMzY4ZjYwNDFjMzU0Yzk3MDBiNTViZWM=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MzJmNWE4YTU4MWRjMTExMDYwN2JkNGQyYzBhMWNiOWRjNGY3ZmJlYjg5MzQ0
|
14
|
-
NzBlNDY3N2I2OWJiMWU1OWEzNDFkYzliZDkzYzcwZDA1ZmY4ZWUzOTBhODky
|
15
|
-
NzI4NTA5OWM0YjY3ZDYyNmNhMmVjMjM4N2U4MWRmY2VjNzlmMzc=
|
6
|
+
metadata.gz: c60df52fb547fc080706b1e38f550fc26fd4aacf9cfbae471f8d39cebce76e9f03df7b782df0df7fe13ab5496d27650d526a815e481786900dc0c523d8802aa6
|
7
|
+
data.tar.gz: d999a4beba6dddf954b7f5c670c117222b42247a64e92015bd4bae13ebc648df82988a49294eaac5bca22992a4b9daa719b98e997d06738c106b258a462cb35a
|
data/.rspec
CHANGED
data/bin/consistency_fail
CHANGED
@@ -13,37 +13,7 @@ require File.join(Dir.pwd, "config", "environment")
|
|
13
13
|
$:<< File.join(File.dirname(__FILE__), "..", "lib")
|
14
14
|
require "consistency_fail"
|
15
15
|
|
16
|
-
|
17
|
-
models.map do |m|
|
18
|
-
[m, introspector.missing_indexes(m)]
|
19
|
-
end.reject do |m, indexes|
|
20
|
-
indexes.empty?
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
models = ConsistencyFail::Models.new($LOAD_PATH)
|
25
|
-
models.preload_all
|
26
|
-
|
27
|
-
reporter = ConsistencyFail::Reporter.new
|
28
|
-
|
29
|
-
success = true
|
30
|
-
|
31
|
-
introspector = ConsistencyFail::Introspectors::ValidatesUniquenessOf.new
|
32
|
-
problems = problems(models.all, introspector)
|
33
|
-
reporter.report_validates_uniqueness_problems(problems)
|
34
|
-
success &&= problems.empty?
|
35
|
-
|
36
|
-
introspector = ConsistencyFail::Introspectors::HasOne.new
|
37
|
-
problems = problems(models.all, introspector)
|
38
|
-
reporter.report_has_one_problems(problems)
|
39
|
-
success &&= problems.empty?
|
40
|
-
|
41
|
-
introspector = ConsistencyFail::Introspectors::Polymorphic.new
|
42
|
-
problems = problems(models.all, introspector)
|
43
|
-
reporter.report_polymorphic_problems(problems)
|
44
|
-
success &&= problems.empty?
|
45
|
-
|
46
|
-
if success
|
16
|
+
if ConsistencyFail.run
|
47
17
|
exit 0
|
48
18
|
else
|
49
19
|
exit 1
|
data/consistency_fail.gemspec
CHANGED
@@ -22,7 +22,8 @@ EOF
|
|
22
22
|
s.license = "MIT"
|
23
23
|
|
24
24
|
s.add_development_dependency "activerecord", "~>3.0"
|
25
|
-
s.add_development_dependency "
|
25
|
+
s.add_development_dependency "sqlite3", "~>1.3"
|
26
|
+
s.add_development_dependency "rspec", "~>3.2"
|
26
27
|
|
27
28
|
s.files = `git ls-files`.split("\n")
|
28
29
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/lib/consistency_fail.rb
CHANGED
@@ -4,3 +4,41 @@ require 'consistency_fail/introspectors/validates_uniqueness_of'
|
|
4
4
|
require 'consistency_fail/introspectors/has_one'
|
5
5
|
require 'consistency_fail/introspectors/polymorphic'
|
6
6
|
require 'consistency_fail/reporter'
|
7
|
+
|
8
|
+
module ConsistencyFail
|
9
|
+
def self.run
|
10
|
+
models = ConsistencyFail::Models.new($LOAD_PATH)
|
11
|
+
models.preload_all
|
12
|
+
|
13
|
+
reporter = ConsistencyFail::Reporter.new
|
14
|
+
|
15
|
+
success = true
|
16
|
+
|
17
|
+
introspector = ConsistencyFail::Introspectors::ValidatesUniquenessOf.new
|
18
|
+
problems = problems(models.all, introspector)
|
19
|
+
reporter.report_validates_uniqueness_problems(problems)
|
20
|
+
success &&= problems.empty?
|
21
|
+
|
22
|
+
introspector = ConsistencyFail::Introspectors::HasOne.new
|
23
|
+
problems = problems(models.all, introspector)
|
24
|
+
reporter.report_has_one_problems(problems)
|
25
|
+
success &&= problems.empty?
|
26
|
+
|
27
|
+
introspector = ConsistencyFail::Introspectors::Polymorphic.new
|
28
|
+
problems = problems(models.all, introspector)
|
29
|
+
reporter.report_polymorphic_problems(problems)
|
30
|
+
success &&= problems.empty?
|
31
|
+
|
32
|
+
success
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def self.problems(models, introspector)
|
38
|
+
models.map do |m|
|
39
|
+
[m, introspector.missing_indexes(m)]
|
40
|
+
end.reject do |m, indexes|
|
41
|
+
indexes.empty?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -5,11 +5,29 @@ module ConsistencyFail
|
|
5
5
|
@model = model
|
6
6
|
@table_name = table_name
|
7
7
|
@columns = columns.map(&:to_s)
|
8
|
+
handle_associations
|
8
9
|
end
|
9
10
|
|
10
11
|
def ==(other)
|
11
12
|
self.table_name == other.table_name &&
|
12
13
|
self.columns.sort == other.columns.sort
|
13
14
|
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def handle_associations
|
19
|
+
references = @model.reflect_on_all_associations(:belongs_to)
|
20
|
+
names = references.map(&:name).map(&:to_s)
|
21
|
+
@columns.map! do |column|
|
22
|
+
next column unless names.include?(column)
|
23
|
+
reflection = @model.reflect_on_association(column.to_sym)
|
24
|
+
if reflection.options[:polymorphic]
|
25
|
+
[reflection.foreign_key, reflection.foreign_type]
|
26
|
+
else
|
27
|
+
reflection.foreign_key
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@columns.flatten!
|
31
|
+
end
|
14
32
|
end
|
15
33
|
end
|
@@ -16,7 +16,7 @@ module ConsistencyFail
|
|
16
16
|
as_id = "#{as}_id"
|
17
17
|
|
18
18
|
ConsistencyFail::Index.new(
|
19
|
-
a.
|
19
|
+
a.klass,
|
20
20
|
a.table_name.to_s,
|
21
21
|
[as_type, as_id]
|
22
22
|
)
|
@@ -37,7 +37,6 @@ module ConsistencyFail
|
|
37
37
|
existing_indexes.include?(index)
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
41
40
|
end
|
42
41
|
end
|
43
42
|
end
|
data/spec/index_spec.rb
CHANGED
@@ -1,40 +1,59 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'consistency_fail/index'
|
3
|
-
|
4
1
|
describe ConsistencyFail::Index do
|
5
|
-
|
2
|
+
|
3
|
+
let(:index) do
|
4
|
+
ConsistencyFail::Index.new(
|
5
|
+
CorrectAddress,
|
6
|
+
CorrectAddress.table_name,
|
7
|
+
["city", "state"]
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
6
11
|
describe "value objectiness" do
|
7
12
|
it "holds onto model, table name, and columns" do
|
8
|
-
model
|
9
|
-
index
|
10
|
-
expect(index.
|
11
|
-
|
12
|
-
|
13
|
+
expect(index.model).to eq(CorrectAddress)
|
14
|
+
expect(index.table_name).to eq("correct_addresses")
|
15
|
+
expect(index.columns).to eq(
|
16
|
+
["city", "state"]
|
17
|
+
)
|
13
18
|
end
|
14
19
|
|
15
20
|
it "leaves columns in the initial order (since we only care about presence, not performance)" do
|
16
|
-
index
|
17
|
-
|
21
|
+
expect(index.columns).to eq(
|
22
|
+
["city", "state"]
|
23
|
+
)
|
18
24
|
end
|
19
25
|
end
|
20
26
|
|
21
|
-
describe "equality test" do
|
27
|
+
describe "equality test" do
|
22
28
|
it "passes when everything matches" do
|
23
|
-
expect(
|
24
|
-
ConsistencyFail::Index.new(
|
29
|
+
expect(index).to eq(
|
30
|
+
ConsistencyFail::Index.new(
|
31
|
+
"CorrectAddress".constantize,
|
32
|
+
"correct_addresses",
|
33
|
+
["city", "state"]
|
34
|
+
)
|
25
35
|
)
|
26
36
|
end
|
27
37
|
|
28
38
|
it "fails when tables are different" do
|
29
|
-
expect(
|
30
|
-
ConsistencyFail::Index.new(
|
39
|
+
expect(index).not_to eq(
|
40
|
+
ConsistencyFail::Index.new(
|
41
|
+
CorrectAttachment,
|
42
|
+
CorrectAttachment.table_name,
|
43
|
+
["attachable_id", "attachable_type"]
|
44
|
+
)
|
31
45
|
)
|
32
46
|
end
|
33
47
|
|
34
48
|
it "fails when columns are different" do
|
35
|
-
expect(
|
36
|
-
ConsistencyFail::Index.new(
|
49
|
+
expect(index).not_to eq(
|
50
|
+
ConsistencyFail::Index.new(
|
51
|
+
CorrectAddress,
|
52
|
+
CorrectAddress.table_name,
|
53
|
+
["correct_user_id"]
|
54
|
+
)
|
37
55
|
)
|
38
56
|
end
|
39
57
|
end
|
58
|
+
|
40
59
|
end
|
@@ -1,96 +1,21 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'consistency_fail/introspectors/table_data'
|
3
|
-
require 'consistency_fail/introspectors/has_one'
|
4
|
-
|
5
1
|
describe ConsistencyFail::Introspectors::HasOne do
|
6
|
-
|
7
|
-
ConsistencyFail::Introspectors::HasOne.new(model)
|
8
|
-
end
|
9
|
-
|
10
|
-
describe "instances of has_one" do
|
11
|
-
it "finds none" do
|
12
|
-
model = fake_ar_model("User")
|
13
|
-
allow(model).to receive(:reflect_on_all_associations).and_return([])
|
14
|
-
|
15
|
-
expect(subject.instances(model)).to eq([])
|
16
|
-
end
|
17
|
-
|
18
|
-
it "finds one" do
|
19
|
-
model = fake_ar_model("User")
|
20
|
-
association = double("association", :macro => :has_one, :options => {})
|
21
|
-
allow(model).to receive(:reflect_on_all_associations).and_return([association])
|
22
|
-
|
23
|
-
expect(subject.instances(model)).to eq([association])
|
24
|
-
end
|
25
|
-
|
26
|
-
it "finds other associations, but not has_one" do
|
27
|
-
model = fake_ar_model("User")
|
28
|
-
validation = double("validation", :macro => :has_many)
|
29
|
-
allow(model).to receive(:reflect_on_all_associations).and_return([validation])
|
30
|
-
|
31
|
-
expect(subject.instances(model)).to eq([])
|
32
|
-
end
|
33
|
-
|
34
|
-
it "finds one, but it's a polymorphic association" do
|
35
|
-
model = fake_ar_model("User")
|
36
|
-
association = double("association", :macro => :has_one, :options => {:as => "addressable"})
|
37
|
-
allow(model).to receive(:reflect_on_all_associations).and_return([association])
|
38
|
-
|
39
|
-
expect(subject.instances(model)).to eq([])
|
40
|
-
end
|
41
|
-
|
42
|
-
it "finds one, but it's a :through association" do
|
43
|
-
model = fake_ar_model("User")
|
44
|
-
association = double("association", :macro => :has_one, :options => {:through => :amodel})
|
45
|
-
allow(model).to receive(:reflect_on_all_associations).and_return([association])
|
46
|
-
|
47
|
-
expect(subject.instances(model)).to eq([])
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
2
|
+
|
51
3
|
describe "finding missing indexes" do
|
52
|
-
before do
|
53
|
-
@association = double("association", :macro => :has_one, :options => {})
|
54
|
-
@model = fake_ar_model("User", :table_exists? => true,
|
55
|
-
:table_name => "users",
|
56
|
-
:class_name => "User",
|
57
|
-
:reflect_on_all_associations => [@association])
|
58
|
-
@address_class = double("Address Class")
|
59
|
-
@address_string = "Address"
|
60
|
-
allow(@address_string).to receive(:constantize).and_return(@address_class)
|
61
|
-
end
|
62
|
-
|
63
4
|
it "finds one" do
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
indexes = subject.missing_indexes(@model)
|
76
|
-
expect(indexes).to eq([ConsistencyFail::Index.new(fake_ar_model("Address"), "addresses", ["user_id"])])
|
77
|
-
end
|
78
|
-
|
5
|
+
indexes = subject.missing_indexes(WrongUser)
|
6
|
+
|
7
|
+
expect(indexes).to eq([
|
8
|
+
ConsistencyFail::Index.new(
|
9
|
+
WrongAddress,
|
10
|
+
WrongAddress.table_name,
|
11
|
+
["wrong_user_id"]
|
12
|
+
)
|
13
|
+
])
|
14
|
+
end
|
15
|
+
|
79
16
|
it "finds none when they're already in place" do
|
80
|
-
|
81
|
-
index = ConsistencyFail::Index.new(double('model'), "addresses", ["user_id"])
|
82
|
-
|
83
|
-
fake_connection = double("connection")
|
84
|
-
allow(@address_class).to receive_message_chain(:connection).and_return(fake_connection)
|
85
|
-
|
86
|
-
allow(ConsistencyFail::Introspectors::TableData).to receive_message_chain(:new, :unique_indexes_by_table).
|
87
|
-
with(@address_class, fake_connection, "addresses").
|
88
|
-
and_return([index])
|
89
|
-
|
90
|
-
expect(subject.missing_indexes(@model)).to eq([])
|
17
|
+
expect(subject.missing_indexes(CorrectUser)).to be_empty
|
91
18
|
end
|
92
|
-
|
93
19
|
end
|
20
|
+
|
94
21
|
end
|
95
|
-
|
96
|
-
|
@@ -1,77 +1,26 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'consistency_fail/introspectors/table_data'
|
3
|
-
require 'consistency_fail/introspectors/polymorphic'
|
4
|
-
|
5
1
|
describe ConsistencyFail::Introspectors::Polymorphic do
|
6
|
-
def introspector(model)
|
7
|
-
ConsistencyFail::Introspectors::Polymorphic.new(model)
|
8
|
-
end
|
9
|
-
|
10
|
-
describe "instances of polymorphic" do
|
11
|
-
it "finds none" do
|
12
|
-
model = fake_ar_model("User")
|
13
|
-
allow(model).to receive(:reflect_on_all_associations).and_return([])
|
14
|
-
|
15
|
-
expect(subject.instances(model)).to eq([])
|
16
|
-
end
|
17
|
-
|
18
|
-
it "finds one" do
|
19
|
-
model = fake_ar_model("User")
|
20
|
-
association = double("association", :macro => :has_one, :options => {:as => "addressable"})
|
21
|
-
allow(model).to receive(:reflect_on_all_associations).and_return([association])
|
22
|
-
|
23
|
-
expect(subject.instances(model)).to eq([association])
|
24
|
-
end
|
25
|
-
|
26
|
-
it "finds other has_one associations, but not polymorphic" do
|
27
|
-
model = fake_ar_model("User")
|
28
|
-
validation = double("association", :macro => :has_one, :options => {})
|
29
|
-
allow(model).to receive(:reflect_on_all_associations).and_return([validation])
|
30
|
-
|
31
|
-
expect(subject.instances(model)).to eq([])
|
32
|
-
end
|
33
|
-
|
34
|
-
it "finds other non has_one associations" do
|
35
|
-
model = fake_ar_model("User")
|
36
|
-
validation = double("association", :macro => :has_many)
|
37
|
-
allow(model).to receive(:reflect_on_all_associations).and_return([validation])
|
38
|
-
|
39
|
-
expect(subject.instances(model)).to eq([])
|
40
|
-
end
|
41
|
-
end
|
42
2
|
|
43
3
|
describe "finding missing indexes" do
|
44
|
-
before do
|
45
|
-
@association = double("association", :macro => :has_one, :options => {:as => "addressable"})
|
46
|
-
@model = fake_ar_model("User", :table_exists? => true,
|
47
|
-
:table_name => "users",
|
48
|
-
:class_name => "User",
|
49
|
-
:reflect_on_all_associations => [@association])
|
50
|
-
@address_class = double("Address Class")
|
51
|
-
@address_string = "Address"
|
52
|
-
allow(@address_string).to receive(:constantize).and_return(@address_class)
|
53
|
-
end
|
54
|
-
|
55
4
|
it "finds one" do
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
5
|
+
indexes = subject.missing_indexes(WrongPost)
|
6
|
+
|
7
|
+
expect(indexes).to eq([
|
8
|
+
ConsistencyFail::Index.new(
|
9
|
+
WrongAttachment,
|
10
|
+
WrongAttachment.table_name,
|
11
|
+
["attachable_type", "attachable_id"]
|
12
|
+
)
|
13
|
+
])
|
61
14
|
end
|
62
15
|
|
63
16
|
it "finds none when they're already in place" do
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
fake_connection = double("connection")
|
68
|
-
allow(@address_class).to receive_message_chain(:connection).and_return(fake_connection)
|
69
|
-
|
70
|
-
allow(ConsistencyFail::Introspectors::TableData).to receive_message_chain(:new, :unique_indexes_by_table).
|
71
|
-
with(@address_class, fake_connection, "addresses").
|
72
|
-
and_return([index])
|
17
|
+
expect(subject.missing_indexes(CorrectPost)).to be_empty
|
18
|
+
end
|
73
19
|
|
74
|
-
|
20
|
+
it "finds none with nested modules" do
|
21
|
+
expect(subject.missing_indexes(CorrectUser)).to eq([])
|
75
22
|
end
|
23
|
+
|
76
24
|
end
|
25
|
+
|
77
26
|
end
|