schema_plus_foreign_keys 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +21 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +200 -0
  7. data/Rakefile +9 -0
  8. data/gemfiles/Gemfile.base +4 -0
  9. data/gemfiles/activerecord-4.2.0/Gemfile.base +3 -0
  10. data/gemfiles/activerecord-4.2.0/Gemfile.mysql2 +10 -0
  11. data/gemfiles/activerecord-4.2.0/Gemfile.postgresql +10 -0
  12. data/gemfiles/activerecord-4.2.0/Gemfile.sqlite3 +10 -0
  13. data/gemfiles/activerecord-4.2.1/Gemfile.base +3 -0
  14. data/gemfiles/activerecord-4.2.1/Gemfile.mysql2 +10 -0
  15. data/gemfiles/activerecord-4.2.1/Gemfile.postgresql +10 -0
  16. data/gemfiles/activerecord-4.2.1/Gemfile.sqlite3 +10 -0
  17. data/lib/schema_plus/foreign_keys.rb +78 -0
  18. data/lib/schema_plus/foreign_keys/active_record/base.rb +33 -0
  19. data/lib/schema_plus/foreign_keys/active_record/connection_adapters/abstract_adapter.rb +168 -0
  20. data/lib/schema_plus/foreign_keys/active_record/connection_adapters/foreign_key_definition.rb +137 -0
  21. data/lib/schema_plus/foreign_keys/active_record/connection_adapters/mysql2_adapter.rb +126 -0
  22. data/lib/schema_plus/foreign_keys/active_record/connection_adapters/postgresql_adapter.rb +89 -0
  23. data/lib/schema_plus/foreign_keys/active_record/connection_adapters/sqlite3_adapter.rb +77 -0
  24. data/lib/schema_plus/foreign_keys/active_record/connection_adapters/table_definition.rb +108 -0
  25. data/lib/schema_plus/foreign_keys/active_record/migration/command_recorder.rb +29 -0
  26. data/lib/schema_plus/foreign_keys/middleware/dumper.rb +88 -0
  27. data/lib/schema_plus/foreign_keys/middleware/migration.rb +147 -0
  28. data/lib/schema_plus/foreign_keys/middleware/model.rb +15 -0
  29. data/lib/schema_plus/foreign_keys/middleware/mysql.rb +20 -0
  30. data/lib/schema_plus/foreign_keys/middleware/sql.rb +27 -0
  31. data/lib/schema_plus/foreign_keys/version.rb +5 -0
  32. data/lib/schema_plus_foreign_keys.rb +1 -0
  33. data/schema_dev.yml +9 -0
  34. data/schema_plus_foreign_keys.gemspec +31 -0
  35. data/spec/deprecation_spec.rb +161 -0
  36. data/spec/foreign_key_definition_spec.rb +34 -0
  37. data/spec/foreign_key_spec.rb +207 -0
  38. data/spec/migration_spec.rb +570 -0
  39. data/spec/named_schemas_spec.rb +136 -0
  40. data/spec/schema_dumper_spec.rb +257 -0
  41. data/spec/spec_helper.rb +60 -0
  42. data/spec/support/reference.rb +79 -0
  43. metadata +221 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dd82359f12e9dfcd2ee7413a727637098fa160a3
4
+ data.tar.gz: c8b86be4f354952b4f64c5681c28336b4c77153a
5
+ SHA512:
6
+ metadata.gz: ac770774c777fbae496152c989b884f613148eefbcc2574a7e0359fa8f95ec34514b58329b965200aa46f3da628ae5b026104bdd6a3f75df0a9e4ea6aff360a4
7
+ data.tar.gz: fd9687b783c5204c743f9646187751b8f519f1e847326a796cb88817a1a0e79d3d6d63c71cad4b99f15545a6aa820420ea4552cf78c50d7d1e068ddf85ca99ea
@@ -0,0 +1,9 @@
1
+ /coverage
2
+ /tmp
3
+ /pkg
4
+ /Gemfile.local
5
+
6
+ *.lock
7
+ *.log
8
+ *.sqlite3
9
+ !gemfiles/**/*.sqlite3
@@ -0,0 +1,21 @@
1
+ # This file was auto-generated by the schema_dev tool, based on the data in
2
+ # ./schema_dev.yml
3
+ # Please do not edit this file; any changes will be overwritten next time
4
+ # schema_dev gets run.
5
+ ---
6
+ sudo: false
7
+ rvm:
8
+ - 2.1.5
9
+ gemfile:
10
+ - gemfiles/activerecord-4.2.0/Gemfile.mysql2
11
+ - gemfiles/activerecord-4.2.0/Gemfile.postgresql
12
+ - gemfiles/activerecord-4.2.0/Gemfile.sqlite3
13
+ - gemfiles/activerecord-4.2.1/Gemfile.mysql2
14
+ - gemfiles/activerecord-4.2.1/Gemfile.postgresql
15
+ - gemfiles/activerecord-4.2.1/Gemfile.sqlite3
16
+ env: POSTGRESQL_DB_USER=postgres MYSQL_DB_USER=travis
17
+ addons:
18
+ postgresql: '9.4'
19
+ before_script: bundle exec rake create_databases
20
+ after_script: bundle exec rake drop_databases
21
+ script: bundle exec rake travis
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ File.exist?(gemfile_local = File.expand_path('../Gemfile.local', __FILE__)) and eval File.read(gemfile_local), binding, gemfile_local
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 ronen barzel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,200 @@
1
+ [![Gem Version](https://badge.fury.io/rb/schema_plus_foreign_keys.svg)](http://badge.fury.io/rb/schema_plus_foreign_keys)
2
+ [![Build Status](https://secure.travis-ci.org/SchemaPlus/schema_plus_foreign_keys.svg)](http://travis-ci.org/SchemaPlus/schema_plus_foreign_keys)
3
+ [![Coverage Status](https://img.shields.io/coveralls/SchemaPlus/schema_plus_foreign_keys.svg)](https://coveralls.io/r/SchemaPlus/schema_plus_foreign_keys)
4
+ [![Dependency Status](https://gemnasium.com/lomba/schema_plus_foreign_keys.svg)](https://gemnasium.com/SchemaPlus/schema_plus_foreign_keys)
5
+
6
+ # SchemaPlus::ForeignKeys
7
+
8
+ SchemaPlus::ForeignKeys provides extended support in ActiveRecord. This includes extended support for declaraing foreign key constraints in migrations; support for deferrable constraints; support for SQLite3; cleaner schema dumps.
9
+
10
+ SchemaPlus::ForeignKeys is part of the [SchemaPlus](https://github.com/SchemaPlus/) family of Ruby on Rails ActiveRecord extension gems.
11
+
12
+ For extra convenience, see also [schema_auto_foreign_keys](https://github.com/SchemaPlus/schema_auto_foreign_keys), which creates foriegn key constraints automatically.
13
+
14
+
15
+ ## Installation
16
+
17
+ <!-- SCHEMA_DEV: TEMPLATE INSTALLATION - begin -->
18
+ <!-- These lines are auto-inserted from a schema_dev template -->
19
+ As usual:
20
+
21
+ ```ruby
22
+ gem "schema_plus_foreign_keys" # in a Gemfile
23
+ gem.add_dependency "schema_plus_foreign_keys" # in a .gemspec
24
+ ```
25
+
26
+ <!-- SCHEMA_DEV: TEMPLATE INSTALLATION - end -->
27
+
28
+ ## Usage
29
+
30
+ ### Migrations
31
+
32
+ To declare a foreign key constraint for a column, use the `:foreign_key`
33
+ option. The same options can be used with `t.integer`, `t.references`, `t.belongs_to`, `t.foreign_key`, `change_column`, and `add_foreign_key`:
34
+
35
+ t.integer :author_id, foreign_key: true # create a foreign_key to table "authors"
36
+ t.integer :author_id, foreign_key: {} # create a foreign_key to table "authors"
37
+ t.integer :author_id, foreign_key: false # don't create a constraint (this is the default)
38
+ t.integer :author, foreign_key: true # create a foreign_key to table "authors"
39
+
40
+ t.integer :parent_id, foreign_key: true # special column parent_id defaults to referencing its own table
41
+
42
+ Specify the target table and its primary key using the `:references` and `:primary_key`:
43
+
44
+ t.integer :author_id, foreign_key: { references: :authors } # the default
45
+ t.integer :author, foreign_key: { references: :authors } # the default
46
+ t.integer :author_id, foreign_key: { references: :people } # choose table name
47
+ t.integer :author_id, foreign_key: { primary_key: :ssn] } # choose primary key
48
+ t.integer :author_id, foreign_key: { references: :people, primary_key: :ssn] } # choose both
49
+ t.integer :author_id, foreign_key: { references: [:people, :ssn] } # shortcut for both
50
+ t.integer :author_id, foreign_key: { references: nil } # same as foreign_key: false
51
+
52
+ You can also specify other attributes:
53
+
54
+ t.integer :author_id, foreign_key: { name: "my_fk" } # override default auto-generated constraint name
55
+ t.integer :author_id, foreign_key: { on_delete: :cascade }
56
+ t.integer :author_id, foreign_key: { on_update: :set_null }
57
+ t.integer :author_id, foreign_key: { deferrable: true }
58
+ t.integer :author_id, foreign_key: { deferrable: :initially_deferred }
59
+
60
+ Of course the options can be combined:
61
+
62
+ t.integer :author_id, foreign_key: { references: :people, primary_key: :ssn, name: "my_fk", on_delet: :no_action }
63
+
64
+
65
+ As a shorthand, all options except `:name` can be specified without placing
66
+ them in a `foreign_key` hash, e.g.
67
+
68
+ t.integer :author_id, on_delete: :cascade # shorthand for foreign_key: { on_delete: :cascade }
69
+ t.integer :author_id, references: :people # shorthand for foreign_key: { references: :people }
70
+
71
+ To remove a foreign key constraint, you can either change the column, specifying `foreign_key: false`, or use `migration.remove_foreign_key(table, column)`
72
+
73
+ ### Introspection
74
+
75
+ To examine the foreign keys on a model, you can use:
76
+
77
+ Model.foreign_keys # foreign key constraints from this model to another
78
+ Model.reverse_foreign_keys # foreign key constraints from other models to this
79
+
80
+ (These results are cached along with other column-specific information; if you change the table definition, call `Model.reset_column_information` to clear the cache)
81
+
82
+ You can also query at the connection level (uncached):
83
+
84
+ connection.foreign_keys(table_name)
85
+ connection.reverse_foreign_keys(table_name)
86
+
87
+ These calls all return an array of ForeignKeyDefinition objects, which support these methods:
88
+
89
+ fk.from_table
90
+ fk.column
91
+ fk.to_table
92
+ fk.primary_key
93
+ fk.on_update
94
+ fk.on_delete
95
+ fk.deferrable
96
+
97
+ ### Configuring Defaults
98
+
99
+ If you don't specify `on_update` and `on_delete` when creating a foreign key
100
+ constraint, they normally default to whatever the DBMS's default behavior is.
101
+
102
+ But you can also configure a global default (e.g. in a Rails initializer):
103
+
104
+ ```ruby
105
+ SchemaPlus::ForeignKey.setup do |config|
106
+ config.on_update = :cascade # default is nil, meaning use default dbms behavior
107
+ config.on_delete = :nullify # default is nil, meaning use default dbms behavior
108
+ end
109
+ ```
110
+
111
+ Or you can configure a per-table default in a migration:
112
+
113
+ ```ruby
114
+ create_table :things, foreign_key: { on_update: :set_null } do |t|
115
+ ...
116
+ end
117
+ ```
118
+
119
+ ### SQLite 3 Notes
120
+
121
+ SchemaPlus::ForeignKeys supports foreign key constraints in SQLite3.
122
+
123
+ However note that SQLite3 requires you to declare the constraints as part of
124
+ the table definition, and does not allow you to add, remove, or change
125
+ constraints after the fact. Thus you'll get an exception if you try
126
+ `add_foreign_key`, `remove_foreign_key`, or `change_column` changing the
127
+ foreign key options.
128
+
129
+
130
+ ### Schema Dump
131
+
132
+ For clarity (and because it's required for SQLite3), in the generated `schema_dump.rb` file, the foreign key definitions are inluded within the table definitions.
133
+
134
+ This means that the tables are output sorted that a table is
135
+ defined before others that depend on it. If, however, there are circularities in the
136
+ foreign key relations, this won't be possible; In that case some table definitions will include comments indicating a "forward reference" to a table that's farther down in the file, and the constraint will be defined once that table is defined (this can never happen with SQLite3).
137
+
138
+
139
+ ## Compatibility
140
+
141
+ SchemaPlus::ForeignKeys is tested on:
142
+
143
+ <!-- SCHEMA_DEV: MATRIX - begin -->
144
+ <!-- These lines are auto-generated by schema_dev based on schema_dev.yml -->
145
+ * ruby **2.1.5** with activerecord **4.2.0**, using **mysql2**, **sqlite3** or **postgresql**
146
+ * ruby **2.1.5** with activerecord **4.2.1**, using **mysql2**, **sqlite3** or **postgresql**
147
+
148
+ <!-- SCHEMA_DEV: MATRIX - end -->
149
+
150
+ ## History
151
+
152
+ * 0.1.0 - Initial release, brought over from schema_plus 1.x via 2.0.0.pre*
153
+
154
+ ## Development & Testing
155
+
156
+ Are you interested in contributing to SchemaPlus::ForeignKeys? Thanks! Please follow
157
+ the standard protocol: fork, feature branch, develop, push, and issue pull
158
+ request.
159
+
160
+ Some things to know about to help you develop and test:
161
+
162
+ <!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_DEV - begin -->
163
+ <!-- These lines are auto-inserted from a schema_dev template -->
164
+ * **schema_dev**: SchemaPlus::ForeignKeys uses [schema_dev](https://github.com/SchemaPlus/schema_dev) to
165
+ facilitate running rspec tests on the matrix of ruby, activerecord, and database
166
+ versions that the gem supports, both locally and on
167
+ [travis-ci](http://travis-ci.org/SchemaPlus/schema_plus_foreign_keys)
168
+
169
+ To to run rspec locally on the full matrix, do:
170
+
171
+ $ schema_dev bundle install
172
+ $ schema_dev rspec
173
+
174
+ You can also run on just one configuration at a time; For info, see `schema_dev --help` or the [schema_dev](https://github.com/SchemaPlus/schema_dev) README.
175
+
176
+ The matrix of configurations is specified in `schema_dev.yml` in
177
+ the project root.
178
+
179
+
180
+ <!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_DEV - end -->
181
+
182
+ <!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_PLUS_CORE - begin -->
183
+ <!-- These lines are auto-inserted from a schema_dev template -->
184
+ * **schema_plus_core**: SchemaPlus::ForeignKeys uses the SchemaPlus::Core API that
185
+ provides middleware callback stacks to make it easy to extend
186
+ ActiveRecord's behavior. If that API is missing something you need for
187
+ your contribution, please head over to
188
+ [schema_plus_core](https://github.com/SchemaPlus/schema_plus_core) and open
189
+ an issue or pull request.
190
+
191
+ <!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_PLUS_CORE - end -->
192
+
193
+ <!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_MONKEY - begin -->
194
+ <!-- These lines are auto-inserted from a schema_dev template -->
195
+ * **schema_monkey**: SchemaPlus::ForeignKeys is implemented as a
196
+ [schema_monkey](https://github.com/SchemaPlus/schema_monkey) client,
197
+ using [schema_monkey](https://github.com/SchemaPlus/schema_monkey)'s
198
+ convention-based protocols for extending ActiveRecord and using middleware stacks.
199
+
200
+ <!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_MONKEY - end -->
@@ -0,0 +1,9 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'schema_dev/tasks'
5
+
6
+ task :default => :spec
7
+
8
+ require 'rspec/core/rake_task'
9
+ RSpec::Core::RakeTask.new(:spec)
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ gemspec :path => File.expand_path('..', __FILE__)
3
+
4
+ File.exist?(gemfile_local = File.expand_path('../Gemfile.local', __FILE__)) and eval File.read(gemfile_local), binding, gemfile_local
@@ -0,0 +1,3 @@
1
+ eval File.read File.expand_path('../../Gemfile.base', __FILE__)
2
+
3
+ gem "activerecord", "4.2.0"
@@ -0,0 +1,10 @@
1
+ require "pathname"
2
+ eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
+
4
+ platform :ruby do
5
+ gem "mysql2"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcmysql-adapter'
10
+ end
@@ -0,0 +1,10 @@
1
+ require "pathname"
2
+ eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
+
4
+ platform :ruby do
5
+ gem "pg"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcpostgresql-adapter'
10
+ end
@@ -0,0 +1,10 @@
1
+ require "pathname"
2
+ eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
+
4
+ platform :ruby do
5
+ gem "sqlite3"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcsqlite3-adapter', '>=1.3.0.beta2'
10
+ end
@@ -0,0 +1,3 @@
1
+ eval File.read File.expand_path('../../Gemfile.base', __FILE__)
2
+
3
+ gem "activerecord", "4.2.1"
@@ -0,0 +1,10 @@
1
+ require "pathname"
2
+ eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
+
4
+ platform :ruby do
5
+ gem "mysql2"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcmysql-adapter'
10
+ end
@@ -0,0 +1,10 @@
1
+ require "pathname"
2
+ eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
+
4
+ platform :ruby do
5
+ gem "pg"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcpostgresql-adapter'
10
+ end
@@ -0,0 +1,10 @@
1
+ require "pathname"
2
+ eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding)
3
+
4
+ platform :ruby do
5
+ gem "sqlite3"
6
+ end
7
+
8
+ platform :jruby do
9
+ gem 'activerecord-jdbcsqlite3-adapter', '>=1.3.0.beta2'
10
+ end
@@ -0,0 +1,78 @@
1
+ require 'schema_plus/core'
2
+ require 'valuable'
3
+
4
+ require_relative 'foreign_keys/version'
5
+ require_relative 'foreign_keys/active_record/base'
6
+ require_relative 'foreign_keys/active_record/connection_adapters/abstract_adapter'
7
+ require_relative 'foreign_keys/active_record/connection_adapters/table_definition'
8
+ require_relative 'foreign_keys/active_record/connection_adapters/foreign_key_definition'
9
+ require_relative 'foreign_keys/active_record/migration/command_recorder'
10
+ require_relative 'foreign_keys/middleware/dumper'
11
+ require_relative 'foreign_keys/middleware/migration'
12
+ require_relative 'foreign_keys/middleware/model'
13
+ require_relative 'foreign_keys/middleware/mysql'
14
+ require_relative 'foreign_keys/middleware/sql'
15
+
16
+ module SchemaPlus::ForeignKeys
17
+ module ActiveRecord
18
+ module ConnectionAdapters
19
+ autoload :Mysql2Adapter, 'schema_plus/foreign_keys/active_record/connection_adapters/mysql2_adapter'
20
+ autoload :PostgresqlAdapter, 'schema_plus/foreign_keys/active_record/connection_adapters/postgresql_adapter'
21
+ autoload :Sqlite3Adapter, 'schema_plus/foreign_keys/active_record/connection_adapters/sqlite3_adapter'
22
+ end
23
+ end
24
+
25
+ # This global configuation options for SchemaPlus::ForeignKeys.
26
+ # Set them in +config/initializers/schema_plus_foreign_keys.rb+ using:
27
+ #
28
+ # SchemaPlus::ForeignKeys.setup do |config|
29
+ # ...
30
+ # end
31
+ #
32
+ class Config < Valuable
33
+
34
+
35
+ ##
36
+ # :attr_accessor: on_update
37
+ #
38
+ # The default value for +:on_update+ when creating foreign key
39
+ # constraints for columns. Valid values are as described in
40
+ # ForeignKeyDefinition, or +nil+ to let the database connection use
41
+ # its own default. Default is +nil+.
42
+ has_value :on_update
43
+
44
+ ##
45
+ # :attr_accessor: on_delete
46
+ #
47
+ # The default value for +:on_delete+ when creating foreign key
48
+ # constraints for columns. Valid values are as described in
49
+ # ForeignKeyDefinition, or +nil+ to let the database connection use
50
+ # its own default. Default is +nil+.
51
+ has_value :on_delete
52
+
53
+ def merge(opts)
54
+ dup.update_attributes(opts)
55
+ end
56
+ end
57
+
58
+
59
+ # Returns the global configuration, i.e., the singleton instance of Config
60
+ def self.config
61
+ @config ||= Config.new
62
+ end
63
+
64
+ # Initialization block is passed a global Config instance that can be
65
+ # used to configure SchemaPlus::ForeignKeys behavior. E.g., put
66
+ # something like the following in config/initializers/schema_plus_foreign_keys.rb :
67
+ #
68
+ # SchemaPlus::ForeignKeys.setup do |config|
69
+ # config.on_update = :cascade
70
+ # end
71
+ #
72
+ def self.setup # :yields: config
73
+ yield config
74
+ end
75
+
76
+ end
77
+
78
+ SchemaMonkey.register SchemaPlus::ForeignKeys