Skip to content

Commit

Permalink
Refactoring HasOne
Browse files Browse the repository at this point in the history
  • Loading branch information
durran committed Jan 24, 2010
1 parent a3fc8a2 commit c21d791
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 39 deletions.
2 changes: 1 addition & 1 deletion lib/mongoid/associations.rb
Expand Up @@ -227,7 +227,7 @@ def add_association(type, options)
def add_builder(type, options)
name = options.name.to_s
define_method("build_#{name}") do |attrs|
reset(name) { type.new(self, attrs, options) }
reset(name) { type.new(self, attrs.stringify_keys, options) }
end
end

Expand Down
13 changes: 6 additions & 7 deletions lib/mongoid/associations/belongs_to.rb
Expand Up @@ -5,15 +5,15 @@ class BelongsTo #:nodoc:
include Proxy

# Creates the new association by setting the internal
# document as the passed in Document. This should be the
# target as the passed in Document. This should be the
# parent.
#
# All method calls on this object will then be delegated
# to the internal document itself.
#
# Options:
#
# document: The parent +Document+
# target: The parent +Document+
# options: The association options
def initialize(target, options)
@target, @options = target, options
Expand All @@ -26,11 +26,6 @@ def find(id)
@target
end

# Delegate all missing methods over to the parent +Document+.
def method_missing(name, *args, &block)
@target.send(name, *args, &block)
end

class << self
# Creates the new association by setting the internal
# document as the passed in Document. This should be the
Expand All @@ -53,6 +48,10 @@ def macro
# Perform an update of the relationship of the parent and child. This
# is initialized by setting a parent object as the association on the
# +Document+. Will properly set a has_one or a has_many.
#
# Returns:
#
# A new +BelongsTo+ association proxy.
def update(target, child, options)
child.parentize(target, options.inverse_of)
child.notify
Expand Down
56 changes: 26 additions & 30 deletions lib/mongoid/associations/has_one.rb
Expand Up @@ -4,7 +4,10 @@ module Associations #:nodoc:
class HasOne #:nodoc:
include Proxy

attr_reader :association_name, :document, :parent, :options
# Build a new object for the association.
def build(attrs = {}, type = nil)
@target = attrs.assimilate(@parent, @options, type); self
end

# Creates the new association by finding the attributes in
# the parent document with its name, and instantiating a
Expand All @@ -16,43 +19,31 @@ class HasOne #:nodoc:
# Options:
#
# document: The parent +Document+
# attributes: The attributes of the decorated object.
# attributes: The attributes of the target object.
# options: The association options.
def initialize(document, attributes, options)
@parent, @options, @association_name = document, options, options.name
attrs = attributes.stringify_keys
klass = attrs["_type"] ? attrs["_type"].constantize : nil
@document = attrs.assimilate(@parent, @options, klass)
end

# Delegate all missing methods over to the +Document+.
def method_missing(name, *args, &block)
@document.send(name, *args, &block)
#
# Returns:
#
# A new +HashOne+ association proxy.
def initialize(document, attrs, options)
@parent, @options = document, options
@target = attrs.assimilate(@parent, @options, attrs.klass)
end

# Used for setting the association via a nested attributes setter on the
# parent +Document+.
# parent +Document+. Called when using accepts_nested_attributes_for.
#
# Options:
#
# attributes: The attributes for the new association
#
# Returns:
#
# A new target document.
def nested_build(attributes)
build(attributes)
end

# This will get deprecated
def to_a
[@document]
end

# Need to override here for when the underlying document is nil.
def valid?
@document.valid?
end

protected
# Build a new object for the association.
def build(attrs = {}, type = nil)
@document = attrs.assimilate(@parent, @options, type)
self
end

class << self
# Preferred method of instantiating a new +HasOne+, since nil values
# will be handled properly.
Expand Down Expand Up @@ -84,8 +75,13 @@ def macro
# Example:
#
# <tt>HasOne.update({:first_name => "Hank"}, person, options)</tt>
#
# Returns:
#
# A new +HasOne+ association proxy.
def update(child, parent, options)
child.assimilate(parent, options)
instantiate(parent, options)
end
end

Expand Down
6 changes: 6 additions & 0 deletions lib/mongoid/associations/proxy.rb
Expand Up @@ -14,6 +14,12 @@ module InstanceMethods #:nodoc:
attr_reader \
:options,
:target

# Default behavior of method missing should be to delegate all calls
# to the target of the proxy. This can be overridden in special cases.
def method_missing(name, *args, &block)
@target.send(name, *args, &block)
end
end
end
end
Expand Down
6 changes: 6 additions & 0 deletions lib/mongoid/extensions/hash/accessors.rb
Expand Up @@ -26,6 +26,12 @@ def insert(key, attrs)
self[key] = key.singular? ? attrs : [attrs]
end
end

# If a _type key exists in the hash, return the class for the value.
def klass
class_name = self["_type"]
class_name ? class_name.constantize : nil
end
end
end
end
Expand Down
22 changes: 21 additions & 1 deletion spec/unit/mongoid/associations/has_one_spec.rb
Expand Up @@ -178,7 +178,7 @@
before do
@name = Name.new(:first_name => "Donald")
@person = Person.new(:title => "Sir")
Mongoid::Associations::HasOne.update(
@association = Mongoid::Associations::HasOne.update(
@name,
@person,
Mongoid::Associations::Options.new(:name => :name)
Expand All @@ -194,6 +194,10 @@
{ "_id" => "donald", "first_name" => "Donald", "_type" => "Name" }
end

it "returns the proxy" do
@association.target.should == @name
end

end

context "when setting the object to nil" do
Expand All @@ -216,6 +220,22 @@

end

describe "#to_a" do

before do
@association = Mongoid::Associations::HasOne.new(
@document,
@attributes["mixed_drink"],
Mongoid::Associations::Options.new(:name => :mixed_drink)
)
end

it "returns the target in a new array" do
@association.to_a.first.should be_a_kind_of(MixedDrink)
end

end

describe "#valid?" do

context "when the document is not nil" do
Expand Down
20 changes: 20 additions & 0 deletions spec/unit/mongoid/extensions/hash/accessors_spec.rb
Expand Up @@ -21,6 +21,26 @@
}
end

describe "#klass" do

context "when _type exists" do

it "returns the class" do
{ "_type" => "Person" }.klass.should == Person
end

end

context "when _type does not exist" do

it "returns nil" do
{}.klass.should be_nil
end

end

end

describe "#remove" do

context "when removing from an array" do
Expand Down

0 comments on commit c21d791

Please sign in to comment.