dbchecker 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/README.md +93 -0
- data/Rakefile +7 -0
- data/TODO.md +6 -0
- data/dbchecker.gemspec +29 -0
- data/lib/dbchecker.rb +8 -0
- data/lib/dbchecker/base.rb +14 -0
- data/lib/dbchecker/checker.rb +14 -0
- data/lib/dbchecker/dsl.rb +107 -0
- data/lib/dbchecker/railtie.rb +5 -0
- data/lib/dbchecker/version.rb +3 -0
- data/lib/tasks/db_check.rake +6 -0
- data/spec/dsl/check_duplicates_spec.rb +41 -0
- data/spec/dsl/check_equal_spec.rb +36 -0
- data/spec/dsl/check_negatives_spec.rb +36 -0
- data/spec/dsl/check_nil_spec.rb +35 -0
- data/spec/dsl/check_references_spec.rb +37 -0
- data/spec/dsl/check_zero_spec.rb +36 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/profile.rb +5 -0
- data/spec/dummy/app/models/provider.rb +3 -0
- data/spec/dummy/app/models/user.rb +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +59 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +10 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/migrate/20130703094110_create_users.rb +11 -0
- data/spec/dummy/db/migrate/20130703094241_create_profiles.rb +15 -0
- data/spec/dummy/db/migrate/20130703102715_create_providers.rb +9 -0
- data/spec/dummy/db/schema.rb +41 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/factories.rb +12 -0
- data/spec/spec_helper.rb +30 -0
- metadata +228 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 31be9ae06b5b43ee6505de97fd299296978c3411
|
4
|
+
data.tar.gz: 1d41bb6ce48d79e14a9d675fa61e566be36c6b03
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 81afc0e03a2e47a2398060a3a84bf959fbcbcf1827722423f5b514db3d17a4e4f704ad8f57351f84bd09c28b56e08a8dea0bfd9d3f9fb10f8da1177cfd55ec3f
|
7
|
+
data.tar.gz: 8231133864f955e49171b5c4297952e5e4bc162568af9d90af974c927bb00ce02893924f31995468f0c9336e9dae78bd5a51d30bd6c60067af9219122b8a1724
|
data/.gitignore
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
spec/dummy/log
|
19
|
+
spec/dummy/tmp
|
20
|
+
spec/dummy/public
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# Dbchecker
|
2
|
+
|
3
|
+
Dbchecker is a rails gem to help you maintain the consistency of your database.
|
4
|
+
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your rails application's Gemfile:
|
9
|
+
|
10
|
+
gem 'dbchecker'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
First, you need to have a place to put the definition of the checkers, so create it:
|
20
|
+
|
21
|
+
mkdir db/checks
|
22
|
+
|
23
|
+
Now, you can create inside it several files with the checks. The recommended structure is one file/class per model:
|
24
|
+
|
25
|
+
**db/checks/user.rb**
|
26
|
+
class UserChecker < Dbchecker::Checker
|
27
|
+
model :user
|
28
|
+
|
29
|
+
check_nil :email, :name
|
30
|
+
check_equal :follower_id, :followed_id
|
31
|
+
check_negative :logged_in_times
|
32
|
+
end
|
33
|
+
|
34
|
+
With the `model` directive, you are specifiying the model you are working with.
|
35
|
+
There are several checkers, explained below. Each of them return the ids of the affected models, if any.
|
36
|
+
|
37
|
+
|
38
|
+
## Checks
|
39
|
+
|
40
|
+
### check_nil
|
41
|
+
This checks for the presence of `nil` in the specified fields.
|
42
|
+
You can pass several fields to `check_nil`.
|
43
|
+
|
44
|
+
check_nil :important_field
|
45
|
+
check_nil :field1, :field2
|
46
|
+
|
47
|
+
### check_references
|
48
|
+
This checks for the presence of the referenced model.
|
49
|
+
You can pass several fields to `check_references`.
|
50
|
+
|
51
|
+
For example, if you have a `Profile` model with a `belongs_to :user`, that means that
|
52
|
+
in `Profile` you will have a `user_id` field. To check that the reference exists, use:
|
53
|
+
|
54
|
+
check_references :user
|
55
|
+
|
56
|
+
Note that here we are no passing the name of the field but the name of the relationship.
|
57
|
+
|
58
|
+
### check_negatives
|
59
|
+
This checks for negatives in the passed in fields.
|
60
|
+
|
61
|
+
check_negatives :points, :logged_in_times
|
62
|
+
|
63
|
+
### check_zero
|
64
|
+
This checks for zero in the passed in fields.
|
65
|
+
|
66
|
+
check_zero :warnings_left
|
67
|
+
|
68
|
+
### check_equal
|
69
|
+
This checks for the equality of two fields (that should be different).
|
70
|
+
You have to pass the name of both fields.
|
71
|
+
|
72
|
+
check_equal :follower_id, :followed_id
|
73
|
+
|
74
|
+
### check_duplicates
|
75
|
+
This checks for duplicates in the table.
|
76
|
+
|
77
|
+
check_duplicates :user_id
|
78
|
+
|
79
|
+
|
80
|
+
## Testing
|
81
|
+
|
82
|
+
Clone this git repo and then run the tests:
|
83
|
+
|
84
|
+
rake
|
85
|
+
|
86
|
+
|
87
|
+
## Contributing
|
88
|
+
|
89
|
+
1. Fork it
|
90
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
91
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
92
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
93
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/TODO.md
ADDED
data/dbchecker.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dbchecker/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dbchecker"
|
8
|
+
spec.version = Dbchecker::VERSION
|
9
|
+
spec.authors = ["Alejandro Andres"]
|
10
|
+
spec.email = ["alej@redradix.com"]
|
11
|
+
spec.description = %q{Check your database consistency}
|
12
|
+
spec.summary = %q{This gem offers a set of tools to check the consistency of your database}
|
13
|
+
spec.homepage = "http://github.com/redradix/dbchecker"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "factory_girl"
|
25
|
+
spec.add_development_dependency "sqlite3"
|
26
|
+
spec.add_development_dependency "database_cleaner"
|
27
|
+
|
28
|
+
spec.add_dependency "rails", "~> 3.2.13"
|
29
|
+
end
|
data/lib/dbchecker.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
module Dbchecker
|
2
|
+
module DSL
|
3
|
+
|
4
|
+
def check_nil(*fields)
|
5
|
+
raise 'Empty fields' if fields.empty?
|
6
|
+
res = []
|
7
|
+
|
8
|
+
fields.each do |field|
|
9
|
+
bad = table.select(:id).where(field => nil)
|
10
|
+
total = bad.map(&:id)
|
11
|
+
|
12
|
+
unless total.empty?
|
13
|
+
self.log "There are #{total.size} invalid rows in #{table} because #{field} is nil"
|
14
|
+
self.log "IDs: #{total}"
|
15
|
+
end
|
16
|
+
res.push total
|
17
|
+
end
|
18
|
+
|
19
|
+
res
|
20
|
+
end
|
21
|
+
|
22
|
+
def check_references(*fields)
|
23
|
+
raise 'Empty fields' if fields.empty?
|
24
|
+
res = []
|
25
|
+
|
26
|
+
fields.each do |field|
|
27
|
+
field_name = "#{field}_id"
|
28
|
+
references = table.select(:id).all.map{|r| r.id }
|
29
|
+
from = field.to_s.classify.constantize
|
30
|
+
all = from.select(:id).all.map(&:id)
|
31
|
+
total = references - all
|
32
|
+
|
33
|
+
if total.size > 0
|
34
|
+
self.log "There are #{total.size} invalid rows in #{table} because #{field_name} is not existant"
|
35
|
+
self.log "IDs: #{total}"
|
36
|
+
end
|
37
|
+
res.push total
|
38
|
+
end
|
39
|
+
|
40
|
+
res
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_negatives(*fields)
|
44
|
+
raise 'Empty fields' if fields.empty?
|
45
|
+
res = []
|
46
|
+
|
47
|
+
fields.each do |field|
|
48
|
+
total = table.select(:id).where("#{field} < 0")
|
49
|
+
if total.size > 0
|
50
|
+
self.log "There are #{total.size} invalid rows in #{table} because #{field} is negative"
|
51
|
+
self.log "IDs: #{total}"
|
52
|
+
end
|
53
|
+
res.push total
|
54
|
+
end
|
55
|
+
|
56
|
+
res
|
57
|
+
end
|
58
|
+
|
59
|
+
def check_zero(*fields)
|
60
|
+
raise 'Empty fields' if fields.empty?
|
61
|
+
res = []
|
62
|
+
|
63
|
+
fields.each do |field|
|
64
|
+
total = table.select(:id).where("#{field} = 0")
|
65
|
+
if total.size > 0
|
66
|
+
self.log "There are #{total.size} invalid rows in #{table} because #{field} is zero"
|
67
|
+
self.log "IDs: #{total}"
|
68
|
+
end
|
69
|
+
res.push total
|
70
|
+
end
|
71
|
+
|
72
|
+
res
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_equal(*fields)
|
76
|
+
raise 'Empty fields' if fields.empty?
|
77
|
+
raise 'Two fields required' if fields.size != 2
|
78
|
+
first_field, second_field = fields[0], fields[1]
|
79
|
+
|
80
|
+
total = table.select(:id).where("#{first_field} = #{second_field}")
|
81
|
+
if total.size > 0
|
82
|
+
self.log "There are #{total.size} invalid rows in #{table} because #{first_field} and #{second_field} are equal"
|
83
|
+
self.log "IDs: #{total}"
|
84
|
+
end
|
85
|
+
|
86
|
+
total.map(&:id)
|
87
|
+
end
|
88
|
+
|
89
|
+
def check_duplicates(*fields)
|
90
|
+
raise 'Empty fields' if fields.empty?
|
91
|
+
fields_str = fields.join(', ')
|
92
|
+
|
93
|
+
total = table.select(:id).group("#{fields_str}").having("COUNT(id) > ?", 1)
|
94
|
+
unless total.empty?
|
95
|
+
self.log "There are #{total.size} invalid rows in #{table} because #{fields_str} are duplicated"
|
96
|
+
self.log "IDs: #{total}"
|
97
|
+
end
|
98
|
+
|
99
|
+
total.map(&:id)
|
100
|
+
end
|
101
|
+
|
102
|
+
def log(msg)
|
103
|
+
puts msg unless Rails.env.test?
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'check_duplicates' do
|
4
|
+
it 'should error if no arguments' do
|
5
|
+
expect {
|
6
|
+
ProfileSubject.check_duplicates
|
7
|
+
}.to raise_exception(StandardError, 'Empty fields')
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should accept one argument' do
|
11
|
+
expect {
|
12
|
+
ProfileSubject.check_duplicates :user_id
|
13
|
+
}.not_to raise_exception
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should accept several arguments' do
|
17
|
+
expect {
|
18
|
+
ProfileSubject.check_duplicates :user_id, :provider_id
|
19
|
+
}.not_to raise_exception
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should return if duplicates' do
|
23
|
+
create :user
|
24
|
+
create :profile, user_id: User.first.id
|
25
|
+
create :profile, user_id: User.first.id
|
26
|
+
res = ProfileSubject.check_duplicates :user_id
|
27
|
+
res.class.should == Array
|
28
|
+
res.size.should == 1
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should return empty if all ok' do
|
32
|
+
create :user
|
33
|
+
create :user
|
34
|
+
create :profile, user_id: User.first.id
|
35
|
+
create :profile, user_id: User.last.id
|
36
|
+
res = ProfileSubject.check_duplicates :user_id
|
37
|
+
res.class.should == Array
|
38
|
+
res.size.should == 0
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'check_equal' do
|
4
|
+
it 'should error if no arguments' do
|
5
|
+
expect {
|
6
|
+
ProfileSubject.check_equal
|
7
|
+
}.to raise_exception(StandardError, 'Empty fields')
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should error if one argument' do
|
11
|
+
expect {
|
12
|
+
ProfileSubject.check_equal :number_of_friends
|
13
|
+
}.to raise_exception(StandardError, 'Two fields required')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should accept two arguments' do
|
17
|
+
expect {
|
18
|
+
ProfileSubject.check_equal :number_of_friends, :number_of_points
|
19
|
+
}.not_to raise_exception
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should return if equal' do
|
23
|
+
create :profile, number_of_points: 3, number_of_friends: 3
|
24
|
+
res = ProfileSubject.check_equal :number_of_points, :number_of_friends
|
25
|
+
res.class.should == Array
|
26
|
+
res.size.should == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should return empty if all ok' do
|
30
|
+
create :profile, number_of_points: 3, number_of_friends: 2
|
31
|
+
res = ProfileSubject.check_equal :number_of_points, :number_of_friends
|
32
|
+
res.class.should == Array
|
33
|
+
res.size.should == 0
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'check_negatives' do
|
4
|
+
it 'should error if no arguments' do
|
5
|
+
expect {
|
6
|
+
ProfileSubject.check_negatives
|
7
|
+
}.to raise_exception(StandardError, 'Empty fields')
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should accept one argument' do
|
11
|
+
expect {
|
12
|
+
ProfileSubject.check_negatives :number_of_friends
|
13
|
+
}.not_to raise_exception
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should accept several arguments' do
|
17
|
+
expect {
|
18
|
+
ProfileSubject.check_negatives :number_of_friends, :number_of_points
|
19
|
+
}.not_to raise_exception
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should return if negative' do
|
23
|
+
create :profile, number_of_points: -1
|
24
|
+
res = ProfileSubject.check_negatives :number_of_points
|
25
|
+
res.class.should == Array
|
26
|
+
res.first.size.should == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should return empty if all ok' do
|
30
|
+
create :profile, number_of_points: 1
|
31
|
+
res = ProfileSubject.check_negatives :number_of_points
|
32
|
+
res.class.should == Array
|
33
|
+
res.first.size.should == 0
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|