sparkfly-foreigner 0.5.4
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.
- data/.gitignore +3 -0
- data/MIT-LICENSE +21 -0
- data/README.textile +105 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/lib/foreigner.rb +58 -0
- data/lib/foreigner/connection_adapters/abstract/schema_definitions.rb +154 -0
- data/lib/foreigner/connection_adapters/abstract/schema_statements.rb +75 -0
- data/lib/foreigner/connection_adapters/mysql_adapter.rb +48 -0
- data/lib/foreigner/connection_adapters/postgresql_adapter.rb +47 -0
- data/lib/foreigner/connection_adapters/sqlite3_adapter.rb +46 -0
- data/lib/foreigner/schema_dumper.rb +47 -0
- data/lib/foreigner/semantics/sql_2003.rb +78 -0
- data/spec/adapter_helper.rb +100 -0
- data/spec/factory_helper.rb +70 -0
- data/spec/mysql/schema_extractor_spec.rb +143 -0
- data/spec/mysql/schema_spec.rb +87 -0
- data/spec/mysql/semantics_spec.rb +75 -0
- data/spec/postgresql/schema_extractor_spec.rb +143 -0
- data/spec/postgresql/schema_spec.rb +152 -0
- data/spec/postgresql/semantics_spec.rb +75 -0
- data/spec/schema_dumper_spec.rb +96 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/sqlite3/schema_spec.rb +86 -0
- data/tasks/foreigner_tasks.rake +4 -0
- metadata +96 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
module Foreigner
|
2
|
+
module ConnectionAdapters
|
3
|
+
module MysqlAdapter
|
4
|
+
include Foreigner::Semantics::Sql2003
|
5
|
+
|
6
|
+
|
7
|
+
# Override SQL2003 Semantics for MySQL
|
8
|
+
def sql_for_remove_foreign_key(table, foreign_key_name)
|
9
|
+
"ALTER TABLE #{quote_table_name(table)} DROP FOREIGN KEY #{quote_column_name(foreign_key_name)}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def foreign_keys(table_name)
|
13
|
+
fk_info = select_all %{
|
14
|
+
SELECT fk.referenced_table_name as 'to_table'
|
15
|
+
,fk.referenced_column_name as 'primary_key'
|
16
|
+
,fk.column_name as 'column'
|
17
|
+
,fk.constraint_name as 'name'
|
18
|
+
FROM information_schema.key_column_usage fk
|
19
|
+
WHERE fk.referenced_column_name is not null
|
20
|
+
AND fk.table_schema = '#{@config[:database]}'
|
21
|
+
AND fk.table_name = '#{table_name}'
|
22
|
+
}
|
23
|
+
|
24
|
+
create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
25
|
+
|
26
|
+
fk_info.map do |row|
|
27
|
+
options = {:column => row['column'], :name => row['name'], :primary_key => row['primary_key']}
|
28
|
+
|
29
|
+
if create_table_info =~ /CONSTRAINT #{quote_column_name(row['name'])} FOREIGN KEY .* REFERENCES .* ON DELETE (CASCADE|SET NULL)/
|
30
|
+
options[:dependent] = case $1
|
31
|
+
when 'CASCADE' then :delete
|
32
|
+
when 'SET NULL' then :nullify
|
33
|
+
end
|
34
|
+
end
|
35
|
+
ForeignKeyDefinition.new(table_name.to_s, row['to_table'], options)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module ActiveRecord
|
43
|
+
module ConnectionAdapters
|
44
|
+
MysqlAdapter.class_eval do
|
45
|
+
include Foreigner::ConnectionAdapters::MysqlAdapter
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Foreigner
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQLAdapter
|
4
|
+
include Foreigner::Semantics::Sql2003
|
5
|
+
|
6
|
+
|
7
|
+
def foreign_keys(table_name)
|
8
|
+
fk_info = select_all %{
|
9
|
+
SELECT tc.constraint_name as name
|
10
|
+
,ccu.table_name as to_table
|
11
|
+
,ccu.column_name as primary_key
|
12
|
+
,kcu.column_name as column
|
13
|
+
,rc.delete_rule as dependency
|
14
|
+
FROM information_schema.table_constraints tc
|
15
|
+
JOIN information_schema.key_column_usage kcu
|
16
|
+
USING (constraint_catalog, constraint_schema, constraint_name)
|
17
|
+
JOIN information_schema.referential_constraints rc
|
18
|
+
USING (constraint_catalog, constraint_schema, constraint_name)
|
19
|
+
JOIN information_schema.constraint_column_usage ccu
|
20
|
+
USING (constraint_catalog, constraint_schema, constraint_name)
|
21
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
22
|
+
AND tc.constraint_catalog = '#{@config[:database]}'
|
23
|
+
AND tc.table_name = '#{table_name}'
|
24
|
+
}
|
25
|
+
|
26
|
+
fk_info.map do |row|
|
27
|
+
options = {:column => row['column'], :name => row['name'], :primary_key => row['primary_key']}
|
28
|
+
|
29
|
+
options[:dependent] = case row['dependency']
|
30
|
+
when 'CASCADE' then :delete
|
31
|
+
when 'SET NULL' then :nullify
|
32
|
+
end
|
33
|
+
|
34
|
+
ForeignKeyDefinition.new(table_name.to_s, row['to_table'], options)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module ActiveRecord
|
42
|
+
module ConnectionAdapters
|
43
|
+
PostgreSQLAdapter.class_eval do
|
44
|
+
include Foreigner::ConnectionAdapters::PostgreSQLAdapter
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'foreigner/connection_adapters/sql_2003'
|
2
|
+
|
3
|
+
module Foreigner
|
4
|
+
module ConnectionAdapters
|
5
|
+
module SQLite3Adapter
|
6
|
+
include Foreigner::Semantics::Sql2003
|
7
|
+
|
8
|
+
def foreign_keys(table_name)
|
9
|
+
foreign_keys = []
|
10
|
+
create_table_info = select_value %{
|
11
|
+
SELECT sql
|
12
|
+
FROM sqlite_master
|
13
|
+
WHERE sql LIKE '%FOREIGN KEY%'
|
14
|
+
AND name = '#{table_name}'
|
15
|
+
}
|
16
|
+
unless create_table_info.nil?
|
17
|
+
fk_columns = create_table_info.scan(/FOREIGN KEY\s*\(\"([^\"]+)\"\)/)
|
18
|
+
fk_tables = create_table_info.scan(/REFERENCES\s*\"([^\"]+)\"/)
|
19
|
+
fk_references = create_table_info.scan(/REFERENCES[^\,]+/)
|
20
|
+
if fk_columns.size == fk_tables.size && fk_references.size == fk_columns.size
|
21
|
+
fk_columns.each_with_index do |fk_column, index|
|
22
|
+
if fk_references[index] =~ /ON DELETE CASCADE/
|
23
|
+
fk_references[index] = :delete
|
24
|
+
elsif fk_references[index] =~ /ON DELETE SET NULL/
|
25
|
+
fk_references[index] = :nullify
|
26
|
+
else
|
27
|
+
fk_references[index] = nil
|
28
|
+
end
|
29
|
+
foreign_keys << ForeignKeyDefinition.new(table_name, fk_tables[index][0], :column => fk_column[0], :dependent => fk_references[index])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
foreign_keys
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module ActiveRecord
|
40
|
+
module ConnectionAdapters
|
41
|
+
SQLite3Adapter.class_eval do
|
42
|
+
include Foreigner::ConnectionAdapters::SQLite3Adapter
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Foreigner
|
2
|
+
module SchemaDumper
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
include InstanceMethods
|
6
|
+
alias_method_chain :tables, :foreign_keys
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
def tables_with_foreign_keys(stream)
|
12
|
+
tables_without_foreign_keys(stream)
|
13
|
+
@connection.tables.sort.each do |table|
|
14
|
+
next unless foreign_keys = @connection.foreign_keys(table)
|
15
|
+
stream.puts generate_foreign_keys_statements(foreign_keys).join("\n")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Generates a string for a given list of ForeignKeyDefinition
|
22
|
+
# Has no concept of streams or connections, so this can be tested in isolation.
|
23
|
+
def generate_foreign_keys_statements(foreign_keys)
|
24
|
+
decorator = [
|
25
|
+
# [ :option_name, lambda { |fk| filter } ],
|
26
|
+
[ :name, lambda { |fk| fk.options[:name] } ],
|
27
|
+
[ :column, lambda { |fk| fk.options[:column] && fk.options[:column] != "#{fk.to_table.singularize}_id" } ],
|
28
|
+
[ :primary_key, lambda { |fk| fk.options[:primary_key] && fk.options[:primary_key] != 'id' } ],
|
29
|
+
[ :dependent, lambda { |fk| fk.options[:dependent].present? } ]
|
30
|
+
]
|
31
|
+
|
32
|
+
foreign_keys.map do |foreign_key|
|
33
|
+
statement_parts = [[ ' ', 'add_foreign_key', foreign_key.from_table.to_sym.inspect].join(' ') ]
|
34
|
+
statement_parts << foreign_key.to_table.to_sym.inspect
|
35
|
+
|
36
|
+
if foreign_key.options
|
37
|
+
statement_parts << decorator.map do |option, guard|
|
38
|
+
[ ':', option, ' => ', foreign_key.options[option].inspect ].join if guard.call(foreign_key)
|
39
|
+
end - [nil]
|
40
|
+
end
|
41
|
+
' ' + statement_parts.join(', ')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end # InstanceMethods
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Foreigner
|
2
|
+
module Semantics
|
3
|
+
module Sql2003
|
4
|
+
def supports_foreign_keys?
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
def foreign_key_definition(to_table, options = {})
|
9
|
+
column = options[:column] || "#{to_table.to_s.singularize}_id"
|
10
|
+
dependency = sql_for_dependency(options[:dependent])
|
11
|
+
|
12
|
+
sql = "FOREIGN KEY (#{quote_column_name(column)}) REFERENCES #{quote_table_name(to_table)}(id)"
|
13
|
+
sql << " #{dependency}" unless dependency.blank?
|
14
|
+
sql
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_foreign_key(from_table, to_table, options = {})
|
18
|
+
column = options[:column] || "#{to_table.to_s.singularize}_id"
|
19
|
+
foreign_key_name = foreign_key_name(from_table, column, options)
|
20
|
+
primary_key = options[:primary_key] || "id"
|
21
|
+
reference = sql_for_reference(to_table, primary_key)
|
22
|
+
dependency = sql_for_dependency(options[:dependent])
|
23
|
+
|
24
|
+
execute(sql_for_add_foreign_key(from_table, foreign_key_name, column, reference, dependency))
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove_foreign_key(table, options)
|
28
|
+
# If the second argument is table name (String/Symbol) then convert that to the
|
29
|
+
# to the full options hash
|
30
|
+
options = { :column => column_name(options) } if String === options || Symbol === options
|
31
|
+
execute(sql_for_remove_foreign_key(table, foreign_key_name(table, options[:column], options)))
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def foreign_key_name(table, column, options = {})
|
37
|
+
return options[:name] if options[:name]
|
38
|
+
"fk_#{table}_#{column}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def column_name(column)
|
42
|
+
"#{column.to_s.singularize}_id"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Generates SQL and returns it.
|
46
|
+
def sql_for_add_foreign_key(from_table, foreign_key_name, column, reference, dependent = nil)
|
47
|
+
sql = [
|
48
|
+
"ALTER TABLE #{quote_table_name(from_table)}",
|
49
|
+
"ADD CONSTRAINT #{quote_column_name(foreign_key_name)}",
|
50
|
+
"FOREIGN KEY (#{quote_column_name(column)})",
|
51
|
+
"REFERENCES #{reference}"
|
52
|
+
]
|
53
|
+
|
54
|
+
sql << "#{dependent}" unless dependent.blank?
|
55
|
+
sql.join(' ')
|
56
|
+
end
|
57
|
+
|
58
|
+
def sql_for_remove_foreign_key(table, foreign_key_name)
|
59
|
+
"ALTER TABLE #{quote_table_name(table)} DROP CONSTRAINT #{quote_column_name(foreign_key_name)}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def sql_for_reference(to_table, primary_key)
|
63
|
+
"#{quote_table_name(ActiveRecord::Migrator.proper_table_name(to_table))}(#{primary_key})"
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def sql_for_dependency(dependency)
|
68
|
+
case dependency
|
69
|
+
when :nullify then 'ON DELETE SET NULL'
|
70
|
+
when :delete then 'ON DELETE CASCADE'
|
71
|
+
else ''
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
# CONFIGURATIONS is defined in spec_helper
|
3
|
+
|
4
|
+
module AdapterHelper
|
5
|
+
module AdapterTestHarness
|
6
|
+
def recreate_test_environment(env)
|
7
|
+
ActiveRecord::Base.establish_connection(CONFIGURATIONS[env])
|
8
|
+
|
9
|
+
@database = CONFIGURATIONS[env][:database]
|
10
|
+
ActiveRecord::Base.connection.drop_database(@database)
|
11
|
+
ActiveRecord::Base.connection.create_database(@database)
|
12
|
+
ActiveRecord::Base.connection.reset!
|
13
|
+
|
14
|
+
FactoryHelpers::CreateCollection.up
|
15
|
+
end
|
16
|
+
|
17
|
+
def schema(table_name)
|
18
|
+
raise 'This method must be overridden'
|
19
|
+
end
|
20
|
+
|
21
|
+
def foreign_keys(table)
|
22
|
+
ActiveRecord::Base.connection.foreign_keys(table)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def execute(sql, name = nil)
|
28
|
+
sql
|
29
|
+
end
|
30
|
+
|
31
|
+
def quote_column_name(name)
|
32
|
+
"`#{name}`"
|
33
|
+
end
|
34
|
+
|
35
|
+
def quote_table_name(name)
|
36
|
+
quote_column_name(name).gsub('.', '`.`')
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
class PostgreSQLTestAdapter
|
42
|
+
include Foreigner::ConnectionAdapters::PostgreSQLAdapter
|
43
|
+
include AdapterTestHarness
|
44
|
+
|
45
|
+
def recreate_test_environment
|
46
|
+
ActiveRecord::Base.establish_connection(CONFIGURATIONS[:postgresql_admin])
|
47
|
+
@database = CONFIGURATIONS[:postgresql][:database]
|
48
|
+
|
49
|
+
ActiveRecord::Base.connection.drop_database(@database)
|
50
|
+
ActiveRecord::Base.connection.create_database(@database)
|
51
|
+
|
52
|
+
ActiveRecord::Base.connection.disconnect!
|
53
|
+
ActiveRecord::Base.establish_connection(CONFIGURATIONS[:postgresql])
|
54
|
+
|
55
|
+
FactoryHelpers::CreateCollection.up
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class MySQLTestAdapter
|
60
|
+
include Foreigner::ConnectionAdapters::MysqlAdapter
|
61
|
+
include AdapterTestHarness
|
62
|
+
|
63
|
+
def schema(table_name)
|
64
|
+
ActiveRecord::Base.connection.select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
|
65
|
+
end
|
66
|
+
|
67
|
+
def recreate_test_environment
|
68
|
+
super(:mysql)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class SQLite3TestAdapter
|
73
|
+
include Foreigner::ConnectionAdapters::SQLite3Adapter
|
74
|
+
include AdapterTestHarness
|
75
|
+
|
76
|
+
def schema(table_name) ActiveRecord::Base.connection.select_value %{
|
77
|
+
SELECT sql
|
78
|
+
FROM sqlite_master
|
79
|
+
WHERE name = '#{table_name}'
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def foreign_keys(table)
|
84
|
+
raise "Unimplemented"
|
85
|
+
end
|
86
|
+
|
87
|
+
def recreate_test_environment
|
88
|
+
ActiveRecord::Base.establish_connection(CONFIGURATIONS[:sqlite3])
|
89
|
+
|
90
|
+
@database = CONFIGURATIONS[:sqlite3][:database]
|
91
|
+
#ActiveRecord::Base.connection.drop_database(@database)
|
92
|
+
#ActiveRecord::Base.connection.create_database(@database)
|
93
|
+
ActiveRecord::Base.connection.reset!
|
94
|
+
|
95
|
+
FactoryHelpers::CreateCollection.up
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module FactoryHelpers
|
2
|
+
class CreateCollection < ActiveRecord::Migration
|
3
|
+
def self.up
|
4
|
+
create_table :collections do |t|
|
5
|
+
t.string :name
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.down
|
10
|
+
drop_table :collections
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module MigrationFactory
|
16
|
+
|
17
|
+
# Creates a new anonymous migration and puts something into self.up
|
18
|
+
# Example:
|
19
|
+
# migration = create_migration do
|
20
|
+
# create_table :items do |t|
|
21
|
+
# t.string :name
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
def create_migration(&blk)
|
25
|
+
migration = Class.new(ActiveRecord::Migration)
|
26
|
+
|
27
|
+
# This is the equivalent of
|
28
|
+
# class Foo
|
29
|
+
# def self.up
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
migration.metaclass.class_eval do
|
33
|
+
define_method(:up, &blk)
|
34
|
+
end
|
35
|
+
migration
|
36
|
+
end
|
37
|
+
|
38
|
+
# Creates a new, anonymous table migration and activates it
|
39
|
+
# Example:
|
40
|
+
# migration = create_table do |t|
|
41
|
+
# t.string :name
|
42
|
+
# end
|
43
|
+
def create_table(table = :items, opts = {}, &blk)
|
44
|
+
migration = create_migration do
|
45
|
+
create_table(table, opts, &blk)
|
46
|
+
end
|
47
|
+
migration.up
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
module ForeignKeyDefinitionFactory
|
53
|
+
def valid_foreign_key_definition(opt = {})
|
54
|
+
options = {
|
55
|
+
:from_table => 'items',
|
56
|
+
:to_table => 'collections'
|
57
|
+
}.merge(opt)
|
58
|
+
end
|
59
|
+
|
60
|
+
def valid_foreign_key_args(definition)
|
61
|
+
from_table = definition.delete(:from_table)
|
62
|
+
to_table = definition.delete(:to_table)
|
63
|
+
[from_table, to_table, definition]
|
64
|
+
end
|
65
|
+
|
66
|
+
def new_foreign_key(opt = {})
|
67
|
+
args = valid_foreign_key_args(valid_foreign_key_definition(opt))
|
68
|
+
Foreigner::ConnectionAdapters::ForeignKeyDefinition.new(*args)
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require File.expand_path('../spec_helper.rb', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
describe Foreigner::ConnectionAdapters::MysqlAdapter do
|
4
|
+
include MigrationFactory
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@adapter = AdapterHelper::MySQLTestAdapter.new
|
8
|
+
@adapter.recreate_test_environment
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'when extracting foreign keys from a table' do
|
12
|
+
it 'should extract single foreign key' do
|
13
|
+
create_table :items do |t|
|
14
|
+
t.string :name
|
15
|
+
t.references :collection, :null => false
|
16
|
+
t.foreign_key :collection
|
17
|
+
end
|
18
|
+
|
19
|
+
@adapter.foreign_keys(:items).length.should eql(1)
|
20
|
+
foreign_key = @adapter.foreign_keys(:items)[0]
|
21
|
+
|
22
|
+
# Duck Typing
|
23
|
+
foreign_key.should be_respond_to(:from_table)
|
24
|
+
foreign_key.should be_respond_to(:to_table)
|
25
|
+
foreign_key.should be_respond_to(:options)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should extract multiple foreign keys' do
|
29
|
+
create_table :owners do |t|
|
30
|
+
t.string :name
|
31
|
+
end
|
32
|
+
|
33
|
+
create_table :items do |t|
|
34
|
+
t.string :name
|
35
|
+
t.references :collection, :null => false
|
36
|
+
t.foreign_key :collection
|
37
|
+
t.references :owner, :null => false
|
38
|
+
t.foreign_key :owner
|
39
|
+
end
|
40
|
+
|
41
|
+
@adapter.foreign_keys(:items).length.should eql(2)
|
42
|
+
foreign_key_names = @adapter.foreign_keys(:items).map(&:to_table)
|
43
|
+
foreign_key_names.should be_include('collections')
|
44
|
+
foreign_key_names.should be_include('owners')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should extract referencing table' do
|
48
|
+
create_table :items do |t|
|
49
|
+
t.string :name
|
50
|
+
t.references :collection, :null => false
|
51
|
+
t.foreign_key :collection
|
52
|
+
end
|
53
|
+
|
54
|
+
@adapter.foreign_keys(:items).length.should eql(1)
|
55
|
+
foreign_key = @adapter.foreign_keys(:items)[0]
|
56
|
+
foreign_key.from_table.should eql('items')
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should extract foreign table' do
|
60
|
+
create_table :items do |t|
|
61
|
+
t.string :name
|
62
|
+
t.references :collection, :null => false
|
63
|
+
t.foreign_key :collection
|
64
|
+
end
|
65
|
+
|
66
|
+
@adapter.foreign_keys(:items).length.should eql(1)
|
67
|
+
foreign_key = @adapter.foreign_keys(:items)[0]
|
68
|
+
foreign_key.to_table.should eql('collections')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should extract foreign key name' do
|
72
|
+
fk_name = 'custom_foreign_key'
|
73
|
+
|
74
|
+
create_migration do
|
75
|
+
create_table :items do |t|
|
76
|
+
t.string :name
|
77
|
+
t.references :collection, :null => false
|
78
|
+
end
|
79
|
+
add_foreign_key :items, :collections, :name => fk_name
|
80
|
+
end.up
|
81
|
+
|
82
|
+
@adapter.foreign_keys(:items).length.should eql(1)
|
83
|
+
foreign_key = @adapter.foreign_keys(:items)[0]
|
84
|
+
foreign_key.options[:name].should eql(fk_name)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should extract foreign column' do
|
88
|
+
create_table :items do |t|
|
89
|
+
t.string :name
|
90
|
+
t.references :collection, :null => false
|
91
|
+
t.foreign_key :collection
|
92
|
+
end
|
93
|
+
|
94
|
+
@adapter.foreign_keys(:items).length.should eql(1)
|
95
|
+
foreign_key = @adapter.foreign_keys(:items)[0]
|
96
|
+
foreign_key.options[:column].should eql('collection_id')
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should extract primary key' do
|
100
|
+
primary_key = 'acctno'
|
101
|
+
create_table :accounts, :primary_key => primary_key do |t|
|
102
|
+
t.integer primary_key, :null => false
|
103
|
+
t.string :name
|
104
|
+
end
|
105
|
+
|
106
|
+
create_migration do
|
107
|
+
create_table :items do |t|
|
108
|
+
t.string :name
|
109
|
+
t.integer primary_key, :null => false
|
110
|
+
end
|
111
|
+
add_foreign_key :items, :accounts, :column => primary_key, :primary_key => primary_key
|
112
|
+
end.up
|
113
|
+
|
114
|
+
@adapter.foreign_keys(:items).length.should eql(1)
|
115
|
+
foreign_key = @adapter.foreign_keys(:items)[0]
|
116
|
+
foreign_key.options[:primary_key].should eql(primary_key)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should extract :dependent => :nullify' do
|
120
|
+
@dependent = :nullify
|
121
|
+
create_table :items do |t|
|
122
|
+
t.string :name
|
123
|
+
t.references :collection, :foreign_key => {:dependent => @dependent}
|
124
|
+
end
|
125
|
+
|
126
|
+
foreign_key = @adapter.foreign_keys(:items)[0]
|
127
|
+
foreign_key.options[:dependent].should eql(@dependent)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should extract :dependent => :delete' do
|
131
|
+
@dependent = :delete
|
132
|
+
create_table :items do |t|
|
133
|
+
t.string :name
|
134
|
+
t.references :collection, :foreign_key => {:dependent => @dependent}
|
135
|
+
end
|
136
|
+
|
137
|
+
foreign_key = @adapter.foreign_keys(:items)[0]
|
138
|
+
foreign_key.options[:dependent].should eql(@dependent)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|