Skip to content

Commit

Permalink
Test 1.2.6, 2.0.4 and 2.1.1 named scope usage. New named scope back p…
Browse files Browse the repository at this point in the history
…ort method since mislav back port from will paginate was not complete for 1.2.6.
  • Loading branch information
metaskills committed Sep 29, 2008
1 parent 9c438e9 commit 693b17e
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 61 deletions.
2 changes: 1 addition & 1 deletion README.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ GroupedScope aims to make two things easier in your ActiveRecord models. First,
easy way to group objects, second, to allow the group to share associated object via existing
has_many relationships. See installation and usage for more details.

By the way, this plugin has been tested with rails 1.2.6 and 2.1.1.
By the way, this plugin has been tested with rails 1.2.6, 2.0.4, and 2.1.1.


=== Installation & Usage
Expand Down
28 changes: 23 additions & 5 deletions test/grouped_scope/has_many_association_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,15 @@ def setup
end

should 'use assoc extension SQL along with group reflection' do
sql_regex = /SELECT \* FROM "?reports"? *WHERE \("?reports"?.employee_id IN \(2,3\) AND \("?reports"?."?title"? = 'URGENT'\)\)/
assert_sql(sql_regex) do
assert_sql(select_from_reports, where_for_groups, where_for_urgent_title) do
@e2.group.reports.urgent
end
end

end

context 'training named scopes' do

setup do
@e1 = Factory(:employee_with_urgent_reports, :group_id => 1)
@e2 = Factory(:employee, :group_id => 1)
Expand All @@ -95,8 +94,8 @@ def setup
end

should 'use named scope SQL along with group reflection' do
assert_sql(/body LIKE '%URGENT%'/,/"title" = 'URGENT'/,/employee_id IN/) do
@e2.group.reports(true).with_urgent_title.with_urgent_body.inspect
assert_sql(select_from_reports, where_for_groups, where_for_urgent_body, where_for_urgent_title) do
@e2.group.reports.with_urgent_title.with_urgent_body.inspect
end
end

Expand Down Expand Up @@ -125,4 +124,23 @@ def setup
end


protected

def select_from_reports
/SELECT \* FROM "?reports"?/
end

def where_for_groups
/WHERE.*"?reports"?.employee_id IN \(2,3\)/
end

def where_for_urgent_body
/WHERE.*body LIKE '%URGENT%'/
end

def where_for_urgent_title
/WHERE.*"?reports"?."?title"? = 'URGENT'/
end


end
4 changes: 2 additions & 2 deletions test/lib/boot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
require 'active_support'

unless defined? ActiveRecord::NamedScope
require 'core_ext'
require 'named_scope'
require 'named_scope_patch'
require ActiveRecord::Base.respond_to?(:find_first) ? 'named_scope_patch_1.2.6' : 'named_scope_patch_2.0'
ActiveRecord::Base.send :include, GroupedScope::NamedScope
end

20 changes: 20 additions & 0 deletions test/lib/core_ext.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

unless Hash.instance_methods.include? 'except'

Hash.class_eval do

# Returns a new hash without the given keys.
def except(*keys)
rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
reject { |key,| rejected.include?(key) }
end

# Replaces the hash without only the given keys.
def except!(*keys)
replace(except(*keys))
end

end

end

60 changes: 48 additions & 12 deletions test/lib/named_scope.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
## stolen from: http://dev.rubyonrails.org/browser/trunk/activerecord/lib/active_record/named_scope.rb?rev=9084

module GroupedScope
# This is a feature backported from Rails 2.1 because of its usefullness not only with will_paginate,
# but in other aspects when managing complex conditions that you want to be reusable.
module NamedScope
# All subclasses of ActiveRecord::Base have two named_scopes:
# * <tt>all</tt>, which is similar to a <tt>find(:all)</tt> query, and
# * <tt>scoped</tt>, which allows for the creation of anonymous scopes, on the fly:
#
# Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)
# * <tt>scoped</tt>, which allows for the creation of anonymous scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
#
# These anonymous scopes tend to be useful when procedurally generating complex queries, where passing
# intermediate values (scopes) around as first-class objects is convenient.
def self.included(base)
base.class_eval do
extend ClassMethods
named_scope :all
named_scope :scoped, lambda { |scope| scope }
end
end

module ClassMethods
def scopes #:nodoc:
def scopes
read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
end

Expand All @@ -46,7 +39,7 @@ def scopes #:nodoc:
# Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
# for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
#
# All scopes are available as class methods on the ActiveRecord descendent upon which the scopes were defined. But they are also available to
# All scopes are available as class methods on the ActiveRecord::Base descendent upon which the scopes were defined. But they are also available to
# <tt>has_many</tt> associations. If,
#
# class Person < ActiveRecord::Base
Expand Down Expand Up @@ -76,7 +69,20 @@ def scopes #:nodoc:
# end
# end
#
#
# For testing complex named scopes, you can examine the scoping options using the
# <tt>proxy_options</tt> method on the proxy itself.
#
# class Shirt < ActiveRecord::Base
# named_scope :colored, lambda { |color|
# { :conditions => { :color => color } }
# }
# end
#
# expected_options = { :conditions => { :colored => 'red' } }
# assert_equal expected_options, Shirt.colored('red').proxy_options
def named_scope(name, options = {}, &block)
name = name.to_sym
scopes[name] = lambda do |parent_scope, *args|
Scope.new(parent_scope, case options
when Hash
Expand All @@ -93,9 +99,15 @@ def named_scope(name, options = {}, &block)
end
end

class Scope #:nodoc:
class Scope
attr_reader :proxy_scope, :proxy_options
[].methods.each { |m| delegate m, :to => :proxy_found unless m =~ /(^__|^nil\?|^send|class|extend|find|count|sum|average|maximum|minimum|paginate)/ }

[].methods.each do |m|
unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|^find$|count|sum|average|maximum|minimum|paginate|first|last|empty\?|respond_to\?)/
delegate m, :to => :proxy_found
end
end

delegate :scopes, :with_scope, :to => :proxy_scope

def initialize(proxy_scope, options, &block)
Expand All @@ -108,6 +120,30 @@ def reload
load_found; self
end

def first(*args)
if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
proxy_found.first(*args)
else
find(:first, *args)
end
end

def last(*args)
if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
proxy_found.last(*args)
else
find(:last, *args)
end
end

def empty?
@found ? @found.empty? : count.zero?
end

def respond_to?(method, include_private = false)
super || @proxy_scope.respond_to?(method, include_private)
end

protected
def proxy_found
@found || load_found
Expand Down
41 changes: 0 additions & 41 deletions test/lib/named_scope_patch.rb

This file was deleted.

61 changes: 61 additions & 0 deletions test/lib/named_scope_patch_1.2.6.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

ActiveRecord::Associations::AssociationProxy.class_eval do
protected
def with_scope(*args, &block)
@reflection.klass.send :with_scope, *args, &block
end
end

ActiveRecord::Associations::HasManyAssociation.class_eval do
protected
def method_missing(method, *args, &block)
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
super
elsif @reflection.klass.scopes.include?(method)
@reflection.klass.scopes[method].call(self, *args)
else
create_scoping = {}
set_belongs_to_association_for(create_scoping)

@reflection.klass.with_scope(
:create => create_scoping,
:find => {
:conditions => @finder_sql,
:joins => @join_sql,
:readonly => false
}
) do
@reflection.klass.send(method, *args, &block)
end
end
end
end

ActiveRecord::Associations::HasManyThroughAssociation.class_eval do
protected
def method_missing(method, *args, &block)
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
super
elsif @reflection.klass.scopes.include?(method)
@reflection.klass.scopes[method].call(self, *args)
else
@reflection.klass.with_scope(construct_scope) { @reflection.klass.send(method, *args, &block) }
end
end
end

ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
protected
def method_missing(method, *args, &block)
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
super
elsif @reflection.klass.scopes.include?(method)
@reflection.klass.scopes[method].call(self, *args)
else
@reflection.klass.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do
@reflection.klass.send(method, *args, &block)
end
end
end
end

32 changes: 32 additions & 0 deletions test/lib/named_scope_patch_2.0.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

ActiveRecord::Associations::AssociationProxy.class_eval do
protected
def with_scope(*args, &block)
@reflection.klass.send :with_scope, *args, &block
end
end


ActiveRecord::Associations::AssociationCollection.class_eval do
protected
def method_missing(method, *args)
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
if block_given?
super { |*block_args| yield(*block_args) }
else
super
end
elsif @reflection.klass.scopes.include?(method)
@reflection.klass.scopes[method].call(self, *args)
else
with_scope(construct_scope) do
if block_given?
@reflection.klass.send(method, *args) { |*block_args| yield(*block_args) }
else
@reflection.klass.send(method, *args)
end
end
end
end
end

0 comments on commit 693b17e

Please sign in to comment.