Permalink
Browse files

Fixed that the belongs_to and has_one proxy would fail a test like 'i…

…f project.manager' -- this unfortunately also means that you can't call methods like project.manager.build unless there already is a manager on the project #492 [Tim Bates]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@456 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 652f1ef commit bce0e1493024ccfe78efc93da14740a8d503cb7c @dhh dhh committed Jan 18, 2005
View
@@ -1,5 +1,7 @@
*SVN*
+* Fixed that the belongs_to and has_one proxy would fail a test like 'if project.manager' -- this unfortunately also means that you can't call methods like project.manager.build unless there already is a manager on the project #492 [Tim Bates]
+
* Fixed that the Ruby/MySQL adapter wouldn't connect if the password was empty #503 [Pelle]
@@ -228,7 +228,7 @@ def has_many(association_id, options = {})
add_multiple_associated_save_callbacks(association_name)
- association_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, HasManyAssociation)
+ collection_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, HasManyAssociation)
# deprecated api
deprecated_collection_count_method(association_name)
@@ -289,7 +289,8 @@ def has_one(association_id, options = {})
module_eval do
after_save <<-EOF
association = instance_variable_get("@#{association_name}")
- if (true or @new_record_before_save) and association.respond_to?(:loaded?) and not association.nil?
+ loaded = instance_variable_get("@#{association_name}_loaded")
+ if loaded and not association.nil?
association["#{association_class_primary_key_name}"] = id
association.save(true)
association.send(:construct_sql)
@@ -364,7 +365,8 @@ def belongs_to(association_id, options = {})
module_eval do
before_save <<-EOF
association = instance_variable_get("@#{association_name}")
- if association.respond_to?(:loaded?) and not association.nil? and association.new_record?
+ loaded = instance_variable_get("@#{association_name}_loaded")
+ if loaded and not association.nil? and association.new_record?
association.save(true)
self["#{association_class_primary_key_name}"] = association.id
association.send(:construct_sql)
@@ -469,7 +471,7 @@ def has_and_belongs_to_many(association_id, options = {})
add_multiple_associated_save_callbacks(association_name)
- association_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, HasAndBelongsToManyAssociation)
+ collection_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, HasAndBelongsToManyAssociation)
before_destroy_sql = "DELETE FROM #{options[:join_table]} WHERE #{association_class_primary_key_name} = \\\#{self.quoted_id}"
module_eval(%{before_destroy "self.connection.delete(%{#{before_destroy_sql}})"}) # "
@@ -514,6 +516,49 @@ def associate_identification(association_id, association_class_name, foreign_key
def association_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, association_proxy_class)
define_method(association_name) do |*params|
force_reload = params.first unless params.empty?
+ loaded = instance_variable_get("@#{association_name}_loaded")
+ if loaded and not force_reload
+ association = instance_variable_get("@#{association_name}")
+ else
+ association = association_proxy_class.new(self,
+ association_name, association_class_name,
+ association_class_primary_key_name, options)
+ retval = association.reload
+ unless retval.nil?
+ instance_variable_set("@#{association_name}", association)
+ instance_variable_set("@#{association_name}_loaded", true)
+ else
+ instance_variable_set("@#{association_name}", nil)
+ instance_variable_set("@#{association_name}_loaded", nil)
+ return nil
+ end
+ end
+ association
+ end
+
+ define_method("#{association_name}=") do |new_value|
+ loaded = instance_variable_get("@#{association_name}_loaded")
+ association = instance_variable_get("@#{association_name}")
+ unless loaded and association
+ association = association_proxy_class.new(self,
+ association_name, association_class_name,
+ association_class_primary_key_name, options)
+ end
+ association.replace(new_value)
+ unless new_value.nil?
+ instance_variable_set("@#{association_name}", association)
+ instance_variable_set("@#{association_name}_loaded", true)
+ else
+ instance_variable_set("@#{association_name}", nil)
+ instance_variable_set("@#{association_name}_loaded", nil)
+ end
+ association
+ end
+ end
+
+ def collection_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, association_proxy_class)
+ define_method(association_name) do |*params|
+ force_reload = params.first unless params.empty?
association = instance_variable_get("@#{association_name}")
unless association.respond_to?(:loaded?)
association = association_proxy_class.new(self,
@@ -11,10 +11,6 @@ def reset
@loaded = false
end
- def reload
- reset
- end
-
# Add +records+ to this association. Returns +self+ so method calls may be chained.
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
def <<(*records)
@@ -14,6 +14,11 @@ def initialize(owner, association_name, association_class_name, association_clas
reset
end
+ def reload
+ reset
+ load_target
+ end
+
def method_missing(symbol, *args, &block)
load_target
@target.send(symbol, *args, &block)
@@ -7,11 +7,6 @@ def reset
@loaded = false
end
- def reload
- reset
- load_target
- end
-
def create(attributes = {})
record = build(attributes)
record.save
@@ -34,12 +29,8 @@ def replace(obj, dont_save = false)
@owner[@association_class_primary_key_name] = obj.id unless obj.new_record?
end
@loaded = true
- end
- # Ugly workaround - .nil? is done in C and the method_missing trick doesn't work when we pretend to be nil
- def nil?
- load_target
- @target.nil?
+ return (@target.nil? ? nil : self)
end
private
@@ -65,10 +56,3 @@ def construct_sql
end
end
end
-
-class NilClass #:nodoc:
- # Ugly workaround - nil comparison is usually done in C and so a proxy object pretending to be nil doesn't work.
- def ==(other)
- other.nil?
- end
-end
@@ -25,9 +25,9 @@ def replace(obj, dont_save = false)
@loaded = true
unless @owner.new_record? or obj.nil? or dont_save
- return (obj.save ? obj : false)
+ return (obj.save ? self : false)
else
- return obj
+ return (obj.nil? ? nil : self)
end
end
@@ -105,7 +105,7 @@ def test_build
firm = Firm.new("name" => "GlobalMegaCorp")
firm.save
- account = firm.account.build("credit_limit" => 1000)
+ firm.account = account = Account.new("credit_limit" => 1000)
assert_equal account, firm.account
assert account.save
assert_equal account, firm.account
@@ -125,7 +125,7 @@ def test_build_before_child_saved
def test_build_before_either_saved
firm = Firm.new("name" => "GlobalMegaCorp")
- account = firm.account.build("credit_limit" => 1000)
+ firm.account = account = Account.new("credit_limit" => 1000)
assert_equal account, firm.account
assert account.new_record?
assert firm.save
@@ -137,7 +137,7 @@ def test_failing_build_association
firm = Firm.new("name" => "GlobalMegaCorp")
firm.save
- account = firm.account.build
+ firm.account = account = Account.new
assert_equal account, firm.account
assert !account.save
assert_equal account, firm.account
@@ -147,12 +147,14 @@ def test_failing_build_association
def test_create
firm = Firm.new("name" => "GlobalMegaCorp")
firm.save
- assert_equal firm.account.create("credit_limit" => 1000), firm.account
+ firm.account = account = Account.create("credit_limit" => 1000)
+ assert_equal account, firm.account
end
def test_create_before_save
firm = Firm.new("name" => "GlobalMegaCorp")
- assert_equal firm.account.create("credit_limit" => 1000), firm.account
+ firm.account = account = Account.create("credit_limit" => 1000)
+ assert_equal account, firm.account
end
def test_dependence_with_missing_association
@@ -243,6 +245,21 @@ def test_counting_using_sql
assert_equal 1, Firm.find_first.clients_using_counter_sql.size
assert_equal 0, Firm.find_first.clients_using_zero_counter_sql.size
end
+
+ def test_belongs_to_sanity
+ c = Client.new
+ assert_nil c.firm
+
+ if c.firm
+ assert false, "belongs_to failed if check"
+ end
+
+ unless c.firm
+ else
+ assert false, "belongs_to failed unless check"
+ end
+
+ end
def test_find_ids
firm = Firm.find_first

0 comments on commit bce0e14

Please sign in to comment.