Permalink
Browse files

Merge branch 'named_scopes'

Conflicts:
	.gitignore
  • Loading branch information...
2 parents db2f079 + eca3bfd commit d5d9bd0e8b2a95234624157b8637c69a327acf52 @jamesgolick committed Dec 20, 2009
View
2 .document
@@ -1,2 +1,2 @@
-README.rdoc
+README.md
lib/**/*.rb
View
2 .gitignore
@@ -22,3 +22,5 @@ pkg
test.rb
test_active_record.rb
spec/config.yml
+.yardoc
+doc
View
3 lib/friendly.rb
@@ -8,8 +8,11 @@
require 'friendly/document_table'
require 'friendly/index'
require 'friendly/memcached'
+require 'friendly/named_scope'
+require 'friendly/named_scope_set'
require 'friendly/query'
require 'friendly/sequel_monkey_patches'
+require 'friendly/scope'
require 'friendly/storage_factory'
require 'friendly/storage_proxy'
require 'friendly/translator'
View
36 lib/friendly/document.rb
@@ -25,7 +25,9 @@ def create_tables!
end
module ClassMethods
- attr_writer :storage_proxy, :query_klass, :table_name, :collection_klass
+ attr_writer :storage_proxy, :query_klass,
+ :table_name, :collection_klass,
+ :named_scope_set
def create_tables!
storage_proxy.create_tables!
@@ -95,6 +97,38 @@ def table_name
@table_name ||= name.pluralize.underscore
end
+ def named_scope_set
+ @named_scope_set ||= NamedScopeSet.new(self)
+ end
+
+ # Add a named scope to this Document.
+ #
+ # e.g.
+ #
+ # class Post
+ # indexes :created_at
+ # named_scope :recent, :order! => :created_at.desc
+ # end
+ #
+ # Then, you can access the recent posts with:
+ #
+ # Post.recent.all
+ # ...or...
+ # Post.recent.first
+ #
+ # Both #all and #first also take additional parameters:
+ #
+ # Post.recent.all(:author_id => @author.id)
+ #
+ # Scopes are presently not chainable, though that is coming soon.
+ #
+ # @param [Symbol] name the name of the scope.
+ # @param [Hash] parameters the query that this named scope will perform.
+ #
+ def named_scope(name, parameters)
+ named_scope_set.add(name, parameters)
+ end
+
protected
def query(conditions)
conditions.is_a?(Query) ? conditions : query_klass.new(conditions)
View
17 lib/friendly/named_scope.rb
@@ -0,0 +1,17 @@
+require 'friendly/scope'
+
+module Friendly
+ class NamedScope
+ attr_reader :klass, :parameters, :scope_klass
+
+ def initialize(klass, parameters, scope_klass = Scope)
+ @klass = klass
+ @parameters = parameters
+ @scope_klass = scope_klass
+ end
+
+ def scope
+ @scope_klass.new(@klass, @parameters)
+ end
+ end
+end
View
37 lib/friendly/named_scope_set.rb
@@ -0,0 +1,37 @@
+require 'friendly/named_scope'
+
+module Friendly
+ class NamedScopeSet
+ attr_reader :klass, :named_scope_klass, :scopes
+
+ def initialize(klass, named_scope_klass = NamedScope)
+ @klass = klass
+ @named_scope_klass = named_scope_klass
+ @scopes = {}
+ end
+
+ def add(name, parameters)
+ scopes[name] = named_scope_klass.new(klass, parameters)
+ add_scope_method_to_klass(name)
+ end
+
+ def get(name)
+ scopes[name]
+ end
+
+ def get_instance(name)
+ get(name).scope
+ end
+
+ protected
+ def add_scope_method_to_klass(scope_name)
+ klass.class_eval do
+ eval <<-__END__
+ def self.#{scope_name}
+ named_scope_set.get_instance(:#{scope_name})
+ end
+ __END__
+ end
+ end
+ end
+end
View
18 lib/friendly/scope.rb
@@ -0,0 +1,18 @@
+module Friendly
+ class Scope
+ attr_reader :klass, :parameters
+
+ def initialize(klass, parameters)
+ @klass = klass
+ @parameters = parameters
+ end
+
+ def all(extra_parameters = {})
+ klass.all(parameters.merge(extra_parameters))
+ end
+
+ def first(extra_parameters = {})
+ klass.first(parameters.merge(extra_parameters))
+ end
+ end
+end
View
34 spec/integration/named_scope_spec.rb
@@ -0,0 +1,34 @@
+require File.expand_path("../../spec_helper", __FILE__)
+
+describe "named_scope" do
+ describe "calling a single named_scope" do
+ before do
+ User.all(:name => "Quagmire").each { |q| q.destroy }
+ 5.times { User.create(:name => "Quagmire") }
+ end
+
+ describe "all" do
+ it "returns all objects matching the conditions" do
+ User.named_quagmire.all.should == User.all(:name => "Quagmire")
+ end
+
+ it "accepts extra conditions" do
+ User.create(:name => "Fred")
+ found = User.named_quagmire.all(:name => "Fred")
+ found.should == User.all(:name => "Fred")
+ end
+ end
+
+ describe "first" do
+ it "returns the first object matching the conditions" do
+ User.named_quagmire.first.should == User.first(:name => "Quagmire")
+ end
+
+ it "accepts extra conditions" do
+ User.create(:name => "Fred")
+ found = User.named_quagmire.first(:name => "Fred")
+ found.should == User.first(:name => "Fred")
+ end
+ end
+ end
+end
View
2 spec/spec_helper.rb
@@ -43,6 +43,8 @@ class User
indexes :friend
indexes :name
indexes :name, :created_at
+
+ named_scope :named_quagmire, :name => "Quagmire"
end
User.create_tables!
View
13 spec/unit/document_spec.rb
@@ -307,5 +307,18 @@
@collection.should have_received(:replace).with(@docs)
end
end
+
+ describe "creating a named_scope" do
+ before do
+ @named_scope_set = stub(:add => nil)
+ @klass.named_scope_set = @named_scope_set
+ @klass.named_scope(:by_name, :order => :name)
+ end
+
+ it "asks the named_scope_set to add it" do
+ @klass.named_scope_set.should have_received(:add).
+ with(:by_name, :order => :name)
+ end
+ end
end
View
32 spec/unit/named_scope_set_spec.rb
@@ -0,0 +1,32 @@
+require File.expand_path("../../spec_helper", __FILE__)
+
+describe "Friendly::NamedScopeSet" do
+ before do
+ @klass = Class.new
+ @named_scope_klass = stub
+ @scope_set = Friendly::NamedScopeSet.new(@klass, @named_scope_klass)
+ @scope = stub
+ @named_scope = stub(:scope => @scope)
+ @params = {:order! => :created_at.desc}
+ @named_scope_klass.stubs(:new).with(@klass, @params).returns(@named_scope)
+ @klass.stubs(:named_scope_set).returns(@scope_set)
+ @scope_set.add(:recent, @params)
+ end
+
+ describe "adding a scope" do
+ it "adds a named_scope by that name to the set" do
+ @scope_set.get(:recent).should == @named_scope
+ end
+
+ it "adds a method to the klass that returns an instance of the scope" do
+ @klass.recent.should == @scope
+ end
+ end
+
+ describe "getting an instance of a scope" do
+
+ it "delegates to the named_scope" do
+ @scope_set.get_instance(:recent).should == @scope
+ end
+ end
+end
View
16 spec/unit/named_scope_spec.rb
@@ -0,0 +1,16 @@
+require File.expand_path("../../spec_helper", __FILE__)
+
+describe "Friendly::NamedScope" do
+ before do
+ @klass = stub
+ @scope = stub
+ @scope_klass = stub
+ @parameters = {:name => "James"}
+ @scope_klass.stubs(:new).with(@klass, @parameters).returns(@scope)
+ @named_scope = Friendly::NamedScope.new(@klass, @parameters, @scope_klass)
+ end
+
+ it "provides scope instances with the given parameters" do
+ @named_scope.scope.should == @scope
+ end
+end
View
43 spec/unit/scope_spec.rb
@@ -0,0 +1,43 @@
+require File.expand_path("../../spec_helper", __FILE__)
+
+describe "Friendly::Scope" do
+ before do
+ @klass = stub
+ @scope_parameters = {:name => "Quagmire", :order! => :created_at.desc}
+ @scope = Friendly::Scope.new(@klass, @scope_parameters)
+ end
+
+ describe "#all" do
+ before do
+ @documents = stub
+ end
+
+ it "delegates to klass with the scope parameters" do
+ @klass.stubs(:all).with(@scope_parameters).returns(@documents)
+ @scope.all.should == @documents
+ end
+
+ it "merges additional parameters" do
+ merged = @scope_parameters.merge(:name => "Joe")
+ @klass.stubs(:all).with(merged).returns(@documents)
+ @scope.all(:name => "Joe").should == @documents
+ end
+ end
+
+ describe "#first" do
+ before do
+ @document = stub
+ end
+
+ it "delegates to klass with the scope parameters" do
+ @klass.stubs(:first).with(@scope_parameters).returns(@document)
+ @scope.first.should == @document
+ end
+
+ it "merges additional parameters" do
+ merged = @scope_parameters.merge(:name => "Joe")
+ @klass.stubs(:first).with(merged).returns(@document)
+ @scope.first(:name => "Joe").should == @document
+ end
+ end
+end

0 comments on commit d5d9bd0

Please sign in to comment.