aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rwxr-xr-xactiverecord/lib/active_record/associations.rb89
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb1
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb70
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb21
5 files changed, 142 insertions, 43 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 70bc4a4d65..86b7101c64 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1,6 +1,7 @@
require 'active_record/associations/association_proxy'
require 'active_record/associations/association_collection'
require 'active_record/associations/belongs_to_association'
+require 'active_record/associations/belongs_to_polymorphic_association'
require 'active_record/associations/has_one_association'
require 'active_record/associations/has_many_association'
require 'active_record/associations/has_and_belongs_to_many_association'
@@ -344,7 +345,7 @@ module ActiveRecord
:foreign_key, :class_name, :exclusively_dependent, :dependent,
:conditions, :order, :include, :finder_sql, :counter_sql,
:before_add, :after_add, :before_remove, :after_remove, :extend,
- :group
+ :group, :as
)
options[:extend] = create_extension_module(association_id, extension) if block_given?
@@ -516,47 +517,73 @@ module ActiveRecord
# belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
# :conditions => 'discounts > #{payments_count}'
def belongs_to(association_id, options = {})
- options.assert_valid_keys(:class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend)
+ options.assert_valid_keys(:class_name, :foreign_key, :foreign_type, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :polymorphic)
association_name, association_class_name, class_primary_key_name =
associate_identification(association_id, options[:class_name], options[:foreign_key], false)
- require_association_class(association_class_name)
-
association_class_primary_key_name = options[:foreign_key] || association_class_name.foreign_key
- association_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, BelongsToAssociation)
- association_constructor_method(:build, association_name, association_class_name, association_class_primary_key_name, options, BelongsToAssociation)
- association_constructor_method(:create, association_name, association_class_name, association_class_primary_key_name, options, BelongsToAssociation)
+ if options[:polymorphic]
+ options[:foreign_type] ||= association_class_name.underscore + "_type"
- module_eval do
- before_save <<-EOF
- association = instance_variable_get("@#{association_name}")
- if not association.nil?
- if association.new_record?
- association.save(true)
- association.send(:construct_sql)
+ association_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, BelongsToPolymorphicAssociation)
+
+ module_eval do
+ before_save <<-EOF
+ association = instance_variable_get("@#{association_name}")
+ if !association.nil?
+ if association.new_record?
+ association.save(true)
+ association.send(:construct_sql)
+ end
+
+ if association.updated?
+ self["#{association_class_primary_key_name}"] = association.id
+ self["#{options[:foreign_type]}"] = ActiveRecord::Base.send(:class_name_of_active_record_descendant, association.class).to_s
+ end
end
- self["#{association_class_primary_key_name}"] = association.id if association.updated?
- end
- EOF
- end
+ EOF
+ end
+ else
+ require_association_class(association_class_name)
+
+ association_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, BelongsToAssociation)
+ association_constructor_method(:build, association_name, association_class_name, association_class_primary_key_name, options, BelongsToAssociation)
+ association_constructor_method(:create, association_name, association_class_name, association_class_primary_key_name, options, BelongsToAssociation)
+
+ module_eval do
+ before_save <<-EOF
+ association = instance_variable_get("@#{association_name}")
+ if !association.nil?
+ if association.new_record?
+ association.save(true)
+ association.send(:construct_sql)
+ end
+
+ if association.updated?
+ self["#{association_class_primary_key_name}"] = association.id
+ end
+ end
+ EOF
+ end
- if options[:counter_cache]
- module_eval(
- "after_create '#{association_class_name}.increment_counter(\"#{self.to_s.underscore.pluralize + "_count"}\", #{association_class_primary_key_name})" +
- " unless #{association_name}.nil?'"
- )
+ if options[:counter_cache]
+ module_eval(
+ "after_create '#{association_class_name}.increment_counter(\"#{self.to_s.underscore.pluralize + "_count"}\", #{association_class_primary_key_name})" +
+ " unless #{association_name}.nil?'"
+ )
- module_eval(
- "before_destroy '#{association_class_name}.decrement_counter(\"#{self.to_s.underscore.pluralize + "_count"}\", #{association_class_primary_key_name})" +
- " unless #{association_name}.nil?'"
- )
- end
+ module_eval(
+ "before_destroy '#{association_class_name}.decrement_counter(\"#{self.to_s.underscore.pluralize + "_count"}\", #{association_class_primary_key_name})" +
+ " unless #{association_name}.nil?'"
+ )
+ end
- # deprecated api
- deprecated_has_association_method(association_name)
- deprecated_association_comparison_method(association_name, association_class_name)
+ # deprecated api
+ deprecated_has_association_method(association_name)
+ deprecated_association_comparison_method(association_name, association_class_name)
+ end
end
# Associates two classes via an intermediate join table. Unless the join table is explicitly specified as
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index 17b8cc6446..8a7d925d0d 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -76,7 +76,6 @@ module ActiveRecord
end
private
-
def method_missing(method, *args, &block)
load_target
@target.send(method, *args, &block)
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 528c42cea7..39d128aef1 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -1,7 +1,6 @@
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
@@ -43,9 +42,6 @@ module ActiveRecord
@updated
end
- protected
-
-
private
def find_target
if @options[:conditions]
diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
new file mode 100644
index 0000000000..e2a7f1a58e
--- /dev/null
+++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
@@ -0,0 +1,70 @@
+module ActiveRecord
+ module Associations
+ class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
+ def initialize(owner, association_name, association_class_name, association_class_primary_key_name, options)
+ @owner = owner
+ @options = options
+ @association_name = association_name
+ @association_class_primary_key_name = association_class_primary_key_name
+
+ proxy_extend(options[:extend]) if options[:extend]
+
+ reset
+ end
+
+ def create(attributes = {})
+ raise ActiveRecord::ActiveRecordError, "Can't create an abstract polymorphic object"
+ end
+
+ def build(attributes = {})
+ raise ActiveRecord::ActiveRecordError, "Can't build an abstract polymorphic object"
+ end
+
+ def replace(obj, dont_save = false)
+ if obj.nil?
+ @target = @owner[@association_class_primary_key_name] = @owner[@options[:foreign_type]] = nil
+ else
+ @target = (AssociationProxy === obj ? obj.target : obj)
+
+ unless obj.new_record?
+ @owner[@association_class_primary_key_name] = obj.id
+ @owner[@options[:foreign_type]] = ActiveRecord::Base.send(:class_name_of_active_record_descendant, obj.class).to_s
+ end
+
+ @updated = true
+ end
+
+ @loaded = true
+
+ return (@target.nil? ? nil : self)
+ end
+
+ private
+ def find_target
+ return nil if association_class.nil?
+
+ if @options[:conditions]
+ association_class.find(
+ @owner[@association_class_primary_key_name],
+ :conditions => interpolate_sql(@options[:conditions]),
+ :include => @options[:include]
+ )
+ else
+ association_class.find(@owner[@association_class_primary_key_name], :include => @options[:include])
+ end
+ end
+
+ def foreign_key_present
+ !@owner[@association_class_primary_key_name].nil?
+ end
+
+ def target_obsolete?
+ @owner[@association_class_primary_key_name] != @target.id
+ end
+
+ def association_class
+ @owner[@options[:foreign_type]] ? @owner[@options[:foreign_type]].constantize : nil
+ 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 288742d965..465e1f8c72 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -163,11 +163,19 @@ module ActiveRecord
end
def construct_sql
- if @options[:finder_sql]
- @finder_sql = interpolate_sql(@options[:finder_sql])
- else
- @finder_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}"
- @finder_sql << " AND (#{interpolate_sql(@conditions)})" if @conditions
+ case
+ when @options[:as]
+ @finder_sql =
+ "#{@association_class.table_name}.#{@options[:as]}_id = #{@owner.quoted_id} AND " +
+ "#{@association_class.table_name}.#{@options[:as]}_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, @owner.class).to_s}'"
+ @finder_sql << " AND (#{interpolate_sql(@conditions)})" if @conditions
+
+ when @options[:finder_sql]
+ @finder_sql = interpolate_sql(@options[:finder_sql])
+
+ else
+ @finder_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}"
+ @finder_sql << " AND (#{interpolate_sql(@conditions)})" if @conditions
end
if @options[:counter_sql]
@@ -176,8 +184,7 @@ module ActiveRecord
@options[:counter_sql] = @options[:finder_sql].gsub(/SELECT (.*) FROM/i, "SELECT COUNT(*) FROM")
@counter_sql = interpolate_sql(@options[:counter_sql])
else
- @counter_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}"
- @counter_sql << " AND (#{interpolate_sql(@conditions)})" if @conditions
+ @counter_sql = @finder_sql
end
end
end