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.
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