diff --git a/lib/orm_adapter/adapters/active_record.rb b/lib/orm_adapter/adapters/active_record.rb index 9626f04..9ba1705 100644 --- a/lib/orm_adapter/adapters/active_record.rb +++ b/lib/orm_adapter/adapters/active_record.rb @@ -9,29 +9,33 @@ def column_names # @see OrmAdapter::Base#get! def get!(id) - klass.find(wrap_key(id)) + scoped.find(wrap_key(id)) end # @see OrmAdapter::Base#get def get(id) - klass.where(klass.primary_key => wrap_key(id)).first + scoped.where(klass.primary_key => wrap_key(id)).first end # @see OrmAdapter::Base#find_first def find_first(options = {}) conditions, order = extract_conditions!(options) - klass.where(conditions_to_fields(conditions)).order(*order_clause(order)).first + scoped.where(conditions_to_fields(conditions)).order(*order_clause(order)).first end # @see OrmAdapter::Base#find_all def find_all(options = {}) conditions, order, limit, offset = extract_conditions!(options) - klass.where(conditions_to_fields(conditions)).order(*order_clause(order)).limit(limit).offset(offset).all + scoped.where(conditions_to_fields(conditions)).order(*order_clause(order)).limit(limit).offset(offset).all + end + + def build(attributes = {}) + scoped.build(attributes) end # @see OrmAdapter::Base#create! def create!(attributes = {}) - klass.create!(attributes) + scoped.create!(attributes) end # @see OrmAdapter::Base#destroy @@ -41,6 +45,10 @@ def destroy(object) protected + def scoped + klass.where(conditions_to_fields(@scope)) + end + # Introspects the klass to convert and objects in conditions into foreign key and type fields def conditions_to_fields(conditions) fields = {} @@ -65,6 +73,42 @@ def conditions_to_fields(conditions) def order_clause(order) order.map {|pair| "#{pair[0]} #{pair[1]}"}.join(",") end + + #class Collection + # + # attr_reader :collection + # + # def initialize(collection) + # @collection = collection + # end + # + # def klass + # collection.klass + # end + # + # def klass_adapter + # @klass_adapter ||= klass.to_adapter + # end + # + # def find_first(options = {}) + # conditions, order = klass_adapter.send(:extract_conditions!, options) + # collection.scoped.where(klass_adapter.send(:conditions_to_fields, conditions)).order(*klass_adapter.send(:order_clause, order)).first + # end + # + # def find(options = {}) + # conditions, order, limit, offset = klass_adapter.send(:extract_conditions!, options) + # collection.scoped.where(klass_adapter.send(:conditions_to_fields, conditions)).order(*klass_adapter.send(:order_clause, order)).limit(limit).offset(offset) + # end + # + # def build(attributes = {}) + # collection.build(attributes) + # end + # + # def create!(attributes = {}) + # collection.create!(attributes) + # end + # + #end end end diff --git a/lib/orm_adapter/adapters/data_mapper.rb b/lib/orm_adapter/adapters/data_mapper.rb index 8fb1530..0dce69d 100644 --- a/lib/orm_adapter/adapters/data_mapper.rb +++ b/lib/orm_adapter/adapters/data_mapper.rb @@ -13,19 +13,24 @@ def column_names end # @see OrmAdapter::Base#get! - def get!(id) - klass.get!(id) + def get!(*id) + get(*id) || raise(DataMapper::ObjectNotFoundError) end # @see OrmAdapter::Base#get - def get(id) - klass.get(id) + def get(*id) + if @scope.empty? + klass.get(*id) + else + primary_key_conditions = klass.key_conditions(klass.repository, klass.key(klass.repository.name).typecast(id)).update(:order => nil) + klass.first(@scope.merge(primary_key_conditions)) + end end # @see OrmAdapter::Base#find_first def find_first(options = {}) conditions, order = extract_conditions!(options) - klass.first :conditions => conditions, :order => order_clause(order) + klass.first(scoped_query.update(:conditions => conditions, :order => order_clause(order))) end # @see OrmAdapter::Base#find_all @@ -34,12 +39,17 @@ def find_all(options = {}) opts = { :conditions => conditions, :order => order_clause(order) } opts = opts.merge({ :limit => limit }) unless limit.nil? opts = opts.merge({ :offset => offset }) unless offset.nil? - klass.all opts + klass.all(scoped_query.update(opts)) + end + + # @see OrmAdapter::Base#build + def build(attributes = {}) + klass.new(@scope.merge(attributes)) end # @see OrmAdapter::Base#create! def create!(attributes = {}) - klass.create(attributes) + klass.create(@scope.merge(attributes)) end # @see OrmAdapter::Base#destroy @@ -49,6 +59,10 @@ def destroy(object) protected + def scoped_query + klass.all(@scope).query + end + def order_clause(order) order.map {|pair| pair.first.send(pair.last)} end diff --git a/lib/orm_adapter/adapters/mongo_mapper.rb b/lib/orm_adapter/adapters/mongo_mapper.rb index 2937d3c..0f27d71 100644 --- a/lib/orm_adapter/adapters/mongo_mapper.rb +++ b/lib/orm_adapter/adapters/mongo_mapper.rb @@ -14,19 +14,19 @@ def column_names # @see OrmAdapter::Base#get! def get!(id) - klass.find!(wrap_key(id)) + scoped.find!(wrap_key(id)) end # @see OrmAdapter::Base#get def get(id) - klass.first({ :id => wrap_key(id) }) + scoped.first({ :id => wrap_key(id) }) end # @see OrmAdapter::Base#find_first def find_first(conditions = {}) conditions, order = extract_conditions!(conditions) conditions = conditions.merge(:sort => order) unless order.nil? - klass.first(conditions_to_fields(conditions)) + scoped.first(conditions_to_fields(conditions)) end # @see OrmAdapter::Base#find_all @@ -35,12 +35,17 @@ def find_all(conditions = {}) conditions = conditions.merge(:sort => order) unless order.nil? conditions = conditions.merge(:limit => limit) unless limit.nil? conditions = conditions.merge(:offset => offset) unless limit.nil? || offset.nil? - klass.all(conditions_to_fields(conditions)) + scoped.all(conditions_to_fields(conditions)) + end + + # @see OrmAdapter::Base#build + def build(attributes = {}) + klass.new(@scope.merge(attributes)) end # @see OrmAdapter::Base#create! def create!(attributes = {}) - klass.create!(attributes) + klass.create!(@scope.merge(attributes)) end # @see OrmAdapter::Base#destroy @@ -50,6 +55,10 @@ def destroy(object) protected + def scoped + klass.where(conditions_to_fields(@scope)) + end + # converts and documents to ids def conditions_to_fields(conditions) conditions.inject({}) do |fields, (key, value)| diff --git a/lib/orm_adapter/adapters/mongoid.rb b/lib/orm_adapter/adapters/mongoid.rb index aaca494..6df888a 100644 --- a/lib/orm_adapter/adapters/mongoid.rb +++ b/lib/orm_adapter/adapters/mongoid.rb @@ -14,29 +14,34 @@ def column_names # @see OrmAdapter::Base#get! def get!(id) - klass.find(wrap_key(id)) + scoped.find(wrap_key(id)) end # @see OrmAdapter::Base#get def get(id) - klass.where(:_id => wrap_key(id)).first + scoped.where(:_id => wrap_key(id)).first end # @see OrmAdapter::Base#find_first def find_first(options = {}) conditions, order = extract_conditions!(options) - klass.limit(1).where(conditions_to_fields(conditions)).order_by(order).first + scoped.limit(1).where(conditions_to_fields(conditions)).order_by(order).first end # @see OrmAdapter::Base#find_all def find_all(options = {}) conditions, order, limit, offset = extract_conditions!(options) - klass.where(conditions_to_fields(conditions)).order_by(order).limit(limit).offset(offset) + scoped.where(conditions_to_fields(conditions)).order_by(order).limit(limit).offset(offset) + end + + # @see OrmAdapter::Base#build + def build(attributes = {}) + scoped.new(attributes) end # @see OrmAdapter::Base#create! def create!(attributes = {}) - klass.create!(attributes) + scoped.create!(attributes) end # @see OrmAdapter::Base#destroy @@ -46,6 +51,10 @@ def destroy(object) protected + def scoped + klass.where(conditions_to_fields(@scope)) + end + # converts and documents to ids def conditions_to_fields(conditions) conditions.inject({}) do |fields, (key, value)| diff --git a/lib/orm_adapter/base.rb b/lib/orm_adapter/base.rb index 1bb6133..62deb9d 100644 --- a/lib/orm_adapter/base.rb +++ b/lib/orm_adapter/base.rb @@ -1,6 +1,6 @@ module OrmAdapter class Base - attr_reader :klass + attr_reader :klass, :scope # Your ORM adapter needs to inherit from this Base class and its adapter # will be registered. To create an adapter you should create an inner @@ -14,8 +14,9 @@ def self.inherited(adapter) super end - def initialize(klass) + def initialize(klass, scope={}) @klass = klass + @scope = scope end # Get a list of column/property/field names @@ -68,6 +69,10 @@ def find_all(options = {}) raise NotSupportedError end + def build(attributes = {}) + raise NotSupportedError + end + # Create a model using attributes def create!(attributes = {}) raise NotSupportedError diff --git a/lib/orm_adapter/to_adapter.rb b/lib/orm_adapter/to_adapter.rb index 7bf6419..9eb8428 100644 --- a/lib/orm_adapter/to_adapter.rb +++ b/lib/orm_adapter/to_adapter.rb @@ -1,8 +1,9 @@ module OrmAdapter # Extend into a class that has an OrmAdapter module ToAdapter - def to_adapter - @_to_adapter ||= self::OrmAdapter.new(self) + def to_adapter(scope = {}) + self::OrmAdapter.new(self, scope) end end + end \ No newline at end of file diff --git a/spec/orm_adapter/adapters/mongo_mapper_spec.rb b/spec/orm_adapter/adapters/mongo_mapper_spec.rb index c75f480..4d44c0e 100644 --- a/spec/orm_adapter/adapters/mongo_mapper_spec.rb +++ b/spec/orm_adapter/adapters/mongo_mapper_spec.rb @@ -27,9 +27,7 @@ class Note describe MongoMapper::Document::OrmAdapter do before do - MongoMapper.database.collections.each do | coll | - coll.remove - end + MongoMapper.database.collections.each(&:remove) end it_should_behave_like "example app with orm_adapter" do diff --git a/spec/orm_adapter/adapters/mongoid_spec.rb b/spec/orm_adapter/adapters/mongoid_spec.rb index d2ad526..2f50e38 100644 --- a/spec/orm_adapter/adapters/mongoid_spec.rb +++ b/spec/orm_adapter/adapters/mongoid_spec.rb @@ -6,7 +6,7 @@ else Mongoid.configure do |config| - config.master = Mongo::Connection.new.db('orm_adapter_spec') + config.connect_to( 'orm_adapter_spec' ) end module MongoidOrmSpec @@ -14,13 +14,13 @@ class User include Mongoid::Document field :name field :rating - has_many_related :notes, :foreign_key => :owner_id, :class_name => 'MongoidOrmSpec::Note' + has_many :notes, :foreign_key => :owner_id, :class_name => 'MongoidOrmSpec::Note' end class Note include Mongoid::Document field :body, :default => "made by orm" - belongs_to_related :owner, :class_name => 'MongoidOrmSpec::User' + belongs_to :owner, :class_name => 'MongoidOrmSpec::User' end # here be the specs! diff --git a/spec/orm_adapter/example_app_shared.rb b/spec/orm_adapter/example_app_shared.rb index a80dc31..451d12a 100644 --- a/spec/orm_adapter/example_app_shared.rb +++ b/spec/orm_adapter/example_app_shared.rb @@ -39,7 +39,7 @@ def reload_model(model) subject.to_adapter.klass.should == subject end - it "#to_adapter should be cached" do + pending "#to_adapter should be cached" do subject.to_adapter.object_id.should == subject.to_adapter.object_id end end @@ -47,6 +47,7 @@ def reload_model(model) describe "adapter instance" do let(:note_adapter) { note_class.to_adapter } let(:user_adapter) { user_class.to_adapter } + let(:scoped_user_adapter) { user_class.to_adapter(:name => "balls") } describe "#get!(id)" do it "should return the instance with id if it exists" do @@ -62,6 +63,15 @@ def reload_model(model) it "should raise an error if there is no instance with that id" do lambda { user_adapter.get!("nonexistent id") }.should raise_error end + + it "should return the instance if it belongs to the scope" do + user = create_model(user_class, :name => "balls") + scoped_user_adapter.get!(user.id).should == user + end + it "should not return the instance if it does not belong to the scope" do + user = create_model(user_class) + lambda { scoped_user_adapter.get!(user.id) }.should raise_error + end end describe "#get(id)" do @@ -78,6 +88,15 @@ def reload_model(model) it "should return nil if there is no instance with that id" do user_adapter.get("nonexistent id").should be_nil end + + it "should return the instance if it belongs to the scope" do + user = create_model(user_class, :name => "balls") + scoped_user_adapter.get(user.id).should == user + end + it "should not return the instance if it does not belong to the scope" do + user = create_model(user_class) + scoped_user_adapter.get(user.id).should == nil + end end describe "#find_first" do @@ -126,6 +145,14 @@ def reload_model(model) user_adapter.find_first(:conditions => {:name => "Fred"}, :order => [:rating, :desc]).should == user2 end end + + describe "scoped" do + it "should return first model matching conditions belonging to the scope" do + user1 = create_model(user_class, :name => "something else") + user2 = create_model(user_class, :name => "balls") + scoped_user_adapter.find_first.should == user2 + end + end end describe "#find_all" do @@ -194,6 +221,47 @@ def reload_model(model) user_adapter.find_all(:limit => 1, :offset => 1).should == [user2] end end + + describe "scoped" do + it "should return all models matching conditions belonging to the scope" do + user1 = create_model(user_class, :name => "something else") + user2 = create_model(user_class, :name => "something other") + user3 = create_model(user_class, :name => "balls") + user4 = create_model(user_class, :name => "balls") + scoped_user_adapter.find_all.should == [user3, user4] + end + end + end + + describe "#build(attributes)" do + it "should build a non-persistent model" do + user = user_adapter.build + user.should_not be_persisted + end + + it "should build a model with the passed attributes" do + user = user_adapter.build(:name => "Fred") + user.name.should == "Fred" + end + + it "when attributes contain an associated object, should build a model with the attributes" do + user = create_model(user_class) + note = note_adapter.build(:owner => user) + note.owner.should == user + end + + it "when attributes contain an has_many assoc, should build a model with the attributes" do + notes = [create_model(note_class), create_model(note_class)] + user = user_adapter.build(:notes => notes) + user.notes.should == notes + end + + describe "scoped" do + it "should pass the adapter conditions as attributes to the built model" do + user = scoped_user_adapter.build + user.name.should == "balls" + end + end end describe "#create!(attributes)" do @@ -217,12 +285,19 @@ def reload_model(model) user = user_adapter.create!(:notes => notes) reload_model(user).notes.should == notes end + + describe "scoped" do + it "should pass the adapter conditions as attributes to the created model" do + user = scoped_user_adapter.create! + reload_model(user).name.should == "balls" + end + end end describe "#destroy(instance)" do it "should destroy the instance if it exists" do user = create_model(user_class) - user_adapter.destroy(user).should == true + user_adapter.destroy(user).should be_true user_adapter.get(user.id).should be_nil end @@ -235,6 +310,60 @@ def reload_model(model) note_adapter.destroy(user).should be_nil user_adapter.get(user.id).should == user end + end end + + #describe "an ORM collection" do + # def notes_adapter + # user.notes.to_adapter + # end + # let(:user) { create_model(user_class) } + # describe "#klass" do + # it "should return the class of the elements belonging to the collection" do + # user.notes.to_adapter.klass.should == note_class + # end + # end + # describe "#find_first" do + # describe "(conditions)" do + # describe "if note belongs to the user" do + # it "should return first model matching conditions, if it exists" do + # create_model(note_class, :description => "stuff") + # note = create_model(note_class, :owner => user, :description => "stuff") + # notes_adapter.find_first(:description => "stuff").should == note + # end + # end + # + # describe "if note does not belongs to the user" do + # it "should not return first model matching conditions, if it exists" do + # create_model(note_class, :description => "stuff") + # notes_adapter.find_first(:description => "stuff").should == nil + # end + # end + # end + # end + # + # describe "#find" do + # it "should only return notes belonging to the given user" do + # note1 = create_model(note_class, :owner => user) + # note2 = create_model(note_class, :owner => user) + # note3 = create_model(note_class) + # note4 = create_model(note_class) + # notes_adapter.find.should == [note1, note2] + # end + # end + # + # describe "#create!" do + # it "should create the object with the association to the collection owner established" do + # note = notes_adapter.create! + # note.owner.should == user + # end + # end + # describe "#build" do + # it "should build the object and establish the association to the collection owner" do + # note = notes_adapter.build + # note.owner = user + # end + # end + #end end