Created
January 12, 2010 20:49
-
-
Save zef/275594 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is an incomplete implementation. | |
module MongoMapper | |
module NestedAttributes | |
def self.included(base) | |
base.extend(ClassMethods) | |
base.send :include, InstanceMethods | |
end | |
module ClassMethods | |
def accepts_nested_attributes_for(*attr_names) | |
options = { :allow_destroy => false } | |
options.update(attr_names.extract_options!) | |
options.assert_valid_keys(:allow_destroy, :reject_if) | |
for association_name in attr_names | |
if associations.any? { |key, value| key == association_name.to_s } | |
module_eval %{ | |
def #{association_name}_attributes=(attributes) | |
assign_nested_attributes_for_association(:#{association_name}, attributes, #{options[:allow_destroy]}) | |
end | |
}, __FILE__, __LINE__ | |
else | |
raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?" | |
end | |
end | |
end | |
end | |
module InstanceMethods | |
UNASSIGNABLE_KEYS = %w{ id _id _destroy } | |
def assign_nested_attributes_for_association(association_name, attributes_collection, allow_destroy) | |
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array) | |
raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})" | |
end | |
if attributes_collection.is_a? Hash | |
attributes_collection = attributes_collection.sort_by { |index, _| index.to_i }.map { |_, attributes| attributes } | |
end | |
attributes_collection.each do |attributes| | |
attributes.stringify_keys! | |
if attributes['_id'].blank? | |
send(association_name) << association_name.to_s.classify.constantize.new(attributes) | |
elsif existing_record = send(association_name).detect { |record| record.id.to_s == attributes['_id'].to_s } | |
if existing_record.has_destroy_flag?(attributes) && allow_destroy | |
send(association_name).delete(existing_record) | |
existing_record.destroy unless association_name.to_s.classify.constantize.embeddable? | |
else | |
existing_record.attributes = attributes.except(*UNASSIGNABLE_KEYS) | |
end | |
end | |
end | |
end | |
def has_destroy_flag?(hash) | |
Boolean.to_mongo(hash['_destroy']) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'test_helper' | |
require 'models' | |
class NestedAttributesTest < Test::Unit::TestCase | |
def setup | |
Project.accepts_nested_attributes_for(:people, :collaborators, :allow_destroy => true) | |
Project.collection.remove | |
end | |
context "A Document" do | |
setup do | |
@project = Project.create(:name => 'Nesting Attributes', | |
:people_attributes => [{:name => 'Dude'}], | |
:collaborators_attributes => [{:name => 'Another Dude'}] | |
) | |
end | |
should "accept nested attributes for embedded documents" do | |
@project.people.size.should == 1 | |
end | |
should "accept nested attributes for associated documents" do | |
@project.collaborators.size.should == 1 | |
end | |
context "which already exists" do | |
setup do | |
person = @project.people.first.attributes.merge({:_destroy => true}) | |
collaborator = @project.collaborators.first.attributes.merge({:_destroy => true}) | |
# @project.update_attributes(:people_attributes => [person], :collaborators_attributes => [collaborator]) | |
@project.attributes = {:people_attributes => [person], :collaborators_attributes => [collaborator]} | |
end | |
should "not destroy associated documents until the document is saved" do | |
@project.collaborators.size.should == 1 | |
end | |
should "destroy embedded documents when saved" do | |
@project.save | |
@project.reload | |
@project.people.size.should == 0 | |
end | |
should "destroy associated documents when saved" do | |
@project.save | |
@project.reload | |
@project.collaborators.size.should == 0 | |
end | |
end | |
end | |
should "raise an ArgumentError for non existing associations" do | |
lambda { | |
Project.accepts_nested_attributes_for :blah | |
}.should raise_error(ArgumentError) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment