rails-bigint-pk 0.0.2
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 +8 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/.travis.yml +3 -0
- data/.vim/snippets/rspec.snippets +4 -0
- data/.vimrc +2 -0
- data/Changelog +11 -0
- data/Gemfile +4 -0
- data/Guardfile +20 -0
- data/LICENSE +7 -0
- data/Rakefile +6 -0
- data/Readme.md +61 -0
- data/lib/bigint_pk.rb +92 -0
- data/lib/bigint_pk/version.rb +3 -0
- data/lib/generators/bigint_pk.rb +5 -0
- data/lib/generators/bigint_pk/install_generator.rb +21 -0
- data/lib/generators/bigint_pk/templates/bigint_pk.rb +9 -0
- data/lib/generators/bigint_pk/templates/migration.rb +20 -0
- data/rails-bigint-pk.gemspec +37 -0
- data/spec/fixtures/Gemfile +8 -0
- data/spec/fixtures/mysql_database.yml +13 -0
- data/spec/fixtures/postgresql_database.yml +12 -0
- data/spec/fixtures/sqlite3_database.yml +11 -0
- data/spec/fixtures/test_rails_app/app/models/empire.rb +2 -0
- data/spec/fixtures/test_rails_app/app/models/ruler.rb +4 -0
- data/spec/fixtures/test_rails_app/db/migrate/19000101100000_create_empires_and_rulers.rb +10 -0
- data/spec/fixtures/test_rails_app/db/migrate/19000102100000_update_rulers_add_favourite_ruler.rb +7 -0
- data/spec/integration/generator_spec.rb +40 -0
- data/spec/integration/schema_spec.rb +222 -0
- data/spec/migration_spec.rb +102 -0
- data/spec/monkey_patch/connection_adapters_spec.rb +149 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/support/cmd.rb +42 -0
- data/spec/support/file_utils.rb +7 -0
- data/spec/support/logging.rb +5 -0
- data/spec/support/rails.rb +32 -0
- data/spec/support/rspec.rb +3 -0
- metadata +340 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Migrations', :integration do
|
4
|
+
after :each do
|
5
|
+
execute_sql %Q{
|
6
|
+
delete from rulers;
|
7
|
+
delete from empires;
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'create_table' do
|
12
|
+
before :each do
|
13
|
+
in_directory( RailsDir ) do
|
14
|
+
run 'rails generate bigint_pk:install'
|
15
|
+
FileUtils.rm Dir['./db/migrate/**change_keys_to_bigint*'].first
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.they_use_bigint_primary_keys
|
20
|
+
describe 'primary keys' do
|
21
|
+
they 'default to 64bit' do
|
22
|
+
expect{
|
23
|
+
execute_sql %Q{
|
24
|
+
insert into empires values(#{2 ** 31 - 1});
|
25
|
+
insert into empires values(#{2 ** 31});
|
26
|
+
}
|
27
|
+
}.to_not raise_error
|
28
|
+
|
29
|
+
expect(
|
30
|
+
execute_ruby 'puts Empire.all.collect(&:id)'
|
31
|
+
).to eq ["#{2**31-1}", "#{2**31}"].join("\n")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.they_use_bigint_foreign_keys
|
37
|
+
describe 'foreign keys' do
|
38
|
+
they 'can reference 64bit values' do
|
39
|
+
expect{
|
40
|
+
execute_sql %Q{
|
41
|
+
insert into empires values(#{2 ** 31 - 1});
|
42
|
+
insert into empires values(#{2 ** 31});
|
43
|
+
insert into rulers( empire_id ) values(#{2 ** 31 - 1});
|
44
|
+
insert into rulers( empire_id ) values(#{2 ** 31});
|
45
|
+
}
|
46
|
+
}.to_not raise_error
|
47
|
+
|
48
|
+
expect(
|
49
|
+
execute_ruby 'puts Ruler.all.map{|r| r.empire_id}'
|
50
|
+
).to eq ["#{2**31 - 1}", "#{2**31}"].join("\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'that were added to an existing table' do
|
54
|
+
they 'can reference 64bit values' do
|
55
|
+
expect{
|
56
|
+
execute_sql %Q{
|
57
|
+
insert into rulers( id ) values(#{2 ** 31 - 1});
|
58
|
+
insert into rulers( id ) values(#{2 ** 31});
|
59
|
+
|
60
|
+
insert into rulers( favourite_ruler_id ) values(#{2 ** 31 - 1});
|
61
|
+
insert into rulers( favourite_ruler_id ) values(#{2 ** 31});
|
62
|
+
}
|
63
|
+
}.to_not raise_error
|
64
|
+
|
65
|
+
expect(
|
66
|
+
execute_ruby('puts Ruler.all.map{|r| r.favourite_ruler_id}').split
|
67
|
+
).to eq ["#{2**31 - 1}", "#{2**31}"]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with a mysql database' do
|
74
|
+
before(:each) do
|
75
|
+
use_database! :mysql
|
76
|
+
rake 'db:migrate'
|
77
|
+
end
|
78
|
+
|
79
|
+
they_use_bigint_primary_keys
|
80
|
+
they_use_bigint_foreign_keys
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'with a postgresql database' do
|
84
|
+
before(:each) do
|
85
|
+
use_database! :postgres
|
86
|
+
rake 'db:migrate'
|
87
|
+
end
|
88
|
+
|
89
|
+
they_use_bigint_primary_keys
|
90
|
+
they_use_bigint_foreign_keys
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'with a sqlite3 database' do
|
94
|
+
before(:each) do
|
95
|
+
use_database! :sqlite3
|
96
|
+
rake 'db:migrate'
|
97
|
+
end
|
98
|
+
|
99
|
+
they_use_bigint_primary_keys
|
100
|
+
they_use_bigint_foreign_keys
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'existing tables' do
|
105
|
+
InitializerFile = "./config/initializers/bigint_pk.rb"
|
106
|
+
|
107
|
+
before :each do
|
108
|
+
in_directory( RailsDir ) do
|
109
|
+
run 'rails generate bigint_pk:install'
|
110
|
+
|
111
|
+
initializer = File.read InitializerFile
|
112
|
+
initializer.gsub!( /enabled = true/, 'enabled = false' )
|
113
|
+
File.open( InitializerFile, 'w'){|f| f.print initializer }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.they_have_their_primary_keys_migrated
|
118
|
+
they 'have their primary keys migrated' do
|
119
|
+
expect{
|
120
|
+
execute_sql %Q{
|
121
|
+
insert into empires values(#{2 ** 31 - 1});
|
122
|
+
insert into empires values(#{2 ** 31});
|
123
|
+
}
|
124
|
+
}.to raise_error
|
125
|
+
|
126
|
+
execute_ruby 'Empire.delete_all'
|
127
|
+
rake 'db:migrate'
|
128
|
+
|
129
|
+
expect{
|
130
|
+
execute_sql %Q{
|
131
|
+
insert into empires values(#{2 ** 31 - 1});
|
132
|
+
insert into empires values(#{2 ** 31});
|
133
|
+
}
|
134
|
+
}.to_not raise_error
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.they_have_their_foreign_keys_migrated
|
139
|
+
they 'have their foreign keys migrated' do
|
140
|
+
expect{
|
141
|
+
execute_sql %Q{
|
142
|
+
insert into empires values(#{2 ** 31 - 1});
|
143
|
+
insert into empires values(#{2 ** 31});
|
144
|
+
insert into rulers( empire_id ) values(#{2 ** 31});
|
145
|
+
insert into rulers( empire_id ) values(#{2 ** 31 - 1});
|
146
|
+
}
|
147
|
+
}.to raise_error
|
148
|
+
|
149
|
+
execute_ruby 'Ruler.delete_all'
|
150
|
+
execute_ruby 'Empire.delete_all'
|
151
|
+
rake 'db:migrate'
|
152
|
+
|
153
|
+
expect{
|
154
|
+
execute_sql %Q{
|
155
|
+
insert into empires values(#{2 ** 31 - 1});
|
156
|
+
insert into empires values(#{2 ** 31});
|
157
|
+
insert into rulers( empire_id ) values(#{2 ** 31 - 1});
|
158
|
+
insert into rulers( empire_id ) values(#{2 ** 31});
|
159
|
+
}
|
160
|
+
}.to_not raise_error
|
161
|
+
|
162
|
+
expect(
|
163
|
+
execute_ruby 'puts Ruler.all.collect{|r| [r.id, r.empire.id]}'
|
164
|
+
).to eq [ "1", "#{2**31 - 1}",
|
165
|
+
"2", "#{2**31}"].join("\n")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'with a mysql database' do
|
170
|
+
before(:each) do
|
171
|
+
use_database! :mysql
|
172
|
+
rake 'db:migrate VERSION=19000101100000'
|
173
|
+
end
|
174
|
+
|
175
|
+
they_have_their_primary_keys_migrated
|
176
|
+
they_have_their_foreign_keys_migrated
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'with a postgresql database' do
|
180
|
+
before(:each) do
|
181
|
+
use_database! :postgres
|
182
|
+
rake 'db:migrate VERSION=19000101100000'
|
183
|
+
end
|
184
|
+
|
185
|
+
they_have_their_primary_keys_migrated
|
186
|
+
they_have_their_foreign_keys_migrated
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "with models & migrations created after bigint migration" do
|
191
|
+
before :each do
|
192
|
+
in_directory( RailsDir ) do
|
193
|
+
run 'rails generate bigint_pk:install'
|
194
|
+
run 'rails generate model subject'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.it_completes_migration
|
199
|
+
it "completes the migration" do
|
200
|
+
in_directory( RailsDir ) do
|
201
|
+
run("rake db:migrate")
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context "with a mysql database" do
|
207
|
+
before(:each) do
|
208
|
+
use_database! :mysql
|
209
|
+
end
|
210
|
+
|
211
|
+
it_completes_migration
|
212
|
+
end
|
213
|
+
|
214
|
+
context "with a postgresql database" do
|
215
|
+
before(:each) do
|
216
|
+
use_database! :postgres
|
217
|
+
end
|
218
|
+
|
219
|
+
it_completes_migration
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'generators/bigint_pk/templates/migration'
|
3
|
+
|
4
|
+
describe 'ChangeKeysToBigint' do
|
5
|
+
let(:connection) do
|
6
|
+
double('Connection').tap do |c|
|
7
|
+
c.stub(:quote_table_name){|tn| "`#{tn}`"}
|
8
|
+
c.stub(:quote_column_name){|column| "`#{column}`"}
|
9
|
+
c.stub(:table_exists?) { true }
|
10
|
+
c.stub(:execute){|sql| queries << sql }
|
11
|
+
c.stub(:column_exists?) do |table_name, column_name|
|
12
|
+
if table_name.to_s == 'coaches' && column_name == 'team_id'
|
13
|
+
false
|
14
|
+
else
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
let(:queries){ [] }
|
21
|
+
|
22
|
+
before do
|
23
|
+
stub_const('Team', double('Team', table_name: 'teams', primary_key: 'id'))
|
24
|
+
stub_const('Player', double('Player', table_name: 'players', primary_key: 'id'))
|
25
|
+
stub_const('Coach', double('Coach', table_name: 'coaches', primary_key: 'id'))
|
26
|
+
Team.stub( reflect_on_all_associations: [])
|
27
|
+
Player.stub( reflect_on_all_associations: [
|
28
|
+
double('belongs_to', macro: :belongs_to, foreign_key: 'team_id')])
|
29
|
+
Coach.stub( reflect_on_all_associations: [
|
30
|
+
double('belongs_to', macro: :belongs_to, foreign_key: 'team_id')])
|
31
|
+
|
32
|
+
stub_const('Rails', double('Rails'))
|
33
|
+
Rails.stub_chain 'application.eager_load!'
|
34
|
+
|
35
|
+
ActiveRecord::Base.stub( subclasses: [ Team, Player, Coach ], connection: connection)
|
36
|
+
ActiveRecord::Base.stub_chain('connection_pool.with_connection') do |&prok|
|
37
|
+
prok.call connection
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with a postgres database' do
|
42
|
+
before { connection.stub( adapter_name: 'PostgreSQL') }
|
43
|
+
|
44
|
+
it 'migrates primary keys' do
|
45
|
+
ChangeKeysToBigint.migrate :up
|
46
|
+
expect( queries ).to include(
|
47
|
+
'ALTER TABLE `teams` ALTER COLUMN `id` TYPE bigint',
|
48
|
+
'ALTER TABLE `players` ALTER COLUMN `id` TYPE bigint',
|
49
|
+
'ALTER TABLE `coaches` ALTER COLUMN `id` TYPE bigint'
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'migrates foreign keys' do
|
54
|
+
ChangeKeysToBigint.migrate :up
|
55
|
+
expect( queries ).to include(
|
56
|
+
'ALTER TABLE `players` ALTER COLUMN `team_id` TYPE bigint'
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'does not migrate foreign keys that do not exist in database' do
|
61
|
+
ChangeKeysToBigint.migrate :up
|
62
|
+
expect( queries ).to_not include(
|
63
|
+
'ALTER TABLE `coaches` ALTER COLUMN `team_id` TYPE bigint'
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'with a mysql database' do
|
69
|
+
before { connection.stub( adapter_name: 'MySQL') }
|
70
|
+
|
71
|
+
it 'migrates primary keys' do
|
72
|
+
ChangeKeysToBigint.migrate :up
|
73
|
+
expect( queries ).to include(
|
74
|
+
'ALTER TABLE `teams` MODIFY COLUMN `id` bigint(20) DEFAULT NULL auto_increment',
|
75
|
+
'ALTER TABLE `players` MODIFY COLUMN `id` bigint(20) DEFAULT NULL auto_increment',
|
76
|
+
'ALTER TABLE `coaches` MODIFY COLUMN `id` bigint(20) DEFAULT NULL auto_increment'
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'migrates foreign keys' do
|
81
|
+
ChangeKeysToBigint.migrate :up
|
82
|
+
expect( queries ).to include(
|
83
|
+
'ALTER TABLE `players` MODIFY COLUMN `team_id` bigint(20) DEFAULT NULL')
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'does not migrate foreign keys that do not exist in database' do
|
87
|
+
ChangeKeysToBigint.migrate :up
|
88
|
+
expect( queries ).to_not include(
|
89
|
+
'ALTER TABLE `coaches` MODIFY COLUMN `team_id` bigint(20) DEFAULT NULL'
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'with a sqlite database' do
|
95
|
+
before { connection.stub( adapter_name: 'SQLite') }
|
96
|
+
|
97
|
+
it 'does not migrate any keys' do
|
98
|
+
ChangeKeysToBigint.migrate :up
|
99
|
+
expect( queries ).to eq []
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
3
|
+
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
4
|
+
|
5
|
+
describe BigintPk do
|
6
|
+
describe '::setup' do
|
7
|
+
def reset_connection_adapters!
|
8
|
+
if defined? ::ActiveRecord::ConnectionAdapters
|
9
|
+
[:PostgreSQLAdapter, :AbstractMysqlAdapter].each do |adapter|
|
10
|
+
if ActiveRecord::ConnectionAdapters.const_defined? adapter
|
11
|
+
ActiveRecord::ConnectionAdapters.send :remove_const, adapter
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
load 'active_record/connection_adapters/postgresql_adapter.rb'
|
16
|
+
load 'active_record/connection_adapters/abstract_mysql_adapter.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
before do
|
20
|
+
ActiveRecord::Base.class_eval do
|
21
|
+
def self.establish_connection; end
|
22
|
+
end
|
23
|
+
|
24
|
+
reset_connection_adapters!
|
25
|
+
BigintPk.setup &prok
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when enabled' do
|
29
|
+
let(:prok){ lambda{|c| c.enabled = true }}
|
30
|
+
|
31
|
+
it 'updates the default primary key for the postgres adapter' do
|
32
|
+
expect(
|
33
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::
|
34
|
+
NATIVE_DATABASE_TYPES[:primary_key]
|
35
|
+
).to eq 'bigserial primary key'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'updates the default primary key for both mysql adapters' do
|
39
|
+
expect(
|
40
|
+
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::
|
41
|
+
NATIVE_DATABASE_TYPES[:primary_key]
|
42
|
+
).to eq 'bigint(20) DEFAULT NULL auto_increment PRIMARY KEY'
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.it_makes_references_default_to_64bit
|
46
|
+
describe '#references' do
|
47
|
+
before { abstract_table_class.any_instance.stub :references_without_default_bigint_fk }
|
48
|
+
|
49
|
+
|
50
|
+
let(:args){ ['some_other_table', options ].compact }
|
51
|
+
|
52
|
+
context 'when a limit is specified' do
|
53
|
+
let(:options){{ limit: 6 }}
|
54
|
+
|
55
|
+
it 'uses the specified limit' do
|
56
|
+
abstract_table.should_receive(:references_without_default_bigint_fk).with(
|
57
|
+
'some_other_table', hash_including( limit: 6 )
|
58
|
+
)
|
59
|
+
abstract_table.references *args
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when a limit is not specified' do
|
64
|
+
let(:options){}
|
65
|
+
|
66
|
+
it 'defaults the limit to 8' do
|
67
|
+
abstract_table.should_receive(:references_without_default_bigint_fk).with(
|
68
|
+
'some_other_table', hash_including( limit: 8 )
|
69
|
+
)
|
70
|
+
abstract_table.references *args
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when reference is polymorphic' do
|
75
|
+
context 'when there is not additional options' do
|
76
|
+
let(:options){{ polymorphic: true }}
|
77
|
+
|
78
|
+
it 'should not contain limit' do
|
79
|
+
abstract_table.should_receive(:references_without_default_bigint_fk).with(
|
80
|
+
'some_other_table', hash_including( polymorphic: {} )
|
81
|
+
)
|
82
|
+
abstract_table.references *args
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when there is common options' do
|
87
|
+
let(:options){{ null: false, polymorphic: true }}
|
88
|
+
|
89
|
+
it "should contain common options" do
|
90
|
+
abstract_table.should_receive(:references_without_default_bigint_fk).with(
|
91
|
+
'some_other_table', hash_including( polymorphic: { null: false } )
|
92
|
+
)
|
93
|
+
abstract_table.references *args
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "when there is polymorphic options" do
|
98
|
+
let(:options){{ null: false, polymorphic: { limit: 120 } }}
|
99
|
+
|
100
|
+
it "should contain polymorphic options" do
|
101
|
+
abstract_table.should_receive(:references_without_default_bigint_fk).with(
|
102
|
+
'some_other_table', hash_including( polymorphic: { limit: 120 } )
|
103
|
+
)
|
104
|
+
abstract_table.references *args
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe ActiveRecord::ConnectionAdapters::Table do
|
112
|
+
let(:abstract_table_class){ ActiveRecord::ConnectionAdapters::TableDefinition }
|
113
|
+
let(:abstract_table){ abstract_table_class.new Object.new }
|
114
|
+
it_makes_references_default_to_64bit
|
115
|
+
end
|
116
|
+
|
117
|
+
describe ActiveRecord::ConnectionAdapters::TableDefinition do
|
118
|
+
let(:connection_adapter_double) do
|
119
|
+
double("Connection Adapter").tap do |double|
|
120
|
+
double.stub :add_column
|
121
|
+
end
|
122
|
+
end
|
123
|
+
let(:abstract_table_class){ ActiveRecord::ConnectionAdapters::Table }
|
124
|
+
let(:abstract_table) do
|
125
|
+
abstract_table_class.new 'test_table', connection_adapter_double
|
126
|
+
end
|
127
|
+
it_makes_references_default_to_64bit
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when not enabled' do
|
132
|
+
let(:prok){ lambda{|c| c.enabled = false }}
|
133
|
+
|
134
|
+
it 'does not alter the default primary key for the postgres adapter' do
|
135
|
+
expect(
|
136
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::
|
137
|
+
NATIVE_DATABASE_TYPES[:primary_key]
|
138
|
+
).to eq 'serial primary key'
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'does not alter the default primary key for either mysql adapters' do
|
142
|
+
expect(
|
143
|
+
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::
|
144
|
+
NATIVE_DATABASE_TYPES[:primary_key]
|
145
|
+
).to eq 'int(11) DEFAULT NULL auto_increment PRIMARY KEY'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|