mini_record 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ee891eba3f9e58c760acce562bd83a6595c4b9ef
4
- data.tar.gz: 9aba338c47d2a027e73c4b81347a4f4cf35d0964
3
+ metadata.gz: 09d1ec6c447a806ef8ecc9c33e51c52404ccc948
4
+ data.tar.gz: 23f2145d50bfc6b57bd3517af5312b9ca57d04fa
5
5
  SHA512:
6
- metadata.gz: eee2c19a616f355e653d37be8c7a9a913d001c01af3486cde45ca6ba755d35152b872ed803786efdbe6e79730c679dd053bea90e9c648951b66446e82a0013d9
7
- data.tar.gz: 90e4353472d858b08a3124e8ac56d265cb4d2a0326583c89375f4ed0dd1c3a6f8019ea82a7782e1e0ca151765741ed4afb0ab9442f99eecdc56a8b3d63cd7dc8
6
+ metadata.gz: b0a1daa513f1d05437be824e33672092e79b699848678dbafc9c743904694aa83c079caa20d770862311e39bf6ab82f0b7bcc78af9bb72b33d7aac847e18c2ad
7
+ data.tar.gz: ba06f494683ca8028b95985cc2ee9a7735b25d01d497f5fb6d47f92fc065881b17312f058a0b947777d128e5d091c9a529249f9c094453f422453225d365f28b
data/README.md CHANGED
@@ -25,6 +25,14 @@ Add to your `Gemfile`:
25
25
  gem 'mini_record'
26
26
  ```
27
27
 
28
+ To optionally block any destructive actions on the database, create a file `config/initializers/mini_record.rb` and add:
29
+
30
+ ```ruby
31
+ MiniRecord.configure do |config|
32
+ config.destructive = false
33
+ end
34
+ ```
35
+
28
36
  That's all!
29
37
 
30
38
  ## Examples
@@ -3,7 +3,7 @@ module MiniRecord
3
3
  def self.included(base)
4
4
  base.extend(ClassMethods)
5
5
  end
6
-
6
+
7
7
  module ClassMethods
8
8
  def init_table_definition(connection)
9
9
  #connection.create_table(table_name) unless connection.table_exists?(table_name)
@@ -23,7 +23,7 @@ module MiniRecord
23
23
  "Unsupported number of args for ActiveRecord::ConnectionAdapters::TableDefinition.new()"
24
24
  end
25
25
  end
26
-
26
+
27
27
  def schema_tables
28
28
  @@_schema_tables ||= []
29
29
  end
@@ -158,6 +158,7 @@ module MiniRecord
158
158
  end
159
159
 
160
160
  def clear_tables!
161
+ return unless MiniRecord.configuration.destructive == true
161
162
  (connection.tables - schema_tables).each do |name|
162
163
  connection.drop_table(name)
163
164
  schema_tables.delete(name)
@@ -172,6 +173,7 @@ module MiniRecord
172
173
 
173
174
  # Remove foreign keys for indexes with :foreign=>false option
174
175
  def remove_foreign_keys
176
+ return unless MiniRecord.configuration.destructive == true
175
177
  indexes.each do |name, options|
176
178
  if options[:foreign]==false
177
179
  foreign_key = foreign_keys.detect { |fk| fk.options[:column] == options[:column].to_s }
@@ -260,20 +262,73 @@ module MiniRecord
260
262
  index inheritance_column
261
263
  end
262
264
 
263
- # Rename fields
264
- rename_fields.each do |old_name, new_name|
265
- old_column = fields_in_db[old_name.to_s]
266
- new_column = fields_in_db[new_name.to_s]
267
- if old_column && !new_column
268
- connection.rename_column(table_name, old_column.name, new_name)
265
+ # Group Destructive Actions
266
+ if MiniRecord.configuration.destructive == true
267
+
268
+ # Rename fields
269
+ rename_fields.each do |old_name, new_name|
270
+ old_column = fields_in_db[old_name.to_s]
271
+ new_column = fields_in_db[new_name.to_s]
272
+ if old_column && !new_column
273
+ connection.rename_column(table_name, old_column.name, new_name)
274
+ end
269
275
  end
270
- end
276
+
277
+ # Remove fields from db no longer in schema
278
+ columns_to_delete = fields_in_db.keys - fields.keys & fields_in_db.keys
279
+ columns_to_delete.each do |field|
280
+ column = fields_in_db[field]
281
+ connection.remove_column table_name, column.name
282
+ end
283
+
284
+ # Change attributes of existent columns
285
+ (fields.keys & fields_in_db.keys).each do |field|
286
+ if field != primary_key #ActiveRecord::Base.get_primary_key(table_name)
287
+ changed = false # flag
288
+ new_attr = {}
289
+
290
+ # Special catch for precision/scale, since *both* must be specified together
291
+ # Always include them in the attr struct, but they'll only get applied if changed = true
292
+ new_attr[:precision] = fields[field][:precision]
293
+ new_attr[:scale] = fields[field][:scale]
294
+
295
+ # If we have precision this is also the limit
296
+ fields[field][:limit] ||= fields[field][:precision]
297
+
298
+ # Next, iterate through our extended attributes, looking for any differences
299
+ # This catches stuff like :null, :precision, etc
300
+ # Ignore junk attributes that different versions of Rails include
301
+ [:name, :limit, :precision, :scale, :default, :null].each do |att|
302
+ value = fields[field][att]
303
+ value = true if att == :null && value.nil?
304
+
305
+ # Skip unspecified limit/precision/scale as DB will set them to defaults,
306
+ # and on subsequent runs, this will be erroneously detected as a change.
307
+ next if value.nil? and [:limit, :precision, :scale].include?(att)
308
+
309
+ old_value = fields_in_db[field].send(att)
310
+ if value != old_value
311
+ logger.debug "[MiniRecord] Detected schema change for #{table_name}.#{field}##{att} " +
312
+ "from #{old_value.inspect} to #{value.inspect}" if logger
313
+ new_attr[att] = value
314
+ changed = true
315
+ end
316
+ end
317
+
318
+ # Change the column if applicable
319
+ new_type = fields[field].type.to_sym
320
+ connection.change_column table_name, field, new_type, new_attr if changed
321
+ end
322
+ end
323
+
324
+ remove_foreign_keys if connection.respond_to?(:foreign_keys)
271
325
 
272
- # Remove fields from db no longer in schema
273
- columns_to_delete = fields_in_db.keys - fields.keys & fields_in_db.keys
274
- columns_to_delete.each do |field|
275
- column = fields_in_db[field]
276
- connection.remove_column table_name, column.name
326
+ # Remove old index
327
+ index_names = indexes.collect{|name,opts| opts[:name] || name }
328
+ (indexes_in_db.keys - index_names).each do |name|
329
+ connection.remove_index(table_name, :name => name)
330
+ end
331
+
277
332
  end
278
333
 
279
334
  # Add fields to db new to schema
@@ -286,54 +341,6 @@ module MiniRecord
286
341
  connection.add_column table_name, column.name, column.type.to_sym, options
287
342
  end
288
343
 
289
- # Change attributes of existent columns
290
- (fields.keys & fields_in_db.keys).each do |field|
291
- if field != primary_key #ActiveRecord::Base.get_primary_key(table_name)
292
- changed = false # flag
293
- new_attr = {}
294
-
295
- # Special catch for precision/scale, since *both* must be specified together
296
- # Always include them in the attr struct, but they'll only get applied if changed = true
297
- new_attr[:precision] = fields[field][:precision]
298
- new_attr[:scale] = fields[field][:scale]
299
-
300
- # If we have precision this is also the limit
301
- fields[field][:limit] ||= fields[field][:precision]
302
-
303
- # Next, iterate through our extended attributes, looking for any differences
304
- # This catches stuff like :null, :precision, etc
305
- # Ignore junk attributes that different versions of Rails include
306
- [:name, :limit, :precision, :scale, :default, :null].each do |att|
307
- value = fields[field][att]
308
- value = true if att == :null && value.nil?
309
-
310
- # Skip unspecified limit/precision/scale as DB will set them to defaults,
311
- # and on subsequent runs, this will be erroneously detected as a change.
312
- next if value.nil? and [:limit, :precision, :scale].include?(att)
313
-
314
- old_value = fields_in_db[field].send(att)
315
- if value != old_value
316
- logger.debug "[MiniRecord] Detected schema change for #{table_name}.#{field}##{att} " +
317
- "from #{old_value.inspect} to #{value.inspect}" if logger
318
- new_attr[att] = value
319
- changed = true
320
- end
321
- end
322
-
323
- # Change the column if applicable
324
- new_type = fields[field].type.to_sym
325
- connection.change_column table_name, field, new_type, new_attr if changed
326
- end
327
- end
328
-
329
- remove_foreign_keys if connection.respond_to?(:foreign_keys)
330
-
331
- # Remove old index
332
- index_names = indexes.collect{|name,opts| opts[:name] || name }
333
- (indexes_in_db.keys - index_names).each do |name|
334
- connection.remove_index(table_name, :name => name)
335
- end
336
-
337
344
  # Add indexes
338
345
  indexes.each do |name, options|
339
346
  options = options.dup
@@ -0,0 +1,22 @@
1
+ module MiniRecord
2
+ class << self
3
+ attr_accessor :configuration
4
+ end
5
+
6
+ def self.configure
7
+ self.configuration ||= Configuration.new
8
+ yield(configuration)
9
+ end
10
+
11
+ def self.reset_configuration!
12
+ self.configuration = Configuration.new
13
+ end
14
+
15
+ class Configuration
16
+ attr_accessor :destructive
17
+
18
+ def initialize
19
+ @destructive = true
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module MiniRecord
2
- VERSION = "0.4.1"
2
+ VERSION = "0.4.2"
3
3
  end
data/lib/mini_record.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  require 'rubygems' unless defined?(Gem)
2
2
  require 'active_record'
3
+ require 'mini_record/configuration'
3
4
  require 'mini_record/auto_schema'
4
5
 
6
+ MiniRecord.configure do |config|
7
+ end
5
8
  ActiveRecord::Base.send(:include, MiniRecord::AutoSchema)
@@ -11,6 +11,7 @@ describe MiniRecord do
11
11
  ActiveSupport::DescendantsTracker.direct_descendants(ActiveRecord::Base).clear
12
12
  load File.expand_path('../models.rb', __FILE__)
13
13
  ActiveRecord::Base.auto_upgrade!
14
+ MiniRecord.reset_configuration!
14
15
  end
15
16
 
16
17
  it 'has #schema inside model' do
@@ -264,6 +265,31 @@ describe MiniRecord do
264
265
  refute_includes Foo.db_columns, 'image_id'
265
266
  assert_empty Foo.db_indexes
266
267
  end
268
+
269
+ it 'doesnt remove a column and index when relation is removed and destructive is false' do
270
+ MiniRecord.configuration.destructive = false
271
+ class Foo < ActiveRecord::Base
272
+ key :name
273
+ belongs_to :image, :polymorphic => true
274
+ end
275
+ Foo.auto_upgrade!
276
+ assert_includes Foo.db_columns, 'name'
277
+ assert_includes Foo.db_columns, 'image_type'
278
+ assert_includes Foo.db_columns, 'image_id'
279
+ assert_includes Foo.db_indexes, 'index_foos_on_image_id_and_image_type'
280
+
281
+ Foo.class_eval do
282
+ reset_table_definition!
283
+ reflections.clear
284
+ indexes.clear
285
+ key :name
286
+ end
287
+ Foo.auto_upgrade!
288
+ assert_includes Foo.db_columns, 'name'
289
+ assert_includes Foo.db_columns, 'image_type'
290
+ assert_includes Foo.db_columns, 'image_id'
291
+ assert_includes Foo.db_indexes, 'index_foos_on_image_id_and_image_type'
292
+ end
267
293
 
268
294
  it 'creates columns and index based on polymorphic relation' do
269
295
  Attachment.create(:name => 'Avatar', :attachable_id => 1, :attachable_type => 'Post')
@@ -363,6 +389,32 @@ describe MiniRecord do
363
389
 
364
390
  assert_nil connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'publisher_id'}
365
391
  end
392
+
393
+ it 'doesnt remove foreign key with :foreign option, when Foreigner gem used on mysql and destructive = false' do
394
+ MiniRecord.configuration.destructive = false
395
+ skip unless conn.adapter_name =~ /mysql/i
396
+
397
+ class Book < ActiveRecord::Base
398
+ belongs_to :publisher
399
+ index :publisher_id, :foreign => true
400
+ end
401
+ Book.auto_upgrade!
402
+
403
+ assert_includes Book.db_columns, 'publisher_id'
404
+ assert_includes Book.db_indexes, 'index_books_on_publisher_id'
405
+
406
+ assert connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'publisher_id'}
407
+
408
+ Object.send(:remove_const, :Book)
409
+ class Book < ActiveRecord::Base
410
+ belongs_to :publisher
411
+ index :publisher_id, :foreign => false
412
+ end
413
+ Book.auto_upgrade!
414
+
415
+ assert_includes Book.db_columns, 'publisher_id'
416
+ assert_includes Book.db_indexes, 'index_books_on_publisher_id'
417
+ end
366
418
 
367
419
  it 'add/remove named foreign key with :foreign option, when Foreigner gem used on mysql' do
368
420
  skip unless conn.adapter_name =~ /mysql/i
@@ -388,6 +440,33 @@ describe MiniRecord do
388
440
  assert_nil connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'publisher_id'}
389
441
  Object.send(:remove_const, :Book)
390
442
  end
443
+
444
+ it 'doesnt remove named foreign key with :foreign option, when Foreigner gem used on mysql and destructive = false' do
445
+ MiniRecord.configuration.destructive = false
446
+ skip unless conn.adapter_name =~ /mysql/i
447
+
448
+ class Book < ActiveRecord::Base
449
+ belongs_to :publisher
450
+ index :publisher_id, :name => 'my_super_publisher_id_fk', :foreign => true
451
+ end
452
+ Book.auto_upgrade!
453
+
454
+ assert_includes Book.db_columns, 'publisher_id'
455
+ assert_includes Book.db_indexes, 'my_super_publisher_id_fk'
456
+
457
+ assert connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'publisher_id'}
458
+
459
+ Object.send(:remove_const, :Book)
460
+ class Book < ActiveRecord::Base
461
+ belongs_to :publisher
462
+ index :publisher_id, :name => 'my_super_publisher_id_fk', :foreign => false
463
+ end
464
+ Book.auto_upgrade!
465
+
466
+ assert_includes Book.db_columns, 'publisher_id'
467
+ assert_includes Book.db_indexes, 'my_super_publisher_id_fk'
468
+ Object.send(:remove_const, :Book)
469
+ end
391
470
 
392
471
  it 'support :foreign option in the index with custom :foreign_key in the belong_to association' do
393
472
  skip unless conn.adapter_name =~ /mysql/i
@@ -412,6 +491,32 @@ describe MiniRecord do
412
491
 
413
492
  assert_nil connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'second_publisher_id'}
414
493
  end
494
+
495
+ it 'support :foreign option in the index with custom :foreign_key in the belong_to association and wont remove if destructive = false' do
496
+ MiniRecord.configuration.destructive = false
497
+ skip unless conn.adapter_name =~ /mysql/i
498
+
499
+ class Book < ActiveRecord::Base
500
+ belongs_to :second_publisher, :foreign_key => 'second_publisher_id', :class_name => 'Publisher'
501
+ index :second_publisher_id, :foreign => true
502
+ end
503
+ Book.auto_upgrade!
504
+
505
+ assert_includes Book.db_columns, 'second_publisher_id'
506
+ assert_includes Book.db_indexes, 'index_books_on_second_publisher_id'
507
+
508
+ assert connection.foreign_keys(:books).detect {|fk| fk.options[:column] == 'second_publisher_id'}
509
+
510
+ Object.send(:remove_const, :Book)
511
+ class Book < ActiveRecord::Base
512
+ belongs_to :second_publisher, :foreign_key => 'second_publisher_id', :class_name => 'Publisher'
513
+ index :second_publisher_id, :foreign => false
514
+ end
515
+ Book.auto_upgrade!
516
+
517
+ assert_includes Book.db_columns, 'second_publisher_id'
518
+ assert_includes Book.db_indexes, 'index_books_on_second_publisher_id'
519
+ end
415
520
 
416
521
  end
417
522
 
@@ -436,6 +541,19 @@ describe MiniRecord do
436
541
  ActiveRecord::Base.clear_tables!
437
542
  refute_includes Tool.connection.tables, 'purposes_tools'
438
543
  end
544
+
545
+ it 'keeps join table if has_and_belongs_to_many relation is deleted and destructive = false' do
546
+ MiniRecord.configuration.destructive = false
547
+
548
+ tables = Tool.connection.tables
549
+ assert_includes tables, 'purposes_tools'
550
+
551
+ Tool.schema_tables.delete('purposes_tools')
552
+ refute_includes ActiveRecord::Base.schema_tables, 'purposes_tools'
553
+
554
+ ActiveRecord::Base.clear_tables!
555
+ assert_includes Tool.connection.tables, 'purposes_tools'
556
+ end
439
557
 
440
558
  it 'has_and_belongs_to_many with custom join_table and foreign keys' do
441
559
  class Foo < ActiveRecord::Base
@@ -534,6 +652,47 @@ describe MiniRecord do
534
652
  refute_empty Foo.queries
535
653
  assert_equal 255, Foo.db_fields[:string].limit
536
654
  end
655
+
656
+ it 'should not change #limit if destructive = false' do
657
+ MiniRecord.configuration.destructive = false
658
+ class Foo < ActiveRecord::Base
659
+ key :number, :as => :integer
660
+ key :string, :limit => 100
661
+ end
662
+ Foo.auto_upgrade!
663
+ assert_match /CREATE TABLE/, Foo.queries
664
+
665
+ Foo.auto_upgrade!
666
+ refute_match /alter/i, Foo.queries
667
+
668
+ # According to this:
669
+ # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L476-487
670
+ Foo.key :number, :as => :integer, :limit => 4
671
+ Foo.auto_upgrade!
672
+ case conn.adapter_name
673
+ when /sqlite/i
674
+ # In sqlite there is a difference between limit: 4 and limit: 11
675
+ assert_match Foo.queries, ""
676
+ assert_equal nil, Foo.db_fields[:number].limit
677
+ assert_equal 4, Foo.schema_fields[:number].limit
678
+ when /mysql/i
679
+ # In mysql according to this: http://goo.gl/bjZE7 limit: 4 is same of limit:11
680
+ assert_empty Foo.queries
681
+ assert_equal nil, Foo.db_fields[:number].limit
682
+ assert_equal 4, Foo.schema_fields[:number].limit
683
+ when /postgres/i
684
+ # In postgres limit: 4 will be translated to nil
685
+ assert_match Foo.queries, ""
686
+ assert_equal nil, Foo.db_fields[:number].limit
687
+ assert_equal 4, Foo.schema_fields[:number].limit
688
+ end
689
+
690
+ # Change limit to string
691
+ Foo.key :string, :limit => 255
692
+ Foo.auto_upgrade!
693
+ assert_empty Foo.queries
694
+ assert_equal 100, Foo.db_fields[:string].limit
695
+ end
537
696
 
538
697
  it 'should change #null' do
539
698
  class Foo < ActiveRecord::Base
@@ -558,6 +717,31 @@ describe MiniRecord do
558
717
  assert_match /alter/i, Foo.queries
559
718
  refute Foo.db_fields[:string].null
560
719
  end
720
+
721
+ it 'should not change #null if destructive = false' do
722
+ MiniRecord.configuration.destructive = false
723
+ class Foo < ActiveRecord::Base
724
+ key :string
725
+ end
726
+ Foo.auto_upgrade!
727
+ assert Foo.db_fields[:string].null
728
+
729
+ # Same as above
730
+ Foo.key :string, :null => true
731
+ Foo.auto_upgrade!
732
+ refute_match /alter/i, Foo.queries
733
+ assert Foo.db_fields[:string].null
734
+
735
+ Foo.key :string, :null => nil
736
+ Foo.auto_upgrade!
737
+ refute_match /alter/i, Foo.queries
738
+ assert Foo.db_fields[:string].null
739
+
740
+ Foo.key :string, :null => false
741
+ Foo.auto_upgrade!
742
+ assert_match "", Foo.queries
743
+ assert Foo.db_fields[:string].null
744
+ end
561
745
 
562
746
  it 'should change #scale #precision' do
563
747
  class Foo < ActiveRecord::Base
@@ -578,6 +762,27 @@ describe MiniRecord do
578
762
  assert_equal 2, Foo.db_fields[:currency].scale
579
763
  assert_equal 4, Foo.db_fields[:currency].limit
580
764
  end
765
+
766
+ it 'should not change #scale #precision if destructive = false' do
767
+ MiniRecord.configuration.destructive = false
768
+ class Foo < ActiveRecord::Base
769
+ field :currency, :as => :decimal, :precision => 8, :scale => 2
770
+ end
771
+ Foo.auto_upgrade!
772
+ assert_equal 8, Foo.db_fields[:currency].precision
773
+ assert_equal 2, Foo.db_fields[:currency].scale
774
+ assert_equal 8, Foo.db_fields[:currency].limit
775
+
776
+ Foo.auto_upgrade!
777
+ refute_match /alter/i, Foo.queries
778
+
779
+ Foo.field :currency, :as => :decimal, :precision => 4, :scale => 2, :limit => 5
780
+ Foo.auto_upgrade!
781
+ assert_match "", Foo.queries
782
+ assert_equal 8, Foo.db_fields[:currency].precision
783
+ assert_equal 2, Foo.db_fields[:currency].scale
784
+ assert_equal 8, Foo.db_fields[:currency].limit
785
+ end
581
786
 
582
787
  it 'should ignore abstract classes' do
583
788
  class Foo < ActiveRecord::Base
@@ -649,4 +854,37 @@ describe MiniRecord do
649
854
  assert_match '', Foo.queries
650
855
 
651
856
  end
857
+
858
+ it 'should note rename a column specified by rename_field if destructive = false' do
859
+ MiniRecord.configuration.destructive = false
860
+ class Foo < ActiveRecord::Base
861
+ field :currency, :limit => 3
862
+ end
863
+ Foo.auto_upgrade!
864
+ assert_match /CREATE TABLE/, Foo.queries
865
+
866
+ Foo.create :currency => 'USD'
867
+
868
+ Foo.rename_field :currency, :new_name => :currency_iso
869
+ Foo.field :currency_iso, :limit => 3
870
+
871
+ Foo.auto_upgrade!
872
+
873
+ case conn.adapter_name
874
+ when /sqlite/i
875
+ assert_match "", Foo.queries
876
+ when /mysql/i
877
+ assert_match "", Foo.queries
878
+ when /postgres/i
879
+ assert_match "", Foo.queries
880
+ end
881
+
882
+ cols = conn.columns('foos').map(&:name)
883
+ assert_includes cols, "currency_iso"
884
+ assert_includes cols, "currency"
885
+
886
+ Foo.auto_upgrade!
887
+ assert_match '', Foo.queries
888
+
889
+ end
652
890
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Davide D'Agostino
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-18 00:00:00.000000000 Z
11
+ date: 2014-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -40,6 +40,7 @@ files:
40
40
  - Rakefile
41
41
  - lib/mini_record.rb
42
42
  - lib/mini_record/auto_schema.rb
43
+ - lib/mini_record/configuration.rb
43
44
  - lib/mini_record/version.rb
44
45
  - mini_record.gemspec
45
46
  - test/helper.rb