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