activerecord 2.2.3 → 2.3.2

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 (120) hide show
  1. data/CHANGELOG +438 -396
  2. data/Rakefile +4 -2
  3. data/lib/active_record.rb +46 -43
  4. data/lib/active_record/association_preload.rb +34 -19
  5. data/lib/active_record/associations.rb +193 -251
  6. data/lib/active_record/associations/association_collection.rb +38 -21
  7. data/lib/active_record/associations/association_proxy.rb +11 -4
  8. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -2
  9. data/lib/active_record/associations/has_many_association.rb +2 -2
  10. data/lib/active_record/associations/has_many_through_association.rb +8 -8
  11. data/lib/active_record/associations/has_one_association.rb +11 -2
  12. data/lib/active_record/attribute_methods.rb +1 -0
  13. data/lib/active_record/autosave_association.rb +349 -0
  14. data/lib/active_record/base.rb +292 -106
  15. data/lib/active_record/batches.rb +73 -0
  16. data/lib/active_record/calculations.rb +34 -16
  17. data/lib/active_record/callbacks.rb +37 -8
  18. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +16 -0
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -0
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +103 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -6
  22. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +28 -25
  23. data/lib/active_record/connection_adapters/abstract_adapter.rb +29 -5
  24. data/lib/active_record/connection_adapters/mysql_adapter.rb +50 -21
  25. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -41
  26. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  27. data/lib/active_record/connection_adapters/sqlite_adapter.rb +41 -21
  28. data/lib/active_record/dirty.rb +1 -1
  29. data/lib/active_record/dynamic_scope_match.rb +25 -0
  30. data/lib/active_record/fixtures.rb +193 -198
  31. data/lib/active_record/locale/en.yml +1 -1
  32. data/lib/active_record/locking/optimistic.rb +33 -0
  33. data/lib/active_record/migration.rb +8 -2
  34. data/lib/active_record/named_scope.rb +13 -6
  35. data/lib/active_record/nested_attributes.rb +329 -0
  36. data/lib/active_record/query_cache.rb +25 -13
  37. data/lib/active_record/reflection.rb +6 -1
  38. data/lib/active_record/schema_dumper.rb +2 -0
  39. data/lib/active_record/serialization.rb +3 -1
  40. data/lib/active_record/serializers/json_serializer.rb +19 -0
  41. data/lib/active_record/serializers/xml_serializer.rb +28 -13
  42. data/lib/active_record/session_store.rb +318 -0
  43. data/lib/active_record/test_case.rb +15 -9
  44. data/lib/active_record/timestamp.rb +2 -2
  45. data/lib/active_record/transactions.rb +58 -8
  46. data/lib/active_record/validations.rb +29 -24
  47. data/lib/active_record/version.rb +2 -2
  48. data/test/cases/ar_schema_test.rb +0 -1
  49. data/test/cases/associations/belongs_to_associations_test.rb +35 -131
  50. data/test/cases/associations/cascaded_eager_loading_test.rb +8 -0
  51. data/test/cases/associations/eager_load_nested_include_test.rb +29 -0
  52. data/test/cases/associations/eager_test.rb +137 -7
  53. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +45 -7
  54. data/test/cases/associations/has_many_associations_test.rb +110 -149
  55. data/test/cases/associations/has_many_through_associations_test.rb +39 -7
  56. data/test/cases/associations/has_one_associations_test.rb +39 -92
  57. data/test/cases/associations/has_one_through_associations_test.rb +34 -3
  58. data/test/cases/associations/inner_join_association_test.rb +0 -5
  59. data/test/cases/associations/join_model_test.rb +5 -7
  60. data/test/cases/attribute_methods_test.rb +13 -1
  61. data/test/cases/autosave_association_test.rb +901 -0
  62. data/test/cases/base_test.rb +41 -21
  63. data/test/cases/batches_test.rb +61 -0
  64. data/test/cases/calculations_test.rb +37 -17
  65. data/test/cases/callbacks_test.rb +43 -5
  66. data/test/cases/connection_pool_test.rb +25 -0
  67. data/test/cases/copy_table_test_sqlite.rb +11 -0
  68. data/test/cases/datatype_test_postgresql.rb +1 -0
  69. data/test/cases/defaults_test.rb +37 -26
  70. data/test/cases/dirty_test.rb +26 -2
  71. data/test/cases/finder_test.rb +79 -44
  72. data/test/cases/fixtures_test.rb +15 -19
  73. data/test/cases/helper.rb +26 -19
  74. data/test/cases/inheritance_test.rb +2 -2
  75. data/test/cases/json_serialization_test.rb +1 -1
  76. data/test/cases/locking_test.rb +23 -5
  77. data/test/cases/method_scoping_test.rb +126 -3
  78. data/test/cases/migration_test.rb +253 -237
  79. data/test/cases/named_scope_test.rb +73 -3
  80. data/test/cases/nested_attributes_test.rb +509 -0
  81. data/test/cases/query_cache_test.rb +0 -4
  82. data/test/cases/reflection_test.rb +13 -3
  83. data/test/cases/reload_models_test.rb +3 -1
  84. data/test/cases/repair_helper.rb +50 -0
  85. data/test/cases/schema_dumper_test.rb +0 -1
  86. data/test/cases/transactions_test.rb +177 -12
  87. data/test/cases/validations_i18n_test.rb +288 -294
  88. data/test/cases/validations_test.rb +230 -180
  89. data/test/cases/xml_serialization_test.rb +19 -1
  90. data/test/fixtures/fixture_database.sqlite3 +0 -0
  91. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  92. data/test/fixtures/member_types.yml +6 -0
  93. data/test/fixtures/members.yml +3 -1
  94. data/test/fixtures/people.yml +10 -1
  95. data/test/fixtures/toys.yml +4 -0
  96. data/test/models/author.rb +1 -2
  97. data/test/models/bird.rb +3 -0
  98. data/test/models/category.rb +1 -0
  99. data/test/models/company.rb +3 -0
  100. data/test/models/developer.rb +12 -0
  101. data/test/models/event.rb +3 -0
  102. data/test/models/member.rb +1 -0
  103. data/test/models/member_detail.rb +1 -0
  104. data/test/models/member_type.rb +3 -0
  105. data/test/models/owner.rb +2 -1
  106. data/test/models/parrot.rb +2 -0
  107. data/test/models/person.rb +6 -0
  108. data/test/models/pet.rb +2 -1
  109. data/test/models/pirate.rb +55 -1
  110. data/test/models/post.rb +6 -0
  111. data/test/models/project.rb +1 -0
  112. data/test/models/reply.rb +6 -0
  113. data/test/models/ship.rb +8 -1
  114. data/test/models/ship_part.rb +5 -0
  115. data/test/models/topic.rb +13 -1
  116. data/test/models/toy.rb +4 -0
  117. data/test/schema/schema.rb +35 -2
  118. metadata +70 -9
  119. data/test/fixtures/fixture_database.sqlite +0 -0
  120. data/test/fixtures/fixture_database_2.sqlite +0 -0
@@ -1,15 +1,7 @@
1
1
  require "active_support/test_case"
2
2
 
3
- module ActiveRecord
3
+ module ActiveRecord
4
4
  class TestCase < ActiveSupport::TestCase #:nodoc:
5
- self.fixture_path = FIXTURES_ROOT
6
- self.use_instantiated_fixtures = false
7
- self.use_transactional_fixtures = true
8
-
9
- def create_fixtures(*table_names, &block)
10
- Fixtures.create_fixtures(FIXTURES_ROOT, table_names, {}, &block)
11
- end
12
-
13
5
  def assert_date_from_db(expected, actual, message = nil)
14
6
  # SybaseAdapter doesn't have a separate column type just for dates,
15
7
  # so the time is in the string and incorrectly formatted
@@ -35,6 +27,7 @@ module ActiveRecord
35
27
  $queries_executed = []
36
28
  yield
37
29
  ensure
30
+ %w{ BEGIN COMMIT }.each { |x| $queries_executed.delete(x) }
38
31
  assert_equal num, $queries_executed.size, "#{$queries_executed.size} instead of #{num} queries were executed.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
39
32
  end
40
33
 
@@ -56,5 +49,18 @@ module ActiveRecord
56
49
  ActiveRecord::Base.clear_all_connections!
57
50
  ActiveRecord::Base.establish_connection(@connection)
58
51
  end
52
+
53
+ def with_kcode(kcode)
54
+ if RUBY_VERSION < '1.9'
55
+ orig_kcode, $KCODE = $KCODE, kcode
56
+ begin
57
+ yield
58
+ ensure
59
+ $KCODE = orig_kcode
60
+ end
61
+ else
62
+ yield
63
+ end
64
+ end
59
65
  end
60
66
  end
@@ -23,8 +23,8 @@ module ActiveRecord
23
23
  write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil?
24
24
  write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil?
25
25
 
26
- write_attribute('updated_at', t) if respond_to?(:updated_at)
27
- write_attribute('updated_on', t) if respond_to?(:updated_on)
26
+ write_attribute('updated_at', t) if respond_to?(:updated_at) && updated_at.nil?
27
+ write_attribute('updated_on', t) if respond_to?(:updated_on) && updated_on.nil?
28
28
  end
29
29
  create_without_timestamps
30
30
  end
@@ -120,16 +120,66 @@ module ActiveRecord
120
120
  # end
121
121
  #
122
122
  # One should restart the entire transaction if a StatementError occurred.
123
+ #
124
+ # == Nested transactions
125
+ #
126
+ # #transaction calls can be nested. By default, this makes all database
127
+ # statements in the nested transaction block become part of the parent
128
+ # transaction. For example:
129
+ #
130
+ # User.transaction do
131
+ # User.create(:username => 'Kotori')
132
+ # User.transaction do
133
+ # User.create(:username => 'Nemu')
134
+ # raise ActiveRecord::Rollback
135
+ # end
136
+ # end
137
+ #
138
+ # User.find(:all) # => empty
139
+ #
140
+ # It is also possible to requires a sub-transaction by passing
141
+ # <tt>:requires_new => true</tt>. If anything goes wrong, the
142
+ # database rolls back to the beginning of the sub-transaction
143
+ # without rolling back the parent transaction. For example:
144
+ #
145
+ # User.transaction do
146
+ # User.create(:username => 'Kotori')
147
+ # User.transaction(:requires_new => true) do
148
+ # User.create(:username => 'Nemu')
149
+ # raise ActiveRecord::Rollback
150
+ # end
151
+ # end
152
+ #
153
+ # User.find(:all) # => Returns only Kotori
154
+ #
155
+ # Most databases don't support true nested transactions. At the time of
156
+ # writing, the only database that we're aware of that supports true nested
157
+ # transactions, is MS-SQL. Because of this, Active Record emulates nested
158
+ # transactions by using savepoints. See
159
+ # http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
160
+ # for more information about savepoints.
161
+ #
162
+ # === Caveats
163
+ #
164
+ # If you're on MySQL, then do not use DDL operations in nested transactions
165
+ # blocks that are emulated with savepoints. That is, do not execute statements
166
+ # like 'CREATE TABLE' inside such blocks. This is because MySQL automatically
167
+ # releases all savepoints upon executing a DDL operation. When #transaction
168
+ # is finished and tries to release the savepoint it created earlier, a
169
+ # database error will occur because the savepoint has already been
170
+ # automatically released. The following example demonstrates the problem:
171
+ #
172
+ # Model.connection.transaction do # BEGIN
173
+ # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
174
+ # Model.connection.create_table(...) # active_record_1 now automatically released
175
+ # end # RELEASE savepoint active_record_1
176
+ # # ^^^^ BOOM! database error!
177
+ # end
123
178
  module ClassMethods
124
179
  # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
125
- def transaction(&block)
126
- connection.increment_open_transactions
127
-
128
- begin
129
- connection.transaction(connection.open_transactions == 1, &block)
130
- ensure
131
- connection.decrement_open_transactions
132
- end
180
+ def transaction(options = {}, &block)
181
+ # See the ConnectionAdapters::DatabaseStatements#transaction API docs.
182
+ connection.transaction(options, &block)
133
183
  end
134
184
  end
135
185
 
@@ -89,7 +89,7 @@ module ActiveRecord
89
89
 
90
90
  message, options[:default] = options[:default], message if options[:default].is_a?(Symbol)
91
91
 
92
- defaults = @base.class.self_and_descendents_from_active_record.map do |klass|
92
+ defaults = @base.class.self_and_descendants_from_active_record.map do |klass|
93
93
  [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
94
94
  :"models.#{klass.name.underscore}.#{message}" ]
95
95
  end
@@ -203,9 +203,8 @@ module ActiveRecord
203
203
  if attr == "base"
204
204
  full_messages << message
205
205
  else
206
- #key = :"activerecord.att.#{@base.class.name.underscore.to_sym}.#{attr}"
207
206
  attr_name = @base.class.human_attribute_name(attr)
208
- full_messages << attr_name + ' ' + message
207
+ full_messages << attr_name + I18n.t('activerecord.errors.format.separator', :default => ' ') + message
209
208
  end
210
209
  end
211
210
  end
@@ -494,18 +493,20 @@ module ActiveRecord
494
493
  # The first_name attribute must be in the object and it cannot be blank.
495
494
  #
496
495
  # If you want to validate the presence of a boolean field (where the real values are true and false),
497
- # you will want to use validates_inclusion_of :field_name, :in => [true, false]
498
- # This is due to the way Object#blank? handles boolean values. false.blank? # => true
496
+ # you will want to use <tt>validates_inclusion_of :field_name, :in => [true, false]</tt>.
497
+ #
498
+ # This is due to the way Object#blank? handles boolean values: <tt>false.blank? # => true</tt>.
499
499
  #
500
500
  # Configuration options:
501
501
  # * <tt>message</tt> - A custom error message (default is: "can't be blank").
502
- # * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
502
+ # * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>,
503
+ # <tt>:update</tt>).
503
504
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
504
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
505
- # method, proc or string should return or evaluate to a true or false value.
505
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
506
+ # The method, proc or string should return or evaluate to a true or false value.
506
507
  # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
507
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
508
- # method, proc or string should return or evaluate to a true or false value.
508
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
509
+ # The method, proc or string should return or evaluate to a true or false value.
509
510
  #
510
511
  def validates_presence_of(*attr_names)
511
512
  configuration = { :on => :save }
@@ -574,6 +575,8 @@ module ActiveRecord
574
575
  # Get range option and value.
575
576
  option = range_options.first
576
577
  option_value = options[range_options.first]
578
+ key = {:is => :wrong_length, :minimum => :too_short, :maximum => :too_long}[option]
579
+ custom_message = options[:message] || options[key]
577
580
 
578
581
  case option
579
582
  when :within, :in
@@ -582,9 +585,9 @@ module ActiveRecord
582
585
  validates_each(attrs, options) do |record, attr, value|
583
586
  value = options[:tokenizer].call(value) if value.kind_of?(String)
584
587
  if value.nil? or value.size < option_value.begin
585
- record.errors.add(attr, :too_short, :default => options[:too_short], :count => option_value.begin)
588
+ record.errors.add(attr, :too_short, :default => custom_message || options[:too_short], :count => option_value.begin)
586
589
  elsif value.size > option_value.end
587
- record.errors.add(attr, :too_long, :default => options[:too_long], :count => option_value.end)
590
+ record.errors.add(attr, :too_long, :default => custom_message || options[:too_long], :count => option_value.end)
588
591
  end
589
592
  end
590
593
  when :is, :minimum, :maximum
@@ -592,13 +595,10 @@ module ActiveRecord
592
595
 
593
596
  # Declare different validations per option.
594
597
  validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" }
595
- message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }
596
598
 
597
599
  validates_each(attrs, options) do |record, attr, value|
598
600
  value = options[:tokenizer].call(value) if value.kind_of?(String)
599
601
  unless !value.nil? and value.size.method(validity_checks[option])[option_value]
600
- key = message_options[option]
601
- custom_message = options[:message] || options[key]
602
602
  record.errors.add(attr, key, :default => custom_message, :count => option_value)
603
603
  end
604
604
  end
@@ -720,20 +720,20 @@ module ActiveRecord
720
720
  # class (which has a database table to query from).
721
721
  finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
722
722
 
723
- is_text_column = finder_class.columns_hash[attr_name.to_s].text?
723
+ column = finder_class.columns_hash[attr_name.to_s]
724
724
 
725
725
  if value.nil?
726
726
  comparison_operator = "IS ?"
727
- elsif is_text_column
727
+ elsif column.text?
728
728
  comparison_operator = "#{connection.case_sensitive_equality_operator} ?"
729
- value = value.to_s
729
+ value = column.limit ? value.to_s[0, column.limit] : value.to_s
730
730
  else
731
731
  comparison_operator = "= ?"
732
732
  end
733
733
 
734
734
  sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}"
735
735
 
736
- if value.nil? || (configuration[:case_sensitive] || !is_text_column)
736
+ if value.nil? || (configuration[:case_sensitive] || !column.text?)
737
737
  condition_sql = "#{sql_attribute} #{comparison_operator}"
738
738
  condition_params = [value]
739
739
  else
@@ -744,7 +744,7 @@ module ActiveRecord
744
744
  if scope = configuration[:scope]
745
745
  Array(scope).map do |scope_item|
746
746
  scope_value = record.send(scope_item)
747
- condition_sql << " AND #{record.class.quoted_table_name}.#{scope_item} #{attribute_condition(scope_value)}"
747
+ condition_sql << " AND " << attribute_condition("#{record.class.quoted_table_name}.#{scope_item}", scope_value)
748
748
  condition_params << scope_value
749
749
  end
750
750
  end
@@ -903,7 +903,7 @@ module ActiveRecord
903
903
  configuration.update(attr_names.extract_options!)
904
904
 
905
905
  validates_each(attr_names, configuration) do |record, attr_name, value|
906
- unless (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v }
906
+ unless (value.is_a?(Array) ? value : [value]).collect { |r| r.nil? || r.valid? }.all?
907
907
  record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
908
908
  end
909
909
  end
@@ -1040,6 +1040,11 @@ module ActiveRecord
1040
1040
  errors.empty?
1041
1041
  end
1042
1042
 
1043
+ # Performs the opposite of <tt>valid?</tt>. Returns true if errors were added, false otherwise.
1044
+ def invalid?
1045
+ !valid?
1046
+ end
1047
+
1043
1048
  # Returns the Errors object that holds all information about attribute error messages.
1044
1049
  def errors
1045
1050
  @errors ||= Errors.new(self)
@@ -1047,15 +1052,15 @@ module ActiveRecord
1047
1052
 
1048
1053
  protected
1049
1054
  # Overwrite this method for validation checks on all saves and use <tt>Errors.add(field, msg)</tt> for invalid attributes.
1050
- def validate #:doc:
1055
+ def validate
1051
1056
  end
1052
1057
 
1053
1058
  # Overwrite this method for validation checks used only on creation.
1054
- def validate_on_create #:doc:
1059
+ def validate_on_create
1055
1060
  end
1056
1061
 
1057
1062
  # Overwrite this method for validation checks used only on updates.
1058
- def validate_on_update # :doc:
1063
+ def validate_on_update
1059
1064
  end
1060
1065
  end
1061
1066
  end
@@ -1,8 +1,8 @@
1
1
  module ActiveRecord
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 2
4
- MINOR = 2
5
- TINY = 3
4
+ MINOR = 3
5
+ TINY = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,5 +1,4 @@
1
1
  require "cases/helper"
2
- require 'active_record/schema'
3
2
 
4
3
  if ActiveRecord::Base.connection.supports_migrations?
5
4
 
@@ -154,6 +154,23 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
154
154
  assert_equal 0, Topic.find(t2.id).replies.size
155
155
  end
156
156
 
157
+ def test_belongs_to_reassign_with_namespaced_models_and_counters
158
+ t1 = Web::Topic.create("title" => "t1")
159
+ t2 = Web::Topic.create("title" => "t2")
160
+ r1 = Web::Reply.new("title" => "r1", "content" => "r1")
161
+ r1.topic = t1
162
+
163
+ assert r1.save
164
+ assert_equal 1, Web::Topic.find(t1.id).replies.size
165
+ assert_equal 0, Web::Topic.find(t2.id).replies.size
166
+
167
+ r1.topic = Web::Topic.find(t2.id)
168
+
169
+ assert r1.save
170
+ assert_equal 0, Web::Topic.find(t1.id).replies.size
171
+ assert_equal 1, Web::Topic.find(t2.id).replies.size
172
+ end
173
+
157
174
  def test_belongs_to_counter_after_save
158
175
  topic = Topic.create!(:title => "monday night")
159
176
  topic.replies.create!(:title => "re: monday night", :content => "football")
@@ -190,19 +207,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
190
207
  assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count")
191
208
  end
192
209
 
193
- def test_assignment_before_parent_saved
194
- client = Client.find(:first)
195
- apple = Firm.new("name" => "Apple")
196
- client.firm = apple
197
- assert_equal apple, client.firm
198
- assert apple.new_record?
199
- assert client.save
200
- assert apple.save
201
- assert !apple.new_record?
202
- assert_equal apple, client.firm
203
- assert_equal apple, client.firm(true)
204
- end
205
-
206
210
  def test_assignment_before_child_saved
207
211
  final_cut = Client.new("name" => "Final Cut")
208
212
  firm = Firm.find(1)
@@ -215,19 +219,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
215
219
  assert_equal firm, final_cut.firm(true)
216
220
  end
217
221
 
218
- def test_assignment_before_either_saved
219
- final_cut = Client.new("name" => "Final Cut")
220
- apple = Firm.new("name" => "Apple")
221
- final_cut.firm = apple
222
- assert final_cut.new_record?
223
- assert apple.new_record?
224
- assert final_cut.save
225
- assert !final_cut.new_record?
226
- assert !apple.new_record?
227
- assert_equal apple, final_cut.firm
228
- assert_equal apple, final_cut.firm(true)
229
- end
230
-
231
222
  def test_new_record_with_foreign_key_but_no_object
232
223
  c = Client.new("firm_id" => 1)
233
224
  assert_equal Firm.find(:first), c.firm_with_basic_id
@@ -274,90 +265,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
274
265
  assert_equal 17, reply.replies.size
275
266
  end
276
267
 
277
- def test_store_two_association_with_one_save
278
- num_orders = Order.count
279
- num_customers = Customer.count
280
- order = Order.new
281
-
282
- customer1 = order.billing = Customer.new
283
- customer2 = order.shipping = Customer.new
284
- assert order.save
285
- assert_equal customer1, order.billing
286
- assert_equal customer2, order.shipping
287
-
288
- order.reload
289
-
290
- assert_equal customer1, order.billing
291
- assert_equal customer2, order.shipping
292
-
293
- assert_equal num_orders +1, Order.count
294
- assert_equal num_customers +2, Customer.count
295
- end
296
-
297
-
298
- def test_store_association_in_two_relations_with_one_save
299
- num_orders = Order.count
300
- num_customers = Customer.count
301
- order = Order.new
302
-
303
- customer = order.billing = order.shipping = Customer.new
304
- assert order.save
305
- assert_equal customer, order.billing
306
- assert_equal customer, order.shipping
307
-
308
- order.reload
309
-
310
- assert_equal customer, order.billing
311
- assert_equal customer, order.shipping
312
-
313
- assert_equal num_orders +1, Order.count
314
- assert_equal num_customers +1, Customer.count
315
- end
316
-
317
- def test_store_association_in_two_relations_with_one_save_in_existing_object
318
- num_orders = Order.count
319
- num_customers = Customer.count
320
- order = Order.create
321
-
322
- customer = order.billing = order.shipping = Customer.new
323
- assert order.save
324
- assert_equal customer, order.billing
325
- assert_equal customer, order.shipping
326
-
327
- order.reload
328
-
329
- assert_equal customer, order.billing
330
- assert_equal customer, order.shipping
331
-
332
- assert_equal num_orders +1, Order.count
333
- assert_equal num_customers +1, Customer.count
334
- end
335
-
336
- def test_store_association_in_two_relations_with_one_save_in_existing_object_with_values
337
- num_orders = Order.count
338
- num_customers = Customer.count
339
- order = Order.create
340
-
341
- customer = order.billing = order.shipping = Customer.new
342
- assert order.save
343
- assert_equal customer, order.billing
344
- assert_equal customer, order.shipping
345
-
346
- order.reload
347
-
348
- customer = order.billing = order.shipping = Customer.new
349
-
350
- assert order.save
351
- order.reload
352
-
353
- assert_equal customer, order.billing
354
- assert_equal customer, order.shipping
355
-
356
- assert_equal num_orders +1, Order.count
357
- assert_equal num_customers +2, Customer.count
358
- end
359
-
360
-
361
268
  def test_association_assignment_sticks
362
269
  post = Post.find(:first)
363
270
 
@@ -410,32 +317,29 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
410
317
  assert_equal nil, sponsor.sponsorable_id
411
318
  end
412
319
 
413
- def test_save_fails_for_invalid_belongs_to
414
- assert log = AuditLog.create(:developer_id=>0,:message=>"")
415
-
416
- log.developer = Developer.new
417
- assert !log.developer.valid?
418
- assert !log.valid?
419
- assert !log.save
420
- assert_equal "is invalid", log.errors.on("developer")
421
- end
422
-
423
- def test_save_succeeds_for_invalid_belongs_to_with_validate_false
424
- assert log = AuditLog.create(:developer_id=>0,:message=>"")
425
-
426
- log.unvalidated_developer = Developer.new
427
- assert !log.unvalidated_developer.valid?
428
- assert log.valid?
429
- assert log.save
430
- end
431
-
432
320
  def test_belongs_to_proxy_should_not_respond_to_private_methods
433
- assert_raises(NoMethodError) { companies(:first_firm).private_method }
434
- assert_raises(NoMethodError) { companies(:second_client).firm.private_method }
321
+ assert_raise(NoMethodError) { companies(:first_firm).private_method }
322
+ assert_raise(NoMethodError) { companies(:second_client).firm.private_method }
435
323
  end
436
324
 
437
325
  def test_belongs_to_proxy_should_respond_to_private_methods_via_send
438
326
  companies(:first_firm).send(:private_method)
439
327
  companies(:second_client).firm.send(:private_method)
440
328
  end
329
+
330
+ def test_save_of_record_with_loaded_belongs_to
331
+ @account = companies(:first_firm).account
332
+
333
+ assert_nothing_raised do
334
+ Account.find(@account.id).save!
335
+ Account.find(@account.id, :include => :firm).save!
336
+ end
337
+
338
+ @account.firm.delete
339
+
340
+ assert_nothing_raised do
341
+ Account.find(@account.id).save!
342
+ Account.find(@account.id, :include => :firm).save!
343
+ end
344
+ end
441
345
  end