share_examples_for 'A public Resource' do
  before :all do
    @no_join = defined?(DataMapper::Adapters::InMemoryAdapter) && @adapter.kind_of?(DataMapper::Adapters::InMemoryAdapter) ||
               defined?(DataMapper::Adapters::YamlAdapter)     && @adapter.kind_of?(DataMapper::Adapters::YamlAdapter)

    relationship        = @user_model.relationships[:referrer]
    @one_to_one_through = relationship.kind_of?(DataMapper::Associations::OneToOne::Relationship) && relationship.respond_to?(:through)

    @skip = @no_join && @one_to_one_through
  end

  before :all do
    unless @skip
      %w[ @user_model @user @comment_model ].each do |ivar|
        raise "+#{ivar}+ should be defined in before block" unless instance_variable_get(ivar)
      end
    end
  end

  before do
    pending if @skip
  end

  [ :==, :=== ].each do |method|
    it { @user.should respond_to(method) }

    describe "##{method}" do
      describe 'when comparing to the same resource' do
        before :all do
          @other  = @user
          @return = @user.__send__(method, @other)
        end

        it 'should return true' do
          @return.should be(true)
        end
      end

      describe 'when comparing to an resource that does not respond to resource methods' do
        before :all do
          @other  = Object.new
          @return = @user.__send__(method, @other)
        end

        it 'should return false' do
          @return.should be(false)
        end
      end

      describe 'when comparing to a resource with the same properties, but the model is a subclass' do
        before :all do
          rescue_if @skip do
            @other  = @author_model.new(@user.attributes)
            @return = @user.__send__(method, @other)
          end
        end

        it 'should return true' do
          @return.should be(true)
        end
      end

      describe 'when comparing to a resource with the same repository, key and neither self or the other resource is dirty' do
        before :all do
          rescue_if @skip do
            @other  = @user_model.get(*@user.key)
            @return = @user.__send__(method, @other)
          end
        end

        it 'should return true' do
          @return.should be(true)
        end
      end

      describe 'when comparing to a resource with the same repository, key but either self or the other resource is dirty' do
        before :all do
          rescue_if @skip do
            @user.age = 20
            @other  = @user_model.get(*@user.key)
            @return = @user.__send__(method, @other)
          end
        end

        it 'should return false' do
          @return.should be(false)
        end
      end

      describe 'when comparing to a resource with the same properties' do
        before :all do
          rescue_if @skip do
            @other  = @user_model.new(@user.attributes)
            @return = @user.__send__(method, @other)
          end
        end

        it 'should return true' do
          @return.should be(true)
        end
      end

      with_alternate_adapter do
        before :all do
          if @user_model.respond_to?(:auto_migrate!)
            # force the user model to be available in the alternate repository
            @user_model.auto_migrate!(@adapter.name)
          end
        end

        describe 'when comparing to a resource with a different repository, but the same properties' do
          before :all do
            rescue_if @skip do
              @other = @repository.scope { @user_model.create(@user.attributes) }
              @return = @user.__send__(method, @other)
            end
          end

          it 'should return false' do
            @return.should be(false)
          end
        end
      end
    end
  end

  it { @user.should respond_to(:<=>) }

  describe '#<=>' do
    describe 'when the default order properties are equal with another resource' do
      before :all do
        rescue_if @skip && RUBY_VERSION < '1.9.2' do
          @other = @user_model.new(:name => 'dbussink')
          @return = @user <=> @other
        end
      end

      it 'should return 0' do
        @return.should == 0
      end
    end

    describe 'when the default order property values are sorted before another resource' do
      before :all do
        rescue_if @skip && RUBY_VERSION < '1.9.2' do
          @other = @user_model.new(:name => 'c')
          @return = @user <=> @other
        end
      end

      it 'should return 1' do
        @return.should == 1
      end
    end

    describe 'when the default order property values are sorted after another resource' do
      before :all do
        rescue_if @skip && RUBY_VERSION < '1.9.2' do
          @other = @user_model.new(:name => 'e')
          @return = @user <=> @other
        end
      end

      it 'should return -1' do
        @return.should == -1
      end
    end

    describe 'when comparing an unrelated type of Object' do
      it 'should raise an exception' do
        lambda { @user <=> @comment_model.new }.should raise_error(ArgumentError, "Cannot compare a #{@comment_model} instance with a #{@user_model} instance")
      end
    end
  end

  it { @user.should respond_to(:attribute_get) }

  describe '#attribute_get' do

    it { @user.attribute_get(:name).should == 'dbussink' }

  end

  it { @user.should respond_to(:attribute_set) }

  describe '#attribute_set' do

    before { @user.attribute_set(:name, 'dkubb') }

    it { @user.name.should == 'dkubb' }

  end

  it { @user.should respond_to(:attributes) }

  describe '#attributes' do
    describe 'with a new resource' do
      before :all do
        rescue_if @skip do
          @user = @user.model.new
        end
      end

      it 'should return the expected values' do
        @user.attributes.should == {}
      end
    end

    describe 'with a new resource with a property set' do
      before :all do
        rescue_if @skip do
          @user = @user.model.new
          @user.name = 'dbussink'
        end
      end

      it 'should return the expected values' do
        @user.attributes.should == {:name => 'dbussink'}
      end
    end

    describe 'with a saved resource' do
      it 'should return the expected values' do
        DataMapper::Ext::Hash.only(@user.attributes, :name, :description, :age).should ==
          { :name => 'dbussink', :description => 'Test', :age => 25 }
      end
    end
  end

  it { @user.should respond_to(:attributes=) }

  describe '#attributes=' do
    describe 'when a public mutator is specified' do
      before :all do
        rescue_if @skip do
          @user.attributes = { :name => 'dkubb', @user.class.properties[:age] => '42' }
        end
      end

      it 'should set the value' do
        @user.name.should eql('dkubb')
      end

      it 'should typecast and set the value' do
        @user.age.should eql(42)
      end
    end

    describe 'when a non-public mutator is specified' do
      it 'should raise an exception' do
        lambda {
          @user.attributes = { :admin => true }
        }.should raise_error(ArgumentError, "The attribute \'admin\' is not accessible in #{@user_model}")
      end
    end
  end

  [ :destroy, :destroy! ].each do |method|
    it { @user.should respond_to(:destroy) }

    describe "##{method}" do
      describe 'on a single resource' do
        before :all do
          @resource = @user_model.create(:name => 'hacker', :age => 20, :comment => @comment)

          @return = @resource.__send__(method)
        end

        it 'should successfully remove a resource' do
          @return.should be(true)
        end

        it 'should mark the destroyed resource as readonly' do
          @resource.should be_readonly
        end

        it "should return true when calling #{method} on a destroyed resource" do
          @resource.__send__(method).should be(true)
        end

        it 'should remove resource from persistent storage' do
          @user_model.get(*@resource.key).should be_nil
        end
      end

      describe 'with has relationship resources' do
        it 'should raise an exception'
      end
    end
  end

  it { @user.should respond_to(:dirty?) }

  describe '#dirty?' do
    describe 'on a record, with dirty attributes' do
      before { @user.age = 100 }

      it { @user.should be_dirty }
    end

    describe 'on a record, with no dirty attributes, and dirty parents' do
      before :all do
        rescue_if @skip do
          @user.should_not be_dirty

          parent = @user.parent = @user_model.new(:name => 'Parent')
          parent.should be_dirty
        end
      end

      it { @user.should be_dirty }
    end

    describe 'on a record, with no dirty attributes, and dirty children' do
      before :all do
        rescue_if @skip do
          @user.should_not be_dirty

          child = @user.children.new(:name => 'Child')
          child.should be_dirty
        end
      end

      it { @user.should be_dirty }
    end

    describe 'on a record, with no dirty attributes, and dirty siblings' do
      before :all do
        rescue_if @skip do
          @user.should_not be_dirty

          parent = @user_model.create(:name => 'Parent', :comment => @comment)
          parent.should_not be_dirty

          @user.update(:parent => parent)
          @user.should_not be_dirty

          sibling = parent.children.new(:name => 'Sibling')
          sibling.should be_dirty
          parent.should be_dirty
        end
      end

      it { @user.should_not be_dirty }
    end

    describe 'on a saved record, with no dirty attributes' do
      it { @user.should_not be_dirty }
    end

    describe 'on a new record, with no dirty attributes, no default attributes, and no identity field' do
      before { @user = @user_model.new }

      it { @user.should_not be_dirty }
    end

    describe 'on a new record, with no dirty attributes, no default attributes, and an identity field' do
      before { @comment = @comment_model.new }

      it { @comment.should be_dirty }
    end

    describe 'on a new record, with no dirty attributes, default attributes, and no identity field' do
      before { @default = Default.new }

      it { @default.should be_dirty }
    end

    describe 'on a record with itself as a parent (circular dependency)' do
      before :all do
        rescue_if @skip do
          @user.parent = @user
        end
      end

      it 'should not raise an exception' do
        lambda {
          @user.dirty?.should be(true)
        }.should_not raise_error(SystemStackError)
      end
    end

    describe 'on a record with itself as a child (circular dependency)' do
      before :all do
        rescue_if @skip do
          @user.children = [ @user ]
        end
      end

      it 'should not raise an exception' do
        lambda {
          @user.dirty?.should be(true)
        }.should_not raise_error(SystemStackError)
      end
    end

    describe 'on a record with a parent as a child (circular dependency)' do
      before :all do
        rescue_if @skip do
          @user.children = [ @user.parent = @user_model.new(:name => 'Parent', :comment => @comment) ]
          @user.save.should be(true)
        end
      end

      it 'should not raise an exception' do
        lambda {
          @user.dirty?.should be(true)
        }.should_not raise_error(SystemStackError)
      end
    end
  end

  it { @user.should respond_to(:eql?) }

  describe '#eql?' do
    describe 'when comparing to the same resource' do
      before :all do
        @other  = @user
        @return = @user.eql?(@other)
      end

      it 'should return true' do
        @return.should be(true)
      end
    end

    describe 'when comparing to an resource that does not respond to model' do
      before :all do
        @other  = Object.new
        @return = @user.eql?(@other)
      end

      it 'should return false' do
        @return.should be(false)
      end
    end

    describe 'when comparing to a resource with the same properties, but the model is a subclass' do
      before :all do
        rescue_if @skip do
          @other  = @author_model.new(@user.attributes)
          @return = @user.eql?(@other)
        end
      end

      it 'should return false' do
        @return.should be(false)
      end
    end

    describe 'when comparing to a resource with a different key' do
      before :all do
        @other  = @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment)
        @return = @user.eql?(@other)
      end

      it 'should return false' do
        @return.should be(false)
      end
    end

    describe 'when comparing to a resource with the same repository, key and neither self or the other resource is dirty' do
      before :all do
        rescue_if @skip do
          @other  = @user_model.get(*@user.key)
          @return = @user.eql?(@other)
        end
      end

      it 'should return true' do
        @return.should be(true)
      end
    end

    describe 'when comparing to a resource with the same repository, key but either self or the other resource is dirty' do
      before :all do
        rescue_if @skip do
          @user.age = 20
          @other  = @user_model.get(*@user.key)
          @return = @user.eql?(@other)
        end
      end

      it 'should return false' do
        @return.should be(false)
      end
    end

    describe 'when comparing to a resource with the same properties' do
      before :all do
        rescue_if @skip do
          @other  = @user_model.new(@user.attributes)
          @return = @user.eql?(@other)
        end
      end

      it 'should return true' do
        @return.should be(true)
      end
    end

    with_alternate_adapter do
      before :all do
        if @user_model.respond_to?(:auto_migrate!)
          # force the user model to be available in the alternate repository
          @user_model.auto_migrate!(@adapter.name)
        end
      end

      describe 'when comparing to a resource with a different repository, but the same properties' do
        before :all do
          rescue_if @skip do
            @other = @repository.scope { @user_model.create(@user.attributes) }
            @return = @user.eql?(@other)
          end
        end

        it 'should return false' do
          @return.should be(false)
        end
      end
    end
  end

  it { @user.should respond_to(:inspect) }

  describe '#inspect' do

    before :all do
      rescue_if @skip do
        @user = @user_model.get(*@user.key)
        @inspected = @user.inspect
      end
    end

    it { @inspected.should match(/^#<#{@user_model}/) }

    it { @inspected.should match(/name="dbussink"/) }

    it { @inspected.should match(/age=25/) }

    it { @inspected.should match(/description=<not loaded>/) }

  end

  it { @user.should respond_to(:key) }

  describe '#key' do

    before :all do
      rescue_if @skip do
        @key = @user.key
        @user.name = 'dkubb'
      end
    end

    it { @key.should be_kind_of(Array) }

    it 'should always return the key value persisted in the back end' do
      @key.first.should eql("dbussink")
    end

    it { @user.key.should eql(@key) }

  end

  it { @user.should respond_to(:new?) }

  describe '#new?' do

    describe 'on an existing record' do

      it { @user.should_not be_new }

    end

    describe 'on a new record' do

      before { @user = @user_model.new }

      it { @user.should be_new }

    end

  end

  it { @user.should respond_to(:reload) }

  describe '#reload' do
    before do
      # reset the user for each spec
      rescue_if(@skip) do
        @user.update(:name => 'dbussink', :age => 25, :description => 'Test')
      end
    end

    subject { rescue_if(@skip) { @user.reload } }

    describe 'on a resource not persisted' do
      before do
        @user.attributes = { :description => 'Changed' }
      end

      it { should be_kind_of(DataMapper::Resource) }

      it { should equal(@user) }

      it { should be_clean }

      it 'reset the changed attributes' do
        method(:subject).should change(@user, :description).from('Changed').to('Test')
      end
    end

    describe 'on a resource where the key is changed, but not persisted' do
      before do
        @user.attributes = { :name => 'dkubb' }
      end

      it { should be_kind_of(DataMapper::Resource) }

      it { should equal(@user) }

      it { should be_clean }

      it 'reset the changed attributes' do
        method(:subject).should change(@user, :name).from('dkubb').to('dbussink')
      end
    end

    describe 'on a resource that is changed outside another resource' do
      before do
        rescue_if @skip do
          @user.dup.update(:description => 'Changed')
        end
      end

      it { should be_kind_of(DataMapper::Resource) }

      it { should equal(@user) }

      it { should be_clean }

      it 'should reload the resource from the data store' do
        method(:subject).should change(@user, :description).from('Test').to('Changed')
      end
    end

    describe 'on an anonymous resource' do
      before do
        rescue_if @skip do
          @user = @user.model.first(:fields => [ :description ])
          @user.description.should == 'Test'
        end
      end

      it { should be_kind_of(DataMapper::Resource) }

      it { should equal(@user) }

      it { should be_clean }

      it 'should not reload any attributes' do
        method(:subject).should_not change(@user, :attributes)
      end
    end
  end

  it { @user.should respond_to(:readonly?) }

  describe '#readonly?' do
    describe 'on a new resource' do
      before :all do
        rescue_if @skip do
          @user = @user.model.new
        end
      end

      it 'should return false' do
        @user.readonly?.should be(false)
      end
    end

    describe 'on a saved resource' do
      before :all do
        rescue_if @skip do
          @user.should be_saved
        end
      end

      it 'should return false' do
        @user.readonly?.should be(false)
      end
    end

    describe 'on a destroyed resource' do
      before :all do
        rescue_if @skip do
          @user.destroy.should be(true)
        end
      end

      it 'should return true' do
        @user.readonly?.should be(true)
      end
    end

    describe 'on an anonymous resource' do
      before :all do
        rescue_if @skip do
          # load the user without a key
          @user = @user.model.first(:fields => @user_model.properties - @user_model.key)
        end
      end

      it 'should return true' do
        @user.readonly?.should be(true)
      end
    end
  end

  [ :save, :save! ].each do |method|
    it { @user.should respond_to(method) }

    describe "##{method}" do
      before :all do
        @user_model.class_eval do
          attr_accessor :save_hook_call_count

          before :save do
            @save_hook_call_count ||= 0
            @save_hook_call_count += 1
          end
        end
      end

      describe 'on a new, not dirty resource' do
        before :all do
          @user = @user_model.new
          @return = @user.__send__(method)
        end

        it 'should return false' do
          @return.should be(false)
        end

        it 'should call save hook expected number of times' do
          @user.save_hook_call_count.should be_nil
        end
      end

      describe 'on a not new, not dirty resource' do
        before :all do
          rescue_if @skip do
            @return = @user.__send__(method)
          end
        end

        it 'should return true even when resource is not dirty' do
          @return.should be(true)
        end

        it 'should call save hook expected number of times' do
          @user.save_hook_call_count.should be_nil
        end
      end

      describe 'on a not new, dirty resource' do
        before :all do
          rescue_if @skip do
            @user.age = 26
            @return = @user.__send__(method)
          end
        end

        it 'should save a resource succesfully when dirty' do
          @return.should be(true)
        end

        it 'should actually store the changes to persistent storage' do
          @user.attributes.should == @user.reload.attributes
        end

        it 'should call save hook expected number of times' do
          @user.save_hook_call_count.should == (method == :save ? 1 : nil)
        end
      end

      describe 'on a new, invalid resource' do
        before :all do
          @user = @user_model.new(:name => nil)
          expect { @user.__send__(method) }.to(raise_error(DataMapper::Property::InvalidValueError) do |error|
            error.property.should == @user_model.properties[:name]
          end)
        end

        it 'should call save hook expected number of times' do
          @user.save_hook_call_count.should == (method == :save ? 1 : nil)
        end
      end

      describe 'on a dirty invalid resource' do
        before :all do
          rescue_if @skip do
            @user.name = nil
          end
        end

        it 'should not save an invalid resource' do
          expect { @user.__send__(method) }.to(raise_error(DataMapper::Property::InvalidValueError) do |error|
            error.property.should == @user_model.properties[:name]
          end)
        end

        it 'should call save hook expected number of times' do
          @user.save_hook_call_count.should == (method == :save ? 1 : nil)
        end
      end

      describe 'with new resources in a has relationship' do
        before do
          rescue_if 'TODO: fix for one to one association', !@user.respond_to?(:comments) do
            @initial_comments = @user.comments.size
            @first_comment    = @user.comments.new(:body => "DM is great!")
            @second_comment   = @comment_model.new(:user => @user, :body => "is it really?")
            @return           = @user.__send__(method)
          end
        end

        it 'should save resource' do
          pending_if !@user.respond_to?(:comments) do
            @return.should be(true)
          end
        end

        it 'should save the first resource created through new' do
          pending_if !@user.respond_to?(:comments) do
            @first_comment.new?.should be(false)
          end
        end

        it 'should save the correct foreign key for the first resource' do
          pending_if !@user.respond_to?(:comments) do
            @first_comment.user.should eql(@user)
          end
        end

        it 'should save the second resource created through the constructor' do
          pending "Changing a belongs_to parent should add the resource to the correct association" do
            @second_comment.new?.should be(false)
          end
        end

        it 'should save the correct foreign key for the second resource' do
          pending_if !@user.respond_to?(:comments) do
            @second_comment.user.should eql(@user)
          end
        end

        it 'should create 2 extra resources in persistent storage' do
          pending "Changing a belongs_to parent should add the resource to the correct association" do
            @user.comments.size.should == @initial_comments + 2
          end
        end
      end

      describe 'with dirty resources in a has relationship' do
        before :all do
          rescue_if 'TODO: fix for one to one association', !@user.respond_to?(:comments) do
            @first_comment  = @user.comments.create(:body => 'DM is great!')
            @second_comment = @comment_model.create(:user => @user, :body => 'is it really?')

            @first_comment.body  = 'It still has rough edges'
            @second_comment.body = 'But these cool specs help fixing that'
            @second_comment.user = @user_model.create(:name => 'dkubb')

            @return = @user.__send__(method)
          end
        end

        it 'should return true' do
          pending_if !@user.respond_to?(:comments) do
            @return.should be(true)
          end
        end

        it 'should not be dirty' do
          @user.should_not be_dirty
        end

        it 'should have saved the first child resource' do
          pending_if !@user.respond_to?(:comments) do
            @first_comment.model.get(*@first_comment.key).body.should == 'It still has rough edges'
          end
        end

        it 'should not have saved the second child resource' do
          pending_if !@user.respond_to?(:comments) do
            @second_comment.model.get(*@second_comment.key).body.should == 'is it really?'
          end
        end
      end

      describe 'with a new dependency' do
        before :all do
          @first_comment      = @comment_model.new(:body => "DM is great!")
          @first_comment.user = @user_model.new(:name => 'dkubb')
        end

        it 'should not raise an exception when saving the resource' do
          pending do
            lambda { @first_comment.send(method).should be(false) }.should_not raise_error
          end
        end
      end

      describe 'with a dirty dependency' do
        before :all do
          rescue_if @skip do
            @user.name = 'dbussink-the-second'

            @first_comment = @comment_model.new(:body => 'DM is great!')
            @first_comment.user = @user

            @return = @first_comment.__send__(method)
          end
        end

        it 'should succesfully save the resource' do
          @return.should be(true)
        end

        it 'should not have a dirty dependency' do
          @user.should_not be_dirty
        end

        it 'should succesfully save the dependency' do
          @user.name.should == @user_model.get(*@user.key).name
        end
      end

      describe 'with a new resource and new relations' do
        before :all do
          @article = @article_model.new(:body => "Main")
          rescue_if 'TODO: fix for one to one association', (!@article.respond_to?(:paragraphs)) do
            @paragraph = @article.paragraphs.new(:text => 'Content')

            @article.__send__(method)
          end
        end

        it 'should not be dirty' do
          pending_if !@article.respond_to?(:paragraphs) do
            @article.should_not be_dirty
          end
        end

        it 'should not be dirty' do
          pending_if !@article.respond_to?(:paragraphs) do
            @paragraph.should_not be_dirty
          end
        end

        it 'should set the related resource' do
          pending_if !@article.respond_to?(:paragraphs) do
            @paragraph.article.should == @article
          end
        end

        it 'should set the foreign key properly' do
          pending_if !@article.respond_to?(:paragraphs) do
            @paragraph.article_id.should == @article.id
          end
        end
      end

      describe 'with a dirty resource with a changed key' do
        before :all do
          rescue_if @skip do
            @original_key = @user.key
            @user.name = 'dkubb'
            @return = @user.__send__(method)
          end
        end

        it 'should save a resource succesfully when dirty' do
          @return.should be(true)
        end

        it 'should actually store the changes to persistent storage' do
          @user.name.should == @user.reload.name
        end

        it 'should update the identity map' do
          @user.repository.identity_map(@user_model).should have_key(%w[ dkubb ])
        end

        it 'should remove the old entry from the identity map' do
          @user.repository.identity_map(@user_model).should_not have_key(@original_key)
        end
      end

      describe 'on a new resource with unsaved parent and grandparent' do
        before :all do
          @grandparent = @user_model.new(:name => 'dkubb',       :comment => @comment)
          @parent      = @user_model.new(:name => 'ashleymoran', :comment => @comment, :referrer => @grandparent)
          @child       = @user_model.new(:name => 'mrship',      :comment => @comment, :referrer => @parent)

          @response = @child.__send__(method)
        end

        it 'should return true' do
          @response.should be(true)
        end

        it 'should save the child' do
          @child.should be_saved
        end

        it 'should save the parent' do
          @parent.should be_saved
        end

        it 'should save the grandparent' do
          @grandparent.should be_saved
        end

        it 'should relate the child to the parent' do
          @child.model.get(*@child.key).referrer.should == @parent
        end

        it 'should relate the parent to the grandparent' do
          @parent.model.get(*@parent.key).referrer.should == @grandparent
        end

        it 'should relate the grandparent to nothing' do
          @grandparent.model.get(*@grandparent.key).referrer.should be_nil
        end
      end

      describe 'on a destroyed resource' do
        before :all do
          rescue_if @skip do
            @user.destroy
          end
        end

        it 'should raise an exception' do
          lambda {
            @user.__send__(method)
          }.should raise_error(DataMapper::PersistenceError, "#{@user.model}##{method} cannot be called on a destroyed resource")
        end
      end

      describe 'on a record with itself as a parent (circular dependency)' do
        before :all do
          rescue_if @skip do
            @user.parent = @user
          end
        end

        it 'should not raise an exception' do
          lambda {
            @user.__send__(method).should be(true)
          }.should_not raise_error(SystemStackError)
        end
      end

      describe 'on a record with itself as a child (circular dependency)' do
        before :all do
          rescue_if @skip do
            @user.children = [ @user ]
          end
        end

        it 'should not raise an exception' do
          lambda {
            @user.__send__(method).should be(true)
          }.should_not raise_error(SystemStackError)
        end
      end

      describe 'on a record with a parent as a child (circular dependency)' do
        before :all do
          rescue_if @skip do
            @user.children = [ @user.parent = @user_model.new(:name => 'Parent', :comment => @comment) ]
          end
        end

        it 'should not raise an exception' do
          lambda {
            @user.__send__(method).should be(true)
          }.should_not raise_error(SystemStackError)
        end
      end
    end
  end

  it { @user.should respond_to(:saved?) }

  describe '#saved?' do

    describe 'on an existing record' do

      it { @user.should be_saved }

    end

    describe 'on a new record' do

      before { @user = @user_model.new }

      it { @user.should_not be_saved }

    end

  end

  [ :update, :update! ].each do |method|
    it { @user.should respond_to(method) }

    describe "##{method}" do
      describe 'with attributes' do
        before :all do
          rescue_if @skip do
            @attributes = { :description => 'Changed' }
            @return = @user.__send__(method, @attributes)
          end
        end

        it 'should return true' do
          @return.should be(true)
        end

        it 'should update attributes of Resource' do
          @attributes.each { |key, value| @user.__send__(key).should == value }
        end

        it 'should persist the changes' do
          resource = @user_model.get(*@user.key)
          @attributes.each { |key, value| resource.__send__(key).should == value }
        end
      end

      describe 'with attributes where one is a parent association' do
        before :all do
          rescue_if @skip do
            @attributes = { :referrer => @user_model.create(:name => 'dkubb', :age => 33, :comment => @comment) }
            @return = @user.__send__(method, @attributes)
          end
        end

        it 'should return true' do
          @return.should be(true)
        end

        it 'should update attributes of Resource' do
          @attributes.each { |key, value| @user.__send__(key).should == value }
        end

        it 'should persist the changes' do
          resource = @user_model.get(*@user.key)
          @attributes.each { |key, value| resource.__send__(key).should == value }
        end
      end

      describe 'with attributes where a value is nil for a property that does not allow nil' do
        before do
          expect { @user.__send__(method, :name => nil) }.to(raise_error(DataMapper::Property::InvalidValueError) do |error|
            error.property.should == @user_model.properties[:name]
          end)
        end

        it 'should not persist the changes' do
          @user.reload.name.should_not be_nil
        end
      end

      describe 'on a new resource' do
        before :all do
          rescue_if @skip do
            @user = @user.model.new(@user.attributes)
            @user.age = 99
          end
        end

        it 'should raise an exception' do
          lambda {
            @user.__send__(method, :admin => true)
          }.should raise_error(DataMapper::UpdateConflictError, "#{@user.model}##{method} cannot be called on a new resource")
        end
      end

      describe 'on a dirty resource' do
        before :all do
          rescue_if @skip do
            @user.age = 99
          end
        end

        it 'should raise an exception' do
          lambda {
            @user.__send__(method, :admin => true)
          }.should raise_error(DataMapper::UpdateConflictError, "#{@user.model}##{method} cannot be called on a dirty resource")
        end
      end
    end
  end

  describe 'lazy loading' do
    before :all do
      rescue_if @skip do
        @user.name    = 'dkubb'
        @user.age     = 33
        @user.summary = 'Programmer'

        # lazy load the description
        @user.description
      end
    end

    it 'should not overwrite dirty attribute' do
      @user.age.should == 33
    end

    it 'should not overwrite dirty lazy attribute' do
      @user.summary.should == 'Programmer'
    end

    it 'should not overwrite dirty key' do
      pending do
        @user.name.should == 'dkubb'
      end
    end
  end
end
