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.
Files changed (38) hide show
  1. data/.gitignore +8 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +3 -0
  5. data/.vim/snippets/rspec.snippets +4 -0
  6. data/.vimrc +2 -0
  7. data/Changelog +11 -0
  8. data/Gemfile +4 -0
  9. data/Guardfile +20 -0
  10. data/LICENSE +7 -0
  11. data/Rakefile +6 -0
  12. data/Readme.md +61 -0
  13. data/lib/bigint_pk.rb +92 -0
  14. data/lib/bigint_pk/version.rb +3 -0
  15. data/lib/generators/bigint_pk.rb +5 -0
  16. data/lib/generators/bigint_pk/install_generator.rb +21 -0
  17. data/lib/generators/bigint_pk/templates/bigint_pk.rb +9 -0
  18. data/lib/generators/bigint_pk/templates/migration.rb +20 -0
  19. data/rails-bigint-pk.gemspec +37 -0
  20. data/spec/fixtures/Gemfile +8 -0
  21. data/spec/fixtures/mysql_database.yml +13 -0
  22. data/spec/fixtures/postgresql_database.yml +12 -0
  23. data/spec/fixtures/sqlite3_database.yml +11 -0
  24. data/spec/fixtures/test_rails_app/app/models/empire.rb +2 -0
  25. data/spec/fixtures/test_rails_app/app/models/ruler.rb +4 -0
  26. data/spec/fixtures/test_rails_app/db/migrate/19000101100000_create_empires_and_rulers.rb +10 -0
  27. data/spec/fixtures/test_rails_app/db/migrate/19000102100000_update_rulers_add_favourite_ruler.rb +7 -0
  28. data/spec/integration/generator_spec.rb +40 -0
  29. data/spec/integration/schema_spec.rb +222 -0
  30. data/spec/migration_spec.rb +102 -0
  31. data/spec/monkey_patch/connection_adapters_spec.rb +149 -0
  32. data/spec/spec_helper.rb +61 -0
  33. data/spec/support/cmd.rb +42 -0
  34. data/spec/support/file_utils.rb +7 -0
  35. data/spec/support/logging.rb +5 -0
  36. data/spec/support/rails.rb +32 -0
  37. data/spec/support/rspec.rb +3 -0
  38. 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