Skip to content

Commit

Permalink
Added Collection#first_or_new and Collection#first_or_create
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Kubb committed Nov 27, 2008
1 parent 734c921 commit 663c2cb
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 26 deletions.
32 changes: 32 additions & 0 deletions lib/dm-core/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,38 @@ def build(*args)
new(*args)
end

##
# Finds the first Resource by conditions, or initializes a new
# Resource with the attributes if none found
#
# @param [Hash] conditions
# The conditions to be used to search
# @param [Hash] attributes
# The attributes to be used to create the record of none is found.
# @return [DataMapper::Resource]
# The instance found by +query+, or created with +attributes+ if none found
#
# @api public
def first_or_new(conditions, attributes = {})
first(conditions) || new(conditions.merge(attributes))
end

##
# Finds the first Resource by conditions, or creates a new
# Resource with the attributes if none found
#
# @param [Hash] conditions
# The conditions to be used to search
# @param [Hash] attributes
# The attributes to be used to create the record of none is found.
# @return [DataMapper::Resource]
# The instance found by +query+, or created with +attributes+ if none found
#
# @api public
def first_or_create(conditions, attributes = {})
first(conditions) || create(conditions.merge(attributes))
end

##
# Initializes a Resource and appends it to the Collection
#
Expand Down
30 changes: 22 additions & 8 deletions lib/dm-core/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -335,21 +335,35 @@ def first(*args)
# TODO: add #last

##
# Finds the #first record by a query, or #create one by attributes
# if none was found
# Finds the first Resource by conditions, or initializes a new
# Resource with the attributes if none found
#
# @param [Hash] query
# The query to be used so search
# @param [Hash] conditions
# The conditions to be used to search
# @param [Hash] attributes
# The attributes to be used to create the record of none is found.
# @return [DataMapper::Resource]
# The instance found by +query+, or created with +attributes+ if none found
#
# @api public
def first_or_new(conditions, attributes = {})
first(conditions) || new(conditions.merge(attributes))
end

##
# Finds the first Resource by conditions, or creates a new
# Resource with the attributes if none found
#
# @param [Hash] conditions
# The conditions to be used to search
# @param [Hash] attributes
# The attributes to be used to create the record of none is found.
# @return [DataMapper::Resource]
# The instance found by +query+, or created with +attributes+ if none found
# @see #first
# @see #create
#
# @api public
def first_or_create(query, attributes = {})
first(query) || create(query.merge(attributes))
def first_or_create(conditions, attributes = {})
first(conditions) || create(conditions.merge(attributes))
end

##
Expand Down
101 changes: 98 additions & 3 deletions spec/public/shared/collection_shared_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -732,9 +732,104 @@
end
end

it 'should respond to #first_or_create' do
pending 'TODO: add Collection#first_or_create'
@articles.should respond_to(:first_or_create)
it { @articles.should respond_to(:first_or_create) }

describe '#first_or_create' do
describe 'with conditions that find an existing Resource' do
before do
@return = @resource = @articles.first_or_create(@article.attributes)
end

it 'should return a Resource' do
@return.should be_kind_of(DataMapper::Resource)
end

it 'should be expected Resource' do
@resource.should == @article
end

it 'should be a saved Resource' do
@resource.should_not be_new_record
end

it 'should relate the Resource to the Collection' do
@resource.collection.should be_equal(@articles)
end
end

describe 'with conditions that do not find an existing Resource' do
before do
@conditions = { :content => 'Unknown Content' }
@attributes = {}
@return = @resource = @articles.first_or_create(@conditions, @attributes)
end

it 'should return a Resource' do
@return.should be_kind_of(DataMapper::Resource)
end

it 'should be expected Resource' do
@resource.attributes.only(:title, :content).should == { :title => 'Sample Article', :content => 'Unknown Content' }
end

it 'should be a saved Resource' do
@resource.should_not be_new_record
end

it 'should relate the Resource to the Collection' do
@resource.collection.should be_equal(@articles)
end
end
end

it { @articles.should respond_to(:first_or_new) }

describe '#first_or_new' do
describe 'with conditions that find an existing Resource' do
before do
@return = @resource = @articles.first_or_new(@article.attributes)
end

it 'should return a Resource' do
@return.should be_kind_of(DataMapper::Resource)
end

it 'should be expected Resource' do
@resource.should == @article
end

it 'should be a saved Resource' do
@resource.should_not be_new_record
end

it 'should relate the Resource to the Collection' do
@resource.collection.should be_equal(@articles)
end
end

describe 'with conditions that do not find an existing Resource' do
before do
@conditions = { :content => 'Unknown Content' }
@attributes = {}
@return = @resource = @articles.first_or_new(@conditions, @attributes)
end

it 'should return a Resource' do
@return.should be_kind_of(DataMapper::Resource)
end

it 'should be expected Resource' do
@resource.attributes.only(:title, :content).should == { :title => 'Sample Article', :content => 'Unknown Content' }
end

it 'should not be a saved Resource' do
@resource.should be_new_record
end

it 'should relate the Resource to the Collection' do
@resource.collection.should be_equal(@articles)
end
end
end

it { @articles.should respond_to(:get) }
Expand Down
30 changes: 15 additions & 15 deletions spec/public/transaction_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))

describe DataMapper::Resource, "Transactions" do

before do
Object.send(:remove_const, :User) if defined?(User)
class User
Expand All @@ -24,7 +24,7 @@ class Comment
belongs_to :user
end
end

supported_by :postgres, :mysql do
before(:each) do
# --- Temporary private api use to get around rspec limitations ---
Expand All @@ -33,48 +33,48 @@ class Comment
transaction.begin
repository.adapter.push_transaction(transaction)
end

@model = User
@child_model = Comment
@user = @model.create(:name => 'dbussink', :age => 25, :description => "Test")
end

after do
repository = repository(:default)
while repository.adapter.current_transaction
repository.adapter.current_transaction.rollback
repository.adapter.pop_transaction
end
end

it_should_behave_like 'A public Resource'

end

supported_by :postgres, :mysql do

describe "#transaction" do

it "should have access to resources presisted before the transaction" do
User.create(:name => "carllerche")
User.transaction do
User.first.name.should == "carllerche"
end
end

it "should have access to resources persisted in the transaction" do
User.transaction do
User.create(:name => "carllerche")
User.first.name.should == "carllerche"
end
end

it "should not mark persisted resources as new records" do
User.transaction do
User.create(:name => "carllerche").should_not be_new_record
end
end

it "should rollback when an error is thrown in a transaction" do
lambda {
User.transaction do
Expand All @@ -84,9 +84,9 @@ class Comment
}.should raise_error("I love coffee")
User.first.should be_nil
end

end

end

end

0 comments on commit 663c2cb

Please sign in to comment.