dbchecker 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/README.md +93 -0
  6. data/Rakefile +7 -0
  7. data/TODO.md +6 -0
  8. data/dbchecker.gemspec +29 -0
  9. data/lib/dbchecker.rb +8 -0
  10. data/lib/dbchecker/base.rb +14 -0
  11. data/lib/dbchecker/checker.rb +14 -0
  12. data/lib/dbchecker/dsl.rb +107 -0
  13. data/lib/dbchecker/railtie.rb +5 -0
  14. data/lib/dbchecker/version.rb +3 -0
  15. data/lib/tasks/db_check.rake +6 -0
  16. data/spec/dsl/check_duplicates_spec.rb +41 -0
  17. data/spec/dsl/check_equal_spec.rb +36 -0
  18. data/spec/dsl/check_negatives_spec.rb +36 -0
  19. data/spec/dsl/check_nil_spec.rb +35 -0
  20. data/spec/dsl/check_references_spec.rb +37 -0
  21. data/spec/dsl/check_zero_spec.rb +36 -0
  22. data/spec/dummy/Rakefile +7 -0
  23. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  24. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  25. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  26. data/spec/dummy/app/models/.gitkeep +0 -0
  27. data/spec/dummy/app/models/profile.rb +5 -0
  28. data/spec/dummy/app/models/provider.rb +3 -0
  29. data/spec/dummy/app/models/user.rb +4 -0
  30. data/spec/dummy/config.ru +4 -0
  31. data/spec/dummy/config/application.rb +59 -0
  32. data/spec/dummy/config/boot.rb +10 -0
  33. data/spec/dummy/config/database.yml +10 -0
  34. data/spec/dummy/config/environment.rb +5 -0
  35. data/spec/dummy/config/environments/development.rb +37 -0
  36. data/spec/dummy/config/environments/production.rb +67 -0
  37. data/spec/dummy/config/environments/test.rb +37 -0
  38. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  39. data/spec/dummy/config/initializers/inflections.rb +15 -0
  40. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  41. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  42. data/spec/dummy/config/initializers/session_store.rb +8 -0
  43. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  44. data/spec/dummy/config/routes.rb +58 -0
  45. data/spec/dummy/db/migrate/20130703094110_create_users.rb +11 -0
  46. data/spec/dummy/db/migrate/20130703094241_create_profiles.rb +15 -0
  47. data/spec/dummy/db/migrate/20130703102715_create_providers.rb +9 -0
  48. data/spec/dummy/db/schema.rb +41 -0
  49. data/spec/dummy/db/test.sqlite3 +0 -0
  50. data/spec/factories.rb +12 -0
  51. data/spec/spec_helper.rb +30 -0
  52. 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
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dbchecker.gemspec
4
+ gemspec
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
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+
6
+ task :test => :spec #alias
7
+ task :default => :spec
data/TODO.md ADDED
@@ -0,0 +1,6 @@
1
+ * Rescue from errors
2
+ * Add more DSL
3
+ * Counter caches
4
+ * Add more options to DSL, so it's more flexible
5
+ * Make table names and fields smart, using rails helpers instead of manually
6
+ * Comment the code so it's easier for people to contribute
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,8 @@
1
+ require 'dbchecker/version'
2
+
3
+ require 'dbchecker/railtie' if defined? Rails
4
+ require 'dbchecker/dsl'
5
+ require 'dbchecker/checker'
6
+
7
+ module Dbchecker
8
+ end
@@ -0,0 +1,14 @@
1
+ module Dbchecker
2
+ class Checker
3
+ extend DSL
4
+
5
+ class << self
6
+ attr_accessor :table
7
+ end
8
+
9
+ def self.model(klass)
10
+ self.table = klass.to_s.classify.constantize
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Dbchecker
2
+ class Checker
3
+ extend DSL
4
+
5
+ class << self
6
+ attr_accessor :table
7
+ end
8
+
9
+ def self.model(klass)
10
+ self.table = klass.to_s.classify.constantize
11
+ end
12
+
13
+ end
14
+ end
@@ -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,5 @@
1
+ class Tasks < Rails::Railtie
2
+ rake_tasks do
3
+ Dir[File.join(File.dirname(__FILE__),'../tasks/*.rake')].each { |f| load f }
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Dbchecker
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,6 @@
1
+ namespace :db do
2
+ desc "Checks your database for consistency problems"
3
+ task :check => :environment do
4
+ Dir[File.join(Rails.root,'db/checks/*.rb')].each { |f| load f }
5
+ end
6
+ 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