Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

All specs should pass.

  • Loading branch information...
commit 8b5d946d3cfae982ffe39366793d5187e3c53cea 1 parent 55e0b3d
@david david authored
View
84 lib/data_mapper/associations/one_to_many.rb
@@ -1,7 +1,6 @@
require 'forwardable'
require __DIR__.parent + 'associations'
require __DIR__ + 'relationship'
-require __DIR__ + 'parent_to_child_association'
module DataMapper
module Associations
@@ -14,14 +13,13 @@ def one_to_many(name, options = {})
# TOOD: raise an exception if unknown options are passed in
child_model_name = options[:class_name] || DataMapper::Inflection.classify(name)
- parent_model_name = DataMapper::Inflection.demodulize(self.name)
relationships[name] = Relationship.new(
- DataMapper::Inflection.underscore(parent_model_name).to_sym,
+ DataMapper::Inflection.underscore(self.name).to_sym,
repository.name,
child_model_name,
nil,
- parent_model_name,
+ self.name,
nil
)
@@ -36,8 +34,8 @@ def #{name}_association
@#{name}_association ||= begin
relationship = self.class.relationships[:#{name}]
- association = relationship.with_parent(self, Associations::ParentToChildAssociation) do |repository, child_key, parent_key, child_model, parent_resource|
- repository.all(child_model, child_key.to_query(parent_key.get(parent_resource)))
+ association = Proxy.new(relationship, self) do |repository, relationship|
+ repository.all(*relationship.to_child_query(self))
end
parent_associations << association
@@ -49,6 +47,80 @@ def #{name}_association
relationships[name]
end
+
+ class Proxy
+ extend Forwardable
+ include Enumerable
+
+ def_instance_delegators :entries, :[], :size, :length, :first, :last
+
+ def loaded?
+ !defined?(@children_resources)
+ end
+
+ def clear
+ each { |child_resource| delete(child_resource) }
+ end
+
+ def each(&block)
+ children.each(&block)
+ self
+ end
+
+ def children
+ @children_resources ||= @children_loader.call(repository(@relationship.repository_name), @relationship)
+ end
+
+ def save
+ @dirty_children.each do |child_resource|
+ save_child(child_resource)
+ end
+ end
+
+ def push(*child_resources)
+ child_resources.each do |child_resource|
+ if @parent_resource.new_record?
+ @dirty_children << child_resource
+ else
+ save_child(child_resource)
+ end
+
+ children << child_resource
+ end
+
+ self
+ end
+
+ alias << push
+
+ def delete(child_resource)
+ deleted_resource = children.delete(child_resource)
+ begin
+ @relationship.attach_parent(deleted_resource, nil)
+ repository(@relationship.repository_name).save(deleted_resource)
+ rescue
+ children << child_resource
+ raise
+ end
+ end
+
+ private
+
+ def initialize(relationship, parent_resource, &children_loader)
+ # raise ArgumentError, "+relationship+ should be a DataMapper::Association::Relationship, but was #{relationship.class}", caller unless Relationship === relationship
+ # raise ArgumentError, "+parent_resource+ should be a DataMapper::Resource, but was #{parent_resource.class}", caller unless Resource === parent_resource
+
+ @relationship = relationship
+ @parent_resource = parent_resource
+ @children_loader = children_loader
+ @dirty_children = []
+ end
+
+ def save_child(child_resource)
+ @relationship.attach_parent(child_resource, @parent_resource)
+ repository(@relationship.repository_name).save(child_resource)
+ end
+ end # class Proxy
end # module OneToMany
end # module Associations
end # module DataMapper
View
4 lib/data_mapper/associations/one_to_one.rb
@@ -38,8 +38,8 @@ def #{name}_association
@#{name}_association ||= begin
relationship = self.class.relationships[:#{name}]
- association = relationship.with_parent(self, Associations::ParentToChildAssociation) do |repository, child_key, parent_key, child_model, parent_resource|
- repository.all(child_model, child_key.to_query(parent_key.get(parent_resource)))
+ association = Associations::OneToMany::Proxy.new(relationship, self) do |repository, relationship|
+ repository.all(*relationship.to_child_query(self))
end
parent_associations << association
View
5 lib/data_mapper/associations/relationship.rb
@@ -32,6 +32,11 @@ def parent_key
end
end
+
+ def to_child_query(parent)
+ [child_model, child_key.to_query(parent_key.get(parent))]
+ end
+
def with_child(child_resource, association, &loader)
association.new(self, child_resource) do
yield repository(@repository_name), child_key, parent_key, parent_model, child_resource
View
2  spec/integration/association_spec.rb
@@ -62,7 +62,7 @@ class Slice
property :name, String
repository(:sqlite3) do
- many_to_one :host, :repository_name => :sqlite3
+ many_to_one :host
end
end
View
19 spec/integration/postgres_adapter_spec.rb
@@ -6,24 +6,7 @@
begin
require 'do_postgres'
- DataMapper.setup(:postgres, "postgres://postgres@localhost/dm_core_test")
-
- describe DataMapper::Adapters::PostgresAdapter do
- before do
- @uri = URI.parse("postgres://postgres@localhost/dm_core_test")
- end
-
- it 'should override the path when the option is passed' do
- pending
- adapter = DataMapper::Adapters::PostgresAdapter.new(:mock, @uri, { :path => '/dm_core_test2' })
- adapter.instance_variable_get("@uri").should == URI.parse("postgres://postgres@localhost/dm_core_test2")
- end
-
- it 'should accept the uri when no overrides exist' do
- adapter = DataMapper::Adapters::PostgresAdapter.new(:mock, @uri)
- adapter.instance_variable_get("@uri").should == @uri
- end
- end
+ DataMapper.setup(:postgres, ENV["POSTGRES_SPEC_URI"] || "postgres://postgres@localhost/dm_core_test")
describe DataMapper::Adapters::DataObjectsAdapter do
before do
View
119 spec/unit/associations/one_to_many_spec.rb
@@ -4,16 +4,119 @@
describe "DataMapper::Associations::OneToMany" do
before do
- @adapter = DataMapper::Repository.adapters[:relationship_spec] || DataMapper.setup(:relationship_spec, 'mock://localhost')
+ @class = Class.new do
+ def self.name
+ "Hannibal"
+ end
+
+ include DataMapper::Resource
+
+ property :id, Fixnum
+
+ send :one_to_many, :vehicles
+ end
+
+ @relationship = mock("relationship")
end
+
+ it "should install the association's methods" do
+ victim = @class.new
+
+ victim.should respond_to(:vehicles)
+ end
+
+ it "should work with classes inside modules"
- it "should allow a declaration" do
+ describe DataMapper::Associations::OneToMany::Proxy do
+ describe "when loading" do
+ def init
+ DataMapper::Associations::OneToMany::Proxy.new(@relationship, nil) do |one, two|
+ @tester.weee
+ end
+ end
- lambda do
- class Manufacturer
- one_to_many :vehicles
+ before do
+ @tester = mock("tester")
end
- end.should_not raise_error
- end
-end
+ it "should not load on initialize" do
+ @tester.should_not_receive(:weee)
+ init
+ end
+
+ it "should load when accessed" do
+ @relationship.should_receive(:repository_name).and_return(:a_symbol)
+ @tester.should_receive(:weee).and_return([])
+ a = init
+ a.entries
+ end
+ end
+
+ describe "when adding an element" do
+ before do
+ @parent = mock("parent")
+ @element = mock("element", :null_object => true)
+ @association = DataMapper::Associations::OneToMany::Proxy.new(@relationship, @parent) do
+ []
+ end
+ end
+
+ describe "with a persisted parent" do
+ it "should save the element" do
+ @relationship.should_receive(:repository_name).and_return(:a_symbol)
+ @parent.should_receive(:new_record?).and_return(false)
+ @association.should_receive(:save_child).with(@element)
+
+ @association << @element
+
+ @association.instance_variable_get("@dirty_children").should be_empty
+ end
+ end
+
+ describe "with a non-persisted parent" do
+ it "should not save the element" do
+ @relationship.should_receive(:repository_name).and_return(:a_symbol)
+ @parent.should_receive(:new_record?).and_return(true)
+ @association.should_not_receive(:save_child)
+
+ @association << @element
+
+ @association.instance_variable_get("@dirty_children").should_not be_empty
+ end
+
+ it "should save the element after the parent is saved" do
+
+ end
+
+ it "should add the parent's keys to the element after the parent is saved"
+ end
+ end
+
+ describe "when deleting an element" do
+ it "should delete the element from the database" do
+
+ end
+
+ it "should delete the element from the association"
+
+ it "should erase the ex-parent's keys from the element"
+ end
+
+ describe "when deleting the parent" do
+
+ end
+
+
+ describe "with an unsaved parent" do
+ describe "when deleting an element from an unsaved parent" do
+ it "should remove the element from the association" do
+
+ end
+ end
+ end
+ end
+
+ describe "when changing an element's parent" do
+
+ end
+end
View
5 spec/unit/associations/one_to_one_spec.rb
@@ -6,10 +6,5 @@
describe "DataMapper::Associations::OneToOne" do
it "should allow a declaration" do
- lambda do
- class Manufacturer
- one_to_one :halo_car, :class => 'Vehicle'
- end
- end.should_not raise_error
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.