Skip to content

Commit

Permalink
Added specs for NestedThrough::Reflection
Browse files Browse the repository at this point in the history
Separated db into own file in spec setup
  • Loading branch information
ianwhite committed Dec 15, 2009
1 parent d382a87 commit 19454a4
Show file tree
Hide file tree
Showing 10 changed files with 347 additions and 177 deletions.
14 changes: 6 additions & 8 deletions init.rb
@@ -1,10 +1,8 @@
require 'nested_has_many_through'
#require 'nested_through'

ActiveRecord::Associations::HasManyThroughAssociation.send :include, NestedHasManyThrough::Association
require 'nested_through/reflection'
ActiveRecord::Reflection::AssociationReflection.send :include, NestedThrough::Reflection
ActiveRecord::Reflection::ThroughReflection.send :include, NestedThrough::Reflection

# BC
if defined?(ActiveRecord::Reflection::ThroughReflection)
ActiveRecord::Reflection::ThroughReflection.send :include, NestedHasManyThrough::Reflection
else
ActiveRecord::Reflection::AssociationReflection.send :include, NestedHasManyThrough::Reflection
end
#
#ActiveRecord::Associations::HasManyThroughAssociation.send :include, NestedHasManyThrough::Association
137 changes: 0 additions & 137 deletions lib/nested_has_many_through.rb

This file was deleted.

152 changes: 152 additions & 0 deletions lib/nested_through.rb
@@ -0,0 +1,152 @@
module NestedThrough
# module Associations
# def has_many(association_id, options = {}, &extension)
# if through_association?(options[:through])
# reflection = create_has_many_reflection(association_id, options, &extension)
# configure_dependency_for_has_many(reflection)
# add_association_callbacks(reflection.name, reflection.options)
# collection_accessor_methods(reflection, NestedThrough::NestedHasManyThroughAssociation)
# else
# super
# end
# end
#
# def has_one(association_id, options = {})
# if through_association?(options[:through])
# reflection = create_has_one_through_reflection(association_id, options)
# association_accessor_methods(reflection, NestedThrough::NestedHasOneThroughAssociation)
# else
# super
# end
# end
#
# private
# def through_association?(assoc_name)
# reflection = reflect_on_association(assoc_name)
# reflection && reflection.through_association
# end
# end
#end
## module Association
## def self.included(base)
## base.class_eval do
## def construct_conditions
## @nested_join_attributes ||= construct_nested_join_attributes
## "#{@nested_join_attributes[:remote_key]} = #{@owner.quoted_id} #{@nested_join_attributes[:conditions]}"
## end
##
## def construct_joins(custom_joins = nil)
## @nested_join_attributes ||= construct_nested_join_attributes
## "#{@nested_join_attributes[:joins]} #{custom_joins}"
## end
## end
## end
##
## protected
## # Given any belongs_to or has_many (including has_many :through) association,
## # return the essential components of a join corresponding to that association, namely:
## #
## # * <tt>:joins</tt>: any additional joins required to get from the association's table
## # (reflection.table_name) to the table that's actually joining to the active record's table
## # * <tt>:remote_key</tt>: the name of the key in the join table (qualified by table name) which will join
## # to a field of the active record's table
## # * <tt>:local_key</tt>: the name of the key in the local table (not qualified by table name) which will
## # take part in the join
## # * <tt>:conditions</tt>: any additional conditions (e.g. filtering by type for a polymorphic association,
## # or a :conditions clause explicitly given in the association), including a leading AND
## def construct_nested_join_attributes( reflection = @reflection,
## association_class = reflection.klass,
## table_ids = {association_class.table_name => 1})
## if reflection.macro == :has_many && reflection.through_reflection
## construct_has_many_through_attributes(reflection, table_ids)
## else
## construct_has_many_or_belongs_to_attributes(reflection, association_class, table_ids)
## end
## end
##
## def construct_has_many_through_attributes(reflection, table_ids)
## # Construct the join components of the source association, so that we have a path from
## # the eventual target table of the association up to the table named in :through, and
## # all tables involved are allocated table IDs.
## source_attrs = construct_nested_join_attributes(reflection.source_reflection, reflection.klass, table_ids)
##
## # Determine the alias of the :through table; this will be the last table assigned
## # when constructing the source join components above.
## through_table_alias = through_table_name = reflection.through_reflection.table_name
## through_table_alias += "_#{table_ids[through_table_name]}" unless table_ids[through_table_name] == 1
##
## # Construct the join components of the through association, so that we have a path to
## # the active record's table.
## through_attrs = construct_nested_join_attributes(reflection.through_reflection, reflection.through_reflection.klass, table_ids)
##
## # Any subsequent joins / filters on owner attributes will act on the through association,
## # so that's what we return for the conditions/keys of the overall association.
## conditions = through_attrs[:conditions]
## conditions += " AND #{interpolate_sql(reflection.klass.send(:sanitize_sql, reflection.options[:conditions]))}" if reflection.options[:conditions]
##
## {
## :joins => "%s INNER JOIN %s ON ( %s = %s.%s %s) %s %s" % [
## source_attrs[:joins],
## through_table_name == through_table_alias ? through_table_name : "#{through_table_name} #{through_table_alias}",
## source_attrs[:remote_key],
## through_table_alias, source_attrs[:local_key],
## source_attrs[:conditions],
## through_attrs[:joins],
## reflection.options[:joins]
## ],
## :remote_key => through_attrs[:remote_key],
## :local_key => through_attrs[:local_key],
## :conditions => conditions
## }
## end
##
##
## # reflection is not has_many :through; it's a standard has_many / belongs_to instead
## # TODO: see if we can defer to rails code here a bit more
## def construct_has_many_or_belongs_to_attributes(reflection, association_class, table_ids)
## # Determine the alias used for remote_table_name, if any. In all cases this will already
## # have been assigned an ID in table_ids (either through being involved in a previous join,
## # or - if it's the first table in the query - as the default value of table_ids)
## remote_table_alias = remote_table_name = association_class.table_name
## remote_table_alias += "_#{table_ids[remote_table_name]}" unless table_ids[remote_table_name] == 1
##
## # Assign a new alias for the local table.
## local_table_alias = local_table_name = reflection.active_record.table_name
## if table_ids[local_table_name]
## table_id = table_ids[local_table_name] += 1
## local_table_alias += "_#{table_id}"
## else
## table_ids[local_table_name] = 1
## end
##
## conditions = ''
## # Add type_condition, if applicable
## conditions += " AND #{association_class.send(:type_condition, remote_table_alias)}" unless association_class.descends_from_active_record?
## # Add custom conditions
## conditions += " AND (#{interpolate_sql(association_class.send(:sanitize_sql, reflection.options[:conditions]))})" if reflection.options[:conditions]
##
## if reflection.macro == :belongs_to
## if reflection.options[:polymorphic]
## conditions += " AND #{local_table_alias}.#{reflection.options[:foreign_type]} = #{reflection.active_record.quote_value(association_class.base_class.name.to_s)}"
## end
## {
## :joins => reflection.options[:joins],
## :remote_key => "#{remote_table_alias}.#{association_class.primary_key}",
## :local_key => reflection.primary_key_name,
## :conditions => conditions
## }
## else
## # Association is has_many (without :through)
## if reflection.options[:as]
## conditions += " AND #{remote_table_alias}.#{reflection.options[:as]}_type = #{reflection.active_record.quote_value(reflection.active_record.base_class.name.to_s)}"
## end
## {
## :joins => "#{reflection.options[:joins]}",
## :remote_key => "#{remote_table_alias}.#{reflection.primary_key_name}",
## :local_key => reflection.klass.primary_key,
## :conditions => conditions
## }
## end
## end
## end
end
30 changes: 30 additions & 0 deletions lib/nested_through/associations.rb
@@ -0,0 +1,30 @@
module NestedThrough
# extension for ActiveRecord::Base which created NestedThrough associations
module Associations
def has_many(association_id, options = {}, &extension)
if through_association?(options[:through])
reflection = create_has_many_reflection(association_id, options, &extension)
configure_dependency_for_has_many(reflection)
add_association_callbacks(reflection.name, reflection.options)
collection_accessor_methods(reflection, NestedThrough::Associations::NestedHasManyThroughAssociation)
else
super
end
end

def has_one(association_id, options = {})
if through_association?(options[:through])
reflection = create_has_one_through_reflection(association_id, options)
association_accessor_methods(reflection, NestedThrough::Associations::NestedHasOneThroughAssociation)
else
super
end
end

private
def through_association?(assoc_name)
reflection = reflect_on_association(assoc_name)
reflection && reflection.through_association
end
end
end
@@ -0,0 +1,6 @@
module NestedThrough
module Associations
class NestedHasManyThroughAssociation < ActiveRecord::Associations::HasManyThroughAssociation
end
end
end
@@ -0,0 +1,6 @@
module NestedThrough
module Associations
class NestedHasOneThroughAssociation < ActiveRecord::Associations::HasOneThroughAssociation
end
end
end
25 changes: 25 additions & 0 deletions lib/nested_through/reflection.rb
@@ -0,0 +1,25 @@
module NestedThrough
# mixin for ActiveRecord::Reflection::AssociationReflection & ActiveRecord::Reflection::ThroughReflection
module Reflection
def self.included(reflection_class)
reflection_class.alias_method_chain :check_validity!, :nested_through
end

# NestedThrough allows through source reflections
def check_validity_with_nested_through!
check_validity_without_nested_through!
rescue ActiveRecord::HasManyThroughSourceAssociationMacroError => e
raise e unless source_through?
end

# is this association a nested_through association?
def nested_through?
through_reflection && through_reflection.through_reflection
end

# does this association have a through source?
def source_through?
source_reflection && source_reflection.through_reflection
end
end
end

0 comments on commit 19454a4

Please sign in to comment.