mini_record-cj 0.3.7

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.
@@ -0,0 +1,3 @@
1
+ module MiniRecord
2
+ VERSION = "0.3.7"
3
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mini_record/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "mini_record-cj"
7
+ s.version = MiniRecord::VERSION
8
+ s.authors = ["Davide D'Agostino"]
9
+ s.email = ["d.dagostino@lipsiasoft.com"]
10
+ s.homepage = "https://github.com/DAddYE/mini_record"
11
+ s.summary = %q{MiniRecord is a micro gem that allow you to write schema inside your model as you can do in DataMapper.}
12
+ s.description = %q{
13
+ With it you can add the ability to create columns outside the default schema, directly
14
+ in your model in a similar way that you just know in others projects
15
+ like DataMapper or MongoMapper.
16
+ }.gsub(/^ {4}/, '')
17
+
18
+ s.rubyforge_project = "mini_record"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+
25
+ # specify any dependencies here; for example:
26
+ # s.add_development_dependency "rspec"
27
+ s.add_dependency "activerecord", ">=3.2.0"
28
+ end
@@ -0,0 +1,69 @@
1
+ require 'rubygems' unless defined?(Gem)
2
+ require 'bundler/setup'
3
+ require 'mini_record'
4
+ require 'minitest/autorun'
5
+
6
+ class ActiveRecord::Base
7
+ class << self
8
+ attr_accessor :logs
9
+
10
+ def db_fields
11
+ connection.columns(table_name).inject({}) do |hash, column|
12
+ hash[column.name.to_sym] = column
13
+ hash
14
+ end
15
+ end
16
+
17
+ def db_columns
18
+ connection.columns(table_name).map(&:name).sort
19
+ end
20
+
21
+ def db_indexes
22
+ connection.indexes(table_name).map(&:name).sort
23
+ end
24
+
25
+ def schema_columns
26
+ table_definition.columns.map { |c| c.name.to_s }.sort
27
+ end
28
+
29
+ def schema_fields
30
+ table_definition.columns.inject({}) do |hash, column|
31
+ hash[column.name.to_sym] = column
32
+ hash
33
+ end
34
+ end
35
+
36
+ def queries(pragma=false)
37
+ ActiveRecord::Base.logs.string.gsub(/\e\[[\d;]+m/, '').lines.reject { |l| !pragma && l =~ /pragma/i }.join("\n")
38
+ end
39
+
40
+ def auto_upgrade!
41
+ ActiveRecord::Base.logs = StringIO.new
42
+ ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(ActiveRecord::Base.logs)
43
+ silence_stream(STDERR) { super }
44
+ end
45
+ end
46
+ end # ActiveRecord::Base
47
+
48
+ # Setup Adatper
49
+ case ENV['DB']
50
+ when 'mysql'
51
+ ActiveRecord::Base.establish_connection(:adapter => 'mysql', :database => 'test', :user => 'root')
52
+ when 'mysql2'
53
+ ActiveRecord::Base.establish_connection(:adapter => 'mysql2', :database => 'test', :user => 'root')
54
+ Bundler.require(:test) # require 'foreigner'
55
+ Foreigner.load
56
+ when 'pg', 'postgresql'
57
+ ActiveRecord::Base.establish_connection(:adapter => 'postgresql', :database => 'test', :user => 'postgres', :host => 'localhost')
58
+ else
59
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
60
+ end
61
+
62
+
63
+ # Some helpers to minitest
64
+ class MiniTest::Spec
65
+ def connection
66
+ ActiveRecord::Base.connection
67
+ end
68
+ alias :conn :connection
69
+ end
@@ -0,0 +1,74 @@
1
+ class Person < ActiveRecord::Base
2
+ schema do |s|
3
+ s.string :name
4
+ end
5
+ timestamps
6
+ end
7
+
8
+ class Post < ActiveRecord::Base
9
+ key :title
10
+ key :body
11
+ key :category, :as => :references
12
+ belongs_to :category
13
+ end
14
+
15
+ class Category < ActiveRecord::Base
16
+ key :title
17
+ has_many :articles
18
+ has_many :posts
19
+ has_many :items
20
+ end
21
+
22
+ class Animal < ActiveRecord::Base
23
+ key :name, :index => true
24
+ index :id
25
+ end
26
+
27
+ class Pet < ActiveRecord::Base
28
+ key :name, :index => true
29
+ end
30
+
31
+ class Tool < ActiveRecord::Base
32
+ has_and_belongs_to_many :purposes
33
+ end
34
+
35
+ class Purpose < ActiveRecord::Base
36
+ has_and_belongs_to_many :tools
37
+ end
38
+
39
+ class Publisher < ActiveRecord::Base
40
+ has_many :articles
41
+ col :name
42
+ end
43
+
44
+ class Article < ActiveRecord::Base
45
+ key :title
46
+ belongs_to :publisher
47
+ end
48
+
49
+ class Attachment < ActiveRecord::Base
50
+ key :name
51
+ belongs_to :attachable, :polymorphic => true
52
+ end
53
+
54
+ class Account < ActiveRecord::Base
55
+ key :name
56
+ end
57
+
58
+ class Task < ActiveRecord::Base
59
+ belongs_to :author, :class_name => 'Account'
60
+ end
61
+
62
+ class Activity < ActiveRecord::Base
63
+ belongs_to :author, :class_name => 'Account', :foreign_key => 'custom_id'
64
+ end
65
+
66
+ class Page < ActiveRecord::Base
67
+ key :title
68
+ has_and_belongs_to_many :photogalleries
69
+ end
70
+
71
+ class Photogallery < ActiveRecord::Base
72
+ key :title
73
+ has_and_belongs_to_many :pages
74
+ end
@@ -0,0 +1,621 @@
1
+ require File.expand_path('../helper.rb', __FILE__)
2
+
3
+ describe MiniRecord do
4
+
5
+ before do
6
+ ActiveRecord::Base.clear_reloadable_connections!
7
+ ActiveRecord::Base.clear_cache!
8
+ ActiveRecord::Base.clear_active_connections!
9
+ conn.tables.each { |table| silence_stream(STDERR) { conn.execute "DROP TABLE IF EXISTS #{table}" } }
10
+ ActiveRecord::Base.descendants.each { |klass| Object.send(:remove_const, klass.to_s) if Object.const_defined?(klass.to_s) }
11
+ ActiveSupport::DescendantsTracker.direct_descendants(ActiveRecord::Base).clear
12
+ load File.expand_path('../models.rb', __FILE__)
13
+ ActiveRecord::Base.auto_upgrade!
14
+ end
15
+
16
+ it 'has #schema inside model' do
17
+ assert_equal 'people', Person.table_name
18
+ assert_equal %w[created_at id name updated_at], Person.db_columns.sort
19
+ assert_equal Person.db_columns, Person.column_names.sort
20
+ assert_equal Person.schema_columns, Person.column_names.sort
21
+
22
+ # Check surname attribute
23
+ person = Person.create(:name => 'foo')
24
+ assert_equal 'foo', person.name
25
+ assert_raises(NoMethodError){ person.surname }
26
+
27
+ # Test the timestamp columns exist
28
+ assert_respond_to person, :created_at
29
+ assert_respond_to person, :updated_at
30
+
31
+ # Add a column without lost data
32
+ Person.class_eval do
33
+ schema do |p|
34
+ p.string :name
35
+ p.string :surname
36
+ end
37
+ timestamps
38
+ end
39
+ Person.auto_upgrade!
40
+ assert_equal 1, Person.count
41
+
42
+ person = Person.last
43
+ assert_equal 'foo', person.name
44
+ assert_nil person.surname
45
+
46
+ person.update_column(:surname, 'bar')
47
+ assert_equal %w[created_at id name surname updated_at], Person.db_columns.sort
48
+
49
+ # Remove a column without lost data
50
+ Person.class_eval do
51
+ schema do |p|
52
+ p.string :name
53
+ end
54
+ timestamps
55
+ end
56
+ Person.auto_upgrade!
57
+ person = Person.last
58
+ assert_equal 'foo', person.name
59
+ assert_raises(NoMethodError) { person.surname }
60
+ assert_equal %w[created_at id name updated_at], Person.db_columns
61
+ assert_equal Person.column_names.sort, Person.db_columns
62
+ assert_equal Person.column_names.sort, Person.schema_columns
63
+
64
+ # Change column without lost data
65
+ Person.class_eval do
66
+ schema do |p|
67
+ p.text :name
68
+ end
69
+ end
70
+ person = Person.last
71
+ assert_equal 'foo', person.name
72
+ end
73
+
74
+ it 'has #key,col,property,attribute inside model' do
75
+ assert_equal Post.column_names.sort, Post.db_columns
76
+ assert_equal Category.column_names.sort, Category.schema_columns
77
+
78
+ # Check default properties
79
+ category = Category.create(:title => 'category')
80
+ post = Post.create(:title => 'foo', :body => 'bar', :category_id => category.id)
81
+ post = Post.first
82
+ assert_equal 'foo', post.title
83
+ assert_equal 'bar', post.body
84
+ assert_equal category, post.category
85
+
86
+
87
+ # Remove a column
88
+ Post.reset_table_definition!
89
+ Post.class_eval do
90
+ col :name
91
+ col :category, :as => :references
92
+ end
93
+ Post.auto_upgrade!
94
+ refute_includes %w[title body], Post.db_columns
95
+
96
+ post = Post.first
97
+ assert_nil post.name
98
+ assert_equal category, post.category
99
+ assert_raises(NoMethodError, ActiveModel::MissingAttributeError) { post.title }
100
+ end
101
+
102
+ it 'has indexes inside model' do
103
+ # Check indexes
104
+ assert Animal.db_indexes.size > 0
105
+ assert_equal Animal.db_indexes, Animal.indexes.keys.sort
106
+
107
+
108
+ # Remove an index
109
+ indexes_was = Animal.db_indexes
110
+ Animal.indexes.delete(indexes_was.pop)
111
+ Animal.auto_upgrade!
112
+ assert_equal indexes_was, Animal.indexes.keys
113
+ assert_equal indexes_was, Animal.db_indexes
114
+
115
+ # Add a new index
116
+ Animal.class_eval do
117
+ col :category, :as => :references, :index => true
118
+ end
119
+ Animal.auto_upgrade!
120
+ new_indexes = indexes_was + %w[index_animals_on_category_id]
121
+ assert_includes Animal.db_columns, 'category_id'
122
+ assert_equal new_indexes.sort, Animal.db_indexes
123
+ end
124
+
125
+ it 'not add already defined indexes' do
126
+ class Foo < ActiveRecord::Base
127
+ index :customer_id, :unique => true, :name => 'by_customer'
128
+ belongs_to :customer
129
+ end
130
+ Foo.auto_upgrade!
131
+ assert_equal 1, Foo.db_indexes.size
132
+ assert_includes Foo.db_indexes, 'by_customer'
133
+ end
134
+
135
+ it 'works with STI' do
136
+ class Dog < Pet; end
137
+ class Cat < Pet; end
138
+ class Kitten < Cat; end
139
+ ActiveRecord::Base.auto_upgrade!
140
+
141
+ # Check inheritance column
142
+ assert_includes Pet.db_columns, "type"
143
+
144
+ # Now, let's we know if STI is working
145
+ Pet.create(:name => "foo")
146
+ Dog.create(:name => "bar")
147
+ Kitten.create(:name => 'foxy')
148
+ assert_equal 1, Dog.count
149
+ assert_equal 'bar', Dog.first.name
150
+ assert_equal 3, Pet.count
151
+ assert_equal %w[foo bar foxy], Pet.all.map(&:name)
152
+ assert_equal 'bar', Dog.first.name
153
+
154
+ # What's happen if we change schema?
155
+ assert_equal Dog.table_definition, Pet.table_definition
156
+ assert_equal Dog.indexes, Pet.indexes
157
+
158
+ Dog.class_eval do
159
+ col :bau
160
+ end
161
+ ActiveRecord::Base.auto_upgrade!
162
+ assert_includes Dog.schema_columns, 'bau'
163
+ assert_includes Pet.db_columns, 'bau'
164
+ end
165
+
166
+ it 'works with custom inheritance column' do
167
+ class User < ActiveRecord::Base
168
+ col :name
169
+ col :surname
170
+ col :role
171
+ def self.inheritance_column; 'role'; end
172
+ end
173
+
174
+ class Administrator < User; end
175
+ class Customer < User; end
176
+
177
+ User.auto_upgrade!
178
+ assert_equal 'role', User.inheritance_column
179
+
180
+ Administrator.create(:name => "Davide", :surname => 'DAddYE')
181
+ Customer.create(:name => "Foo", :surname => "Bar")
182
+ assert_equal 1, Administrator.count
183
+ assert_equal 'Davide', Administrator.first.name
184
+ assert_equal 1, Customer.count
185
+ assert_equal 'Foo', Customer.first.name
186
+ assert_equal 2, User.count
187
+ assert_equal 'Administrator', User.first.role
188
+ assert_equal 'Customer', User.last.role
189
+ assert_includes User.db_indexes, 'index_users_on_role'
190
+ end
191
+
192
+ it 'allow multiple columns definitions' do
193
+ class Fake < ActiveRecord::Base
194
+ col :name, :surname
195
+ col :category, :group, :as => :references
196
+ end
197
+ Fake.auto_upgrade!
198
+ Fake.create(:name => 'foo', :surname => 'bar', :category_id => 1, :group_id => 2)
199
+ fake = Fake.first
200
+ assert_equal 'foo', fake.name
201
+ assert_equal 'bar', fake.surname
202
+ assert_equal 1, fake.category_id
203
+ assert_equal 2, fake.group_id
204
+ end
205
+
206
+ it 'allow custom query' do
207
+ skip unless conn.adapter_name =~ /mysql/i
208
+
209
+ class Foo < ActiveRecord::Base
210
+ col :name, :as => "ENUM('foo','bar')"
211
+ end
212
+ Foo.auto_upgrade!
213
+ assert_match /ENUM/, Foo.queries
214
+
215
+ Foo.auto_upgrade!
216
+ assert_empty Foo.queries
217
+ assert_equal %w[id name], Foo.db_columns
218
+ assert_equal %w[id name], Foo.schema_columns
219
+
220
+ foo = Foo.create(:name => 'test')
221
+ assert_empty Foo.first.name
222
+
223
+ foo.update_column(:name, 'foo')
224
+
225
+ assert_equal 'foo', Foo.first.name
226
+ end
227
+
228
+ describe 'relation #belongs_to' do
229
+
230
+ it 'creates a column and index based on relation' do
231
+ Article.create(:title => 'Hello', :publisher_id => 1)
232
+ Article.first.tap do |a|
233
+ assert_equal 'Hello', a.title
234
+ assert_equal 1, a.publisher_id
235
+ end
236
+ assert_includes Article.db_indexes, 'index_articles_on_publisher_id'
237
+
238
+ # Ensure that associated field/index is not deleted on upgrade
239
+ Article.auto_upgrade!
240
+ assert_equal 1, Article.first.publisher_id
241
+ assert_includes Article.db_indexes, 'index_articles_on_publisher_id'
242
+ end
243
+
244
+ it 'removes a column and index when relation is removed' do
245
+ class Foo < ActiveRecord::Base
246
+ key :name
247
+ belongs_to :image, :polymorphic => true
248
+ end
249
+ Foo.auto_upgrade!
250
+ assert_includes Foo.db_columns, 'name'
251
+ assert_includes Foo.db_columns, 'image_type'
252
+ assert_includes Foo.db_columns, 'image_id'
253
+ assert_includes Foo.db_indexes, 'index_foos_on_image_id_and_image_type'
254
+
255
+ Foo.class_eval do
256
+ reset_table_definition!
257
+ reflections.clear
258
+ indexes.clear
259
+ key :name
260
+ end
261
+ Foo.auto_upgrade!
262
+ assert_includes Foo.db_columns, 'name'
263
+ refute_includes Foo.db_columns, 'image_type'
264
+ refute_includes Foo.db_columns, 'image_id'
265
+ assert_empty Foo.db_indexes
266
+ end
267
+
268
+ it 'creates columns and index based on polymorphic relation' do
269
+ Attachment.create(:name => 'Avatar', :attachable_id => 1, :attachable_type => 'Post')
270
+ Attachment.first.tap do |attachment|
271
+ assert_equal 'Avatar', attachment.name
272
+ assert_equal 1, attachment.attachable_id
273
+ assert_equal 'Post', attachment.attachable_type
274
+ end
275
+ index = 'index_attachments_on_attachable_id_and_attachable_type'
276
+ assert_includes Attachment.db_indexes, index
277
+
278
+ # Ensure that associated fields/indexes are not deleted on subsequent upgrade
279
+ Attachment.auto_upgrade!
280
+ assert_equal 1, Attachment.first.attachable_id
281
+ assert_equal 'Post', Attachment.first.attachable_type
282
+ assert_includes Attachment.db_indexes, index
283
+ end
284
+
285
+ it 'should support :class_name' do
286
+ assert_includes Task.schema_columns, 'author_id'
287
+ assert_includes Task.db_columns, 'author_id'
288
+ end
289
+
290
+ it 'should support :foreign_key' do
291
+ assert_includes Activity.schema_columns, 'custom_id'
292
+ assert_includes Activity.db_columns, 'custom_id'
293
+ end
294
+
295
+ it 'should memonize in schema relationships' do
296
+ silence_stream(STDERR) { conn.create_table('foos') }
297
+ conn.add_column :foos, :name, :string
298
+ conn.add_column :foos, :bar_id, :integer
299
+ conn.add_index :foos, :bar_id
300
+ class Foo < ActiveRecord::Base
301
+ col :name
302
+ belongs_to :bar
303
+ end
304
+ assert_includes Foo.db_columns, 'name'
305
+ assert_includes Foo.db_columns, 'bar_id'
306
+ assert_includes Foo.db_indexes, 'index_foos_on_bar_id'
307
+
308
+ Foo.auto_upgrade!
309
+ assert_includes Foo.schema_columns, 'name'
310
+ assert_includes Foo.schema_columns, 'bar_id'
311
+ assert_includes Foo.indexes, 'index_foos_on_bar_id'
312
+ end
313
+
314
+ it 'should add new columns without lost belongs_to schema' do
315
+ publisher = Publisher.create(:name => 'foo')
316
+ article = Article.create(:title => 'bar', :publisher => publisher)
317
+ assert article.valid?
318
+ assert_includes Article.indexes, 'index_articles_on_publisher_id'
319
+
320
+ # Here we perform a schema change
321
+ Article.key :body
322
+ Article.auto_upgrade!
323
+ article.reload
324
+ assert_nil article.body
325
+
326
+ article.update_column(:body, 'null')
327
+ assert_equal 'null', article.body
328
+
329
+ # Finally check the index existance
330
+ assert_includes Article.db_indexes, 'index_articles_on_publisher_id'
331
+ end
332
+
333
+ it 'should not override previous defined column relation' do
334
+ class Foo < ActiveRecord::Base
335
+ key :user, :as => :references, :null => false, :limit => 4
336
+ belongs_to :user
337
+ end
338
+ Foo.auto_upgrade!
339
+ assert_equal 4, Foo.db_fields[:user_id].limit
340
+ assert_equal false, Foo.db_fields[:user_id].null
341
+ end
342
+
343
+ it 'add/remove foreign key with :foreign option, when Foreigner gem used on mysql' do
344
+ skip unless conn.adapter_name =~ /mysql/i
345
+
346
+ class Book < ActiveRecord::Base
347
+ belongs_to :publisher
348
+ index :publisher_id, :foreign => true
349
+ end
350
+ Book.auto_upgrade!
351
+
352
+ assert_includes Book.db_columns, 'publisher_id'
353
+ assert_includes Book.db_indexes, 'index_books_on_publisher_id'
354
+
355
+ assert connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'publisher_id'}
356
+
357
+ Object.send(:remove_const, :Book)
358
+ class Book < ActiveRecord::Base
359
+ belongs_to :publisher
360
+ index :publisher_id, :foreign => false
361
+ end
362
+ Book.auto_upgrade!
363
+
364
+ assert_nil connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'publisher_id'}
365
+ end
366
+
367
+ it 'add/remove named foreign key with :foreign option, when Foreigner gem used on mysql' do
368
+ skip unless conn.adapter_name =~ /mysql/i
369
+
370
+ class Book < ActiveRecord::Base
371
+ belongs_to :publisher
372
+ index :publisher_id, :name => 'my_super_publisher_id_fk', :foreign => true
373
+ end
374
+ Book.auto_upgrade!
375
+
376
+ assert_includes Book.db_columns, 'publisher_id'
377
+ assert_includes Book.db_indexes, 'my_super_publisher_id_fk'
378
+
379
+ assert connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'publisher_id'}
380
+
381
+ Object.send(:remove_const, :Book)
382
+ class Book < ActiveRecord::Base
383
+ belongs_to :publisher
384
+ index :publisher_id, :name => 'my_super_publisher_id_fk', :foreign => false
385
+ end
386
+ Book.auto_upgrade!
387
+
388
+ assert_nil connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'publisher_id'}
389
+ Object.send(:remove_const, :Book)
390
+ end
391
+
392
+ it 'support :foreign option in the index with custom :foreign_key in the belong_to association' do
393
+ skip unless conn.adapter_name =~ /mysql/i
394
+
395
+ class Book < ActiveRecord::Base
396
+ belongs_to :second_publisher, :foreign_key => 'second_publisher_id', :class_name => 'Publisher'
397
+ index :second_publisher_id, :foreign => true
398
+ end
399
+ Book.auto_upgrade!
400
+
401
+ assert_includes Book.db_columns, 'second_publisher_id'
402
+ assert_includes Book.db_indexes, 'index_books_on_second_publisher_id'
403
+
404
+ assert connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'second_publisher_id'}
405
+
406
+ Object.send(:remove_const, :Book)
407
+ class Book < ActiveRecord::Base
408
+ belongs_to :second_publisher, :foreign_key => 'second_publisher_id', :class_name => 'Publisher'
409
+ index :second_publisher_id, :foreign => false
410
+ end
411
+ Book.auto_upgrade!
412
+
413
+ assert_nil connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'second_publisher_id'}
414
+ end
415
+
416
+ end
417
+
418
+ describe 'relation #habtm' do
419
+ it 'creates a join table with indexes for has_and_belongs_to_many relations' do
420
+ tables = Tool.connection.tables
421
+ assert_includes tables, 'purposes_tools'
422
+
423
+ index = 'index_purposes_tools_on_tool_id_and_purpose_id'
424
+ assert_includes Tool.connection.indexes('purposes_tools').map(&:name), index
425
+
426
+ # Ensure that join table is not deleted on subsequent upgrade
427
+ Tool.auto_upgrade!
428
+ assert_includes tables, 'purposes_tools'
429
+ assert_includes Tool.connection.indexes('purposes_tools').map(&:name), index
430
+ end
431
+
432
+ it 'drops join table if has_and_belongs_to_many relation is deleted' do
433
+ Tool.schema_tables.delete('purposes_tools')
434
+ refute_includes ActiveRecord::Base.schema_tables, 'purposes_tools'
435
+
436
+ ActiveRecord::Base.clear_tables!
437
+ refute_includes Tool.connection.tables, 'purposes_tools'
438
+ end
439
+
440
+ it 'has_and_belongs_to_many with custom join_table and foreign keys' do
441
+ class Foo < ActiveRecord::Base
442
+ has_and_belongs_to_many :watchers, :join_table => :watching, :foreign_key => :custom_foo_id, :association_foreign_key => :customer_id
443
+ end
444
+ Foo.auto_upgrade!
445
+ assert_includes conn.tables, 'watching'
446
+
447
+ cols = conn.columns('watching').map(&:name)
448
+ refute_includes cols, 'id'
449
+ assert_includes cols, 'custom_foo_id'
450
+ assert_includes cols, 'customer_id'
451
+ end
452
+
453
+ it 'creates a join table with indexes with long indexes names' do
454
+ class Foo < ActiveRecord::Base
455
+ has_and_belongs_to_many :people, :join_table => :long_people,
456
+ :foreign_key => :custom_long_long_long_long_id,
457
+ :association_foreign_key => :customer_super_long_very_long_trust_me_id
458
+ end
459
+ Foo.auto_upgrade!
460
+ index_name = 'index_long_people_on_custom_long_long_long_long_id_and_customer_super_long_very_long_trust_me_id'[0...conn.index_name_length]
461
+ assert_includes conn.tables, 'people'
462
+ assert_includes conn.indexes(:long_people).map(&:name), index_name
463
+ end
464
+
465
+ it 'adds unique index' do
466
+ page = Page.create(:title => 'Foo')
467
+ photogallery = Photogallery.create(:title => 'Bar')
468
+ assert photogallery.valid?
469
+
470
+ photogallery.pages << page
471
+ refute_empty Photogallery.queries
472
+ assert_includes photogallery.reload.pages, page
473
+ assert_raises(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid){ photogallery.pages << page }
474
+ end
475
+ end
476
+
477
+ it 'should add multiple index' do
478
+ class Foo < ActiveRecord::Base
479
+ key :name, :surname, :index => true
480
+ end
481
+ Foo.auto_upgrade!
482
+ assert_includes Foo.db_indexes, 'index_foos_on_name'
483
+ assert_includes Foo.db_indexes, 'index_foos_on_surname'
484
+ end
485
+
486
+ it 'should create a unique index' do
487
+ class Foo < ActiveRecord::Base
488
+ key :name, :surname
489
+ add_index([:name, :surname], :unique => true)
490
+ end
491
+ Foo.auto_upgrade!
492
+ db_indexes = Foo.connection.indexes('foos')[0]
493
+ assert_equal 'index_foos_on_name_and_surname', db_indexes.name
494
+ assert db_indexes.unique
495
+ assert_equal %w[name surname], db_indexes.columns.sort
496
+ end
497
+
498
+ it 'should change #limit' do
499
+ class Foo < ActiveRecord::Base
500
+ key :number, :as => :integer
501
+ key :string, :limit => 100
502
+ end
503
+ Foo.auto_upgrade!
504
+ assert_match /CREATE TABLE/, Foo.queries
505
+
506
+ Foo.auto_upgrade!
507
+ refute_match /alter/i, Foo.queries
508
+
509
+ # According to this:
510
+ # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L476-487
511
+ Foo.key :number, :as => :integer, :limit => 4
512
+ Foo.auto_upgrade!
513
+ case conn.adapter_name
514
+ when /sqlite/i
515
+ # In sqlite there is a difference between limit: 4 and limit: 11
516
+ assert_match 'altered', Foo.queries
517
+ assert_equal 4, Foo.db_fields[:number].limit
518
+ assert_equal 4, Foo.schema_fields[:number].limit
519
+ when /mysql/i
520
+ # In mysql according to this: http://goo.gl/bjZE7 limit: 4 is same of limit:11
521
+ assert_empty Foo.queries
522
+ assert_equal 4, Foo.db_fields[:number].limit
523
+ assert_equal 4, Foo.schema_fields[:number].limit
524
+ when /postgres/i
525
+ # In postgres limit: 4 will be translated to nil
526
+ assert_match /ALTER COLUMN "number" TYPE integer$/, Foo.queries
527
+ assert_equal nil, Foo.db_fields[:number].limit
528
+ assert_equal 4, Foo.schema_fields[:number].limit
529
+ end
530
+
531
+ # Change limit to string
532
+ Foo.key :string, :limit => 255
533
+ Foo.auto_upgrade!
534
+ refute_empty Foo.queries
535
+ assert_equal 255, Foo.db_fields[:string].limit
536
+ end
537
+
538
+ it 'should change #null' do
539
+ class Foo < ActiveRecord::Base
540
+ key :string
541
+ end
542
+ Foo.auto_upgrade!
543
+ assert Foo.db_fields[:string].null
544
+
545
+ # Same as above
546
+ Foo.key :string, :null => true
547
+ Foo.auto_upgrade!
548
+ refute_match /alter/i, Foo.queries
549
+ assert Foo.db_fields[:string].null
550
+
551
+ Foo.key :string, :null => nil
552
+ Foo.auto_upgrade!
553
+ refute_match /alter/i, Foo.queries
554
+ assert Foo.db_fields[:string].null
555
+
556
+ Foo.key :string, :null => false
557
+ Foo.auto_upgrade!
558
+ assert_match /alter/i, Foo.queries
559
+ refute Foo.db_fields[:string].null
560
+ end
561
+
562
+ it 'should change #scale #precision' do
563
+ class Foo < ActiveRecord::Base
564
+ field :currency, :as => :decimal, :precision => 8, :scale => 2
565
+ end
566
+ Foo.auto_upgrade!
567
+ assert_equal 8, Foo.db_fields[:currency].precision
568
+ assert_equal 2, Foo.db_fields[:currency].scale
569
+ assert_equal 8, Foo.db_fields[:currency].limit
570
+
571
+ Foo.auto_upgrade!
572
+ refute_match /alter/i, Foo.queries
573
+
574
+ Foo.field :currency, :as => :decimal, :precision => 4, :scale => 2, :limit => 5
575
+ Foo.auto_upgrade!
576
+ assert_match /alter/i, Foo.queries
577
+ assert_equal 4, Foo.db_fields[:currency].precision
578
+ assert_equal 2, Foo.db_fields[:currency].scale
579
+ assert_equal 4, Foo.db_fields[:currency].limit
580
+ end
581
+
582
+ it 'should ignore abstract classes' do
583
+ class Foo < ActiveRecord::Base
584
+ self.abstract_class = true
585
+ end
586
+
587
+ class Bar < Foo
588
+ end
589
+
590
+ Foo.auto_upgrade!
591
+ Bar.auto_upgrade!
592
+
593
+ tables = Foo.connection.tables
594
+
595
+ refute_includes tables, 'foos'
596
+ refute_includes tables, ''
597
+ assert_includes tables, 'bars'
598
+ end
599
+
600
+ it 'should prevent abstract table class to leak columns to other tables' do
601
+
602
+ class Base < ActiveRecord::Base
603
+ self.abstract_class = true
604
+ end
605
+
606
+ class User < Base
607
+ col :name
608
+ end
609
+
610
+ class Book < Base
611
+ col :title
612
+ col :author
613
+ end
614
+
615
+ User.auto_upgrade!
616
+ Book.auto_upgrade!
617
+
618
+ assert_equal ['id', 'name'], User.db_columns.sort
619
+ assert_equal ['author', 'id', 'title'], Book.db_columns.sort
620
+ end
621
+ end