require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))

# ------------------------------------------------------------
# -----   Read SPECS for information about how to read   -----
# -----   and contribute to the DataMapper specs.        -----
# ------------------------------------------------------------

if ADAPTER
  describe "DataMapper::Resource with #{ADAPTER}" do

    load_models_for_metaphor :zoo

    before(:each) do
      DataMapper.auto_migrate!(ADAPTER)
      @zoo = Zoo.new(:name => "San Francisco")
      repository(ADAPTER) { @zoo.save }
    end

  # --- Move somewhere ----
    it "should be able to destroy objects" do
      lambda { @zoo.destroy.should be_true }.should_not raise_error
    end

    describe '#attribute_get' do
      it 'should provide #attribute_get' do
        Zoo.new.should respond_to(:attribute_get)
      end

      it 'should delegate to Property#get' do
        Zoo.properties[:name].should_receive(:get).with(zoo = Zoo.new)
        zoo.name
      end

      it "should return Property#get's return value"  do
        Zoo.properties[:name].should_receive(:get).and_return("San Francisco")
        Zoo.new.name.should == "San Francisco"
      end
    end

    describe '#attribute_set' do
      it "should provide #attribute_set" do
        Zoo.new.should respond_to(:attribute_set)
      end

      it 'should delegate to Property#set' do
        Zoo.properties[:name].should_receive(:set).with(zoo = Zoo.new, "San Francisco")
        zoo.name = "San Francisco"
      end
    end

    describe '#eql?' do
      it "should return true if the objects are the same instances"
      it "should return false if the other object is not an instance of the same model"
      it "should return false if the other object is a different class"
      it "should return true if the repositories are the same and the primary key is the same"
      it "should return true if all the properties are the same"
      it "should return false if any of the properties are different"
    end

    describe '#hash' do
      it "should return the same hash values for unsaved objects that are equal" do
        e1 = Employee.new(:name => "John")
        e2 = Employee.new(:name => "John")
        e1.hash.should == e2.hash
      end

      it "should return the same hash values for saved objects that are equal" do
        # Make sure that the object_id's are not the same
        e1 = e2 = nil
        repository(ADAPTER) do
          e1 = Employee.create(:name => "John")
        end
        repository(ADAPTER) do
          e2 = Employee.get("John")
        end
        e1.hash.should == e2.hash
      end

      it "should return a different hash value for different objects of the same type" do
        e1 = Employee.create(:name => "John")
        e2 = Employee.create(:name => "Dan")
        e1.hash.should_not == e2.hash
      end

      it "should return a different hash value for different types of objects with the same key"
    end

    describe '#id' do
      it "should be awesome"
    end

    describe '#inspect' do
      it "should return a string representing the object"
    end

    describe '#key' do
      describe "original_value[:key]" do
        it "should be used when an existing resource's key changes" do
          employee = Employee.create(:name => "John")
          employee.name = "Jon"
          employee.key.should == ["John"]
        end

        it "should be used when saving an existing resource" do
          repository(ADAPTER) do
            employee = Employee.create(:name => "John")
            employee.name = "Jon"
            employee.save.should == true
            Employee.get("Jon").should == employee
          end
        end

        it "should not be used when a new resource's key changes" do
          employee = Employee.new(:name => "John")
          employee.name = "Jon"
          employee.key.should == ["Jon"]
        end
      end
    end

    describe '#pretty_print' do
      it "should display a pretty version of inspect"
    end

    describe '#save' do

      describe 'with a new resource' do
        it 'should set defaults before create'
        it 'should create when dirty'
        it 'should create when non-dirty, and it has a serial key'
      end

      describe 'with an existing resource' do
        it 'should update'
      end

    end

    describe '#repository' do
      it "should return the repository associated with the object if there is one"
      it "should return the repository associated with the model if the object doesn't have one"
    end
  end
end




# ---------- Old specs... BOOOOOOOOOO ---------------
if ADAPTER
  class Orange
    include DataMapper::Resource

    def self.default_repository_name
      ADAPTER
    end

    property :name, String, :key => true
    property :color, String
  end

  class Apple
    include DataMapper::Resource

    def self.default_repository_name
      ADAPTER
    end

    property :id, Serial
    property :color, String, :default => 'green', :nullable => true
  end

  class FortunePig
    include DataMapper::Resource

    def self.default_repository_name
      ADAPTER
    end

    property :id, Serial
    property :name, String

    def to_s
      name
    end

    after :create do
      @created_id = self.id
    end

    after :save do
      @save_id = self.id
    end
  end

  class Car
    include DataMapper::Resource

    def self.default_repository_name
      ADAPTER
    end

    property :brand, String, :key => true
    property :color, String
    property :created_on, Date
    property :touched_on, Date
    property :updated_on, Date

    before :save do
      self.touched_on = Date.today
    end

    before :create do
      self.created_on = Date.today
    end

    before :update do
      self.updated_on = Date.today
    end
  end

  class Male
    include DataMapper::Resource

    def self.default_repository_name
      ADAPTER
    end

    property :id, Serial
    property :name, String
    property :iq, Integer, :default => 100
    property :type, Discriminator
    property :data, Object

    def iq=(i)
      attribute_set(:iq, i - 1)
    end
  end

  class Bully < Male; end

  class Mugger < Bully; end

  class Maniac < Bully; end

  class Psycho < Maniac; end

  class Geek < Male
    property :awkward, Boolean, :default => true

    def iq=(i)
      attribute_set(:iq, i + 30)
    end
  end

  class Flanimal
    include DataMapper::Resource

    def self.default_repository_name
      ADAPTER
    end

    property :id, Serial
    property :type, Discriminator
    property :name, String
  end

  class Sprog < Flanimal; end

  describe "DataMapper::Resource with #{ADAPTER}" do
    before :all do
      Orange.auto_migrate!(ADAPTER)
      Apple.auto_migrate!(ADAPTER)
      FortunePig.auto_migrate!(ADAPTER)

      orange = Orange.new(:color => 'orange')
      orange.name = 'Bob' # Keys are protected from mass-assignment by default.
      repository(ADAPTER) { orange.save }
    end

    it "should be able to overwrite Resource#to_s" do
      repository(ADAPTER) do
        ted = FortunePig.create(:name => "Ted")
        FortunePig.get!(ted.id).to_s.should == 'Ted'
      end
    end

    it "should be able to destroy objects" do
      apple = Apple.create(:color => 'Green')
      lambda do
        apple.destroy.should be_true
      end.should_not raise_error
    end

    it 'should return false to #destroy if the resource is new' do
      Apple.new.destroy.should be_false
    end

    it "should be able to reload objects" do
      orange = repository(ADAPTER) { Orange.get!('Bob') }
      orange.color.should == 'orange'
      orange.color = 'blue'
      orange.color.should == 'blue'
      orange.reload
      orange.color.should == 'orange'
    end

    it "should be able to reload new objects" do
      repository(ADAPTER) do
        Orange.create(:name => 'Tom').reload
      end
    end

    it "should be able to find first or create objects" do
      repository(ADAPTER) do
        orange = Orange.create(:name => 'Naval')

        Orange.first_or_create(:name => 'Naval').should == orange

        purple = Orange.first_or_create(:name => 'Purple', :color => 'Fuschia')
        oranges = Orange.all(:name => 'Purple')
        oranges.size.should == 1
        oranges.first.should == purple
      end
    end

    it "should be able to override a default with a nil" do
      repository(ADAPTER) do
        apple = Apple.new
        apple.color = nil
        apple.save
        apple.color.should be_nil

        apple = Apple.create(:color => nil)
        apple.color.should be_nil
      end
    end

    it "should be able to respond to create hooks" do
      bob = repository(ADAPTER) { FortunePig.create(:name => 'Bob') }
      bob.id.should_not be_nil
      bob.instance_variable_get("@created_id").should == bob.id

      fred = FortunePig.new(:name => 'Fred')
      repository(ADAPTER) { fred.save }
      fred.id.should_not be_nil
      fred.instance_variable_get("@save_id").should == fred.id
    end

    it "should be dirty when Object properties are changed" do
      # pending "Awaiting Property#track implementation"
      repository(ADAPTER) do
        Male.auto_migrate!
      end
      repository(ADAPTER) do
        bob = Male.create(:name => "Bob", :data => {})
        bob.dirty?.should be_false
        bob.data.merge!(:name => "Bob")
        bob.dirty?.should be_true
        bob = Male.first
        bob.data[:name] = "Bob"
        bob.dirty?.should be_true
      end
    end

    describe "hooking" do
      before :all do
        Car.auto_migrate!(ADAPTER)
      end

      it "should execute hooks before creating/updating objects" do
        repository(ADAPTER) do
          c1 = Car.new(:brand => 'BMW', :color => 'white')

          c1.new_record?.should == true
          c1.created_on.should == nil

          c1.save

          c1.new_record?.should == false
          c1.touched_on.should == Date.today
          c1.created_on.should == Date.today
          c1.updated_on.should == nil

          c1.color = 'black'
          c1.save

          c1.updated_on.should == Date.today
        end

      end

    end

    describe "inheritance" do
      before :all do
        Geek.auto_migrate!(ADAPTER)

        repository(ADAPTER) do
          Male.create(:name => 'John Dorian')
          Bully.create(:name => 'Bob', :iq => 69)
          Geek.create(:name => 'Steve', :awkward => false, :iq => 132)
          Geek.create(:name => 'Bill', :iq => 150)
          Bully.create(:name => 'Johnson')
          Mugger.create(:name => 'Frank')
          Maniac.create(:name => 'William')
          Psycho.create(:name => 'Norman')
        end

        Flanimal.auto_migrate!(ADAPTER)

      end

      it "should test bug ticket #302" do
        repository(ADAPTER) do
          Sprog.create(:name => 'Marty')
          Sprog.first(:name => 'Marty').should_not be_nil
        end
      end

      it "should select appropriate types" do
        repository(ADAPTER) do
          males = Male.all
          males.should have(8).entries

          males.each do |male|
            male.class.name.should == male.type.name
          end

          Male.first(:name => 'Steve').should be_a_kind_of(Geek)
          Bully.first(:name => 'Bob').should be_a_kind_of(Bully)
          Geek.first(:name => 'Steve').should be_a_kind_of(Geek)
          Geek.first(:name => 'Bill').should be_a_kind_of(Geek)
          Bully.first(:name => 'Johnson').should be_a_kind_of(Bully)
          Male.first(:name => 'John Dorian').should be_a_kind_of(Male)
        end
      end

      it "should not select parent type" do
        repository(ADAPTER) do
          Male.first(:name => 'John Dorian').should be_a_kind_of(Male)
          Geek.first(:name => 'John Dorian').should be_nil
          Geek.first.iq.should > Bully.first.iq
        end
      end

      it "should select objects of all inheriting classes" do
        repository(ADAPTER) do
          Male.all.should have(8).entries
          Geek.all.should have(2).entries
          Bully.all.should have(5).entries
          Mugger.all.should have(1).entries
          Maniac.all.should have(2).entries
          Psycho.all.should have(1).entries
        end
      end

      it "should inherit setter method from parent" do
        repository(ADAPTER) do
          Bully.first(:name => "Bob").iq.should == 68
        end
      end

      it "should be able to overwrite a setter in a child class" do
        repository(ADAPTER) do
          Geek.first(:name => "Bill").iq.should == 180
        end
      end
    end
  end
end
