diff options
author | Adam Milligan <amilligan@pivotallabs.com> | 2009-05-10 16:20:16 -0700 |
---|---|---|
committer | Jeremy Kemper <jeremy@bitsweat.net> | 2009-06-12 13:52:51 -0700 |
commit | 54a5446641e4386285231385700b95a223931bff (patch) | |
tree | f2c00aac7b43a381ec4bd5f9f5cc26634243fe96 /activerecord/lib/active_record/associations/through_association_scope.rb | |
parent | 8db190acbae5d2a6f25d42d4137793a720a1689c (diff) | |
download | rails-54a5446641e4386285231385700b95a223931bff.tar.gz rails-54a5446641e4386285231385700b95a223931bff.tar.bz2 rails-54a5446641e4386285231385700b95a223931bff.zip |
HasOneThroughAssociation still shouldn't derive from HasManyThroughAssociation.
[#1642 state:committed]
Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
Diffstat (limited to 'activerecord/lib/active_record/associations/through_association_scope.rb')
-rw-r--r-- | activerecord/lib/active_record/associations/through_association_scope.rb | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb new file mode 100644 index 0000000000..7661c50039 --- /dev/null +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -0,0 +1,147 @@ +module ActiveRecord + module Associations + module ThroughAssociationScope + + protected + + def construct_scope + { :create => construct_owner_attributes(@reflection), + :find => { :from => construct_from, + :conditions => construct_conditions, + :joins => construct_joins, + :include => @reflection.options[:include] || @reflection.source_reflection.options[:include], + :select => construct_select, + :order => @reflection.options[:order], + :limit => @reflection.options[:limit], + :readonly => @reflection.options[:readonly], + } } + end + + # Build SQL conditions from attributes, qualified by table name. + def construct_conditions + table_name = @reflection.through_reflection.quoted_table_name + conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value| + "#{table_name}.#{attr} = #{value}" + end + conditions << sql_conditions if sql_conditions + "(" + conditions.join(') AND (') + ")" + end + + # Associate attributes pointing to owner, quoted. + def construct_quoted_owner_attributes(reflection) + if as = reflection.options[:as] + { "#{as}_id" => owner_quoted_id, + "#{as}_type" => reflection.klass.quote_value( + @owner.class.base_class.name.to_s, + reflection.klass.columns_hash["#{as}_type"]) } + elsif reflection.macro == :belongs_to + { reflection.klass.primary_key => @owner[reflection.primary_key_name] } + else + { reflection.primary_key_name => owner_quoted_id } + end + end + + def construct_from + @reflection.quoted_table_name + end + + def construct_select(custom_select = nil) + distinct = "DISTINCT " if @reflection.options[:uniq] + selected = custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*" + end + + def construct_joins(custom_joins = nil) + polymorphic_join = nil + if @reflection.source_reflection.macro == :belongs_to + reflection_primary_key = @reflection.klass.primary_key + source_primary_key = @reflection.source_reflection.primary_key_name + if @reflection.options[:source_type] + polymorphic_join = "AND %s.%s = %s" % [ + @reflection.through_reflection.quoted_table_name, "#{@reflection.source_reflection.options[:foreign_type]}", + @owner.class.quote_value(@reflection.options[:source_type]) + ] + end + else + reflection_primary_key = @reflection.source_reflection.primary_key_name + source_primary_key = @reflection.through_reflection.klass.primary_key + if @reflection.source_reflection.options[:as] + polymorphic_join = "AND %s.%s = %s" % [ + @reflection.quoted_table_name, "#{@reflection.source_reflection.options[:as]}_type", + @owner.class.quote_value(@reflection.through_reflection.klass.name) + ] + end + end + + "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [ + @reflection.through_reflection.quoted_table_name, + @reflection.quoted_table_name, reflection_primary_key, + @reflection.through_reflection.quoted_table_name, source_primary_key, + polymorphic_join + ] + end + + # Construct attributes for associate pointing to owner. + def construct_owner_attributes(reflection) + if as = reflection.options[:as] + { "#{as}_id" => @owner.id, + "#{as}_type" => @owner.class.base_class.name.to_s } + else + { reflection.primary_key_name => @owner.id } + end + end + + # Construct attributes for :through pointing to owner and associate. + def construct_join_attributes(associate) + # TODO: revist this to allow it for deletion, supposing dependent option is supported + raise ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection.new(@owner, @reflection) if @reflection.source_reflection.macro == :has_many + join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id) + if @reflection.options[:source_type] + join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s) + end + join_attributes + end + + def conditions + @conditions = build_conditions unless defined?(@conditions) + @conditions + end + + def build_conditions + association_conditions = @reflection.options[:conditions] + through_conditions = build_through_conditions + source_conditions = @reflection.source_reflection.options[:conditions] + uses_sti = !@reflection.through_reflection.klass.descends_from_active_record? + + if association_conditions || through_conditions || source_conditions || uses_sti + all = [] + + [association_conditions, source_conditions].each do |conditions| + all << interpolate_sql(sanitize_sql(conditions)) if conditions + end + + all << through_conditions if through_conditions + all << build_sti_condition if uses_sti + + all.map { |sql| "(#{sql})" } * ' AND ' + end + end + + def build_through_conditions + conditions = @reflection.through_reflection.options[:conditions] + if conditions.is_a?(Hash) + interpolate_sql(sanitize_sql(conditions)).gsub( + @reflection.quoted_table_name, + @reflection.through_reflection.quoted_table_name) + elsif conditions + interpolate_sql(sanitize_sql(conditions)) + end + end + + def build_sti_condition + @reflection.through_reflection.klass.send(:type_condition) + end + + alias_method :sql_conditions, :conditions + end + end +end |