Skip to content

Commit

Permalink
All specs should pass.
Browse files Browse the repository at this point in the history
  • Loading branch information
david committed Apr 21, 2008
1 parent 55e0b3d commit 8b5d946
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 40 deletions.
84 changes: 78 additions & 6 deletions lib/data_mapper/associations/one_to_many.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
require 'forwardable'
require __DIR__.parent + 'associations'
require __DIR__ + 'relationship'
require __DIR__ + 'parent_to_child_association'

module DataMapper
module Associations
Expand All @@ -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
)

Expand All @@ -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
Expand All @@ -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
4 changes: 2 additions & 2 deletions lib/data_mapper/associations/one_to_one.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions lib/data_mapper/associations/relationship.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion spec/integration/association_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
19 changes: 1 addition & 18 deletions spec/integration/postgres_adapter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
119 changes: 111 additions & 8 deletions spec/unit/associations/one_to_many_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 0 additions & 5 deletions spec/unit/associations/one_to_one_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 8b5d946

Please sign in to comment.