schema_plus 0.4.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -1
- data/.travis.yml +6 -9
- data/Gemfile +0 -4
- data/README.rdoc +168 -70
- data/Rakefile +58 -47
- data/gemfiles/rails-3.2/Gemfile.base +4 -0
- data/gemfiles/rails-3.2/Gemfile.mysql +4 -0
- data/gemfiles/rails-3.2/Gemfile.mysql2 +4 -0
- data/gemfiles/rails-3.2/Gemfile.postgresql +4 -0
- data/gemfiles/rails-3.2/Gemfile.sqlite3 +4 -0
- data/lib/schema_plus.rb +2 -0
- data/lib/schema_plus/active_record/column_options_handler.rb +73 -32
- data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +60 -31
- data/lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb +7 -2
- data/lib/schema_plus/active_record/connection_adapters/index_definition.rb +2 -1
- data/lib/schema_plus/active_record/connection_adapters/mysql_adapter.rb +19 -1
- data/lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb +68 -17
- data/lib/schema_plus/active_record/connection_adapters/sqlite3_adapter.rb +28 -3
- data/lib/schema_plus/active_record/connection_adapters/table_definition.rb +27 -1
- data/lib/schema_plus/active_record/db_default.rb +19 -0
- data/lib/schema_plus/active_record/foreign_keys.rb +40 -32
- data/lib/schema_plus/active_record/schema_dumper.rb +7 -3
- data/lib/schema_plus/version.rb +1 -1
- data/runspecs +5 -8
- data/schema_plus.gemspec +2 -5
- data/spec/column_definition_spec.rb +18 -1
- data/spec/column_spec.rb +39 -2
- data/spec/connection_spec.rb +1 -1
- data/spec/connections/mysql/connection.rb +1 -1
- data/spec/connections/mysql2/connection.rb +1 -1
- data/spec/connections/postgresql/connection.rb +1 -1
- data/spec/foreign_key_definition_spec.rb +0 -4
- data/spec/foreign_key_spec.rb +37 -13
- data/spec/index_definition_spec.rb +54 -2
- data/spec/index_spec.rb +59 -15
- data/spec/migration_spec.rb +336 -85
- data/spec/multiple_schemas_spec.rb +127 -0
- data/spec/schema_dumper_spec.rb +65 -25
- data/spec/schema_spec.rb +16 -18
- data/spec/spec_helper.rb +19 -18
- data/spec/support/matchers/reference.rb +7 -1
- data/spec/views_spec.rb +5 -2
- metadata +43 -54
- data/gemfiles/Gemfile.rails-2.3 +0 -6
- data/gemfiles/Gemfile.rails-2.3.lock +0 -65
- data/gemfiles/Gemfile.rails-3.0 +0 -5
- data/gemfiles/Gemfile.rails-3.0.lock +0 -113
- data/gemfiles/Gemfile.rails-3.1 +0 -5
- data/gemfiles/Gemfile.rails-3.1.lock +0 -123
- data/gemfiles/Gemfile.rails-3.2 +0 -5
- data/gemfiles/Gemfile.rails-3.2.lock +0 -121
- data/spec/models/comment.rb +0 -2
- data/spec/models/post.rb +0 -2
- data/spec/models/user.rb +0 -2
- data/spec/rails3_migration_spec.rb +0 -144
@@ -2,10 +2,6 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
2
2
|
|
3
3
|
describe "Foreign Key definition" do
|
4
4
|
|
5
|
-
before(:all) do
|
6
|
-
load_core_schema
|
7
|
-
end
|
8
|
-
|
9
5
|
let(:definition) { SchemaPlus::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new("posts_user_fkey", :posts, :user, :users, :id) }
|
10
6
|
|
11
7
|
it "it is dumped to sql with quoted values" do
|
data/spec/foreign_key_spec.rb
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
require 'models/user'
|
4
|
-
require 'models/post'
|
5
|
-
require 'models/comment'
|
6
|
-
|
7
3
|
describe "Foreign Key" do
|
8
|
-
|
4
|
+
|
9
5
|
let(:migration) { ::ActiveRecord::Migration }
|
10
6
|
|
11
7
|
context "created with table" do
|
12
8
|
before(:all) do
|
13
|
-
|
9
|
+
define_schema(:auto_create => true) do
|
10
|
+
create_table :users, :force => true do |t|
|
11
|
+
t.string :login
|
12
|
+
end
|
13
|
+
create_table :comments, :force => true do |t|
|
14
|
+
t.integer :user_id
|
15
|
+
t.foreign_key :user_id, :users, :id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
class User < ::ActiveRecord::Base ; end
|
19
|
+
class Comment < ::ActiveRecord::Base ; end
|
14
20
|
end
|
15
21
|
|
16
22
|
it "should report foreign key constraints" do
|
@@ -26,7 +32,27 @@ describe "Foreign Key" do
|
|
26
32
|
context "modification" do
|
27
33
|
|
28
34
|
before(:all) do
|
29
|
-
|
35
|
+
define_schema(:auto_create => false) do
|
36
|
+
create_table :users, :force => true do |t|
|
37
|
+
t.string :login
|
38
|
+
t.datetime :deleted_at
|
39
|
+
end
|
40
|
+
|
41
|
+
create_table :posts, :force => true do |t|
|
42
|
+
t.text :body
|
43
|
+
t.integer :user_id
|
44
|
+
t.integer :author_id
|
45
|
+
end
|
46
|
+
|
47
|
+
create_table :comments, :force => true do |t|
|
48
|
+
t.text :body
|
49
|
+
t.integer :post_id
|
50
|
+
t.foreign_key :post_id, :posts, :id
|
51
|
+
end
|
52
|
+
end
|
53
|
+
class User < ::ActiveRecord::Base ; end
|
54
|
+
class Post < ::ActiveRecord::Base ; end
|
55
|
+
class Comment < ::ActiveRecord::Base ; end
|
30
56
|
end
|
31
57
|
|
32
58
|
if SchemaPlusHelpers.sqlite3?
|
@@ -34,13 +60,13 @@ describe "Foreign Key" do
|
|
34
60
|
it "raises an exception when attempting to add" do
|
35
61
|
expect {
|
36
62
|
add_foreign_key(:posts, :author_id, :users, :id, :on_update => :cascade, :on_delete => :restrict)
|
37
|
-
}.
|
63
|
+
}.to raise_error(NotImplementedError)
|
38
64
|
end
|
39
65
|
|
40
66
|
it "raises an exception when attempting to remove" do
|
41
67
|
expect {
|
42
68
|
remove_foreign_key(:posts, "dummy")
|
43
|
-
}.
|
69
|
+
}.to raise_error(NotImplementedError)
|
44
70
|
end
|
45
71
|
|
46
72
|
else
|
@@ -102,8 +128,6 @@ describe "Foreign Key" do
|
|
102
128
|
Post.reverse_foreign_keys.collect(&:column_names).should_not include(%w[post_id])
|
103
129
|
end
|
104
130
|
|
105
|
-
it "removes auto-generated index"
|
106
|
-
|
107
131
|
end
|
108
132
|
|
109
133
|
context "when referencing column and column is removed" do
|
@@ -117,7 +141,7 @@ describe "Foreign Key" do
|
|
117
141
|
|
118
142
|
end
|
119
143
|
|
120
|
-
context "when table name is a
|
144
|
+
context "when table name is a reserved word" do
|
121
145
|
before(:each) do
|
122
146
|
migration.suppress_messages do
|
123
147
|
migration.create_table :references, :force => true do |t|
|
@@ -132,7 +156,7 @@ describe "Foreign Key" do
|
|
132
156
|
migration.add_foreign_key(:references, :post_id, :posts, :id)
|
133
157
|
foreign_key = migration.foreign_keys(:references).detect{|definition| definition.column_names == ["post_id"]}
|
134
158
|
migration.remove_foreign_key(:references, foreign_key.name)
|
135
|
-
}.
|
159
|
+
}.to_not raise_error
|
136
160
|
end
|
137
161
|
end
|
138
162
|
end
|
@@ -6,7 +6,27 @@ describe "Index definition" do
|
|
6
6
|
let(:migration) { ::ActiveRecord::Migration }
|
7
7
|
|
8
8
|
before(:all) do
|
9
|
-
|
9
|
+
define_schema(:auto_create => false) do
|
10
|
+
create_table :users, :force => true do |t|
|
11
|
+
t.string :login
|
12
|
+
t.datetime :deleted_at
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :posts, :force => true do |t|
|
16
|
+
t.text :body
|
17
|
+
t.integer :user_id
|
18
|
+
t.integer :author_id
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table :comments, :force => true do |t|
|
22
|
+
t.text :body
|
23
|
+
t.integer :post_id
|
24
|
+
t.foreign_key :post_id, :posts, :id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
class User < ::ActiveRecord::Base ; end
|
28
|
+
class Post < ::ActiveRecord::Base ; end
|
29
|
+
class Comment < ::ActiveRecord::Base ; end
|
10
30
|
end
|
11
31
|
|
12
32
|
around(:each) do |example|
|
@@ -27,7 +47,7 @@ describe "Index definition" do
|
|
27
47
|
end
|
28
48
|
|
29
49
|
it "is included in User.indexes" do
|
30
|
-
|
50
|
+
@index.should_not be_nil
|
31
51
|
end
|
32
52
|
|
33
53
|
end
|
@@ -41,6 +61,38 @@ describe "Index definition" do
|
|
41
61
|
end
|
42
62
|
end
|
43
63
|
|
64
|
+
it "should not crash on equality test with nil" do
|
65
|
+
index = ActiveRecord::ConnectionAdapters::IndexDefinition.new(:table, :column)
|
66
|
+
expect{index == nil}.to_not raise_error
|
67
|
+
(index == nil).should be_false
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
unless SchemaPlusHelpers.mysql?
|
72
|
+
context "when index is ordered" do
|
73
|
+
|
74
|
+
quotes = [
|
75
|
+
["unquoted", ''],
|
76
|
+
["double-quoted", '"'],
|
77
|
+
]
|
78
|
+
quotes += [
|
79
|
+
["single-quoted", "'"],
|
80
|
+
["back-quoted", '`']
|
81
|
+
] if SchemaPlusHelpers.sqlite3?
|
82
|
+
|
83
|
+
quotes.each do |quotename, quote|
|
84
|
+
it "index definition includes orders for #{quotename} columns" do
|
85
|
+
migration.execute "CREATE INDEX users_login_index ON users (#{quote}login#{quote} DESC, #{quote}deleted_at#{quote} ASC)"
|
86
|
+
User.reset_column_information
|
87
|
+
index = index_definition(%w[login deleted_at])
|
88
|
+
index.orders.should == {"login" => :desc, "deleted_at" => :asc}
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
44
96
|
if SchemaPlusHelpers.postgresql?
|
45
97
|
|
46
98
|
context "when case insensitive is added" do
|
data/spec/index_spec.rb
CHANGED
@@ -1,11 +1,29 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
require 'models/user'
|
4
|
-
|
5
3
|
describe "add_index" do
|
6
|
-
|
4
|
+
|
7
5
|
before(:all) do
|
8
|
-
|
6
|
+
define_schema(:auto_create => false) do
|
7
|
+
create_table :users, :force => true do |t|
|
8
|
+
t.string :login
|
9
|
+
t.datetime :deleted_at
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :posts, :force => true do |t|
|
13
|
+
t.text :body
|
14
|
+
t.integer :user_id
|
15
|
+
t.integer :author_id
|
16
|
+
end
|
17
|
+
|
18
|
+
create_table :comments, :force => true do |t|
|
19
|
+
t.text :body
|
20
|
+
t.integer :post_id
|
21
|
+
t.foreign_key :post_id, :posts, :id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
class User < ::ActiveRecord::Base ; end
|
25
|
+
class Post < ::ActiveRecord::Base ; end
|
26
|
+
class Comment < ::ActiveRecord::Base ; end
|
9
27
|
end
|
10
28
|
|
11
29
|
let(:migration) { ::ActiveRecord::Migration }
|
@@ -31,21 +49,27 @@ describe "add_index" do
|
|
31
49
|
index_for(:login).name.should == 'users_login_index'
|
32
50
|
end
|
33
51
|
|
52
|
+
unless SchemaPlusHelpers.mysql?
|
53
|
+
it "should assign order" do
|
54
|
+
add_index(:users, [:login, :deleted_at], :order => {:login => :desc, :deleted_at => :asc})
|
55
|
+
index_for([:login, :deleted_at]).orders.should == {"login" => :desc, "deleted_at" => :asc}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
34
60
|
context "for duplicate index" do
|
35
61
|
it "should not complain if the index is the same" do
|
36
62
|
add_index(:users, :login)
|
37
63
|
index_for(:login).should_not be_nil
|
38
64
|
ActiveRecord::Base.logger.should_receive(:warn).with(/login.*Skipping/)
|
39
|
-
expect { add_index(:users, :login) }.
|
65
|
+
expect { add_index(:users, :login) }.to_not raise_error
|
40
66
|
index_for(:login).should_not be_nil
|
41
67
|
end
|
42
|
-
if
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
index_for(:login).should_not be_nil
|
48
|
-
end
|
68
|
+
it "should complain if the index is different" do
|
69
|
+
add_index(:users, :login, :unique => true)
|
70
|
+
index_for(:login).should_not be_nil
|
71
|
+
expect { add_index(:users, :login) }.to raise_error
|
72
|
+
index_for(:login).should_not be_nil
|
49
73
|
end
|
50
74
|
end
|
51
75
|
|
@@ -56,19 +80,39 @@ describe "add_index" do
|
|
56
80
|
index_for(:login).conditions.should == '(deleted_at IS NULL)'
|
57
81
|
end
|
58
82
|
|
59
|
-
it "should assign expression" do
|
83
|
+
it "should assign expression, conditions and kind" do
|
60
84
|
add_index(:users, :expression => "USING hash (upper(login)) WHERE deleted_at IS NULL", :name => 'users_login_index')
|
61
85
|
@index = User.indexes.detect { |i| i.expression.present? }
|
62
86
|
@index.expression.should == "upper((login)::text)"
|
63
87
|
@index.conditions.should == "(deleted_at IS NULL)"
|
88
|
+
@index.kind.should == "hash"
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should allow to specify expression, conditions and kind separately" do
|
92
|
+
add_index(:users, :kind => "hash", :expression => "upper(login)", :conditions => "deleted_at IS NULL", :name => 'users_login_index')
|
93
|
+
@index = User.indexes.detect { |i| i.expression.present? }
|
94
|
+
@index.expression.should == "upper((login)::text)"
|
95
|
+
@index.conditions.should == "(deleted_at IS NULL)"
|
96
|
+
@index.kind.should == "hash"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should allow to specify kind" do
|
100
|
+
add_index(:users, :login, :kind => "hash")
|
101
|
+
index_for(:login).kind.should == 'hash'
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should allow to specify actual expression only" do
|
105
|
+
add_index(:users, :expression => "upper(login)", :name => 'users_login_index')
|
106
|
+
@index = User.indexes.detect { |i| i.expression.present? }
|
107
|
+
@index.expression.should == "upper((login)::text)"
|
64
108
|
end
|
65
109
|
|
66
110
|
it "should raise if no column given and expression is missing" do
|
67
|
-
expect { add_index(:users, :name => 'users_login_index') }.
|
111
|
+
expect { add_index(:users, :name => 'users_login_index') }.to raise_error(ArgumentError)
|
68
112
|
end
|
69
113
|
|
70
114
|
it "should raise if expression without name is given" do
|
71
|
-
expect { add_index(:users, :expression => "USING btree (login)") }.
|
115
|
+
expect { add_index(:users, :expression => "USING btree (login)") }.to raise_error(ArgumentError)
|
72
116
|
end
|
73
117
|
|
74
118
|
end # of postgresql specific examples
|
data/spec/migration_spec.rb
CHANGED
@@ -5,7 +5,34 @@ describe ActiveRecord::Migration do
|
|
5
5
|
include SchemaPlusHelpers
|
6
6
|
|
7
7
|
before(:all) do
|
8
|
-
|
8
|
+
define_schema(:auto_create => true) do
|
9
|
+
|
10
|
+
create_table :users, :force => true do |t|
|
11
|
+
t.string :login, :index => { :unique => true }
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :members, :force => true do |t|
|
15
|
+
t.string :login
|
16
|
+
end
|
17
|
+
|
18
|
+
create_table :comments, :force => true do |t|
|
19
|
+
t.string :content
|
20
|
+
t.integer :user
|
21
|
+
t.integer :user_id
|
22
|
+
t.foreign_key :user_id, :users, :id
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table :posts, :force => true do |t|
|
26
|
+
t.string :content
|
27
|
+
end
|
28
|
+
end
|
29
|
+
class User < ::ActiveRecord::Base ; end
|
30
|
+
class Post < ::ActiveRecord::Base ; end
|
31
|
+
class Comment < ::ActiveRecord::Base ; end
|
32
|
+
end
|
33
|
+
|
34
|
+
around(:each) do |example|
|
35
|
+
with_fk_config(:auto_create => true, :auto_index => true) { example.run }
|
9
36
|
end
|
10
37
|
|
11
38
|
context "when table is created" do
|
@@ -15,107 +42,184 @@ describe ActiveRecord::Migration do
|
|
15
42
|
end
|
16
43
|
|
17
44
|
it "should properly handle default values for booleans" do
|
18
|
-
expect {
|
45
|
+
expect {
|
46
|
+
recreate_table(@model) do |t|
|
47
|
+
t.boolean :bool, :default => true
|
48
|
+
end
|
49
|
+
}.to_not raise_error
|
19
50
|
@model.create.reload.bool.should be_true
|
20
51
|
end
|
21
52
|
|
22
|
-
it "should create foreign keys" do
|
23
|
-
|
24
|
-
|
25
|
-
|
53
|
+
it "should create auto foreign keys" do
|
54
|
+
recreate_table(@model) do |t|
|
55
|
+
t.integer :user_id
|
56
|
+
end
|
26
57
|
@model.should reference(:users, :id).on(:user_id)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should create explicit foreign key with default reference" do
|
61
|
+
recreate_table(@model) do |t|
|
62
|
+
t.integer :user, :foreign_key => true
|
63
|
+
end
|
64
|
+
@model.should reference(:users, :id).on(:user)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should create foreign key with different reference" do
|
68
|
+
recreate_table(@model) do |t|
|
69
|
+
t.integer :author_id, :foreign_key => { :references => :users }
|
70
|
+
end
|
71
|
+
@model.should reference(:users, :id).on(:author_id)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should create foreign key with different reference using shortcut" do
|
75
|
+
recreate_table(@model) do |t|
|
76
|
+
t.integer :author_id, :references => :users
|
77
|
+
end
|
27
78
|
@model.should reference(:users, :id).on(:author_id)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should create foreign key with specified name" do
|
82
|
+
recreate_table @model do |t|
|
83
|
+
t.integer :user_id, :foreign_key => { :name => "wugga" }
|
84
|
+
end
|
85
|
+
@model.should reference(:users, :id).with_name("wugga")
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should suppress foreign key" do
|
89
|
+
recreate_table(@model) do |t|
|
90
|
+
t.integer :member_id, :foreign_key => false
|
91
|
+
end
|
92
|
+
@model.should_not reference.on(:member_id)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should suppress foreign key using shortcut" do
|
96
|
+
recreate_table(@model) do |t|
|
97
|
+
t.integer :member_id, :references => nil
|
98
|
+
end
|
28
99
|
@model.should_not reference.on(:member_id)
|
29
100
|
end
|
30
101
|
|
31
102
|
it "should create foreign key using t.belongs_to" do
|
32
|
-
|
103
|
+
recreate_table(@model) do |t|
|
104
|
+
t.belongs_to :user
|
105
|
+
end
|
33
106
|
@model.should reference(:users, :id).on(:user_id)
|
34
107
|
end
|
35
108
|
|
36
109
|
it "should not create foreign key using t.belongs_to with :polymorphic => true" do
|
37
|
-
|
110
|
+
recreate_table(@model) do |t|
|
111
|
+
t.belongs_to :user, :polymorphic => true
|
112
|
+
end
|
38
113
|
@model.should_not reference(:users, :id).on(:user_id)
|
39
114
|
end
|
40
115
|
|
41
116
|
it "should create foreign key using t.references" do
|
42
|
-
|
117
|
+
recreate_table(@model) do |t|
|
118
|
+
t.references :user
|
119
|
+
end
|
43
120
|
@model.should reference(:users, :id).on(:user_id)
|
44
121
|
end
|
45
122
|
|
46
|
-
it "should not create foreign key using t.references with :
|
47
|
-
|
123
|
+
it "should not create foreign key using t.references with :foreign_key => false" do
|
124
|
+
recreate_table(@model) do |t|
|
125
|
+
t.references :user, :foreign_key => false
|
126
|
+
end
|
48
127
|
@model.should_not reference(:users, :id).on(:user_id)
|
49
128
|
end
|
50
129
|
|
51
130
|
it "should not create foreign key using t.references with :polymorphic => true" do
|
52
|
-
|
131
|
+
recreate_table(@model) do |t|
|
132
|
+
t.references :user, :polymorphic => true
|
133
|
+
end
|
53
134
|
@model.should_not reference(:users, :id).on(:user_id)
|
54
135
|
end
|
55
136
|
|
56
137
|
it "should create foreign key to the same table on parent_id" do
|
57
|
-
|
138
|
+
recreate_table(@model) do |t|
|
139
|
+
t.integer :parent_id
|
140
|
+
end
|
58
141
|
@model.should reference(@model.table_name, :id).on(:parent_id)
|
59
142
|
end
|
60
143
|
|
61
144
|
it "should create an index if specified on column" do
|
62
|
-
|
145
|
+
recreate_table(@model) do |t|
|
146
|
+
t.integer :state, :index => true
|
147
|
+
end
|
63
148
|
@model.should have_index.on(:state)
|
64
149
|
end
|
65
150
|
|
66
151
|
it "should create a unique index if specified on column" do
|
67
|
-
|
152
|
+
recreate_table(@model) do |t|
|
153
|
+
t.integer :state, :index => { :unique => true }
|
154
|
+
end
|
68
155
|
@model.should have_unique_index.on(:state)
|
69
156
|
end
|
157
|
+
|
70
158
|
it "should create a unique index if specified on column using shorthand" do
|
71
|
-
|
159
|
+
recreate_table(@model) do |t|
|
160
|
+
t.integer :state, :index => :unique
|
161
|
+
end
|
72
162
|
@model.should have_unique_index.on(:state)
|
73
163
|
end
|
74
164
|
|
75
165
|
it "should create an index if specified explicitly" do
|
76
|
-
|
166
|
+
recreate_table(@model) do |t|
|
167
|
+
t.integer :state
|
168
|
+
t.index :state
|
169
|
+
end
|
77
170
|
@model.should have_index.on(:state)
|
78
171
|
end
|
79
172
|
|
80
173
|
it "should create a unique index if specified explicitly" do
|
81
|
-
|
174
|
+
recreate_table(@model) do |t|
|
175
|
+
t.integer :state
|
176
|
+
t.index :state, :unique => true
|
177
|
+
end
|
82
178
|
@model.should have_unique_index.on(:state)
|
83
179
|
end
|
84
180
|
|
85
181
|
it "should create a multiple-column index if specified" do
|
86
|
-
|
87
|
-
|
182
|
+
recreate_table(@model) do |t|
|
183
|
+
t.integer :city
|
184
|
+
t.integer :state, :index => { :with => :city }
|
185
|
+
end
|
88
186
|
@model.should have_index.on([:state, :city])
|
89
187
|
end
|
90
188
|
|
91
189
|
it "should auto-index foreign keys only" do
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
@model.should have_index.on(:user_id)
|
97
|
-
@model.should_not have_index.on(:application_id)
|
98
|
-
@model.should_not have_index.on(:state)
|
190
|
+
recreate_table(@model) do |t|
|
191
|
+
t.integer :user_id
|
192
|
+
t.integer :application_id, :references => nil
|
193
|
+
t.integer :state
|
99
194
|
end
|
195
|
+
@model.should have_index.on(:user_id)
|
196
|
+
@model.should_not have_index.on(:application_id)
|
197
|
+
@model.should_not have_index.on(:state)
|
100
198
|
end
|
101
199
|
|
102
200
|
it "should override foreign key auto_create positively" do
|
103
201
|
with_fk_config(:auto_create => false) do
|
104
|
-
|
202
|
+
recreate_table @model, :foreign_keys => {:auto_create => true} do |t|
|
203
|
+
t.integer :user_id
|
204
|
+
end
|
105
205
|
@model.should reference(:users, :id).on(:user_id)
|
106
206
|
end
|
107
207
|
end
|
108
208
|
|
109
209
|
it "should override foreign key auto_create negatively" do
|
110
210
|
with_fk_config(:auto_create => true) do
|
111
|
-
|
211
|
+
recreate_table @model, :foreign_keys => {:auto_create => false} do |t|
|
212
|
+
t.integer :user_id
|
213
|
+
end
|
112
214
|
@model.should_not reference.on(:user_id)
|
113
215
|
end
|
114
216
|
end
|
115
217
|
|
116
218
|
it "should override foreign key auto_index positively" do
|
117
219
|
with_fk_config(:auto_index => false) do
|
118
|
-
|
220
|
+
recreate_table @model, :foreign_keys => {:auto_index => true} do |t|
|
221
|
+
t.integer :user_id
|
222
|
+
end
|
119
223
|
@model.should have_index.on(:user_id)
|
120
224
|
end
|
121
225
|
end
|
@@ -125,87 +229,137 @@ describe ActiveRecord::Migration do
|
|
125
229
|
if SchemaPlusHelpers.mysql?
|
126
230
|
actions.delete(:set_default)
|
127
231
|
it "should raise a not-implemented error for on_update => :set_default" do
|
128
|
-
expect {
|
232
|
+
expect {
|
233
|
+
recreate_table @model do |t|
|
234
|
+
t.integer :user_id, :foreign_key => { :on_update => :set_default }
|
235
|
+
end
|
236
|
+
}.to raise_error(NotImplementedError)
|
129
237
|
end
|
130
238
|
|
131
239
|
it "should raise a not-implemented error for on_delete => :set_default" do
|
132
|
-
expect {
|
240
|
+
expect {
|
241
|
+
recreate_table @model do |t|
|
242
|
+
t.integer :user_id, :foreign_key => { :on_delete => :set_default }
|
243
|
+
end
|
244
|
+
}.to raise_error(NotImplementedError)
|
133
245
|
end
|
134
246
|
end
|
135
247
|
|
136
248
|
actions.each do |action|
|
137
249
|
it "should create and detect on_update #{action.inspect}" do
|
138
|
-
|
250
|
+
recreate_table @model do |t|
|
251
|
+
t.integer :user_id, :foreign_key => { :on_update => action }
|
252
|
+
end
|
253
|
+
@model.should reference.on(:user_id).on_update(action)
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should create and detect on_update #{action.inspect} using shortcut" do
|
257
|
+
recreate_table @model do |t|
|
258
|
+
t.integer :user_id, :on_update => action
|
259
|
+
end
|
139
260
|
@model.should reference.on(:user_id).on_update(action)
|
140
261
|
end
|
141
262
|
|
142
263
|
it "should create and detect on_delete #{action.inspect}" do
|
143
|
-
|
264
|
+
recreate_table @model do |t|
|
265
|
+
t.integer :user_id, :foreign_key => { :on_delete => action }
|
266
|
+
end
|
267
|
+
@model.should reference.on(:user_id).on_delete(action)
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should create and detect on_delete #{action.inspect} using shortcut" do
|
271
|
+
recreate_table @model do |t|
|
272
|
+
t.integer :user_id, :on_delete => action
|
273
|
+
end
|
144
274
|
@model.should reference.on(:user_id).on_delete(action)
|
145
275
|
end
|
146
276
|
end
|
147
277
|
|
148
278
|
it "should use default on_update action" do
|
149
279
|
with_fk_config(:on_update => :cascade) do
|
150
|
-
|
280
|
+
recreate_table @model do |t|
|
281
|
+
t.integer :user_id
|
282
|
+
end
|
151
283
|
@model.should reference.on(:user_id).on_update(:cascade)
|
152
284
|
end
|
153
285
|
end
|
154
286
|
|
155
287
|
it "should use default on_delete action" do
|
156
288
|
with_fk_config(:on_delete => :cascade) do
|
157
|
-
|
289
|
+
recreate_table @model do |t|
|
290
|
+
t.integer :user_id
|
291
|
+
end
|
158
292
|
@model.should reference.on(:user_id).on_delete(:cascade)
|
159
293
|
end
|
160
294
|
end
|
161
295
|
|
162
296
|
it "should override on_update action per table" do
|
163
297
|
with_fk_config(:on_update => :cascade) do
|
164
|
-
|
298
|
+
recreate_table @model, :foreign_keys => {:on_update => :restrict} do |t|
|
299
|
+
t.integer :user_id
|
300
|
+
end
|
165
301
|
@model.should reference.on(:user_id).on_update(:restrict)
|
166
302
|
end
|
167
303
|
end
|
168
304
|
|
169
305
|
it "should override on_delete action per table" do
|
170
306
|
with_fk_config(:on_delete => :cascade) do
|
171
|
-
|
307
|
+
recreate_table @model, :foreign_keys => {:on_delete => :restrict} do |t|
|
308
|
+
t.integer :user_id
|
309
|
+
end
|
172
310
|
@model.should reference.on(:user_id).on_delete(:restrict)
|
173
311
|
end
|
174
312
|
end
|
175
313
|
|
176
314
|
it "should override on_update action per column" do
|
177
315
|
with_fk_config(:on_update => :cascade) do
|
178
|
-
|
316
|
+
recreate_table @model, :foreign_keys => {:on_update => :restrict} do |t|
|
317
|
+
t.integer :user_id, :foreign_key => { :on_update => :set_null }
|
318
|
+
end
|
179
319
|
@model.should reference.on(:user_id).on_update(:set_null)
|
180
320
|
end
|
181
321
|
end
|
182
322
|
|
183
323
|
it "should override on_delete action per column" do
|
184
324
|
with_fk_config(:on_delete => :cascade) do
|
185
|
-
|
325
|
+
recreate_table @model, :foreign_keys => {:on_delete => :restrict} do |t|
|
326
|
+
t.integer :user_id, :foreign_key => { :on_delete => :set_null }
|
327
|
+
end
|
186
328
|
@model.should reference.on(:user_id).on_delete(:set_null)
|
187
329
|
end
|
188
330
|
end
|
189
331
|
|
190
332
|
it "should raise an error for an invalid on_update action" do
|
191
|
-
|
333
|
+
expect {
|
334
|
+
recreate_table @model do |t|
|
335
|
+
t.integer :user_id, :foreign_key => { :on_update => :invalid }
|
336
|
+
end
|
337
|
+
}.to raise_error(ArgumentError)
|
192
338
|
end
|
193
339
|
|
194
340
|
it "should raise an error for an invalid on_delete action" do
|
195
|
-
|
341
|
+
expect {
|
342
|
+
recreate_table @model do |t|
|
343
|
+
t.integer :user_id, :foreign_key => { :on_delete => :invalid }
|
344
|
+
end
|
345
|
+
}.to raise_error(ArgumentError)
|
196
346
|
end
|
197
347
|
|
198
348
|
unless SchemaPlusHelpers.mysql?
|
199
349
|
it "should override foreign key auto_index negatively" do
|
200
350
|
with_fk_config(:auto_index => true) do
|
201
|
-
|
351
|
+
recreate_table @model, :foreign_keys => {:auto_index => false} do |t|
|
352
|
+
t.integer :user_id
|
353
|
+
end
|
202
354
|
@model.should_not have_index.on(:user_id)
|
203
355
|
end
|
204
356
|
end
|
205
357
|
|
206
358
|
it "should disable auto-index for a column" do
|
207
359
|
with_fk_config(:auto_index => true) do
|
208
|
-
|
360
|
+
recreate_table @model do |t|
|
361
|
+
t.integer :user_id, :index => false
|
362
|
+
end
|
209
363
|
@model.should_not have_index.on(:user_id)
|
210
364
|
end
|
211
365
|
end
|
@@ -234,14 +388,20 @@ describe ActiveRecord::Migration do
|
|
234
388
|
end
|
235
389
|
end
|
236
390
|
|
237
|
-
it "should create foreign key to
|
391
|
+
it "should create foreign key to explicitly given table" do
|
392
|
+
add_column(:author_id, :integer, :foreign_key => { :references => :users }) do
|
393
|
+
@model.should reference(:users, :id).on(:author_id)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
it "should create foreign key to explicitly given table using shortcut" do
|
238
398
|
add_column(:author_id, :integer, :references => :users) do
|
239
399
|
@model.should reference(:users, :id).on(:author_id)
|
240
400
|
end
|
241
401
|
end
|
242
402
|
|
243
|
-
it "should create foreign key to
|
244
|
-
add_column(:author_login, :string, :references => [:users, :login]) do
|
403
|
+
it "should create foreign key to explicitly given table and column name" do
|
404
|
+
add_column(:author_login, :string, :foreign_key => { :references => [:users, :login]}) do
|
245
405
|
@model.should reference(:users, :login).on(:author_login)
|
246
406
|
end
|
247
407
|
end
|
@@ -258,7 +418,13 @@ describe ActiveRecord::Migration do
|
|
258
418
|
end
|
259
419
|
end
|
260
420
|
|
261
|
-
it "
|
421
|
+
it "shouldn't create foreign key if specified explicitly" do
|
422
|
+
add_column(:post_id, :integer, :foreign_key => false) do
|
423
|
+
@model.should_not reference.on(:post_id)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
it "shouldn't create foreign key if specified explicitly by shorthand" do
|
262
428
|
add_column(:post_id, :integer, :references => nil) do
|
263
429
|
@model.should_not reference.on(:post_id)
|
264
430
|
end
|
@@ -317,10 +483,6 @@ describe ActiveRecord::Migration do
|
|
317
483
|
SchemaPlus.config.foreign_keys.auto_index = false
|
318
484
|
end
|
319
485
|
|
320
|
-
it "should not auto-index if column already has an index"
|
321
|
-
|
322
|
-
it "should remove auto-created index when removing foreign key"
|
323
|
-
|
324
486
|
it "should use default on_update action" do
|
325
487
|
SchemaPlus.config.foreign_keys.on_update = :cascade
|
326
488
|
add_column(:post_id, :integer) do
|
@@ -340,7 +502,7 @@ describe ActiveRecord::Migration do
|
|
340
502
|
it "should allow to overwrite default actions" do
|
341
503
|
SchemaPlus.config.foreign_keys.on_delete = :cascade
|
342
504
|
SchemaPlus.config.foreign_keys.on_update = :restrict
|
343
|
-
add_column(:post_id, :integer, :on_update => :set_null, :on_delete => :set_null) do
|
505
|
+
add_column(:post_id, :integer, :foreign_key => { :on_update => :set_null, :on_delete => :set_null}) do
|
344
506
|
@model.should reference.on(:post_id).on_delete(:set_null).on_update(:set_null)
|
345
507
|
end
|
346
508
|
SchemaPlus.config.foreign_keys.on_delete = nil
|
@@ -366,28 +528,71 @@ describe ActiveRecord::Migration do
|
|
366
528
|
end
|
367
529
|
|
368
530
|
it "should create foreign key" do
|
369
|
-
change_column :user, :string, :references => [:users, :login]
|
531
|
+
change_column :user, :string, :foreign_key => { :references => [:users, :login] }
|
370
532
|
@model.should reference(:users, :login).on(:user)
|
371
|
-
change_column :user, :string, :references => nil
|
372
533
|
end
|
373
534
|
|
374
535
|
context "and initially references to users table" do
|
375
536
|
|
537
|
+
before(:each) do
|
538
|
+
recreate_table @model do |t|
|
539
|
+
t.integer :user_id
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
376
543
|
it "should have foreign key" do
|
377
544
|
@model.should reference(:users)
|
378
545
|
end
|
379
546
|
|
380
|
-
it "should drop foreign key
|
381
|
-
change_column :user_id, :integer, :references => :members
|
547
|
+
it "should drop foreign key if it is no longer valid" do
|
548
|
+
change_column :user_id, :integer, :foreign_key => { :references => :members }
|
382
549
|
@model.should_not reference(:users)
|
383
|
-
change_column :user_id, :integer, :references => :users
|
384
550
|
end
|
385
551
|
|
386
|
-
it "should
|
387
|
-
change_column :user_id, :integer, :references =>
|
552
|
+
it "should drop foreign key if requested to do so" do
|
553
|
+
change_column :user_id, :integer, :foreign_key => { :references => nil }
|
554
|
+
@model.should_not reference(:users)
|
555
|
+
end
|
556
|
+
|
557
|
+
it "should remove auto-created index if foreign key is removed" do
|
558
|
+
@model.should have_index.on(:user_id) # sanity check that index was auto-created
|
559
|
+
change_column :user_id, :integer, :foreign_key => { :references => nil }
|
560
|
+
@model.should_not have_index.on(:user_id)
|
561
|
+
end
|
562
|
+
|
563
|
+
it "should reference pointed table afterwards if new one is created" do
|
564
|
+
change_column :user_id, :integer, :foreign_key => { :references => :members }
|
388
565
|
@model.should reference(:members)
|
389
566
|
end
|
390
567
|
|
568
|
+
it "should maintain foreign key if it's unaffected by change" do
|
569
|
+
change_column :user_id, :integer, :default => 0
|
570
|
+
@model.should reference(:users)
|
571
|
+
end
|
572
|
+
|
573
|
+
it "should maintain foreign key if it's unaffected by change, even if auto_index is off" do
|
574
|
+
with_fk_config(:auto_create => false) do
|
575
|
+
change_column :user_id, :integer, :default => 0
|
576
|
+
@model.should reference(:users)
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
end
|
581
|
+
|
582
|
+
context "if column defined without foreign key but with index" do
|
583
|
+
before(:each) do
|
584
|
+
recreate_table @model do |t|
|
585
|
+
t.integer :user_id, :foreign_key => false, :index => true
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
it "should create the index" do
|
590
|
+
@model.should have_index.on(:user_id)
|
591
|
+
end
|
592
|
+
|
593
|
+
it "adding foreign key should not fail due to attempt to auto-create existing index" do
|
594
|
+
expect { change_column :user_id, :integer, :foreign_key => true }.to_not raise_error
|
595
|
+
end
|
391
596
|
end
|
392
597
|
|
393
598
|
protected
|
@@ -400,41 +605,87 @@ describe ActiveRecord::Migration do
|
|
400
605
|
end
|
401
606
|
|
402
607
|
end
|
403
|
-
end
|
404
|
-
|
405
|
-
def foreign_key(model, column)
|
406
|
-
columns = Array(column).collect(&:to_s)
|
407
|
-
model.foreign_keys.detect { |fk| fk.table_name == model.table_name && fk.column_names == columns }
|
408
|
-
end
|
409
608
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
t.send method, column, options
|
609
|
+
context "when column is removed" do
|
610
|
+
before(:each) do
|
611
|
+
@model = Comment
|
612
|
+
recreate_table @model do |t|
|
613
|
+
t.integer :post_id
|
416
614
|
end
|
417
|
-
|
418
|
-
|
615
|
+
end
|
616
|
+
|
617
|
+
it "should remove a foreign key" do
|
618
|
+
@model.should reference(:posts)
|
619
|
+
remove_column(:post_id)
|
620
|
+
@model.should_not reference(:posts)
|
621
|
+
end
|
622
|
+
|
623
|
+
it "should remove an index" do
|
624
|
+
@model.should have_index.on(:post_id)
|
625
|
+
remove_column(:post_id)
|
626
|
+
@model.should_not have_index.on(:post_id)
|
627
|
+
end
|
628
|
+
|
629
|
+
protected
|
630
|
+
def remove_column(column_name)
|
631
|
+
table = @model.table_name
|
632
|
+
ActiveRecord::Migration.suppress_messages do
|
633
|
+
ActiveRecord::Migration.remove_column(table, column_name)
|
634
|
+
@model.reset_column_information
|
419
635
|
end
|
420
636
|
end
|
421
|
-
model.reset_column_information
|
422
637
|
end
|
423
|
-
end
|
424
638
|
|
425
|
-
def create_table(model, columns_with_options)
|
426
|
-
create_table_opts(model, {}, columns_with_options)
|
427
639
|
end
|
428
640
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
641
|
+
context "when table is renamed" do
|
642
|
+
|
643
|
+
before(:each) do
|
644
|
+
@model = Comment
|
645
|
+
recreate_table @model do |t|
|
646
|
+
t.integer :user_id
|
647
|
+
t.integer :xyz, :index => true
|
648
|
+
end
|
649
|
+
ActiveRecord::Migration.suppress_messages do
|
650
|
+
ActiveRecord::Migration.rename_table @model.table_name, :newname
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
around(:each) do |example|
|
655
|
+
begin
|
656
|
+
example.run
|
657
|
+
ensure
|
658
|
+
ActiveRecord::Migration.suppress_messages do
|
659
|
+
ActiveRecord::Migration.rename_table :newname, :comments
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
it "should rename rails-named indexes" do
|
665
|
+
index = ActiveRecord::Base.connection.indexes(:newname).find{|index| index.columns == ['xyz']}
|
666
|
+
index.name.should =~ /^index_newname_on/
|
667
|
+
end
|
668
|
+
|
669
|
+
it "should rename fk indexes" do
|
670
|
+
index = ActiveRecord::Base.connection.indexes(:newname).find{|index| index.columns == ['user_id']}
|
671
|
+
index.name.should =~ /^fk__newname_/
|
436
672
|
end
|
673
|
+
|
674
|
+
unless SchemaPlusHelpers.sqlite3?
|
675
|
+
it "should rename foreign key definitions" do
|
676
|
+
ActiveRecord::Base.connection.foreign_keys(:newname).first.name.should =~ /newname/
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
437
680
|
end
|
681
|
+
|
682
|
+
def recreate_table(model, opts={}, &block)
|
683
|
+
ActiveRecord::Migration.suppress_messages do
|
684
|
+
ActiveRecord::Migration.create_table model.table_name, opts.merge(:force => true), &block
|
685
|
+
end
|
686
|
+
model.reset_column_information
|
687
|
+
end
|
688
|
+
|
438
689
|
|
439
690
|
|
440
691
|
end
|