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.
- data/CHANGELOG +438 -396
- data/Rakefile +4 -2
- data/lib/active_record.rb +46 -43
- data/lib/active_record/association_preload.rb +34 -19
- data/lib/active_record/associations.rb +193 -251
- data/lib/active_record/associations/association_collection.rb +38 -21
- data/lib/active_record/associations/association_proxy.rb +11 -4
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -2
- data/lib/active_record/associations/has_many_association.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +8 -8
- data/lib/active_record/associations/has_one_association.rb +11 -2
- data/lib/active_record/attribute_methods.rb +1 -0
- data/lib/active_record/autosave_association.rb +349 -0
- data/lib/active_record/base.rb +292 -106
- data/lib/active_record/batches.rb +73 -0
- data/lib/active_record/calculations.rb +34 -16
- data/lib/active_record/callbacks.rb +37 -8
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +16 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +103 -15
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +28 -25
- data/lib/active_record/connection_adapters/abstract_adapter.rb +29 -5
- data/lib/active_record/connection_adapters/mysql_adapter.rb +50 -21
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -41
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +41 -21
- data/lib/active_record/dirty.rb +1 -1
- data/lib/active_record/dynamic_scope_match.rb +25 -0
- data/lib/active_record/fixtures.rb +193 -198
- data/lib/active_record/locale/en.yml +1 -1
- data/lib/active_record/locking/optimistic.rb +33 -0
- data/lib/active_record/migration.rb +8 -2
- data/lib/active_record/named_scope.rb +13 -6
- data/lib/active_record/nested_attributes.rb +329 -0
- data/lib/active_record/query_cache.rb +25 -13
- data/lib/active_record/reflection.rb +6 -1
- data/lib/active_record/schema_dumper.rb +2 -0
- data/lib/active_record/serialization.rb +3 -1
- data/lib/active_record/serializers/json_serializer.rb +19 -0
- data/lib/active_record/serializers/xml_serializer.rb +28 -13
- data/lib/active_record/session_store.rb +318 -0
- data/lib/active_record/test_case.rb +15 -9
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/transactions.rb +58 -8
- data/lib/active_record/validations.rb +29 -24
- data/lib/active_record/version.rb +2 -2
- data/test/cases/ar_schema_test.rb +0 -1
- data/test/cases/associations/belongs_to_associations_test.rb +35 -131
- data/test/cases/associations/cascaded_eager_loading_test.rb +8 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +29 -0
- data/test/cases/associations/eager_test.rb +137 -7
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +45 -7
- data/test/cases/associations/has_many_associations_test.rb +110 -149
- data/test/cases/associations/has_many_through_associations_test.rb +39 -7
- data/test/cases/associations/has_one_associations_test.rb +39 -92
- data/test/cases/associations/has_one_through_associations_test.rb +34 -3
- data/test/cases/associations/inner_join_association_test.rb +0 -5
- data/test/cases/associations/join_model_test.rb +5 -7
- data/test/cases/attribute_methods_test.rb +13 -1
- data/test/cases/autosave_association_test.rb +901 -0
- data/test/cases/base_test.rb +41 -21
- data/test/cases/batches_test.rb +61 -0
- data/test/cases/calculations_test.rb +37 -17
- data/test/cases/callbacks_test.rb +43 -5
- data/test/cases/connection_pool_test.rb +25 -0
- data/test/cases/copy_table_test_sqlite.rb +11 -0
- data/test/cases/datatype_test_postgresql.rb +1 -0
- data/test/cases/defaults_test.rb +37 -26
- data/test/cases/dirty_test.rb +26 -2
- data/test/cases/finder_test.rb +79 -44
- data/test/cases/fixtures_test.rb +15 -19
- data/test/cases/helper.rb +26 -19
- data/test/cases/inheritance_test.rb +2 -2
- data/test/cases/json_serialization_test.rb +1 -1
- data/test/cases/locking_test.rb +23 -5
- data/test/cases/method_scoping_test.rb +126 -3
- data/test/cases/migration_test.rb +253 -237
- data/test/cases/named_scope_test.rb +73 -3
- data/test/cases/nested_attributes_test.rb +509 -0
- data/test/cases/query_cache_test.rb +0 -4
- data/test/cases/reflection_test.rb +13 -3
- data/test/cases/reload_models_test.rb +3 -1
- data/test/cases/repair_helper.rb +50 -0
- data/test/cases/schema_dumper_test.rb +0 -1
- data/test/cases/transactions_test.rb +177 -12
- data/test/cases/validations_i18n_test.rb +288 -294
- data/test/cases/validations_test.rb +230 -180
- data/test/cases/xml_serialization_test.rb +19 -1
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/member_types.yml +6 -0
- data/test/fixtures/members.yml +3 -1
- data/test/fixtures/people.yml +10 -1
- data/test/fixtures/toys.yml +4 -0
- data/test/models/author.rb +1 -2
- data/test/models/bird.rb +3 -0
- data/test/models/category.rb +1 -0
- data/test/models/company.rb +3 -0
- data/test/models/developer.rb +12 -0
- data/test/models/event.rb +3 -0
- data/test/models/member.rb +1 -0
- data/test/models/member_detail.rb +1 -0
- data/test/models/member_type.rb +3 -0
- data/test/models/owner.rb +2 -1
- data/test/models/parrot.rb +2 -0
- data/test/models/person.rb +6 -0
- data/test/models/pet.rb +2 -1
- data/test/models/pirate.rb +55 -1
- data/test/models/post.rb +6 -0
- data/test/models/project.rb +1 -0
- data/test/models/reply.rb +6 -0
- data/test/models/ship.rb +8 -1
- data/test/models/ship_part.rb +5 -0
- data/test/models/topic.rb +13 -1
- data/test/models/toy.rb +4 -0
- data/test/schema/schema.rb +35 -2
- metadata +70 -9
- data/test/fixtures/fixture_database.sqlite +0 -0
- 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
|
-
|
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.
|
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
|
-
#
|
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>,
|
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.
|
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.
|
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
|
-
|
723
|
+
column = finder_class.columns_hash[attr_name.to_s]
|
724
724
|
|
725
725
|
if value.nil?
|
726
726
|
comparison_operator = "IS ?"
|
727
|
-
elsif
|
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] || !
|
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}
|
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]).
|
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
|
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
|
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
|
1063
|
+
def validate_on_update
|
1059
1064
|
end
|
1060
1065
|
end
|
1061
1066
|
end
|
@@ -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
|
-
|
434
|
-
|
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
|