Skip to content

Commit

Permalink
Merge branch 'nil_associations' of https://github.com/jaronkk/mongoma…
Browse files Browse the repository at this point in the history
…pper

* 'nil_associations' of https://github.com/jaronkk/mongomapper:
  Fixed an issue using the association_name= method to assign a new object updated existing pointers to the proxy, rather than generating a new one
  Modified accessor method for one and belongs_to associations to return nil rather than a proxy object if the association does not exist Added additional method calls for build_<name>, create_<name>, and create<name>! since calling .build(), .create(), and .create!() would raise when the association is nil. Discussion from http://groups.google.com/group/mongomapper/browse_thread/thread/e311dd04b0af576f
  • Loading branch information
bkeepers committed Jan 7, 2011
2 parents 1e3bfd7 + f42d360 commit 7b44ce9
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 21 deletions.
61 changes: 49 additions & 12 deletions lib/mongo_mapper/plugins/associations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,50 @@ def create_association(type, name, options, &extension)
association = Associations::Base.new(type, name, options, &extension)
associations[association.name] = association

define_method(association.name) do
get_proxy(association)
end

define_method("#{association.name}=") do |value|
get_proxy(association).replace(value)
value
end

if association.one? || association.belongs_to?
define_method(association.name) do
proxy = get_proxy(association)
proxy.nil? ? nil : proxy
end

define_method("#{association.name}=") do |value|
proxy = get_proxy(association)

if (proxy.nil? || proxy.target != value)
proxy = build_proxy(association)
end

proxy.replace(value)
value
end

define_method("#{association.name}?") do
get_proxy(association).present?
end

if (association.one?)
define_method("build_#{association.name}") do |*args|
get_proxy(association).build(*args)
end

define_method("create_#{association.name}") do |*args|
get_proxy(association).create(*args)
end

define_method("create_#{association.name}!") do |*args|
get_proxy(association).create!(*args)
end
end
else
define_method(association.name) do
get_proxy(association)
end

define_method("#{association.name}=") do |value|
get_proxy(association).replace(value)
value
end

end

if association.options[:dependent] && association.many? && !association.embeddable?
Expand Down Expand Up @@ -75,11 +106,17 @@ def embedded_associations
association
end
end


def build_proxy(association)
proxy = association.proxy_class.new(self, association)
self.instance_variable_set(association.ivar, proxy)

proxy
end

def get_proxy(association)
unless proxy = self.instance_variable_get(association.ivar)
proxy = association.proxy_class.new(self, association)
self.instance_variable_set(association.ivar, proxy)
proxy = build_proxy(association)
end
proxy
end
Expand Down
4 changes: 3 additions & 1 deletion lib/mongo_mapper/plugins/callbacks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ def run_callbacks(kind, options={}, &block)
self.class.send(callback_chain_method).run(self, options, &block)
self.embedded_associations.each do |association|
if association.one?
self.send(association.name).run_callbacks(kind, options, &block)
if (!self.send("#{association.name}").nil?)
self.send(association.name).run_callbacks(kind, options, &block)
end
else
self.send(association.name).each do |document|
document.run_callbacks(kind, options, &block)
Expand Down
4 changes: 3 additions & 1 deletion lib/mongo_mapper/plugins/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ def reload
if doc = collection.find_one(:_id => id)
tap do |instance|
instance.class.associations.each_key do |association_name|
send(association_name).reset if respond_to?(association_name)
if self.respond_to?(association_name) && !self.send(association_name).nil?
self.send(association_name).reset
end
end
instance.attributes = doc
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def setup
@status.target = project
@status.save.should be_true
project.destroy
@status.reload
end

should "return nil instead of raising error" do
Expand Down
24 changes: 24 additions & 0 deletions test/functional/associations/test_belongs_to_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def setup
should "default to nil" do
@comment_class.new.post.nil?.should be_true
end

should "return nil instead of a proxy" do
nil.should === @comment_class.new.post
end

should "have boolean presence method" do
comment = @comment_class.new(:name => 'Foo!')
Expand All @@ -32,7 +36,27 @@ def setup
comment.post.should == post
comment.post.nil?.should be_false
end

should "generate a new proxy when replacing the association" do
post1 = @post_class.create(:name => 'post1')
post2 = @post_class.create(:name => 'post2')

comment = @comment_class.new(:name => 'Foo!', :post => post1)
comment.save.should be_true


comment = comment.reload
comment.post.should == post1
comment.post.nil?.should be_false

original_post = comment.post
original_post.name.should == 'post1'

comment.post = post2
comment.post.name.should == 'post2'
original_post.name.should == 'post1'
end

should "unset the association" do
post = @post_class.new(:name => 'mongomapper')
comment = @comment_class.new(:name => 'Foo!', :post => post)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def setup
@hm2 = Message.create(:body => 'Hall the king!', :position => 2)
@hall.messages = [@hm1, @hm2, @hm3]
@hall.save
@hall.reload
end

context "dynamic finders" do
Expand Down
2 changes: 1 addition & 1 deletion test/functional/associations/test_one_embedded_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def setup
@post_class.one :author, :class => @author_class

post = @post_class.create
author = post.author.build(:name => "John")
author = post.build_author(:name => "John")
post.author.should be_instance_of(@author_class)
post.author.should be_new
post.author.name.should == 'John'
Expand Down
35 changes: 30 additions & 5 deletions test/functional/associations/test_one_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ def setup
@post_class.new.author.nil?.should be_true
end

should "return nil instead of a proxy" do
@post_class.one :author, :class => @author_class
nil.should === @post_class.new.author
end

should "allow assignment of associated document using a hash" do
@post_class.one :author, :class => @author_class

Expand Down Expand Up @@ -42,8 +47,28 @@ def setup
post.author = new_author
post.author.should == new_author
end
end

should "generate a new proxy instead of modifying the existing one" do
@post_class.one :author, :class => @author_class

post = @post_class.new
author = @author_class.new(:name => 'Frank')
post.author = author
post.reload

post.author.should == author
post.author.nil?.should be_false

original_author = post.author
original_author.name.should == 'Frank'
new_author = @author_class.new(:name => 'Emily')
post.author = new_author
post.author.should == new_author

original_author.name.should == 'Frank'
end
end

context "with a Hash" do
should "convert to an object of the class and work" do
@post_class.one :author, :class => @author_class
Expand Down Expand Up @@ -135,7 +160,7 @@ def setup
@post_class.one :author, :class => @author_class

post = @post_class.create
author = post.author.build(:name => 'John')
author = post.build_author(:name => 'John')
post.author.should be_instance_of(@author_class)
post.author.should be_new
post.author.name.should == 'John'
Expand All @@ -147,7 +172,7 @@ def setup
@post_class.one :author, :class => @author_class

post = @post_class.create
author = post.author.create(:name => 'John')
author = post.create_author(:name => 'John')
post.author.should be_instance_of(@author_class)
post.author.should_not be_new
post.author.name.should == 'John'
Expand All @@ -164,13 +189,13 @@ def setup
should "raise exception if invalid" do
post = @post_class.create
assert_raises(MongoMapper::DocumentNotValid) do
post.author.create!
post.create_author!
end
end

should "work if valid" do
post = @post_class.create
author = post.author.create!(:name => 'John')
author = post.create_author!(:name => 'John')
post.author.should be_instance_of(@author_class)
post.author.should_not be_new
post.author.name.should == 'John'
Expand Down
2 changes: 2 additions & 0 deletions test/functional/test_associations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class AwesomePost
tag2 = AwesomeTag.new(:name => 'grand')
post1 = AwesomePost.create(:creator => user, :tags => [tag1])
post2 = AwesomePost.create(:creator => user, :tags => [tag2])

user.reload
user.posts.should == [post1, post2]

post1 = post1.reload
Expand Down
2 changes: 1 addition & 1 deletion test/functional/test_identity_map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ class ::BlogPost < ::Item

should "work correctly with one proxy create" do
root = Item.create(:title => 'Root')
blog = root.blog.create(:title => 'Blog')
blog = root.create_blog(:title => 'Blog')
blog.parent.should equal(root)
end
end
Expand Down

0 comments on commit 7b44ce9

Please sign in to comment.