Created
January 12, 2010 20:49
-
-
Save zef/275594 to your computer and use it in GitHub Desktop.
Revisions
-
zef created this gist
Jan 12, 2010 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,68 @@ # 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 hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,60 @@ 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