require 'spec_helper' # TODO: move these specs into shared specs for #copy describe DataMapper::Model do before :all do module ::Blog class Article include DataMapper::Resource property :id, Serial property :title, String, :required => true property :content, Text, :writer => :private, :default => lambda { |resource, property| resource.title } property :subtitle, String property :author, String, :required => true belongs_to :original, self, :required => false has n, :revisions, self, :child_key => [ :original_id ] has 1, :previous, self, :child_key => [ :original_id ], :order => [ :id.desc ] end end DataMapper.finalize @article_model = Blog::Article end supported_by :all do before :all do @author = 'Dan Kubb' @original = @article_model.create(:title => 'Original Article', :author => @author) @article = @article_model.create(:title => 'Sample Article', :original => @original, :author => @author) @other = @article_model.create(:title => 'Other Article', :author => @author) end it { @article_model.should respond_to(:copy) } describe '#copy' do with_alternate_adapter do before :all do if @article_model.respond_to?(:auto_migrate!) # force the article model to be available in the alternate repository @article_model.auto_migrate!(@adapter.name) end end describe 'between identical models' do before :all do @return = @resources = @article_model.copy(@repository.name, @adapter.name) end it 'should return a Collection' do @return.should be_a_kind_of(DataMapper::Collection) end it 'should return Resources' do @return.each { |resource| resource.should be_a_kind_of(DataMapper::Resource) } end it 'should have each Resource set to the expected Repository' do @resources.each { |resource| resource.repository.name.should == @adapter.name } end it 'should create the Resources in the expected Repository' do @article_model.all(:repository => DataMapper.repository(@adapter.name)).should == @resources end end describe 'between different models' do before :all do @other.destroy @article.destroy @original.destroy # make sure the default repository is empty @article_model.all(:repository => @repository).should be_empty # add an extra property to the alternate model DataMapper.repository(@adapter.name) do @article_model.property :status, String, :default => 'new' end if @article_model.respond_to?(:auto_migrate!) @article_model.auto_migrate!(@adapter.name) end # add new resources to the alternate repository DataMapper.repository(@adapter.name) do # use an id value that is unique @heff1 = @article_model.create(:id => 99, :title => 'Alternate Repository', :author => @author) end # copy from the alternate to the default repository @return = @resources = @article_model.copy(@adapter.name, :default) end it 'should return a Collection' do @return.should be_a_kind_of(DataMapper::Collection) end it 'should return Resources' do @return.each { |resource| resource.should be_a_kind_of(DataMapper::Resource) } end it 'should have each Resource set to the expected Repository' do @resources.each { |resource| resource.repository.name.should == :default } end it 'should return the expected resources' do # match on id because resources from different repositories are different @resources.map { |resource| resource.id }.should == [ @heff1.id ] end it 'should add the resources to the alternate repository' do @article.model.get(*@heff1.key).should_not be_nil end end end end end end describe DataMapper::Model do extend DataMapper::Spec::CollectionHelpers::GroupMethods self.loaded = false before :all do module ::Blog class Article include DataMapper::Resource property :id, Serial property :title, String, :required => true, :default => 'Default Title' property :content, Text property :subtitle, String belongs_to :original, self, :required => false has n, :revisions, self, :child_key => [ :original_id ] has 1, :previous, self, :child_key => [ :original_id ], :order => [ :id.desc ] has n, :publications, :through => Resource end class Publication include DataMapper::Resource property :id, Serial property :name, String has n, :articles, :through => Resource end end DataMapper.finalize @article_model = Blog::Article @publication_model = Blog::Publication end supported_by :all do # model cannot be a kicker def should_not_be_a_kicker; end def model?; true end before :all do @articles = @article_model @original = @articles.create(:title => 'Original Article') @article = @articles.create(:title => 'Sample Article', :content => 'Sample', :original => @original) @other = @articles.create(:title => 'Other Article', :content => 'Other') end describe '#new' do subject { model.new(*args) } let(:model) { @article_model } context 'with no arguments' do let(:args) { [] } it { should be_instance_of(model) } its(:attributes) { should == { :title => 'Default Title' } } end context 'with an empty Hash' do let(:args) { [ {} ] } it { should be_instance_of(model) } its(:attributes) { should == { :title => 'Default Title' } } end context 'with a non-empty Hash' do let(:attributes) { { :title => 'A Title' } } let(:args) { [ attributes ] } it { should be_instance_of(model) } its(:attributes) { should == attributes } end context 'with nil' do let(:args) { [ nil ] } it { should be_instance_of(model) } its(:attributes) { should == { :title => 'Default Title' } } end end [ :create, :create! ].each do |method| describe "##{method}" do subject { model.send(method, *args) } let(:model) { @article_model } context 'with no arguments' do let(:args) { [] } it { should be_instance_of(model) } it { should be_saved } end context 'with an empty Hash' do let(:args) { [ {} ] } it { should be_instance_of(model) } it { should be_saved } end context 'with a non-empty Hash' do let(:attributes) { { :title => 'A Title' } } let(:args) { [ attributes ] } it { should be_instance_of(model) } it { should be_saved } its(:title) { should == attributes[:title] } end context 'with nil' do let(:args) { [ nil ] } it { should be_instance_of(model) } it { should be_saved } end end end [ :destroy, :destroy! ].each do |method| describe "##{method}" do subject { model.send(method) } let(:model) { @article_model } it 'should remove all resources' do method(:subject).should change { model.any? }.from(true).to(false) end end end [ :update, :update! ].each do |method| describe "##{method}" do subject { model.send(method, *args) } let(:model) { @article_model } context 'with attributes' do let(:attributes) { { :title => 'Updated Title' } } let(:args) { [ attributes ] } it { should be(true) } it 'should persist the changes' do subject model.all(:fields => [ :title ]).map { |resource| resource.title }.uniq.should == [ attributes[:title] ] end end context 'with attributes where one is a parent association' do let(:attributes) { { :original => @other } } let(:args) { [ attributes ] } it { should be(true) } it 'should persist the changes' do subject model.all(:fields => [ :original_id ]).map { |resource| resource.original }.uniq.should == [ attributes[:original] ] end end context 'with attributes where a required property is nil' do let(:attributes) { { :title => nil } } let(:args) { [ attributes ] } it 'should raise InvalidValueError' do expect { subject }.to(raise_error(DataMapper::Property::InvalidValueError) do |error| error.property.should == model.title end) end end end end it_should_behave_like 'Finder Interface' it 'DataMapper::Model should respond to raise_on_save_failure' do DataMapper::Model.should respond_to(:raise_on_save_failure) end describe '.raise_on_save_failure' do subject { DataMapper::Model.raise_on_save_failure } it { should be(false) } end it 'DataMapper::Model should respond to raise_on_save_failure=' do DataMapper::Model.should respond_to(:raise_on_save_failure=) end describe '.raise_on_save_failure=' do after do # reset to the default value reset_raise_on_save_failure(DataMapper::Model) end subject { DataMapper::Model.raise_on_save_failure = @value } describe 'with a true value' do before do @value = true end it { should be(true) } it 'should set raise_on_save_failure' do method(:subject).should change { DataMapper::Model.raise_on_save_failure }.from(false).to(true) end end describe 'with a false value' do before do @value = false end it { should be(false) } it 'should set raise_on_save_failure' do method(:subject).should_not change { DataMapper::Model.raise_on_save_failure } end end end it 'A model should respond to raise_on_save_failure' do @article_model.should respond_to(:raise_on_save_failure) end describe '#raise_on_save_failure' do after do # reset to the default value reset_raise_on_save_failure(DataMapper::Model) reset_raise_on_save_failure(@article_model) end subject { @article_model.raise_on_save_failure } describe 'when DataMapper::Model.raise_on_save_failure has not been set' do it { should be(false) } end describe 'when DataMapper::Model.raise_on_save_failure has been set to true' do before do DataMapper::Model.raise_on_save_failure = true end it { should be(true) } end describe 'when model.raise_on_save_failure has been set to true' do before do @article_model.raise_on_save_failure = true end it { should be(true) } end end it 'A model should respond to raise_on_save_failure=' do @article_model.should respond_to(:raise_on_save_failure=) end describe '#raise_on_save_failure=' do after do # reset to the default value reset_raise_on_save_failure(@article_model) end subject { @article_model.raise_on_save_failure = @value } describe 'with a true value' do before do @value = true end it { should be(true) } it 'should set raise_on_save_failure' do method(:subject).should change { @article_model.raise_on_save_failure }.from(false).to(true) end end describe 'with a false value' do before do @value = false end it { should be(false) } it 'should set raise_on_save_failure' do method(:subject).should_not change { @article_model.raise_on_save_failure } end end end it 'A model should respond to allowed_writer_methods' do @article_model.should respond_to(:allowed_writer_methods) end describe '#allowed_writer_methods' do subject { @article_model.allowed_writer_methods } let(:expected_writer_methods) do %w[ original= revisions= previous= publications= article_publications= id= title= content= subtitle= original_id= persisted_state= ].to_set end it { should be_kind_of(Set) } it { should be_all { |method| method.kind_of?(String) } } it { should be_frozen } it 'is idempotent' do should equal(instance_eval(&self.class.subject)) end it { should eql(expected_writer_methods) } end end end