activerecord 2.3.4 → 2.3.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (37) hide show
  1. data/CHANGELOG +8 -0
  2. data/Rakefile +1 -1
  3. data/lib/active_record/associations.rb +10 -7
  4. data/lib/active_record/associations/association_proxy.rb +7 -8
  5. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -2
  6. data/lib/active_record/associations/has_one_association.rb +9 -0
  7. data/lib/active_record/autosave_association.rb +32 -23
  8. data/lib/active_record/base.rb +7 -0
  9. data/lib/active_record/connection_adapters/mysql_adapter.rb +6 -2
  10. data/lib/active_record/fixtures.rb +1 -1
  11. data/lib/active_record/locking/optimistic.rb +0 -33
  12. data/lib/active_record/locking/pessimistic.rb +0 -22
  13. data/lib/active_record/nested_attributes.rb +101 -38
  14. data/lib/active_record/validations.rb +35 -35
  15. data/lib/active_record/version.rb +1 -1
  16. data/lib/activerecord.rb +1 -0
  17. data/test/cases/associations/has_many_associations_test.rb +12 -0
  18. data/test/cases/associations/has_many_through_associations_test.rb +22 -0
  19. data/test/cases/associations/has_one_associations_test.rb +21 -0
  20. data/test/cases/autosave_association_test.rb +230 -11
  21. data/test/cases/base_test.rb +2 -0
  22. data/test/cases/connection_test_mysql.rb +8 -0
  23. data/test/cases/fixtures_test.rb +2 -2
  24. data/test/cases/locking_test.rb +0 -18
  25. data/test/cases/nested_attributes_test.rb +109 -37
  26. data/test/cases/reflection_test.rb +3 -3
  27. data/test/cases/validations_i18n_test.rb +8 -0
  28. data/test/cases/validations_test.rb +37 -9
  29. data/test/fixtures/accounts.yml +1 -0
  30. data/test/fixtures/fixture_database.sqlite3 +0 -0
  31. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  32. data/test/models/company.rb +10 -0
  33. data/test/models/pirate.rb +9 -2
  34. data/test/models/treasure.rb +2 -0
  35. data/test/schema/mysql_specific_schema.rb +12 -0
  36. data/test/schema/schema.rb +1 -0
  37. metadata +4 -4
@@ -27,11 +27,13 @@ module ActiveRecord
27
27
  end
28
28
 
29
29
  def message
30
- generate_message(@message, options.dup)
30
+ # When type is a string, it means that we do not have to do a lookup, because
31
+ # the user already sent the "final" message.
32
+ type.is_a?(String) ? type : generate_message(default_options)
31
33
  end
32
34
 
33
35
  def full_message
34
- attribute.to_s == 'base' ? message : generate_full_message(message, options.dup)
36
+ attribute.to_s == 'base' ? message : generate_full_message(default_options)
35
37
  end
36
38
 
37
39
  alias :to_s :message
@@ -60,24 +62,19 @@ module ActiveRecord
60
62
  # <li><tt>activerecord.errors.messages.blank</tt></li>
61
63
  # <li>any default you provided through the +options+ hash (in the activerecord.errors scope)</li>
62
64
  # </ol>
63
- def generate_message(message, options = {})
65
+ def generate_message(options = {})
64
66
  keys = @base.class.self_and_descendants_from_active_record.map do |klass|
65
- [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
66
- :"models.#{klass.name.underscore}.#{message}" ]
67
+ [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{@message}",
68
+ :"models.#{klass.name.underscore}.#{@message}" ]
67
69
  end.flatten
68
70
 
69
71
  keys << options.delete(:default)
70
- keys << :"messages.#{message}"
71
- keys << message if message.is_a?(String)
72
- keys << @type unless @type == message
72
+ keys << :"messages.#{@message}"
73
+ keys << @message if @message.is_a?(String)
74
+ keys << @type unless @type == @message
73
75
  keys.compact!
74
76
 
75
- options.reverse_merge! :default => keys,
76
- :scope => [:activerecord, :errors],
77
- :model => @base.class.human_name,
78
- :attribute => @base.class.human_attribute_name(attribute.to_s),
79
- :value => value
80
-
77
+ options.merge!(:default => keys)
81
78
  I18n.translate(keys.shift, options)
82
79
  end
83
80
 
@@ -108,16 +105,24 @@ module ActiveRecord
108
105
  # full_messages:
109
106
  # title:
110
107
  # blank: This title is screwed!
111
- def generate_full_message(message, options = {})
112
- options.reverse_merge! :message => self.message,
113
- :model => @base.class.human_name,
114
- :attribute => @base.class.human_attribute_name(attribute.to_s),
115
- :value => value
116
-
117
- key = :"full_messages.#{@message}"
118
- defaults = [:'full_messages.format', '{{attribute}} {{message}}']
108
+ def generate_full_message(options = {})
109
+ keys = [
110
+ :"full_messages.#{@message}",
111
+ :'full_messages.format',
112
+ '{{attribute}} {{message}}'
113
+ ]
114
+
115
+ options.merge!(:default => keys, :message => self.message)
116
+ I18n.translate(keys.shift, options)
117
+ end
119
118
 
120
- I18n.t(key, options.merge(:default => defaults, :scope => [:activerecord, :errors]))
119
+ # Return user options with default options.
120
+ #
121
+ def default_options
122
+ options.reverse_merge :scope => [:activerecord, :errors],
123
+ :model => @base.class.human_name,
124
+ :attribute => @base.class.human_attribute_name(attribute.to_s),
125
+ :value => value
121
126
  end
122
127
  end
123
128
 
@@ -134,7 +139,8 @@ module ActiveRecord
134
139
  end
135
140
 
136
141
  def initialize(base) # :nodoc:
137
- @base, @errors = base, {}
142
+ @base = base
143
+ clear
138
144
  end
139
145
 
140
146
  # Adds an error to the base object instead of any particular attribute. This is used
@@ -150,16 +156,10 @@ module ActiveRecord
150
156
  # error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
151
157
  # If no +messsage+ is supplied, :invalid is assumed.
152
158
  # If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error).
153
- # def add(attribute, message = nil, options = {})
154
- # message ||= :invalid
155
- # message = generate_message(attribute, message, options)) if message.is_a?(Symbol)
156
- # @errors[attribute.to_s] ||= []
157
- # @errors[attribute.to_s] << message
158
- # end
159
-
160
- def add(error_or_attr, message = nil, options = {})
161
- error, attribute = error_or_attr.is_a?(Error) ? [error_or_attr, error_or_attr.attribute] : [nil, error_or_attr]
159
+ #
160
+ def add(attribute, message = nil, options = {})
162
161
  options[:message] = options.delete(:default) if options.has_key?(:default)
162
+ error, message = message, nil if message.is_a?(Error)
163
163
 
164
164
  @errors[attribute.to_s] ||= []
165
165
  @errors[attribute.to_s] << (error || Error.new(@base, attribute, message, options))
@@ -283,7 +283,7 @@ module ActiveRecord
283
283
 
284
284
  # Removes all errors that have been added.
285
285
  def clear
286
- @errors = {}
286
+ @errors = ActiveSupport::OrderedHash.new
287
287
  end
288
288
 
289
289
  # Returns the total number of errors added. Two errors added to the same attribute will be counted as such.
@@ -321,7 +321,7 @@ module ActiveRecord
321
321
  end
322
322
 
323
323
  def generate_message(attribute, message = :invalid, options = {})
324
- ActiveSupport::Deprecation.warn("ActiveRecord::Errors#generate_message has been deprecated. Please use ActiveRecord::Error#generate_message.")
324
+ ActiveSupport::Deprecation.warn("ActiveRecord::Errors#generate_message has been deprecated. Please use ActiveRecord::Error.new().to_s.")
325
325
  Error.new(@base, attribute, message, options).to_s
326
326
  end
327
327
  end
@@ -2,7 +2,7 @@ module ActiveRecord
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 2
4
4
  MINOR = 3
5
- TINY = 4
5
+ TINY = 5
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1 +1,2 @@
1
1
  require 'active_record'
2
+ ActiveSupport::Deprecation.warn 'require "activerecord" is deprecated and will be removed in Rails 3. Use require "active_record" instead.'
@@ -651,6 +651,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
651
651
  assert_equal 1, Client.find_all_by_client_of(firm.id).size
652
652
  end
653
653
 
654
+ def test_delete_all_association_with_primary_key_deletes_correct_records
655
+ firm = Firm.find(:first)
656
+ # break the vanilla firm_id foreign key
657
+ assert_equal 2, firm.clients.count
658
+ firm.clients.first.update_attribute(:firm_id, nil)
659
+ assert_equal 1, firm.clients(true).count
660
+ assert_equal 1, firm.clients_using_primary_key_with_delete_all.count
661
+ old_record = firm.clients_using_primary_key_with_delete_all.first
662
+ firm = Firm.find(:first)
663
+ firm.destroy
664
+ assert Client.find_by_id(old_record.id).nil?
665
+ end
654
666
 
655
667
  def test_creation_respects_hash_condition
656
668
  ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.build
@@ -132,6 +132,28 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
132
132
  assert !posts(:welcome).reload.people(true).include?(people(:michael))
133
133
  end
134
134
 
135
+ def test_replace_order_is_preserved
136
+ posts(:welcome).people.clear
137
+ posts(:welcome).people = [people(:david), people(:michael)]
138
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
139
+
140
+ # Test the inverse order in case the first success was a coincidence
141
+ posts(:welcome).people.clear
142
+ posts(:welcome).people = [people(:michael), people(:david)]
143
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
144
+ end
145
+
146
+ def test_replace_by_id_order_is_preserved
147
+ posts(:welcome).people.clear
148
+ posts(:welcome).person_ids = [people(:david).id, people(:michael).id]
149
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
150
+
151
+ # Test the inverse order in case the first success was a coincidence
152
+ posts(:welcome).people.clear
153
+ posts(:welcome).person_ids = [people(:michael).id, people(:david).id]
154
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
155
+ end
156
+
135
157
  def test_associate_with_create
136
158
  assert_queries(1) { posts(:thinking) }
137
159
 
@@ -36,6 +36,15 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
36
36
  assert_equal accounts(:rails_core_account), firm.account_using_primary_key
37
37
  end
38
38
 
39
+ def test_update_with_foreign_and_primary_keys
40
+ firm = companies(:first_firm)
41
+ account = firm.account_using_foreign_and_primary_keys
42
+ assert_equal Account.find_by_firm_name(firm.name), account
43
+ firm.save
44
+ firm.reload
45
+ assert_equal account, firm.account_using_foreign_and_primary_keys
46
+ end
47
+
39
48
  def test_can_marshal_has_one_association_with_nil_target
40
49
  firm = Firm.new
41
50
  assert_nothing_raised do
@@ -306,4 +315,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
306
315
  Firm.find(@firm.id, :include => :account).save!
307
316
  end
308
317
  end
318
+
319
+ def test_build_respects_hash_condition
320
+ account = companies(:first_firm).build_account_limit_500_with_hash_conditions
321
+ assert account.save
322
+ assert_equal 500, account.credit_limit
323
+ end
324
+
325
+ def test_create_respects_hash_condition
326
+ account = companies(:first_firm).create_account_limit_500_with_hash_conditions
327
+ assert !account.new_record?
328
+ assert_equal 500, account.credit_limit
329
+ end
309
330
  end
@@ -436,6 +436,70 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
436
436
  end
437
437
  end
438
438
 
439
+ class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase
440
+ def test_autosave_new_record_on_belongs_to_can_be_disabled_per_relationship
441
+ new_account = Account.new("credit_limit" => 1000)
442
+ new_firm = Firm.new("name" => "some firm")
443
+
444
+ assert new_firm.new_record?
445
+ new_account.firm = new_firm
446
+ new_account.save!
447
+
448
+ assert !new_firm.new_record?
449
+
450
+ new_account = Account.new("credit_limit" => 1000)
451
+ new_autosaved_firm = Firm.new("name" => "some firm")
452
+
453
+ assert new_autosaved_firm.new_record?
454
+ new_account.unautosaved_firm = new_autosaved_firm
455
+ new_account.save!
456
+
457
+ assert new_autosaved_firm.new_record?
458
+ end
459
+
460
+ def test_autosave_new_record_on_has_one_can_be_disabled_per_relationship
461
+ firm = Firm.new("name" => "some firm")
462
+ account = Account.new("credit_limit" => 1000)
463
+
464
+ assert account.new_record?
465
+ firm.account = account
466
+ firm.save!
467
+
468
+ assert !account.new_record?
469
+
470
+ firm = Firm.new("name" => "some firm")
471
+ account = Account.new("credit_limit" => 1000)
472
+
473
+ firm.unautosaved_account = account
474
+
475
+ assert account.new_record?
476
+ firm.unautosaved_account = account
477
+ firm.save!
478
+
479
+ assert account.new_record?
480
+ end
481
+
482
+ def test_autosave_new_record_on_has_many_can_be_disabled_per_relationship
483
+ firm = Firm.new("name" => "some firm")
484
+ account = Account.new("credit_limit" => 1000)
485
+
486
+ assert account.new_record?
487
+ firm.accounts << account
488
+
489
+ firm.save!
490
+ assert !account.new_record?
491
+
492
+ firm = Firm.new("name" => "some firm")
493
+ account = Account.new("credit_limit" => 1000)
494
+
495
+ assert account.new_record?
496
+ firm.unautosaved_accounts << account
497
+
498
+ firm.save!
499
+ assert account.new_record?
500
+ end
501
+ end
502
+
439
503
  class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
440
504
  self.use_transactional_fixtures = false
441
505
 
@@ -473,9 +537,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
473
537
  assert !@pirate.valid?
474
538
 
475
539
  @pirate.ship.mark_for_destruction
540
+ @pirate.ship.expects(:valid?).never
476
541
  assert_difference('Ship.count', -1) { @pirate.save! }
477
542
  end
478
543
 
544
+ def test_a_child_marked_for_destruction_should_not_be_destroyed_twice
545
+ @pirate.ship.mark_for_destruction
546
+ assert @pirate.save
547
+ @pirate.ship.expects(:destroy).never
548
+ assert @pirate.save
549
+ end
550
+
479
551
  def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_child
480
552
  # Stub the save method of the @pirate.ship instance to destroy and then raise an exception
481
553
  class << @pirate.ship
@@ -510,9 +582,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
510
582
  assert !@ship.valid?
511
583
 
512
584
  @ship.pirate.mark_for_destruction
585
+ @ship.pirate.expects(:valid?).never
513
586
  assert_difference('Pirate.count', -1) { @ship.save! }
514
587
  end
515
588
 
589
+ def test_a_parent_marked_for_destruction_should_not_be_destroyed_twice
590
+ @ship.pirate.mark_for_destruction
591
+ assert @ship.save
592
+ @ship.pirate.expects(:destroy).never
593
+ assert @ship.save
594
+ end
595
+
516
596
  def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent
517
597
  # Stub the save method of the @ship.pirate instance to destroy and then raise an exception
518
598
  class << @ship.pirate
@@ -553,9 +633,33 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
553
633
  children.each { |child| child.name = '' }
554
634
  assert !@pirate.valid?
555
635
 
556
- children.each { |child| child.mark_for_destruction }
636
+ children.each do |child|
637
+ child.mark_for_destruction
638
+ child.expects(:valid?).never
639
+ end
557
640
  assert_difference("#{association_name.classify}.count", -2) { @pirate.save! }
558
641
  end
642
+
643
+ define_method("test_should_skip_validation_on_the_#{association_name}_association_if_destroyed") do
644
+ @pirate.send(association_name).create!(:name => "#{association_name}_1")
645
+ children = @pirate.send(association_name)
646
+
647
+ children.each { |child| child.name = '' }
648
+ assert !@pirate.valid?
649
+
650
+ children.each { |child| child.destroy }
651
+ assert @pirate.valid?
652
+ end
653
+
654
+ define_method("test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_#{association_name}") do
655
+ @pirate.send(association_name).create!(:name => "#{association_name}_1")
656
+ children = @pirate.send(association_name)
657
+
658
+ children.each { |child| child.mark_for_destruction }
659
+ assert @pirate.save
660
+ children.each { |child| child.expects(:destroy).never }
661
+ assert @pirate.save
662
+ end
559
663
 
560
664
  define_method("test_should_rollback_destructions_if_an_exception_occurred_while_saving_#{association_name}") do
561
665
  2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
@@ -646,15 +750,15 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
646
750
  def test_should_automatically_validate_the_associated_model
647
751
  @pirate.ship.name = ''
648
752
  assert !@pirate.valid?
649
- assert !@pirate.errors.on(:ship_name).blank?
753
+ assert_equal "can't be blank", @pirate.errors.on(:"ship.name")
650
754
  end
651
755
 
652
756
  def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
653
757
  @pirate.ship.name = nil
654
758
  @pirate.catchphrase = nil
655
759
  assert !@pirate.valid?
656
- assert !@pirate.errors.on(:ship_name).blank?
657
- assert !@pirate.errors.on(:catchphrase).blank?
760
+ assert @pirate.errors.full_messages.include?("Name can't be blank")
761
+ assert @pirate.errors.full_messages.include?("Catchphrase can't be blank")
658
762
  end
659
763
 
660
764
  def test_should_still_allow_to_bypass_validations_on_the_associated_model
@@ -736,15 +840,15 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
736
840
  def test_should_automatically_validate_the_associated_model
737
841
  @ship.pirate.catchphrase = ''
738
842
  assert !@ship.valid?
739
- assert !@ship.errors.on(:pirate_catchphrase).blank?
843
+ assert_equal "can't be blank", @ship.errors.on(:"pirate.catchphrase")
740
844
  end
741
845
 
742
846
  def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid
743
847
  @ship.name = nil
744
848
  @ship.pirate.catchphrase = nil
745
849
  assert !@ship.valid?
746
- assert !@ship.errors.on(:name).blank?
747
- assert !@ship.errors.on(:pirate_catchphrase).blank?
850
+ assert @ship.errors.full_messages.include?("Name can't be blank")
851
+ assert @ship.errors.full_messages.include?("Catchphrase can't be blank")
748
852
  end
749
853
 
750
854
  def test_should_still_allow_to_bypass_validations_on_the_associated_model
@@ -806,7 +910,7 @@ module AutosaveAssociationOnACollectionAssociationTests
806
910
  @pirate.send(@association_name).each { |child| child.name = '' }
807
911
 
808
912
  assert !@pirate.valid?
809
- assert_equal "can't be blank", @pirate.errors.on("#{@association_name}_name")
913
+ assert @pirate.errors.full_messages.include?("Name can't be blank")
810
914
  assert @pirate.errors.on(@association_name).blank?
811
915
  end
812
916
 
@@ -814,7 +918,7 @@ module AutosaveAssociationOnACollectionAssociationTests
814
918
  @pirate.send(@association_name).build(:name => '')
815
919
 
816
920
  assert !@pirate.valid?
817
- assert_equal "can't be blank", @pirate.errors.on("#{@association_name}_name")
921
+ assert_equal "can't be blank", @pirate.errors.on("#{@association_name}.name")
818
922
  assert @pirate.errors.on(@association_name).blank?
819
923
  end
820
924
 
@@ -823,7 +927,7 @@ module AutosaveAssociationOnACollectionAssociationTests
823
927
  @pirate.catchphrase = nil
824
928
 
825
929
  assert !@pirate.valid?
826
- assert_equal "can't be blank", @pirate.errors.on("#{@association_name}_name")
930
+ assert_equal "can't be blank", @pirate.errors.on("#{@association_name}.name")
827
931
  assert !@pirate.errors.on(:catchphrase).blank?
828
932
  end
829
933
 
@@ -920,4 +1024,119 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T
920
1024
  end
921
1025
 
922
1026
  include AutosaveAssociationOnACollectionAssociationTests
923
- end
1027
+ end
1028
+
1029
+ class TestAutosaveAssociationValidationsOnAHasManyAssocication < ActiveRecord::TestCase
1030
+ self.use_transactional_fixtures = false
1031
+
1032
+ def setup
1033
+ @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1034
+ @pirate.birds.create(:name => 'cookoo')
1035
+ end
1036
+
1037
+ test "should automatically validate associations" do
1038
+ assert @pirate.valid?
1039
+ @pirate.birds.each { |bird| bird.name = '' }
1040
+
1041
+ assert !@pirate.valid?
1042
+ end
1043
+ end
1044
+
1045
+ class TestAutosaveAssociationValidationsOnAHasOneAssocication < ActiveRecord::TestCase
1046
+ self.use_transactional_fixtures = false
1047
+
1048
+ def setup
1049
+ @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1050
+ @pirate.create_ship(:name => 'titanic')
1051
+ end
1052
+
1053
+ test "should automatically validate associations with :validate => true" do
1054
+ assert @pirate.valid?
1055
+ @pirate.ship.name = ''
1056
+ assert !@pirate.valid?
1057
+ end
1058
+
1059
+ test "should not automatically validate associations without :validate => true" do
1060
+ assert @pirate.valid?
1061
+ @pirate.non_validated_ship.name = ''
1062
+ assert @pirate.valid?
1063
+ end
1064
+ end
1065
+
1066
+ class TestAutosaveAssociationValidationsOnABelongsToAssocication < ActiveRecord::TestCase
1067
+ self.use_transactional_fixtures = false
1068
+
1069
+ def setup
1070
+ @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1071
+ end
1072
+
1073
+ test "should automatically validate associations with :validate => true" do
1074
+ assert @pirate.valid?
1075
+ @pirate.parrot = Parrot.new(:name => '')
1076
+ assert !@pirate.valid?
1077
+ end
1078
+
1079
+ test "should not automatically validate associations without :validate => true" do
1080
+ assert @pirate.valid?
1081
+ @pirate.non_validated_parrot = Parrot.new(:name => '')
1082
+ assert @pirate.valid?
1083
+ end
1084
+ end
1085
+
1086
+ class TestAutosaveAssociationValidationsOnAHABTMAssocication < ActiveRecord::TestCase
1087
+ self.use_transactional_fixtures = false
1088
+
1089
+ def setup
1090
+ @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
1091
+ end
1092
+
1093
+ test "should automatically validate associations with :validate => true" do
1094
+ assert @pirate.valid?
1095
+ @pirate.parrots = [ Parrot.new(:name => 'popuga') ]
1096
+ @pirate.parrots.each { |parrot| parrot.name = '' }
1097
+ assert !@pirate.valid?
1098
+ end
1099
+
1100
+ test "should not automatically validate associations without :validate => true" do
1101
+ assert @pirate.valid?
1102
+ @pirate.non_validated_parrots = [ Parrot.new(:name => 'popuga') ]
1103
+ @pirate.non_validated_parrots.each { |parrot| parrot.name = '' }
1104
+ assert @pirate.valid?
1105
+ end
1106
+ end
1107
+
1108
+ class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCase
1109
+ self.use_transactional_fixtures = false
1110
+
1111
+ def setup
1112
+ @pirate = Pirate.new
1113
+ end
1114
+
1115
+ test "should generate validation methods for has_many associations" do
1116
+ assert @pirate.respond_to?(:validate_associated_records_for_birds)
1117
+ end
1118
+
1119
+ test "should generate validation methods for has_one associations with :validate => true" do
1120
+ assert @pirate.respond_to?(:validate_associated_records_for_ship)
1121
+ end
1122
+
1123
+ test "should not generate validation methods for has_one associations without :validate => true" do
1124
+ assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_ship)
1125
+ end
1126
+
1127
+ test "should generate validation methods for belongs_to associations with :validate => true" do
1128
+ assert @pirate.respond_to?(:validate_associated_records_for_parrot)
1129
+ end
1130
+
1131
+ test "should not generate validation methods for belongs_to associations without :validate => true" do
1132
+ assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrot)
1133
+ end
1134
+
1135
+ test "should generate validation methods for HABTM associations with :validate => true" do
1136
+ assert @pirate.respond_to?(:validate_associated_records_for_parrots)
1137
+ end
1138
+
1139
+ test "should not generate validation methods for HABTM associations without :validate => true" do
1140
+ assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrots)
1141
+ end
1142
+ end