schema_auto_foreign_keys 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/schema_auto_foreign_keys.svg)](http://badge.fury.io/rb/schema_auto_foreign_keys)
|
2
|
+
[![Build Status](https://secure.travis-ci.org/SchemaPlus/schema_auto_foreign_keys.svg)](http://travis-ci.org/SchemaPlus/schema_auto_foreign_keys)
|
3
|
+
[![Coverage Status](https://img.shields.io/coveralls/SchemaPlus/schema_auto_foreign_keys.svg)](https://coveralls.io/r/SchemaPlus/schema_auto_foreign_keys)
|
4
|
+
[![Dependency Status](https://gemnasium.com/lomba/schema_auto_foreign_keys.svg)](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
|