schema_plus 0.4.1 → 1.0.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.
- 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
|