diff options
Diffstat (limited to 'activerecord/lib/active_record/associations')
6 files changed, 66 insertions, 53 deletions
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 140a12eacf..3b7f2df870 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -107,6 +107,15 @@ module ActiveRecord end private + + def method_missing(method, *args, &block) + if @target.respond_to?(method) or (not @association_class.respond_to?(method) and Class.respond_to?(method)) + super + else + @association_class.constrain( :conditions => @finder_sql, :joins => @join_sql) { @association_class.send(method, *args, &block) } + end + end + def raise_on_type_mismatch(record) raise ActiveRecord::AssociationTypeMismatch, "#{@association_class} expected, got #{record.class}" unless record.is_a?(@association_class) end diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index f80a7e996c..ed34b1df6c 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -19,11 +19,6 @@ module ActiveRecord load_target end - def method_missing(symbol, *args, &block) - load_target - @target.send(symbol, *args, &block) - end - def respond_to?(symbol, include_priv = false) proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv)) end @@ -44,7 +39,7 @@ module ActiveRecord @target = t @loaded = true end - + protected def dependent? @options[:dependent] || false @@ -69,8 +64,14 @@ module ActiveRecord def extract_options_from_args!(args) @owner.send(:extract_options_from_args!, args) end - + private + + def method_missing(method, *args, &block) + load_target + @target.send(method, *args, &block) + end + def load_target if !@owner.new_record? || foreign_key_present begin diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index b142a13153..60c31ce486 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -1,6 +1,12 @@ module ActiveRecord module Associations class BelongsToAssociation < AssociationProxy #:nodoc: + + def initialize(owner, association_name, association_class_name, association_class_primary_key_name, options) + super + construct_sql + end + def reset @target = nil @loaded = false @@ -31,6 +37,9 @@ module ActiveRecord return (@target.nil? ? nil : self) end + + protected + private def find_target @@ -48,9 +57,9 @@ module ActiveRecord def target_obsolete? @owner[@association_class_primary_key_name] != @target.id end - + def construct_sql - # no sql to construct + @finder_sql = "#{@association_class.table_name}.#{@association_class.primary_key} = #{@owner.id}" end end end diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 107f3ef12c..7d85a2268b 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -42,19 +42,15 @@ module ActiveRecord def find_first load_target.first end - + def find(*args) - # Return an Array if multiple ids are given. - expects_array = args.first.kind_of?(Array) - - ids = args.flatten.compact.uniq - - # If no block is given, raise RecordNotFound. - if ids.empty? - raise RecordNotFound, "Couldn't find #{@association_class.name} without an ID" + options = Base.send(:extract_options_from_args!, args) # If using a custom finder_sql, scan the entire collection. - elsif @options[:finder_sql] + if @options[:finder_sql] + expects_array = args.first.kind_of?(Array) + ids = args.flatten.compact.uniq + if ids.size == 1 id = ids.first record = load_target.detect { |record| id == record.id } @@ -62,22 +58,25 @@ module ActiveRecord else load_target.select { |record| ids.include?(record.id) } end - - # Otherwise, construct a query. else - ids_list = ids.map { |id| @owner.send(:quote, id) }.join(',') - records = find_target(@finder_sql.sub(/(ORDER BY|$)/, " AND j.#{@association_foreign_key} IN (#{ids_list}) \\1")) - if records.size == ids.size - if ids.size == 1 and !expects_array - records.first - else - records - end - else - raise RecordNotFound, "Couldn't find #{@association_class.name} with ID in (#{ids_list})" + conditions = "#{@finder_sql}" + if sanitized_conditions = sanitize_sql(options[:conditions]) + conditions << " AND #{sanitized_conditions}" + end + options[:conditions] = conditions + options[:joins] = @join_sql + + if options[:order] && @options[:order] + options[:order] = "#{options[:order]}, #{@options[:order]}" + elsif @options[:order] + options[:order] = @options[:order] end + + # Pass through args exactly as we received them. + args << options + @association_class.find(*args) end - end + end def push_with_attributes(record, join_attributes = {}) raise_on_type_mismatch(record) @@ -96,11 +95,17 @@ module ActiveRecord end protected - def find_target(sql = @finder_sql) - records = @association_class.find_by_sql(sql) + def find_target(sql = nil) + + if sql + records = @association_class.find_by_sql(sql) if sql + else + records = find(:all) + end + @options[:uniq] ? uniq(records) : records end - + def count_records load_target.size end @@ -152,28 +157,17 @@ module ActiveRecord def construct_sql interpolate_sql_options!(@options, :finder_sql) - + if @options[:finder_sql] @finder_sql = @options[:finder_sql] else - @finder_sql = - "SELECT t.*, j.* FROM #{@join_table} j, #{@association_table_name} t " + - "WHERE t.#{@association_class.primary_key} = j.#{@association_foreign_key} AND " + - "j.#{@association_class_primary_key_name} = #{@owner.quoted_id} " - + @finder_sql = "#{@association_class_primary_key_name} = #{@owner.quoted_id} " @finder_sql << " AND #{interpolate_sql(@options[:conditions])}" if @options[:conditions] - - unless @association_class.descends_from_active_record? - type_condition = @association_class.send(:subclasses).inject("t.#{@association_class.inheritance_column} = '#{@association_class.name.demodulize}' ") do |condition, subclass| - condition << "OR t.#{@association_class.inheritance_column} = '#{subclass.name.demodulize}' " - end - - @finder_sql << " AND (#{type_condition})" - end - - @finder_sql << " ORDER BY #{@order}" if @order end + + @join_sql = "LEFT JOIN #{@join_table} ON #{@association_class.table_name}.#{@association_class.primary_key} = #{@join_table}.#{@association_foreign_key}" end + end end end diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 21879adb77..fdeadfc181 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -91,7 +91,7 @@ module ActiveRecord @target = [] self end - + protected def find_target find_all diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 4a6e85c8ee..1550db69b5 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -54,7 +54,7 @@ module ActiveRecord return (obj.nil? ? nil : self) end end - + private def find_target @association_class.find(:first, :conditions => @finder_sql, :order => @options[:order]) |