schema_auto_foreign_keys 0.1.0
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 +9 -0
- data/.travis.yml +21 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +144 -0
- data/Rakefile +9 -0
- data/gemfiles/Gemfile.base +4 -0
- data/gemfiles/activerecord-4.2.0/Gemfile.base +3 -0
- data/gemfiles/activerecord-4.2.0/Gemfile.mysql2 +10 -0
- data/gemfiles/activerecord-4.2.0/Gemfile.postgresql +10 -0
- data/gemfiles/activerecord-4.2.0/Gemfile.sqlite3 +10 -0
- data/gemfiles/activerecord-4.2.1/Gemfile.base +3 -0
- data/gemfiles/activerecord-4.2.1/Gemfile.mysql2 +10 -0
- data/gemfiles/activerecord-4.2.1/Gemfile.postgresql +10 -0
- data/gemfiles/activerecord-4.2.1/Gemfile.sqlite3 +10 -0
- data/lib/schema_auto_foreign_keys.rb +31 -0
- data/lib/schema_auto_foreign_keys/active_record/connection_adapters/sqlite3_adapter.rb +22 -0
- data/lib/schema_auto_foreign_keys/middleware/migration.rb +90 -0
- data/lib/schema_auto_foreign_keys/middleware/schema.rb +18 -0
- data/lib/schema_auto_foreign_keys/version.rb +3 -0
- data/schema_auto_foreign_keys.gemspec +30 -0
- data/schema_dev.yml +9 -0
- data/spec/migration_spec.rb +403 -0
- data/spec/schema_spec.rb +42 -0
- data/spec/spec_helper.rb +52 -0
- data/spec/support/matchers/automatic_foreign_key_matchers.rb +2 -0
- data/spec/support/matchers/have_index.rb +60 -0
- data/spec/support/matchers/reference.rb +79 -0
- metadata +191 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c7bb4e877d1795bda1f593666141fcc43c154410
|
4
|
+
data.tar.gz: 171e3b7fafbb1b6cbce610b9a7a83c425b93b7ab
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 207ed1d89fab44de59c78e882b0bc072707626a184045fcbc933e632f79f53871473762eaae3f8d71bc640e1f8735a38cbd18f064ccac496a68fc0de297ee5bf
|
7
|
+
data.tar.gz: c09043f5a1d8151b72c5432fde2e9106c6fb9cdb908736565710ac0fb467d0fca6716457a6740b7b515b6d7c66aeb7dfbf90143bc602157286595da3b142448e
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -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
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
[](http://badge.fury.io/rb/schema_auto_foreign_keys)
|
2
|
+
[](http://travis-ci.org/SchemaPlus/schema_auto_foreign_keys)
|
3
|
+
[](https://coveralls.io/r/SchemaPlus/schema_auto_foreign_keys)
|
4
|
+
[](https://gemnasium.com/SchemaPlus/schema_auto_foreign_keys)
|
5
|
+
|
6
|
+
# SchemaAutoForeignKeys
|
7
|
+
|
8
|
+
|
9
|
+
SchemaAutoForeignKeys is part of the [SchemaPlus](https://github.com/SchemaPlus/) family of Ruby on Rails ActiveRecord extension gems.
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
Many of us think that it should goes without saying that if you define a foreign key *relation* in your database you should also define a foreign key *constraint*.
|
14
|
+
|
15
|
+
Similarly, it should go without saying that if you have a foreign key *constraint* on a column, you should also have an *index* on that column.
|
16
|
+
|
17
|
+
And if you include the `schema_auto_foreign_keys` gem, these will also go without typing! schema_auto_foreign_keys simply turns on some default behavior in your migrations:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
t.integer :user_id # any column named xxxx_id defaults to...
|
21
|
+
t.integer :user_id, foreign_key: true, index: true
|
22
|
+
|
23
|
+
t.references :user # references defaults to...
|
24
|
+
t.references :user, foreign_key: true, index: true
|
25
|
+
|
26
|
+
t.belongs_to :user # belongs_to default to...
|
27
|
+
t.belongs_to :user, foreign_key: true, index: true
|
28
|
+
```
|
29
|
+
|
30
|
+
Note that schema_auto_foreign_keys depends on the [schema_plus_foreign_keys](https://github.com/SchemaPlus/schema_plus_foreign_keys) and [schema_plus_indexes](https://github.com/SchemaPlus/schema_plus_indexes) gems, and so makes available their migration shortcuts.
|
31
|
+
|
32
|
+
There is actually one difference between an auto-created index and specifying `index: true`: if you don't specify anything, schema_auto_foreign_keys will maintain "ownership" of the auto-created index: It will remove the index if the foreign key gets removed; and it will rename the index if the table gets renamed.
|
33
|
+
|
34
|
+
### Overriding
|
35
|
+
|
36
|
+
If you need specific paramaters other than the default, you can of course specify them:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
t.integer :user_id, index: :unique # "has one" relationship between users and this
|
40
|
+
model
|
41
|
+
t.integer :user_id, on_delete: :cascade
|
42
|
+
```
|
43
|
+
|
44
|
+
If you don't want a foreign key constraint (e.g. because "product_id" is a domain-level string rather than a foreign key), or an index just specify falsey:
|
45
|
+
|
46
|
+
```rugy
|
47
|
+
t.integer :product_id, foreign_key: false # also implies index: false
|
48
|
+
t.integer :product_id, references: nil
|
49
|
+
t.integer :user_id, index: false
|
50
|
+
```
|
51
|
+
|
52
|
+
## Configuration
|
53
|
+
|
54
|
+
SchemaAutoForeignKeys adds two new entries to SchemaPlus::ForeignKeys' config:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
SchemaPlus::ForeignKeys.setup do |config|
|
58
|
+
config.auto_create = true # default for schema_auto_foreign_keys
|
59
|
+
config.auto_index = true # default for schema_auto_foreign_keys
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
You can also configure the behavior per-table in a migration:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
create_table :posts, foreign_keys: { auto_create: true, auto_index: true } do |t|
|
67
|
+
t.integer :author_id
|
68
|
+
endf
|
69
|
+
```
|
70
|
+
|
71
|
+
|
72
|
+
## Installation
|
73
|
+
|
74
|
+
<!-- SCHEMA_DEV: TEMPLATE INSTALLATION - begin -->
|
75
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
76
|
+
As usual:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
gem "schema_auto_foreign_keys" # in a Gemfile
|
80
|
+
gem.add_dependency "schema_auto_foreign_keys" # in a .gemspec
|
81
|
+
```
|
82
|
+
|
83
|
+
<!-- SCHEMA_DEV: TEMPLATE INSTALLATION - end -->
|
84
|
+
|
85
|
+
## Compatibility
|
86
|
+
|
87
|
+
SchemaAutoForeignKeys is tested on:
|
88
|
+
|
89
|
+
<!-- SCHEMA_DEV: MATRIX - begin -->
|
90
|
+
<!-- These lines are auto-generated by schema_dev based on schema_dev.yml -->
|
91
|
+
* ruby **2.1.5** with activerecord **4.2.0**, using **mysql2**, **sqlite3** or **postgresql**
|
92
|
+
* ruby **2.1.5** with activerecord **4.2.1**, using **mysql2**, **sqlite3** or **postgresql**
|
93
|
+
|
94
|
+
<!-- SCHEMA_DEV: MATRIX - end -->
|
95
|
+
|
96
|
+
### Platform-specific Notes:
|
97
|
+
|
98
|
+
MySQL automatically creates indexes for foreign key constraints, so when used with MySQL, schema_auto_foreign_keys doesn't include the auto-index capability.
|
99
|
+
|
100
|
+
SQlite3 doesn't support renaming the auto-index whtn the table name changes.
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
## History
|
105
|
+
|
106
|
+
* 0.1.0 - Initial release, extracted from schema_plus 2.0.0.pre*
|
107
|
+
|
108
|
+
## Development & Testing
|
109
|
+
|
110
|
+
Are you interested in contributing to SchemaAutoForeignKeys? Thanks! Please follow
|
111
|
+
the standard protocol: fork, feature branch, develop, push, and issue pull
|
112
|
+
request.
|
113
|
+
|
114
|
+
Some things to know about to help you develop and test:
|
115
|
+
|
116
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_DEV - begin -->
|
117
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
118
|
+
* **schema_dev**: SchemaAutoForeignKeys uses [schema_dev](https://github.com/SchemaPlus/schema_dev) to
|
119
|
+
facilitate running rspec tests on the matrix of ruby, activerecord, and database
|
120
|
+
versions that the gem supports, both locally and on
|
121
|
+
[travis-ci](http://travis-ci.org/SchemaPlus/schema_auto_foreign_keys)
|
122
|
+
|
123
|
+
To to run rspec locally on the full matrix, do:
|
124
|
+
|
125
|
+
$ schema_dev bundle install
|
126
|
+
$ schema_dev rspec
|
127
|
+
|
128
|
+
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.
|
129
|
+
|
130
|
+
The matrix of configurations is specified in `schema_dev.yml` in
|
131
|
+
the project root.
|
132
|
+
|
133
|
+
|
134
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_DEV - end -->
|
135
|
+
|
136
|
+
|
137
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_MONKEY - begin -->
|
138
|
+
<!-- These lines are auto-inserted from a schema_dev template -->
|
139
|
+
* **schema_monkey**: SchemaAutoForeignKeys is implemented as a
|
140
|
+
[schema_monkey](https://github.com/SchemaPlus/schema_monkey) client,
|
141
|
+
using [schema_monkey](https://github.com/SchemaPlus/schema_monkey)'s
|
142
|
+
convention-based protocols for extending ActiveRecord and using middleware stacks.
|
143
|
+
|
144
|
+
<!-- SCHEMA_DEV: TEMPLATE USES SCHEMA_MONKEY - end -->
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'schema_plus/foreign_keys'
|
2
|
+
require 'schema_plus/indexes'
|
3
|
+
|
4
|
+
require_relative 'schema_auto_foreign_keys/middleware/migration'
|
5
|
+
require_relative 'schema_auto_foreign_keys/middleware/schema'
|
6
|
+
|
7
|
+
module SchemaAutoForeignKeys
|
8
|
+
module ActiveRecord
|
9
|
+
module ConnectionAdapters
|
10
|
+
autoload :Sqlite3Adapter, 'schema_auto_foreign_keys/active_record/connection_adapters/sqlite3_adapter'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class SchemaPlus::ForeignKeys::Config
|
16
|
+
##
|
17
|
+
# :attr_accessor: auto_create
|
18
|
+
#
|
19
|
+
# Whether to automatically create foreign key constraints for columns
|
20
|
+
# suffixed with +_id+. Boolean, default is +true+.
|
21
|
+
has_value :auto_create, :klass => :boolean, :default => true
|
22
|
+
|
23
|
+
##
|
24
|
+
# :attr_accessor: auto_index
|
25
|
+
#
|
26
|
+
# Whether to automatically create indexes when creating foreign key constraints for columns.
|
27
|
+
# Boolean, default is +true+.
|
28
|
+
has_value :auto_index, :klass => :boolean, :default => true
|
29
|
+
end
|
30
|
+
|
31
|
+
SchemaMonkey.register SchemaAutoForeignKeys
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SchemaAutoForeignKeys
|
2
|
+
module ActiveRecord
|
3
|
+
module ConnectionAdapters
|
4
|
+
|
5
|
+
# SchemaPlus::ForeignKeys includes an Sqlite3 implementation of the AbstractAdapter
|
6
|
+
# extensions.
|
7
|
+
module Sqlite3Adapter
|
8
|
+
|
9
|
+
def copy_table(*args, &block)
|
10
|
+
fk_override = { :auto_create => false, :auto_index => false }
|
11
|
+
save = Hash[fk_override.keys.collect{|key| [key, SchemaPlus::ForeignKeys.config.send(key)]}]
|
12
|
+
begin
|
13
|
+
SchemaPlus::ForeignKeys.config.update_attributes(fk_override)
|
14
|
+
super
|
15
|
+
ensure
|
16
|
+
SchemaPlus::ForeignKeys.config.update_attributes(save)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module SchemaAutoForeignKeys
|
4
|
+
module AutoCreate
|
5
|
+
# defined below
|
6
|
+
end
|
7
|
+
|
8
|
+
module Middleware
|
9
|
+
module Migration
|
10
|
+
module Column
|
11
|
+
module PostgreSQL ; include AutoCreate ; end
|
12
|
+
module SQLite3 ; include AutoCreate ; end
|
13
|
+
module MySQL
|
14
|
+
include AutoCreate
|
15
|
+
def auto_index?(env, config) ; false end
|
16
|
+
def remove_auto_index?(env) ; false end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module RenameTable
|
21
|
+
def after(env)
|
22
|
+
newname = env.new_name
|
23
|
+
oldname = env.table_name
|
24
|
+
indexes = env.connection.indexes(newname)
|
25
|
+
env.connection.foreign_keys(newname).each do |fk|
|
26
|
+
index = indexes.find(&its.name == AutoCreate.auto_index_name(oldname, fk.column))
|
27
|
+
env.connection.rename_index(newname, index.name, AutoCreate.auto_index_name(newname, index.columns)) if index
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module AutoCreate
|
35
|
+
def before(env)
|
36
|
+
config ||= env.caller.try(:schema_plus_foreign_keys_config) || SchemaPlus::ForeignKeys.config
|
37
|
+
set_foreign_key(env) if auto_fk?(env, config)
|
38
|
+
set_auto_index(env) if auto_index?(env, config)
|
39
|
+
end
|
40
|
+
|
41
|
+
def after(env)
|
42
|
+
remove_auto_index(env) if env.operation == :change and remove_auto_index?(env)
|
43
|
+
end
|
44
|
+
|
45
|
+
def auto_fk?(env, config)
|
46
|
+
return false if env.options.include? :foreign_key
|
47
|
+
return false unless config.auto_create?
|
48
|
+
return true if env.type == :reference
|
49
|
+
return false if env.implements_reference
|
50
|
+
return true if env.column_name.to_s =~ /_id$/ # later on add a config option for this
|
51
|
+
end
|
52
|
+
|
53
|
+
def auto_index?(env, config)
|
54
|
+
return false if env.options.include? :index
|
55
|
+
return false unless env.options[:foreign_key]
|
56
|
+
return true if config.auto_index?
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove_auto_index?(env)
|
60
|
+
env.options.include? :foreign_key and not env.options[:foreign_key]
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_foreign_key(env)
|
64
|
+
env.options[:foreign_key] = true
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_auto_index(env)
|
68
|
+
env.options[:index] = { name: auto_index_name(env) }
|
69
|
+
end
|
70
|
+
|
71
|
+
def remove_auto_index(env)
|
72
|
+
env.caller.remove_index(env.table_name, :name => auto_index_name(env), :column => env.column_name, :if_exists => true)
|
73
|
+
end
|
74
|
+
|
75
|
+
def auto_index_name(env)
|
76
|
+
AutoCreate.auto_index_name(env.table_name, env.column_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.auto_index_name(from_table, column_name)
|
80
|
+
name = "fk__#{fixup_schema_name(from_table)}_#{Array.wrap(column_name).join('_and_')}"
|
81
|
+
name = name.slice(0, 27) + "_" + OpenSSL::Digest::MD5.new.hexdigest(name) if name.length > 60
|
82
|
+
name
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.fixup_schema_name(table_name)
|
86
|
+
# replace . with _
|
87
|
+
table_name.to_s.gsub(/[.]/, '_')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|