db_leftovers 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,53 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
18
+ #
19
+ # * Create a file at ~/.gitignore
20
+ # * Include files you want ignored
21
+ # * Run: git config --global core.excludesfile ~/.gitignore
22
+ #
23
+ # After doing this, these files will be ignored in all your git projects,
24
+ # saving you from having to 'pollute' every project you touch with them
25
+ #
26
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
27
+ #
28
+ # For MacOS:
29
+ #
30
+ #.DS_Store
31
+
32
+ # For TextMate
33
+ #*.tmproj
34
+ #tmtags
35
+
36
+ # For emacs:
37
+ #*~
38
+ #\#*
39
+ #.\#*
40
+
41
+ # For vim:
42
+ .*.swp
43
+ .*.swo
44
+
45
+ # For redcar:
46
+ #.redcar
47
+
48
+ # For rubinius:
49
+ #*.rbc
50
+
51
+ tmp
52
+ README.html
53
+ spec/config/database.yml
data/Gemfile CHANGED
@@ -13,8 +13,6 @@ gem 'rails', '>= 3.0.0'
13
13
  group :development do
14
14
  gem "rspec", "~> 2.4.0"
15
15
  gem "bundler"
16
- gem "jeweler", "~> 1.6.4"
17
- gem "rcov", ">= 0"
18
16
  end
19
17
 
20
18
  group :test do
data/Gemfile.lock CHANGED
@@ -44,15 +44,10 @@ GEM
44
44
  builder (3.0.0)
45
45
  diff-lcs (1.1.3)
46
46
  erubis (2.7.0)
47
- git (1.2.5)
48
47
  hike (1.2.1)
49
48
  i18n (0.6.0)
50
49
  jdbc-mysql (5.1.13)
51
50
  jdbc-postgres (9.1.901)
52
- jeweler (1.6.4)
53
- bundler (~> 1.0)
54
- git (>= 1.2.5)
55
- rake
56
51
  json (1.6.5)
57
52
  json (1.6.5-java)
58
53
  mail (2.3.0)
@@ -89,8 +84,6 @@ GEM
89
84
  rdoc (~> 3.4)
90
85
  thor (~> 0.14.6)
91
86
  rake (0.9.2.2)
92
- rcov (0.9.11)
93
- rcov (0.9.11-java)
94
87
  rdoc (3.12)
95
88
  json (~> 1.4)
96
89
  rspec (2.4.0)
@@ -122,7 +115,5 @@ DEPENDENCIES
122
115
  activerecord-mysql2-adapter
123
116
  activerecord-postgresql-adapter
124
117
  bundler
125
- jeweler (~> 1.6.4)
126
118
  rails (>= 3.0.0)
127
- rcov
128
119
  rspec (~> 2.4.0)
data/README.md CHANGED
@@ -102,6 +102,13 @@ The `table` call is just a convenience so you can group all a table's indexes et
102
102
 
103
103
  You can repeat `table` calls for the same table several times if you like. This lets you put your indexes in one place and your foreign keys in another, for example.
104
104
 
105
+ ### ignore(table\_name, [table\_name, ...])
106
+
107
+ Lets you specify one or more tables you'd like db\_leftovers to ignore completely. No objects will be added/removed to these tables. This is useful if you have tables that shouldn't be managed under db\_leftovers.
108
+
109
+ If you don't call `ignore`, the list of ignored tables defaults to `schema_migrations` and `delayed_jobs`. If you do call `ignore`, you should probably include those in your list. If you do want db\_leftovers to manage those tables, just say `ignore []`.
110
+
111
+
105
112
 
106
113
  Running db\_leftovers
107
114
  ---------------------
@@ -120,7 +127,7 @@ and want books to have at least 100 pages, you can change your config file to sa
120
127
  and db\_leftovers will notice the changed expression. It will drop and re-add the constraint.
121
128
 
122
129
  One caveat, however: we pull the current expression from the database, and sometimes Postgres does things like
123
- add type conversions and extra parentheses. If instance, suppose you said `check :users, :email_length, 'LENGTH(email) > 2'`.
130
+ add type conversions and extra parentheses. For instance, suppose you said `check :users, :email_length, 'LENGTH(email) > 2'`.
124
131
  The second time you run db\_leftovers, it will read the expression from Postgres and get `length((email)::text) > 2`,
125
132
  and so it will drop and re-create the constraint.
126
133
  It will drop and re-create it every time you run the rake task.
data/Rakefile CHANGED
@@ -9,48 +9,19 @@ rescue Bundler::BundlerError => e
9
9
  $stderr.puts "Run `bundle install` to install missing gems"
10
10
  exit e.status_code
11
11
  end
12
- require 'rake'
13
12
 
14
- require 'jeweler'
15
- Jeweler::Tasks.new do |gem|
16
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
- gem.name = "db_leftovers"
18
- gem.homepage = "http://github.com/pjungwir/db_leftovers"
19
- gem.license = "MIT"
20
- gem.summary = 'Used to define indexes and foreign keys for your Rails app'
21
- gem.description = <<-EOT
22
- Define indexes and foreign keys for your Rails app
23
- in one place using an easy-to-read DSL,
24
- then run a rake task to bring your database up-to-date.
25
- EOT
26
- gem.email = "pj@illuminatedcomputing.com"
27
- gem.authors = ["Paul A. Jungwirth"]
28
- # dependencies defined in Gemfile
29
- end
30
- Jeweler::RubygemsDotOrgTasks.new
31
13
 
32
14
  require 'rspec/core'
33
15
  require 'rspec/core/rake_task'
34
16
  RSpec::Core::RakeTask.new(:spec) do |spec|
35
17
  spec.pattern = FileList['spec/**/*_spec.rb']
36
18
  end
37
-
38
- RSpec::Core::RakeTask.new(:rcov) do |spec|
39
- spec.pattern = 'spec/**/*_spec.rb'
40
- spec.rcov = true
41
- end
42
-
19
+ desc 'Default: run specs'
43
20
  task :default => :spec
44
21
 
45
- require 'rdoc/task'
46
- Rake::RDocTask.new do |rdoc|
47
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
22
 
49
- rdoc.rdoc_dir = 'rdoc'
50
- rdoc.title = "db_leftovers #{version}"
51
- rdoc.rdoc_files.include('README*')
52
- rdoc.rdoc_files.include('lib/**/*.rb')
53
- end
23
+ Bundler::GemHelper.install_tasks
24
+
54
25
 
55
26
  task :readme => [] do |task|
56
27
  `markdown README.md >README.html`
data/db_leftovers.gemspec CHANGED
@@ -1,81 +1,28 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
1
+ $:.push File.dirname(__FILE__) + '/lib'
2
+ require 'db_leftovers/version'
5
3
 
6
4
  Gem::Specification.new do |s|
7
5
  s.name = "db_leftovers"
8
- s.version = "1.1.1"
6
+ s.version = DbLeftovers::VERSION
7
+ s.date = "2013-01-15"
9
8
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Paul A. Jungwirth"]
12
- s.date = "2012-10-11"
9
+ s.summary = "Used to define indexes and foreign keys for your Rails app"
13
10
  s.description = " Define indexes and foreign keys for your Rails app\n in one place using an easy-to-read DSL,\n then run a rake task to bring your database up-to-date.\n"
14
- s.email = "pj@illuminatedcomputing.com"
15
- s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
- "README.html",
18
- "README.md",
19
- "TODO"
20
- ]
21
- s.files = [
22
- ".document",
23
- "Gemfile",
24
- "Gemfile.lock",
25
- "LICENSE.txt",
26
- "README.md",
27
- "Rakefile",
28
- "TODO",
29
- "VERSION",
30
- "db_leftovers.gemspec",
31
- "lib/db_leftovers.rb",
32
- "lib/db_leftovers/constraint.rb",
33
- "lib/db_leftovers/definition.rb",
34
- "lib/db_leftovers/dsl.rb",
35
- "lib/db_leftovers/foreign_key.rb",
36
- "lib/db_leftovers/generic_database_interface.rb",
37
- "lib/db_leftovers/index.rb",
38
- "lib/db_leftovers/mysql_database_interface.rb",
39
- "lib/db_leftovers/postgres_database_interface.rb",
40
- "lib/db_leftovers/table_dsl.rb",
41
- "lib/tasks/leftovers.rake",
42
- "spec/config/database.yml.sample",
43
- "spec/db_leftovers_spec.rb",
44
- "spec/mysql_spec.rb",
45
- "spec/postgres_spec.rb",
46
- "spec/spec_helper.rb",
47
- "spec/support/mock_database_interface.rb",
48
- "spec/support/shared_db_tests.rb",
49
- "spec/support/sql_matcher.rb"
50
- ]
11
+
12
+ s.authors = ["Paul A. Jungwirth"]
51
13
  s.homepage = "http://github.com/pjungwir/db_leftovers"
14
+ s.email = "pj@illuminatedcomputing.com"
15
+
52
16
  s.licenses = ["MIT"]
17
+
53
18
  s.require_paths = ["lib"]
54
- s.rubygems_version = "1.8.24"
55
- s.summary = "Used to define indexes and foreign keys for your Rails app"
19
+ s.executables = []
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,fixtures}/*`.split("\n")
56
22
 
57
- if s.respond_to? :specification_version then
58
- s.specification_version = 3
23
+ s.add_runtime_dependency 'rails', '>= 3.0.0'
24
+ s.add_development_dependency 'rspec', '~> 2.4.0'
25
+ s.add_development_dependency 'bundler', '>= 0'
59
26
 
60
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
61
- s.add_runtime_dependency(%q<rails>, [">= 3.0.0"])
62
- s.add_development_dependency(%q<rspec>, ["~> 2.4.0"])
63
- s.add_development_dependency(%q<bundler>, [">= 0"])
64
- s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
65
- s.add_development_dependency(%q<rcov>, [">= 0"])
66
- else
67
- s.add_dependency(%q<rails>, [">= 3.0.0"])
68
- s.add_dependency(%q<rspec>, ["~> 2.4.0"])
69
- s.add_dependency(%q<bundler>, [">= 0"])
70
- s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
71
- s.add_dependency(%q<rcov>, [">= 0"])
72
- end
73
- else
74
- s.add_dependency(%q<rails>, [">= 3.0.0"])
75
- s.add_dependency(%q<rspec>, ["~> 2.4.0"])
76
- s.add_dependency(%q<bundler>, [">= 0"])
77
- s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
78
- s.add_dependency(%q<rcov>, [">= 0"])
79
- end
80
27
  end
81
28
 
@@ -10,6 +10,8 @@ module DBLeftovers
10
10
  @verbose = !!opts[:verbose]
11
11
  @db = opts[:db_interface] || get_database_interface
12
12
 
13
+ @ignored_tables = Set.new(['delayed_jobs', 'schema_migrations'].map{|x| [x.to_s, x.to_sym]}.flatten)
14
+
13
15
  @indexes_by_table = {} # Set from the DSL
14
16
  @old_indexes = @db.lookup_all_indexes
15
17
  @new_indexes = {}
@@ -27,6 +29,12 @@ module DBLeftovers
27
29
  instance_eval(&block)
28
30
  end
29
31
 
32
+ def ignore(*table_names)
33
+ table_names = [table_names] unless table_names.is_a? Array
34
+ table_names = table_names.map{|x| [x.to_s, x.to_sym]}.flatten
35
+ @ignored_tables = Set.new(table_names)
36
+ end
37
+
30
38
  def table(table_name, &block)
31
39
  table_dsl = TableDSL.new(self, table_name)
32
40
  table_dsl.define(&block)
@@ -73,6 +81,7 @@ module DBLeftovers
73
81
  # First create any new indexes:
74
82
  @indexes_by_table.each do |table_name, indexes|
75
83
  indexes.each do |idx|
84
+ next if ignore_index?(idx)
76
85
  # puts "#{idx.table_name}.[#{idx.column_names.join(',')}]"
77
86
  case index_status(idx)
78
87
  when STATUS_EXISTS
@@ -91,6 +100,7 @@ module DBLeftovers
91
100
 
92
101
  # Now drop any old indexes that are no longer in the definition file:
93
102
  @old_indexes.each do |index_name, idx|
103
+ next if ignore_index?(idx)
94
104
  if not @new_indexes[index_name]
95
105
  # puts "#{index_name} #{table_name}"
96
106
  @db.execute_drop_index(idx.table_name, index_name)
@@ -103,6 +113,7 @@ module DBLeftovers
103
113
  # First create any new foreign keys:
104
114
  @foreign_keys_by_table.each do |table_name, fks|
105
115
  fks.each do |fk|
116
+ next if ignore_foreign_key?(fk)
106
117
  case foreign_key_status(fk)
107
118
  when STATUS_EXISTS
108
119
  puts "Foreign Key already exists: #{fk.constraint_name} on #{fk.from_table}" if @verbose
@@ -120,6 +131,7 @@ module DBLeftovers
120
131
 
121
132
  # Now drop any old foreign keys that are no longer in the definition file:
122
133
  @old_foreign_keys.each do |constraint_name, fk|
134
+ next if ignore_foreign_key?(fk)
123
135
  if not @new_foreign_keys[constraint_name]
124
136
  @db.execute_drop_foreign_key(constraint_name, fk.from_table, fk.from_column)
125
137
  puts "Dropped foreign key: #{constraint_name} on #{fk.from_table}"
@@ -131,6 +143,7 @@ module DBLeftovers
131
143
  # First create any new constraints:
132
144
  @constraints_by_table.each do |table_name, chks|
133
145
  chks.each do |chk|
146
+ next if ignore_constraint?(chk)
134
147
  case constraint_status(chk)
135
148
  when STATUS_EXISTS
136
149
  puts "Constraint already exists: #{chk.constraint_name} on #{chk.on_table}" if @verbose
@@ -148,6 +161,7 @@ module DBLeftovers
148
161
 
149
162
  # Now drop any old constraints that are no longer in the definition file:
150
163
  @old_constraints.each do |constraint_name, chk|
164
+ next if ignore_constraint?(chk)
151
165
  if not @new_constraints[constraint_name]
152
166
  @db.execute_drop_constraint(constraint_name, chk.on_table)
153
167
  puts "Dropped CHECK constraint: #{constraint_name} on #{chk.on_table}"
@@ -234,6 +248,18 @@ module DBLeftovers
234
248
  end
235
249
  end
236
250
 
251
+ def ignore_index?(idx)
252
+ @ignored_tables.include?(idx.table_name)
253
+ end
254
+
255
+ def ignore_foreign_key?(fk)
256
+ @ignored_tables.include?(fk.from_table)
257
+ end
258
+
259
+ def ignore_constraint?(chk)
260
+ @ignored_tables.include?(chk.on_table)
261
+ end
262
+
237
263
  end
238
264
 
239
265
  end
@@ -26,7 +26,6 @@ module DBLeftovers
26
26
  AND t.relkind = 'r'
27
27
  AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
28
28
  AND pg_catalog.pg_table_is_visible(t.oid)
29
- AND t.relname NOT IN ('delayed_jobs', 'schema_migrations')
30
29
  AND NOT ix.indisprimary
31
30
  GROUP BY t.relname,
32
31
  i.relname,
@@ -0,0 +1,3 @@
1
+ module DbLeftovers
2
+ VERSION = '1.2.0'
3
+ end
@@ -459,4 +459,45 @@ describe DBLeftovers do
459
459
  EOQ
460
460
  end
461
461
 
462
+ it "should not try to change anything on an ignored table" do
463
+ @db.starts_with([
464
+ DBLeftovers::Index.new(:books, :shelf_id),
465
+ ], [
466
+ DBLeftovers::ForeignKey.new('fk_books_shelf_id', 'books', 'shelf_id', 'shelves', 'id'),
467
+ DBLeftovers::ForeignKey.new('fk_books_author_id', 'books', 'author_id', 'authors', 'id')
468
+ ], [
469
+ DBLeftovers::Constraint.new(:books_have_positive_pages, :books, 'pages_count > 0')
470
+ ])
471
+ DBLeftovers::Definition.define :db_interface => @db do
472
+ ignore :books
473
+ end
474
+ @db.sqls.should have(0).items
475
+ end
476
+
477
+ it "should accept multiple ignored tables" do
478
+ @db.starts_with([
479
+ DBLeftovers::Index.new(:books, :shelf_id),
480
+ DBLeftovers::Index.new(:authors, :last_name),
481
+ ], [
482
+ DBLeftovers::ForeignKey.new('fk_books_shelf_id', 'books', 'shelf_id', 'shelves', 'id'),
483
+ DBLeftovers::ForeignKey.new('fk_books_author_id', 'books', 'author_id', 'authors', 'id')
484
+ ], [
485
+ DBLeftovers::Constraint.new(:books_have_positive_pages, :books, 'pages_count > 0')
486
+ ])
487
+ DBLeftovers::Definition.define :db_interface => @db do
488
+ ignore :books, :authors
489
+ end
490
+ @db.sqls.should have(0).items
491
+ end
492
+
493
+ it "should ignore schema_migrations and delayed_jobs by default" do
494
+ @db.starts_with([
495
+ DBLeftovers::Index.new(:schema_migrations, :foo),
496
+ DBLeftovers::Index.new(:delayed_jobs, :bar),
497
+ ], [], [])
498
+ DBLeftovers::Definition.define :db_interface => @db do
499
+ end
500
+ @db.sqls.should have(0).items
501
+ end
502
+
462
503
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db_leftovers
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-11 00:00:00.000000000 Z
12
+ date: 2013-01-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -59,58 +59,22 @@ dependencies:
59
59
  - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
- - !ruby/object:Gem::Dependency
63
- name: jeweler
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ~>
68
- - !ruby/object:Gem::Version
69
- version: 1.6.4
70
- type: :development
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- version: 1.6.4
78
- - !ruby/object:Gem::Dependency
79
- name: rcov
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- type: :development
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: '0'
94
62
  description: ! " Define indexes and foreign keys for your Rails app\n in
95
63
  one place using an easy-to-read DSL,\n then run a rake task to bring your
96
64
  database up-to-date.\n"
97
65
  email: pj@illuminatedcomputing.com
98
66
  executables: []
99
67
  extensions: []
100
- extra_rdoc_files:
101
- - LICENSE.txt
102
- - README.html
103
- - README.md
104
- - TODO
68
+ extra_rdoc_files: []
105
69
  files:
106
70
  - .document
71
+ - .gitignore
107
72
  - Gemfile
108
73
  - Gemfile.lock
109
74
  - LICENSE.txt
110
75
  - README.md
111
76
  - Rakefile
112
77
  - TODO
113
- - VERSION
114
78
  - db_leftovers.gemspec
115
79
  - lib/db_leftovers.rb
116
80
  - lib/db_leftovers/constraint.rb
@@ -122,6 +86,7 @@ files:
122
86
  - lib/db_leftovers/mysql_database_interface.rb
123
87
  - lib/db_leftovers/postgres_database_interface.rb
124
88
  - lib/db_leftovers/table_dsl.rb
89
+ - lib/db_leftovers/version.rb
125
90
  - lib/tasks/leftovers.rake
126
91
  - spec/config/database.yml.sample
127
92
  - spec/db_leftovers_spec.rb
@@ -131,7 +96,6 @@ files:
131
96
  - spec/support/mock_database_interface.rb
132
97
  - spec/support/shared_db_tests.rb
133
98
  - spec/support/sql_matcher.rb
134
- - README.html
135
99
  homepage: http://github.com/pjungwir/db_leftovers
136
100
  licenses:
137
101
  - MIT
@@ -147,7 +111,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
147
111
  version: '0'
148
112
  segments:
149
113
  - 0
150
- hash: -4583534380635157212
114
+ hash: 1094789418074050073
151
115
  required_rubygems_version: !ruby/object:Gem::Requirement
152
116
  none: false
153
117
  requirements:
data/README.html DELETED
@@ -1,190 +0,0 @@
1
- <h1>db_leftovers</h1>
2
-
3
- <p>Db_leftovers lets you define indexes, foreign keys, and CHECK constraints for your Rails app
4
- in one place using an easy-to-read DSL,
5
- then run a rake task to bring your database up-to-date.
6
- Whenever you edit the DSL, you can re-run the rake task and db_leftovers will alter your database accordingly.
7
- This is useful because of the following limitations in vanilla Rails:</p>
8
-
9
- <ul>
10
- <li>There are no built-in migration methods to create foreign keys or CHECK constraints.</li>
11
- <li>Even if created, foreign keys and CHECK constraints won't appear in your schema.rb.</li>
12
- <li>The built-in <code>add_index</code> method doesn't support WHERE clauses on your indexes.</li>
13
- <li>If you're using Heroku, <code>db:push</code> and <code>db:pull</code> won't transfer your foreign keys and CHECK constraints.</li>
14
- <li>Creating indexes in your migrations makes it hard to manage them.</li>
15
- </ul>
16
-
17
- <p>That last point deserves some elaboration. First, using <code>add_index</code> in your migrations is bug-prone because without rare developer discipline, you wind up missing indexes in some environments. (My rule is "never change a migration after a <code>git push</code>," but I haven't seen this followed elsewhere, and there is nothing that automatically enforces it.) Also, since missing indexes don't cause errors, it's easy to be missing one and not notice until users start complaining about performance.</p>
18
-
19
- <p>Second, Scattering <code>add_index</code> methods throughout migrations also doesn't match the workflow of optimizing database queries. Hopefully you create appropriate indexes when you set up your tables originally, but in practice you often need to go back later and add/remove indexes according to your database usage patterns. Or you just forget the indexes, because you're thinking about modeling the data, not optimizing the queries.
20
- It's easier to vet and analyze database indexes if you can see them all in one place,
21
- and db_leftovers lets you do that easily.
22
- And since you can rest assured that each environment conforms to the same definition, you don't need to second-guess yourself about indexes that are present in development but missing in production.
23
- Db_leftovers lets you rest assured that each environment conforms to a definition that is easy to read and checked into version control.</p>
24
-
25
- <p>At present db_leftovers supports PostgreSQL and MySQL, although since MySQL doesn't support index WHERE clauses or CHECK constraints, using that functionality will raise errors. (If you need to share the same definitions across Postgres and MySQL, you can run arbitrary Ruby code inside the DSL to avoid defining unsupported objects when run against MySQL.)</p>
26
-
27
- <h2>Configuration File</h2>
28
-
29
- <p>db_leftovers reads a file named <code>config/db_leftovers.rb</code> to find out which indexes and constraints you want in your database. This file is a DSL implemented in Ruby, sort of like <code>config/routes.rb</code>. It should look something like this:</p>
30
-
31
- <pre><code>DBLeftovers::Definition.define do
32
-
33
- table :users do
34
- index :email, :unique =&gt; true
35
- check :registered_at_unless_guest, "role_name = 'guest' OR registered_at IS NOT NULL"
36
- end
37
-
38
- foreign_key :orders, :users
39
-
40
- # . .
41
- end
42
- </code></pre>
43
-
44
- <p>Within the DSL file, the following methods are supported:</p>
45
-
46
- <h3>index(table_name, columns, [opts])</h3>
47
-
48
- <p>This ensures that you have an index on the given table and column(s). The <code>columns</code> parameter can be either a string/symbol or a list of strings/symbols. Opts is a hash with the following possible keys:</p>
49
-
50
- <ul>
51
- <li><p><code>:name</code> The name of the index. Defaults to <code>index_</code><em>table_name</em><code>_on_</code><em>column_names</em>, like the <code>add_index</code> method from Rails migrations.</p></li>
52
- <li><p><code>:unique</code> Set this to <code>true</code> if you'd like a unique index.</p></li>
53
- <li><p><code>:where</code> Accepts SQL to include in the <code>WHERE</code> part of the <code>CREATE INDEX</code> command, in case you want to limit the index to a subset of the table's rows.</p></li>
54
- </ul>
55
-
56
- <h4>Examples</h4>
57
-
58
- <pre><code>index :books, :author_id
59
- index :books, [:publisher_id, :published_at]
60
- index :books, :isbn, :unique =&gt; true
61
- </code></pre>
62
-
63
- <h3>foreign_key(from_table, [from_column], to_table, [to_column], [opts])</h3>
64
-
65
- <p>This ensures that you have a foreign key relating the given tables and columns.
66
- All parameters are strings/symbols except <code>opts</code>, which is a hash.
67
- If you omit the column names, db_leftovers will infer them based on Rails conventions. (See examples below.)
68
- The only option that is supported is <code>:on_delete</code>, which may have any of these values:</p>
69
-
70
- <ul>
71
- <li><code>nil</code> Indicates that attempting to delete the referenced row should fail (the default).</li>
72
- <li><code>:set_null</code> Indicates that the foreign key should be set to null if the referenced row is deleted.</li>
73
- <li><code>:cascade</code> Indicates that the referencing row should be deleted if the referenced row is deleted.</li>
74
- </ul>
75
-
76
- <h4>Examples</h4>
77
-
78
- <pre><code>foreign_key :books, :author_id, :authors, :id
79
- foreign_key :pages, :book_id, :books, :id, :on_delete =&gt; :cascade
80
- </code></pre>
81
-
82
- <p>With implicit column names:</p>
83
-
84
- <pre><code>foreign_key :books, :authors
85
- foreign_key :books, :authors, :on_delete =&gt; :cascade
86
- foreign_key :books, :co_author_id, :authors
87
- foreign_key :books, :co_author_id, :authors, :on_delete =&gt; :cascade
88
- </code></pre>
89
-
90
- <h3>check(constraint_name, on_table, expression)</h3>
91
-
92
- <p>This ensures that you have a CHECK constraint on the given table with the given name and expression.
93
- All parameters are strings or symbols.</p>
94
-
95
- <h4>Examples</h4>
96
-
97
- <pre><code>check :books, :books_have_positive_pages, 'page_count &gt; 0'
98
- </code></pre>
99
-
100
- <h3>table(table_name, &amp;block)</h3>
101
-
102
- <p>The <code>table</code> call is just a convenience so you can group all a table's indexes et cetera together and not keep repeating the table name. You use it like this:</p>
103
-
104
- <pre><code>table :books do
105
- index :author_id
106
- foreign_key :publisher_id, :publishers
107
- check :books_have_positive_pages, 'page_count &gt; 0'
108
- end
109
- </code></pre>
110
-
111
- <p>You can repeat <code>table</code> calls for the same table several times if you like. This lets you put your indexes in one place and your foreign keys in another, for example.</p>
112
-
113
- <h2>Running db_leftovers</h2>
114
-
115
- <p>db_leftovers comes with a Rake task named <code>db:leftovers</code>. So you can update your database to match your config file by saying this:</p>
116
-
117
- <pre><code>rake db:leftovers
118
- </code></pre>
119
-
120
- <p>db_leftovers will notice whenever an index, foreign key, or CHECK constraint needs to be added, dropped, or changed.
121
- It will even catch cases where the name of the managed object is the same, but its attributes have changed.
122
- For instance, if you previously required books to have at least 1 page, but now you are introducing a "pamphlet"
123
- and want books to have at least 100 pages, you can change your config file to say:</p>
124
-
125
- <pre><code>check :books, :books_have_positive_pages, 'page_count &gt;= 100'
126
- </code></pre>
127
-
128
- <p>and db_leftovers will notice the changed expression. It will drop and re-add the constraint.</p>
129
-
130
- <p>One caveat, however: we pull the current expression from the database, and sometimes Postgres does things like
131
- add type conversions. If instance, suppose you said <code>check :users, :email_length, 'LENGTH(email) &gt; 2'</code>.
132
- The second time you run db_leftovers, it will read the expression from Postgres and get <code>LENGTH((email)::text) &gt; 2</code>,
133
- and so it will drop and re-create the constraint.
134
- It will drop and re-create it every time you run the rake task.
135
- To get around this, make sure your config file uses the same expression as printed by db_leftovers in the rake output.
136
- This can also happen for index WHERE clauses, fixable by a similar workaround.
137
- MySQL doesn't have this problem because it doesn't support CHECK constraints or index WHERE clauses.</p>
138
-
139
- <p>To print messages even about indexes/foreign keys/constraints that haven't changed, you can say:</p>
140
-
141
- <pre><code>rake db:leftovers VERBOSE=true
142
- </code></pre>
143
-
144
- <p>or</p>
145
-
146
- <pre><code>rake db:leftovers DB_LEFTOVERS_VERBOSE=true
147
- </code></pre>
148
-
149
- <h2>Capistrano Integration</h2>
150
-
151
- <p>I recommend running <code>rake db:migrate</code> any time you deploy, and then running <code>rake db:leftovers</code> after that. Here is what you need in your <code>config/deploy.rb</code> to make that happen:</p>
152
-
153
- <pre><code>set :rails_env, "production"
154
-
155
- namespace :db do
156
- desc "Set up constraints and indexes"
157
- task :leftovers do
158
- run("cd #{deploy_to}/current &amp;&amp; bundle exec rake db:leftovers RAILS_ENV=#{rails_env}")
159
- end
160
- end
161
-
162
- after :deploy, 'deploy:migrate'
163
- after 'deploy:migrate', 'db:leftovers'
164
- </code></pre>
165
-
166
- <p>You could also change this code to <em>not</em> run migrations after each deploy, if you like. But in that case I'd recommend not running db:leftovers until after the latest migrations (if any), since new entries in the DSL are likely to reference newly-created tables/columns.</p>
167
-
168
- <h2>Known Issues</h2>
169
-
170
- <ul>
171
- <li><p>When db_leftovers interrogates your database for the currently-defined indexes et cetera, it doesn't filter things by the current database name. So if you have mutliple Rails projects all accessible to the same user, you'll wind up changing more than you like (probably by DROPing things).</p></li>
172
- <li><p>It'd be nice to have another Rake task that will read your database and generate a new <code>db_leftovers.rb</code> file, to help people migrating existing projects that already have lots of tables.</p></li>
173
- </ul>
174
-
175
- <h2>Contributing to db_leftovers</h2>
176
-
177
- <ul>
178
- <li>Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.</li>
179
- <li>Check out the issue tracker to make sure someone hasn't already requested and/or contributed it.</li>
180
- <li>Fork the project.</li>
181
- <li>Start a feature/bugfix branch.</li>
182
- <li>Commit and push until you are happy with your contribution.</li>
183
- <li>Make be sure to add tests for it. This is important so I don't break it in a future version unintentionally.</li>
184
- <li>Please try not to mess with the Rakefile, version, or history. If you want to have your own version, that is fine, but please isolate that change to its own commit so I can cherry-pick around it.</li>
185
- </ul>
186
-
187
- <h2>Copyright</h2>
188
-
189
- <p>Copyright (c) 2012 Paul A. Jungwirth.
190
- See LICENSE.txt for further details.</p>
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 1.1.1