sparkfly-foreigner 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|