Skip to content

Commit

Permalink
Big move to a custom GroupedScope::AssociationReflection which delega…
Browse files Browse the repository at this point in the history
…tes 99% to ungrouped reflection.
  • Loading branch information
metaskills committed Sep 23, 2008
1 parent a78b28d commit cb9f875
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 36 deletions.
1 change: 1 addition & 0 deletions lib/grouped_scope.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'grouped_scope/errors'
require 'grouped_scope/grouping'
require 'grouped_scope/self_grouping'
require 'grouped_scope/association_reflection'
require 'grouped_scope/class_methods'
require 'grouped_scope/instance_methods'
require 'grouped_scope/has_many_association'
Expand Down
43 changes: 43 additions & 0 deletions lib/grouped_scope/association_reflection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module GroupedScope
class AssociationReflection < ActiveRecord::Reflection::AssociationReflection

(ActiveRecord::Reflection::MacroReflection.instance_methods +
ActiveRecord::Reflection::AssociationReflection.instance_methods).uniq.each do |m|
unless m =~ /^(__|nil\?|send)|^(object_id|options|name)$/
delegate m, :to => :ungrouped_reflection
end
end

def initialize(active_record,ungrouped_name)
@active_record = active_record
@ungrouped_name = ungrouped_name
@name = :"grouped_scope_#{@ungrouped_name}"
verify_ungrouped_reflection
super(ungrouped_reflection.macro, @name, ungrouped_reflection.options.dup, @active_record)
create_grouped_association
end

def ungrouped_reflection
@active_record.reflections[@ungrouped_name]
end


private

def verify_ungrouped_reflection
if ungrouped_reflection.blank? || ungrouped_reflection.macro.to_s !~ /has_many|has_and_belongs_to_many/
raise ArgumentError, "Cannot create a group scope for :#{@ungrouped_name} because it is not a has_many " +
"or a has_and_belongs_to_many association. Make sure to call grouped_scope after " +
"the has_many associations."
end
end

def create_grouped_association
active_record.send :has_many, name, options
active_record.reflections[name] = self
active_record.grouped_scopes[@ungrouped_name] = true
options[:grouped_scope] = true
end

end
end
25 changes: 5 additions & 20 deletions lib/grouped_scope/class_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,17 @@ def grouped_scopes
read_inheritable_attribute(:grouped_scopes) || write_inheritable_attribute(:grouped_scopes, {})
end

def grouped_scope(*args)
create_belongs_to_grouped_scope_grouping
args.each do |association|
existing_assoc = reflect_on_association(association)
unless existing_assoc && existing_assoc.macro == :has_many
raise ArgumentError, "Cannot create a group scope for :#{association} because it is not a has_many " +
"association. Make sure to call grouped_scope after the has_many associations."
end
grouped_scopes[association] = true
grouped_assoc = grouped_scope_for(association)
has_many grouped_assoc, existing_assoc.options.dup
reflect_on_association(grouped_assoc).options[:grouped_scope] = true
end
def grouped_scope(*associations)
create_belongs_to_for_grouped_scope
associations.each { |association| AssociationReflection.new(self,association) }
include InstanceMethods
end

def grouped_scope_for(association)
:"grouped_scope_#{association}"
end


private

def create_belongs_to_grouped_scope_grouping
def create_belongs_to_for_grouped_scope
grouping_class_name = 'GroupedScope::Grouping'
existing_grouping = reflect_on_association(:grouping)
existing_grouping = reflections[:grouping]
return false if existing_grouping && existing_grouping.macro == :belongs_to && existing_grouping.options[:class_name] == grouping_class_name
belongs_to :grouping, :foreign_key => 'group_id', :class_name => grouping_class_name
end
Expand Down
3 changes: 1 addition & 2 deletions lib/grouped_scope/self_grouping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ def proxy_class

def method_missing(method, *args, &block)
if proxy_class.grouped_scopes[method]
grouped_assoc = proxy_owner.class.grouped_scope_for(method)
proxy_owner.send(grouped_assoc, *args, &block)
proxy_owner.send("grouped_scope_#{method}", *args, &block)
else
super
end
Expand Down
49 changes: 49 additions & 0 deletions test/grouped_scope/association_reflection_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require File.dirname(__FILE__) + '/../helper'

class AssociationReflectionTest < GroupedScope::TestCase

def setup
setup_environment
end

context 'Raise and exception' do

setup { @reflection_klass = GroupedScope::AssociationReflection }

should 'when a association does not exist' do
assert_raise(ArgumentError) { @reflection_klass.new(Employee,:foobars) }
end

should 'when the association is not a has_many or a has_and_belongs_to_many' do
Employee.class_eval { belongs_to(:foo) }
assert_raise(ArgumentError) { @reflection_klass.new(Employee,:foo) }
end

end

context 'For #ungrouped_reflection' do

should 'access ungrouped reflection' do
assert_equal Employee.reflections[:reports],
Employee.reflections[:grouped_scope_reports].ungrouped_reflection
end

should 'delegate core instance methods to #ungrouped_reflection' do
[:class_name,:klass,:table_name,:quoted_table_name,:primary_key_name,
:association_foreign_key,:counter_cache_column,:source_reflection].each do |m|
assert_equal Employee.reflections[:reports].send(m),
Employee.reflections[:grouped_scope_reports].send(m),
"The method #{m.inspect} does not appear to be proxed to the ungrouped reflection."
end
end

should 'not delegate to #ungrouped_reflection for #options and #name' do
assert_not_equal Employee.reflections[:reports].name, Employee.reflections[:grouped_scope_reports].name
assert_not_equal Employee.reflections[:reports].options, Employee.reflections[:grouped_scope_reports].options
end

end



end
20 changes: 6 additions & 14 deletions test/grouped_scope/class_methods_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ def setup
context 'For .grouped_scope' do

should 'create a belongs_to :grouping association' do
assert Employee.reflect_on_association(:grouping)
end

should 'raise an exception when has_many association does not exist' do
assert_raise(ArgumentError) { Employee.class_eval{grouped_scope(:foobars)} }
assert Employee.reflections[:grouping]
end

should 'not recreate belongs_to :grouping on additional calls' do
Expand All @@ -36,21 +32,17 @@ def setup
end

should 'create a has_many assoc named :grouped_scope_* using existing association as a suffix' do
grouped_reports_assoc = Employee.reflect_on_association(:grouped_scope_reports)
grouped_reports_assoc = Employee.reflections[:grouped_scope_reports]
assert_instance_of ActiveRecord::Reflection::AssociationReflection, grouped_reports_assoc
assert Factory(:employee).respond_to?(:grouped_scope_reports)
end

should 'not add the :grouped_scope option to existing reflection' do
assert_nil Employee.reflect_on_association(:reports).options[:grouped_scope]
assert_nil Employee.reflections[:reports].options[:grouped_scope]
end

should 'mirror existing options for has_many association, minus :grouped_scope option' do
reports_assoc = Employee.reflect_on_association(:reports)
grouped_reports_assoc = Employee.reflect_on_association(:grouped_scope_reports)
assert_equal({:grouped_scope=>true}, grouped_reports_assoc.options.diff(reports_assoc.options))
reports_assoc = LegacyEmployee.reflect_on_association(:reports)
grouped_reports_assoc = LegacyEmployee.reflect_on_association(:grouped_scope_reports)
assert_equal({:grouped_scope=>true}, grouped_reports_assoc.options.diff(reports_assoc.options))
should 'have added the :grouped_scope option to new grouped reflection' do
assert Employee.reflections[:grouped_scope_reports].options[:grouped_scope]
end

end
Expand Down

0 comments on commit cb9f875

Please sign in to comment.