aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
authorJoshua Peek <josh@joshpeek.com>2009-05-29 16:06:21 -0500
committerJoshua Peek <josh@joshpeek.com>2009-05-29 16:06:21 -0500
commit69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3 (patch)
tree044c2131cc87d21ee54027511aae2b7f2d2ae26a /activerecord/lib
parent5f3f100ce2d689480da85abc88e5e940cf90189e (diff)
parent5ec2c7dc29b36d85b2658465b8a979deb0529d7e (diff)
downloadrails-69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3.tar.gz
rails-69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3.tar.bz2
rails-69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3.zip
Merge branch 'master' into active_model
Conflicts: activemodel/lib/active_model/core.rb activemodel/test/cases/state_machine/event_test.rb activemodel/test/cases/state_machine/state_transition_test.rb activerecord/lib/active_record/validations.rb activerecord/test/cases/validations/i18n_validation_test.rb activeresource/lib/active_resource.rb activeresource/test/abstract_unit.rb
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record.rb13
-rw-r--r--activerecord/lib/active_record/aggregations.rb4
-rw-r--r--activerecord/lib/active_record/association_preload.rb8
-rwxr-xr-xactiverecord/lib/active_record/associations.rb52
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb17
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb14
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb12
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb5
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb17
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb11
-rw-r--r--activerecord/lib/active_record/associations/has_one_through_association.rb16
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb30
-rw-r--r--activerecord/lib/active_record/autosave_association.rb27
-rwxr-xr-xactiverecord/lib/active_record/base.rb42
-rw-r--r--activerecord/lib/active_record/batches.rb4
-rw-r--r--activerecord/lib/active_record/calculations.rb5
-rw-r--r--activerecord/lib/active_record/callbacks.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb4
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb1
-rw-r--r--activerecord/lib/active_record/dirty.rb25
-rw-r--r--activerecord/lib/active_record/fixtures.rb48
-rw-r--r--activerecord/lib/active_record/i18n_interpolation_deprecation.rb26
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb16
-rw-r--r--activerecord/lib/active_record/migration.rb10
-rw-r--r--activerecord/lib/active_record/named_scope.rb27
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb23
-rw-r--r--activerecord/lib/active_record/observer.rb4
-rw-r--r--activerecord/lib/active_record/reflection.rb25
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb4
-rw-r--r--activerecord/lib/active_record/serialization.rb25
-rw-r--r--activerecord/lib/active_record/serializers/json_serializer.rb22
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb5
-rw-r--r--activerecord/lib/active_record/session_store.rb10
-rw-r--r--activerecord/lib/active_record/test_case.rb2
-rw-r--r--activerecord/lib/active_record/timestamp.rb12
-rw-r--r--activerecord/lib/active_record/transactions.rb12
-rw-r--r--activerecord/lib/active_record/validations.rb22
43 files changed, 387 insertions, 249 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 07a10fb023..0ad7b02785 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -21,15 +21,9 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-begin
- require 'active_support'
-rescue LoadError
- activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
- if File.directory?(activesupport_path)
- $:.unshift activesupport_path
- require 'active_support'
- end
-end
+activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift(activesupport_path) if File.directory?(activesupport_path)
+require 'active_support'
begin
require 'active_model'
@@ -87,5 +81,4 @@ module ActiveRecord
end
end
-require 'active_record/i18n_interpolation_deprecation'
I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 1eefebb3b3..9ecf231a66 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -1,8 +1,6 @@
module ActiveRecord
module Aggregations # :nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
def clear_aggregation_cache #:nodoc:
self.class.reflect_on_all_aggregations.to_a.each do |assoc|
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index e4ab69aa1b..af80a579d6 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -1,9 +1,7 @@
module ActiveRecord
# See ActiveRecord::AssociationPreload::ClassMethods for documentation.
module AssociationPreload #:nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
# Implements the details of eager loading of ActiveRecord associations.
# Application developers should not use this module directly.
@@ -126,6 +124,7 @@ module ActiveRecord
association_proxy = parent_record.send(reflection_name)
association_proxy.loaded
association_proxy.target.push(*[associated_record].flatten)
+ association_proxy.__send__(:set_inverse_instance, associated_record, parent_record)
end
end
@@ -152,7 +151,8 @@ module ActiveRecord
seen_keys[associated_record[key].to_s] = true
mapped_records = id_to_record_map[associated_record[key].to_s]
mapped_records.each do |mapped_record|
- mapped_record.send("set_#{reflection_name}_target", associated_record)
+ association_proxy = mapped_record.send("set_#{reflection_name}_target", associated_record)
+ association_proxy.__send__(:set_inverse_instance, associated_record, mapped_record)
end
end
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 2115878e32..157716a477 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1,4 +1,12 @@
+require 'active_support/core_ext/module/delegation'
+
module ActiveRecord
+ class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
+ def initialize(reflection)
+ super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{reflection.class_name})")
+ end
+ end
+
class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
def initialize(owner_class_name, reflection)
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
@@ -71,6 +79,8 @@ module ActiveRecord
# See ActiveRecord::Associations::ClassMethods for documentation.
module Associations # :nodoc:
+ extend ActiveSupport::Concern
+
# These classes will be loaded when associations are created.
# So there is no need to eager load them.
autoload :AssociationCollection, 'active_record/associations/association_collection'
@@ -83,10 +93,6 @@ module ActiveRecord
autoload :HasOneAssociation, 'active_record/associations/has_one_association'
autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association'
- def self.included(base)
- base.extend(ClassMethods)
- end
-
# Clears out the association cache
def clear_association_cache #:nodoc:
self.class.reflect_on_all_associations.to_a.each do |assoc|
@@ -1488,7 +1494,7 @@ module ActiveRecord
:finder_sql, :counter_sql,
:before_add, :after_add, :before_remove, :after_remove,
:extend, :readonly,
- :validate
+ :validate, :inverse_of
]
def create_has_many_reflection(association_id, options, &extension)
@@ -1502,7 +1508,7 @@ module ActiveRecord
@@valid_keys_for_has_one_association = [
:class_name, :foreign_key, :remote, :select, :conditions, :order,
:include, :dependent, :counter_cache, :extend, :as, :readonly,
- :validate, :primary_key
+ :validate, :primary_key, :inverse_of
]
def create_has_one_reflection(association_id, options)
@@ -1521,7 +1527,7 @@ module ActiveRecord
@@valid_keys_for_belongs_to_association = [
:class_name, :foreign_key, :foreign_type, :remote, :select, :conditions,
:include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
- :validate, :touch
+ :validate, :touch, :inverse_of
]
def create_belongs_to_reflection(association_id, options)
@@ -1662,7 +1668,19 @@ module ActiveRecord
def tables_in_string(string)
return [] if string.blank?
- string.scan(/([\.a-zA-Z_]+).?\./).flatten
+ string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten
+ end
+
+ def tables_in_hash(hash)
+ return [] if hash.blank?
+ tables = hash.map do |key, value|
+ if value.is_a?(Hash)
+ key.to_s
+ else
+ tables_in_string(key) if key.is_a?(String)
+ end
+ end
+ tables.flatten.compact
end
def conditions_tables(options)
@@ -1670,12 +1688,12 @@ module ActiveRecord
conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
case cond
when nil then all
- when Array then all << cond.first
- when Hash then all << cond.keys
- else all << cond
+ when Array then all << tables_in_string(cond.first)
+ when Hash then all << tables_in_hash(cond)
+ else all << tables_in_string(cond)
end
end
- tables_in_string(conditions.join(' '))
+ conditions.flatten
end
def order_tables(options)
@@ -1910,21 +1928,27 @@ module ActiveRecord
return nil if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
association = join.instantiate(row)
collection.target.push(association)
+ collection.__send__(:set_inverse_instance, association, record)
when :has_one
return if record.id.to_s != join.parent.record_id(row).to_s
return if record.instance_variable_defined?("@#{join.reflection.name}")
association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
- record.send("set_#{join.reflection.name}_target", association)
+ set_target_and_inverse(join, association, record)
when :belongs_to
return if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
association = join.instantiate(row)
- record.send("set_#{join.reflection.name}_target", association)
+ set_target_and_inverse(join, association, record)
else
raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
end
return association
end
+ def set_target_and_inverse(join, association, record)
+ association_proxy = record.send("set_#{join.reflection.name}_target", association)
+ association_proxy.__send__(:set_inverse_instance, association, record)
+ end
+
class JoinBase # :nodoc:
attr_reader :active_record, :table_joins
delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :to => :active_record
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 3aef1b21e9..e12f6be35d 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -302,6 +302,15 @@ module ActiveRecord
end
end
+ # Returns true if the collection has more than 1 record. Equivalent to collection.size > 1.
+ def many?
+ if block_given?
+ method_missing(:many?) { |*block_args| yield(*block_args) }
+ else
+ size > 1
+ end
+ end
+
def uniq(collection = self)
seen = Set.new
collection.inject([]) do |kept, record|
@@ -399,11 +408,14 @@ module ActiveRecord
find(:all)
end
- @reflection.options[:uniq] ? uniq(records) : records
+ records = @reflection.options[:uniq] ? uniq(records) : records
+ records.each do |record|
+ set_inverse_instance(record, @owner)
+ end
+ records
end
private
-
def create_record(attrs)
attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
ensure_owner_is_not_new
@@ -433,6 +445,7 @@ module ActiveRecord
@target ||= [] unless loaded?
@target << record unless @reflection.options[:uniq] && @target.include?(record)
callback(:after_add, record)
+ set_inverse_instance(record, @owner)
record
end
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index 241b9bfee0..e36b04ea95 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -53,6 +53,7 @@ module ActiveRecord
def initialize(owner, reflection)
@owner, @reflection = owner, reflection
+ reflection.check_validity!
Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
reset
end
@@ -274,6 +275,19 @@ module ActiveRecord
def owner_quoted_id
@owner.quoted_id
end
+
+ def set_inverse_instance(record, instance)
+ return if record.nil? || !we_can_set_the_inverse_on_this?(record)
+ inverse_relationship = @reflection.inverse_of
+ unless inverse_relationship.nil?
+ record.send(:"set_#{inverse_relationship.name}_target", instance)
+ end
+ end
+
+ # Override in subclasses
+ def we_can_set_the_inverse_on_this?(record)
+ false
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index f05c6be075..c88575048a 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -31,6 +31,8 @@ module ActiveRecord
@updated = true
end
+ set_inverse_instance(record, @owner)
+
loaded
record
end
@@ -41,18 +43,26 @@ module ActiveRecord
private
def find_target
- @reflection.klass.find(
+ the_target = @reflection.klass.find(
@owner[@reflection.primary_key_name],
:select => @reflection.options[:select],
:conditions => conditions,
:include => @reflection.options[:include],
:readonly => @reflection.options[:readonly]
)
+ set_inverse_instance(the_target, @owner)
+ the_target
end
def foreign_key_present
!@owner[@reflection.primary_key_name].nil?
end
+
+ # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
+ # has_one associations.
+ def we_can_set_the_inverse_on_this?(record)
+ @reflection.has_inverse? && @reflection.inverse_of.macro == :has_one
+ 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 a2cbabfe0c..73dd50dd07 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -116,6 +116,11 @@ module ActiveRecord
:create => create_scoping
}
end
+
+ def we_can_set_the_inverse_on_this?(record)
+ inverse = @reflection.inverse_of
+ return !inverse.nil?
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 1c091e7d5a..e8dbae9011 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -1,11 +1,6 @@
module ActiveRecord
module Associations
class HasManyThroughAssociation < HasManyAssociation #:nodoc:
- def initialize(owner, reflection)
- reflection.check_validity!
- super
- end
-
alias_method :new, :build
def create!(attrs = nil)
@@ -22,6 +17,13 @@ module ActiveRecord
end
end
+ def destroy(*records)
+ transaction do
+ delete_records(flatten_deeper(records))
+ super
+ end
+ end
+
# Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and
# calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero,
# and you need to fetch that collection afterwards, it'll take one fewer SELECT query if you use #length.
@@ -251,6 +253,11 @@ module ActiveRecord
def cached_counter_attribute_name
"#{@reflection.name}_count"
end
+
+ # NOTE - not sure that we can actually cope with inverses here
+ def we_can_set_the_inverse_on_this?(record)
+ false
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index b92cbbdeab..b72b84343b 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -74,13 +74,15 @@ module ActiveRecord
private
def find_target
- @reflection.klass.find(:first,
+ the_target = @reflection.klass.find(:first,
:conditions => @finder_sql,
:select => @reflection.options[:select],
:order => @reflection.options[:order],
:include => @reflection.options[:include],
:readonly => @reflection.options[:readonly]
)
+ set_inverse_instance(the_target, @owner)
+ the_target
end
def construct_sql
@@ -117,8 +119,15 @@ module ActiveRecord
self.target = record
end
+ set_inverse_instance(record, @owner)
+
record
end
+
+ def we_can_set_the_inverse_on_this?(record)
+ inverse = @reflection.inverse_of
+ return !inverse.nil?
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb
index 8073ebaf9f..d93c8e7852 100644
--- a/activerecord/lib/active_record/associations/has_one_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_through_association.rb
@@ -1,31 +1,31 @@
module ActiveRecord
module Associations
class HasOneThroughAssociation < HasManyThroughAssociation
-
+
def create_through_record(new_value) #nodoc:
klass = @reflection.through_reflection.klass
current_object = @owner.send(@reflection.through_reflection.name)
-
+
if current_object
- current_object.update_attributes(construct_join_attributes(new_value))
+ new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
else
- @owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value)))
+ @owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value))) if new_value
end
end
-
+
private
def find(*args)
super(args.merge(:limit => 1))
end
-
+
def find_target
super.first
end
def reset_target!
@target = nil
- end
- end
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 3ffc48941c..15358979c2 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -1,17 +1,23 @@
+require 'active_support/core_ext/enumerable'
+
module ActiveRecord
module AttributeMethods #:nodoc:
+ extend ActiveSupport::Concern
+
DEFAULT_SUFFIXES = %w(= ? _before_type_cast)
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
- def self.included(base)
- base.extend ClassMethods
- base.attribute_method_suffix(*DEFAULT_SUFFIXES)
- base.cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
- base.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
- base.cattr_accessor :time_zone_aware_attributes, :instance_writer => false
- base.time_zone_aware_attributes = false
- base.class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
- base.skip_time_zone_conversion_for_attributes = []
+ included do
+ attribute_method_suffix(*DEFAULT_SUFFIXES)
+
+ cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
+ self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
+
+ cattr_accessor :time_zone_aware_attributes, :instance_writer => false
+ self.time_zone_aware_attributes = false
+
+ class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
+ self.skip_time_zone_conversion_for_attributes = []
end
# Declare and check for suffixed attribute methods.
@@ -99,8 +105,8 @@ module ActiveRecord
def instance_method_already_implemented?(method_name)
method_name = method_name.to_s
return true if method_name =~ /^id(=$|\?$|$)/
- @_defined_class_methods ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map(&:to_s).to_set
- @@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map(&:to_s).to_set
+ @_defined_class_methods ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map {|m| m.to_s }.to_set
+ @@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map{|m| m.to_s }.to_set
raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
@_defined_class_methods.include?(method_name)
end
@@ -118,7 +124,7 @@ module ActiveRecord
# with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
def cached_attributes
@cached_attributes ||=
- columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map(&:name).to_set
+ columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map{|col| col.name}.to_set
end
# Returns +true+ if the provided attribute is being cached.
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 4a03585b18..a540570f42 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -125,16 +125,15 @@ module ActiveRecord
# post.author.name = ''
# post.save(false) # => true
module AutosaveAssociation
+ extend ActiveSupport::Concern
+
ASSOCIATION_TYPES = %w{ has_one belongs_to has_many has_and_belongs_to_many }
- def self.included(base)
- base.class_eval do
- base.extend(ClassMethods)
- alias_method_chain :reload, :autosave_associations
+ included do
+ alias_method_chain :reload, :autosave_associations
- ASSOCIATION_TYPES.each do |type|
- base.send("valid_keys_for_#{type}_association") << :autosave
- end
+ ASSOCIATION_TYPES.each do |type|
+ send("valid_keys_for_#{type}_association") << :autosave
end
end
@@ -311,11 +310,13 @@ module ActiveRecord
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
def save_has_one_association(reflection)
if (association = association_instance_get(reflection.name)) && !association.target.nil?
- if reflection.options[:autosave] && association.marked_for_destruction?
+ autosave = reflection.options[:autosave]
+
+ if autosave && association.marked_for_destruction?
association.destroy
- elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave]
+ elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave
association[reflection.primary_key_name] = id
- association.save(false)
+ association.save(!autosave)
end
end
end
@@ -330,10 +331,12 @@ module ActiveRecord
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
def save_belongs_to_association(reflection)
if association = association_instance_get(reflection.name)
- if reflection.options[:autosave] && association.marked_for_destruction?
+ autosave = reflection.options[:autosave]
+
+ if autosave && association.marked_for_destruction?
association.destroy
else
- association.save(false) if association.new_record? || reflection.options[:autosave]
+ association.save(!autosave) if association.new_record? || autosave
if association.updated?
self[reflection.primary_key_name] = association.id
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 251d5000f5..ec49d40a12 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1,5 +1,16 @@
require 'yaml'
require 'set'
+require 'active_support/dependencies'
+require 'active_support/time'
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/class/delegating_attributes'
+require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/hash/deep_merge'
+require 'active_support/core_ext/hash/indifferent_access'
+require 'active_support/core_ext/hash/slice'
+require 'active_support/core_ext/string/behavior'
+require 'active_support/core_ext/symbol'
module ActiveRecord #:nodoc:
# Generic Active Record exception class.
@@ -687,14 +698,9 @@ module ActiveRecord #:nodoc:
# Person.exists?(['name LIKE ?', "%#{query}%"])
# Person.exists?
def exists?(id_or_conditions = {})
- connection.select_all(
- construct_finder_sql(
- :select => "#{quoted_table_name}.#{primary_key}",
- :conditions => expand_id_conditions(id_or_conditions),
- :limit => 1
- ),
- "#{name} Exists"
- ).size > 0
+ find_initial(
+ :select => "#{quoted_table_name}.#{primary_key}",
+ :conditions => expand_id_conditions(id_or_conditions)) ? true : false
end
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -1030,7 +1036,7 @@ module ActiveRecord #:nodoc:
# To start from an all-closed default and enable attributes as needed,
# have a look at +attr_accessible+.
def attr_protected(*attributes)
- write_inheritable_attribute(:attr_protected, Set.new(attributes.map(&:to_s)) + (protected_attributes || []))
+ write_inheritable_attribute(:attr_protected, Set.new(attributes.map {|a| a.to_s}) + (protected_attributes || []))
end
# Returns an array of all the attributes that have been protected from mass-assignment.
@@ -1536,12 +1542,12 @@ module ActiveRecord #:nodoc:
end
def reverse_sql_order(order_query)
- reversed_query = order_query.to_s.split(/,/).each { |s|
+ order_query.to_s.split(/,/).each { |s|
if s.match(/\s(asc|ASC)$/)
s.gsub!(/\s(asc|ASC)$/, ' DESC')
elsif s.match(/\s(desc|DESC)$/)
s.gsub!(/\s(desc|DESC)$/, ' ASC')
- elsif !s.match(/\s(asc|ASC|desc|DESC)$/)
+ else
s.concat(' DESC')
end
}.join(',')
@@ -1887,7 +1893,7 @@ module ActiveRecord #:nodoc:
else
find(:#{finder}, options.merge(finder_options))
end
- #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang}
+ #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect { |pair| pair.join(\' = \') }.join(\', \')}")' if bang}
end
}, __FILE__, __LINE__
send(method_id, *arguments)
@@ -2171,7 +2177,7 @@ module ActiveRecord #:nodoc:
# default_scope :order => 'last_name, first_name'
# end
def default_scope(options = {})
- self.default_scoping << { :find => options, :create => (options.is_a?(Hash) && options.has_key?(:conditions)) ? options[:conditions] : {} }
+ self.default_scoping << { :find => options, :create => options[:conditions].is_a?(Hash) ? options[:conditions] : {} }
end
# Test whether the given method and optional key are scoped.
@@ -2609,11 +2615,11 @@ module ActiveRecord #:nodoc:
# Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either
# instance will affect the other.
def becomes(klass)
- returning klass.new do |became|
- became.instance_variable_set("@attributes", @attributes)
- became.instance_variable_set("@attributes_cache", @attributes_cache)
- became.instance_variable_set("@new_record", new_record?)
- end
+ became = klass.new
+ became.instance_variable_set("@attributes", @attributes)
+ became.instance_variable_set("@attributes_cache", @attributes_cache)
+ became.instance_variable_set("@new_record", new_record?)
+ became
end
# Updates a single attribute and saves the record without going through the normal validation procedure.
diff --git a/activerecord/lib/active_record/batches.rb b/activerecord/lib/active_record/batches.rb
index 5a6cecd4ad..e41d38fb8f 100644
--- a/activerecord/lib/active_record/batches.rb
+++ b/activerecord/lib/active_record/batches.rb
@@ -1,8 +1,6 @@
module ActiveRecord
module Batches # :nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
# When processing large numbers of records, it's often a good idea to do
# so in batches to prevent memory ballooning.
diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb
index f077818d3b..727f4c1dc6 100644
--- a/activerecord/lib/active_record/calculations.rb
+++ b/activerecord/lib/active_record/calculations.rb
@@ -1,9 +1,8 @@
module ActiveRecord
module Calculations #:nodoc:
+ extend ActiveSupport::Concern
+
CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include, :from]
- def self.included(base)
- base.extend(ClassMethods)
- end
module ClassMethods
# Count operates using three different approaches.
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index e375037b5b..36f5f2ce47 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -211,21 +211,23 @@ module ActiveRecord
# needs to be aware of it because an ordinary +save+ will raise such exception
# instead of quietly returning +false+.
module Callbacks
+ extend ActiveSupport::Concern
+
CALLBACKS = %w(
after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
after_validation before_validation_on_create after_validation_on_create before_validation_on_update
after_validation_on_update before_destroy after_destroy
)
- def self.included(base) #:nodoc:
- base.extend Observable
+ included do
+ extend Observable
[:create_or_update, :valid?, :create, :update, :destroy].each do |method|
- base.send :alias_method_chain, method, :callbacks
+ alias_method_chain method, :callbacks
end
- base.send :include, ActiveSupport::Callbacks
- base.define_callbacks *CALLBACKS
+ include ActiveSupport::Callbacks
+ define_callbacks *CALLBACKS
end
# Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index aac84cc5f4..500dafdc2e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -1,5 +1,6 @@
require 'monitor'
require 'set'
+require 'active_support/core_ext/module/synchronization'
module ActiveRecord
# Raised when a connection could not be obtained within the connection
@@ -107,13 +108,14 @@ module ActiveRecord
checkin conn if conn
end
- # Reserve a connection, and yield it to a block. Ensure the connection is
- # checked back in when finished.
+ # If a connection already exists yield it to the block. If no connection
+ # exists checkout a connection, yield it to the block, and checkin the
+ # connection when finished.
def with_connection
- conn = checkout
- yield conn
+ fresh_connection = true unless @reserved_connections[current_connection_id]
+ yield connection
ensure
- checkin conn
+ release_connection if fresh_connection
end
# Returns true if a connection has already been opened.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 3a7bf35248..720fba29e9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/big_decimal/conversions'
+
module ActiveRecord
module ConnectionAdapters # :nodoc:
module Quoting
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index c29c1562b4..ff63ea3a2e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -316,7 +316,7 @@ module ActiveRecord
def initialize_schema_migrations_table
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
- unless tables.detect { |t| t == sm_table }
+ unless table_exists?(sm_table)
create_table(sm_table, :id => false) do |schema_migrations_table|
schema_migrations_table.column :version, :string, :null => false
end
@@ -327,7 +327,7 @@ module ActiveRecord
# migrated up to that point:
si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
- if tables.detect { |t| t == si_table }
+ if table_exists?(si_table)
old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
assume_migrated_upto_version(old_version)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index a8cd9f033b..91b111ab55 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -12,6 +12,8 @@ require 'active_record/connection_adapters/abstract/connection_pool'
require 'active_record/connection_adapters/abstract/connection_specification'
require 'active_record/connection_adapters/abstract/query_cache'
+require 'active_support/core_ext/benchmark'
+
module ActiveRecord
module ConnectionAdapters # :nodoc:
# ActiveRecord supports multiple database systems. AbstractAdapter and
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 9300df28ee..d5536e4d67 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -1,4 +1,5 @@
require 'active_record/connection_adapters/abstract_adapter'
+require 'active_support/core_ext/kernel/requires'
require 'set'
module MysqlCompat #:nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 4961793866..002696d2c4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -1,4 +1,5 @@
require 'active_record/connection_adapters/abstract_adapter'
+require 'active_support/core_ext/kernel/requires'
begin
require_library_or_gem 'pg'
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 75420f69aa..5eef692d05 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -25,9 +25,9 @@ module ActiveRecord
module ConnectionAdapters #:nodoc:
class SQLite3Adapter < SQLiteAdapter # :nodoc:
def table_structure(table_name)
- returning structure = @connection.table_info(quote_table_name(table_name)) do
- raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
- end
+ structure = @connection.table_info(quote_table_name(table_name))
+ raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
+ structure
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 05334a830a..c9d0c9574f 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -1,4 +1,5 @@
require 'active_record/connection_adapters/abstract_adapter'
+require 'active_support/core_ext/kernel/requires'
module ActiveRecord
class Base
diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb
index 4a2510aa63..178767e0c3 100644
--- a/activerecord/lib/active_record/dirty.rb
+++ b/activerecord/lib/active_record/dirty.rb
@@ -34,20 +34,21 @@ module ActiveRecord
# person.name << 'by'
# person.name_change # => ['uncle bob', 'uncle bobby']
module Dirty
+ extend ActiveSupport::Concern
+
DIRTY_SUFFIXES = ['_changed?', '_change', '_will_change!', '_was']
- def self.included(base)
- base.attribute_method_suffix *DIRTY_SUFFIXES
- base.alias_method_chain :write_attribute, :dirty
- base.alias_method_chain :save, :dirty
- base.alias_method_chain :save!, :dirty
- base.alias_method_chain :update, :dirty
- base.alias_method_chain :reload, :dirty
+ included do
+ attribute_method_suffix *DIRTY_SUFFIXES
- base.superclass_delegating_accessor :partial_updates
- base.partial_updates = true
+ alias_method_chain :write_attribute, :dirty
+ alias_method_chain :save, :dirty
+ alias_method_chain :save!, :dirty
+ alias_method_chain :update, :dirty
+ alias_method_chain :reload, :dirty
- base.send(:extend, ClassMethods)
+ superclass_delegating_accessor :partial_updates
+ self.partial_updates = true
end
# Do any attributes have unsaved changes?
@@ -167,7 +168,9 @@ module ActiveRecord
module ClassMethods
def self.extended(base)
- base.metaclass.alias_method_chain(:alias_attribute, :dirty)
+ class << base
+ alias_method_chain :alias_attribute, :dirty
+ end
end
def alias_attribute_with_dirty(new_name, old_name)
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index c6501113bf..2b0cfc2c3b 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -1,8 +1,10 @@
require 'erb'
require 'yaml'
require 'csv'
+require 'zlib'
require 'active_support/dependencies'
require 'active_support/test_case'
+require 'active_support/core_ext/logger'
if RUBY_VERSION < '1.9'
module YAML #:nodoc:
@@ -433,6 +435,7 @@ end
# Any fixture labeled "DEFAULTS" is safely ignored.
class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
+ MAX_ID = 2 ** 31 - 1
DEFAULT_FILTER_RE = /\.ya?ml$/
@@all_cached_fixtures = {}
@@ -524,11 +527,10 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
cached_fixtures(connection, table_names)
end
- # Returns a consistent identifier for +label+. This will always
- # be a positive integer, and will always be the same for a given
- # label, assuming the same OS, platform, and version of Ruby.
+ # Returns a consistent, platform-independent identifier for +label+.
+ # Identifiers are positive integers less than 2^32.
def self.identify(label)
- label.to_s.hash.abs
+ Zlib.crc32(label.to_s) % MAX_ID
end
attr_reader :table_name, :name
@@ -805,27 +807,25 @@ end
module ActiveRecord
module TestFixtures
- def self.included(base)
- base.class_eval do
- setup :setup_fixtures
- teardown :teardown_fixtures
-
- superclass_delegating_accessor :fixture_path
- superclass_delegating_accessor :fixture_table_names
- superclass_delegating_accessor :fixture_class_names
- superclass_delegating_accessor :use_transactional_fixtures
- superclass_delegating_accessor :use_instantiated_fixtures # true, false, or :no_instances
- superclass_delegating_accessor :pre_loaded_fixtures
-
- self.fixture_table_names = []
- self.use_transactional_fixtures = false
- self.use_instantiated_fixtures = true
- self.pre_loaded_fixtures = false
-
- self.fixture_class_names = {}
- end
+ extend ActiveSupport::Concern
+
+ included do
+ setup :setup_fixtures
+ teardown :teardown_fixtures
+
+ superclass_delegating_accessor :fixture_path
+ superclass_delegating_accessor :fixture_table_names
+ superclass_delegating_accessor :fixture_class_names
+ superclass_delegating_accessor :use_transactional_fixtures
+ superclass_delegating_accessor :use_instantiated_fixtures # true, false, or :no_instances
+ superclass_delegating_accessor :pre_loaded_fixtures
+
+ self.fixture_table_names = []
+ self.use_transactional_fixtures = false
+ self.use_instantiated_fixtures = true
+ self.pre_loaded_fixtures = false
- base.extend ClassMethods
+ self.fixture_class_names = {}
end
module ClassMethods
diff --git a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb b/activerecord/lib/active_record/i18n_interpolation_deprecation.rb
deleted file mode 100644
index cd634e1b8d..0000000000
--- a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# Deprecates the use of the former message interpolation syntax in activerecord
-# as in "must have %d characters". The new syntax uses explicit variable names
-# as in "{{value}} must have {{count}} characters".
-
-require 'i18n/backend/simple'
-module I18n
- module Backend
- class Simple
- DEPRECATED_INTERPOLATORS = { '%d' => '{{count}}', '%s' => '{{value}}' }
-
- protected
- def interpolate_with_deprecated_syntax(locale, string, values = {})
- return string unless string.is_a?(String)
-
- string = string.gsub(/%d|%s/) do |s|
- instead = DEPRECATED_INTERPOLATORS[s]
- ActiveSupport::Deprecation.warn "using #{s} in messages is deprecated; use #{instead} instead."
- instead
- end
-
- interpolate_without_deprecated_syntax(locale, string, values)
- end
- alias_method_chain :interpolate, :deprecated_syntax
- end
- end
-end \ No newline at end of file
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 7fa7e267d8..cec5ca3324 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -42,17 +42,17 @@ module ActiveRecord
# To override the name of the lock_version column, invoke the <tt>set_locking_column</tt> method.
# This method uses the same syntax as <tt>set_table_name</tt>
module Optimistic
- def self.included(base) #:nodoc:
- base.extend ClassMethods
+ extend ActiveSupport::Concern
- base.cattr_accessor :lock_optimistically, :instance_writer => false
- base.lock_optimistically = true
+ included do
+ cattr_accessor :lock_optimistically, :instance_writer => false
+ self.lock_optimistically = true
- base.alias_method_chain :update, :lock
- base.alias_method_chain :destroy, :lock
- base.alias_method_chain :attributes_from_column_definition, :lock
+ alias_method_chain :update, :lock
+ alias_method_chain :destroy, :lock
+ alias_method_chain :attributes_from_column_definition, :lock
- class << base
+ class << self
alias_method :locking_column=, :set_locking_column
end
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 657acd6dc0..a7be3539d5 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -511,11 +511,11 @@ module ActiveRecord
raise DuplicateMigrationNameError.new(name.camelize)
end
- klasses << returning(MigrationProxy.new) do |migration|
- migration.name = name.camelize
- migration.version = version
- migration.filename = file
- end
+ migration = MigrationProxy.new
+ migration.name = name.camelize
+ migration.version = version
+ migration.filename = file
+ klasses << migration
end
migrations = migrations.sort_by(&:version)
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 1f3ef300f2..1b22fa5e24 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -1,5 +1,10 @@
+require 'active_support/core_ext/array'
+require 'active_support/core_ext/hash/except'
+
module ActiveRecord
module NamedScope
+ extend ActiveSupport::Concern
+
# All subclasses of ActiveRecord::Base have one named scope:
# * <tt>scoped</tt> - which allows for the creation of anonymous \scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
#
@@ -7,11 +12,8 @@ module ActiveRecord
# intermediate values (scopes) around as first-class objects is convenient.
#
# You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
- def self.included(base)
- base.class_eval do
- extend ClassMethods
- named_scope :scoped, lambda { |scope| scope }
- end
+ included do
+ named_scope :scoped, lambda { |scope| scope }
end
module ClassMethods
@@ -107,14 +109,14 @@ module ActiveRecord
class Scope
attr_reader :proxy_scope, :proxy_options, :current_scoped_methods_when_defined
- NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?).to_set
+ NON_DELEGATE_METHODS = %w(nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? many? respond_to?).to_set
[].methods.each do |m|
unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s)
delegate m, :to => :proxy_found
end
end
- delegate :scopes, :with_scope, :to => :proxy_scope
+ delegate :scopes, :with_scope, :scoped_methods, :to => :proxy_scope
def initialize(proxy_scope, options, &block)
options ||= {}
@@ -166,6 +168,15 @@ module ActiveRecord
end
end
+ # Returns true if the named scope has more than 1 matching record.
+ def many?
+ if block_given?
+ proxy_found.many? { |*block_args| yield(*block_args) }
+ else
+ size > 1
+ end
+ end
+
protected
def proxy_found
@found || load_found
@@ -178,7 +189,7 @@ module ActiveRecord
else
with_scope({:find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {}}, :reverse_merge) do
method = :new if method == :build
- if current_scoped_methods_when_defined
+ if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined)
with_scope current_scoped_methods_when_defined do
proxy_scope.send(method, *args, &block)
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index e3122d195a..0beb4321a2 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -1,9 +1,13 @@
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/object/try'
+
module ActiveRecord
module NestedAttributes #:nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- base.class_inheritable_accessor :reject_new_nested_attributes_procs, :instance_writer => false
- base.reject_new_nested_attributes_procs = {}
+ extend ActiveSupport::Concern
+
+ included do
+ class_inheritable_accessor :reject_new_nested_attributes_procs, :instance_writer => false
+ self.reject_new_nested_attributes_procs = {}
end
# == Nested Attributes
@@ -180,10 +184,14 @@ module ActiveRecord
# and the Proc should return either +true+ or +false+. When no Proc
# is specified a record will be built for all attribute hashes that
# do not have a <tt>_delete</tt> that evaluates to true.
+ # Passing <tt>:all_blank</tt> instead of a Proc will create a proc
+ # that will reject a record where all the attributes are blank.
#
# Examples:
# # creates avatar_attributes=
# accepts_nested_attributes_for :avatar, :reject_if => proc { |attributes| attributes['name'].blank? }
+ # # creates avatar_attributes=
+ # accepts_nested_attributes_for :avatar, :reject_if => :all_blank
# # creates avatar_attributes= and posts_attributes=
# accepts_nested_attributes_for :avatar, :posts, :allow_destroy => true
def accepts_nested_attributes_for(*attr_names)
@@ -201,7 +209,12 @@ module ActiveRecord
end
reflection.options[:autosave] = true
- self.reject_new_nested_attributes_procs[association_name.to_sym] = options[:reject_if]
+
+ self.reject_new_nested_attributes_procs[association_name.to_sym] = if options[:reject_if] == :all_blank
+ proc { |attributes| attributes.all? {|k,v| v.blank?} }
+ else
+ options[:reject_if]
+ end
# def pirate_attributes=(attributes)
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes, false)
diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb
index b35e407cc1..89ec0962bf 100644
--- a/activerecord/lib/active_record/observer.rb
+++ b/activerecord/lib/active_record/observer.rb
@@ -3,9 +3,7 @@ require 'set'
module ActiveRecord
module Observing # :nodoc:
- def self.included(base)
- base.extend ClassMethods
- end
+ extend ActiveSupport::Concern
module ClassMethods
# Activates the observers assigned. Examples:
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 2d4c1d5507..0baa9654b7 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -1,8 +1,6 @@
module ActiveRecord
module Reflection # :nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
# Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations.
# This information can, for example, be used in a form builder that took an Active Record object and created input
@@ -212,6 +210,13 @@ module ActiveRecord
end
def check_validity!
+ check_validity_of_inverse!
+ end
+
+ def check_validity_of_inverse!
+ if has_inverse? && inverse_of.nil?
+ raise InverseOfAssociationNotFoundError.new(self)
+ end
end
def through_reflection
@@ -225,6 +230,18 @@ module ActiveRecord
nil
end
+ def has_inverse?
+ !@options[:inverse_of].nil?
+ end
+
+ def inverse_of
+ if has_inverse?
+ @inverse_of ||= klass.reflect_on_association(options[:inverse_of])
+ else
+ nil
+ end
+ end
+
private
def derive_class_name
class_name = name.to_s.camelize
@@ -300,6 +317,8 @@ module ActiveRecord
unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
raise HasManyThroughSourceAssociationMacroError.new(self)
end
+
+ check_validity_of_inverse!
end
def through_reflection_primary_key
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 557a554966..de530a3456 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -1,5 +1,5 @@
require 'stringio'
-require 'bigdecimal'
+require 'active_support/core_ext/big_decimal'
module ActiveRecord
# This class is used to dump the database schema for some connection to some
@@ -176,4 +176,4 @@ HEADER
end
end
end
-end \ No newline at end of file
+end
diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb
index 870b4b2dd4..7959f2b510 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -1,5 +1,3 @@
-require 'active_support/json'
-
module ActiveRecord #:nodoc:
module Serialization
class Serializer #:nodoc:
@@ -20,9 +18,9 @@ module ActiveRecord #:nodoc:
if options[:only]
options.delete(:except)
- attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
+ attribute_names = attribute_names & Array.wrap(options[:only]).collect { |n| n.to_s }
else
- options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
+ options[:except] = Array.wrap(options[:except]) | Array.wrap(@record.class.inheritance_column)
attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
end
@@ -30,7 +28,7 @@ module ActiveRecord #:nodoc:
end
def serializable_method_names
- Array(options[:methods]).inject([]) do |method_attributes, name|
+ Array.wrap(options[:methods]).inject([]) do |method_attributes, name|
method_attributes << name if @record.respond_to?(name.to_s)
method_attributes
end
@@ -51,7 +49,7 @@ module ActiveRecord #:nodoc:
:only => options[:only] }
include_has_options = include_associations.is_a?(Hash)
- associations = include_has_options ? include_associations.keys : Array(include_associations)
+ associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
for association in associations
records = case @record.class.reflect_on_association(association).macro
@@ -73,16 +71,19 @@ module ActiveRecord #:nodoc:
end
def serializable_record
- returning(serializable_record = {}) do
- serializable_names.each { |name| serializable_record[name] = @record.send(name) }
- add_includes do |association, records, opts|
+ record = {}
+ serializable_names.each { |name| record[name] = @record.send(name) }
+
+ add_includes do |association, records, opts|
+ record[association] =
if records.is_a?(Enumerable)
- serializable_record[association] = records.collect { |r| self.class.new(r, opts).serializable_record }
+ records.collect { |r| self.class.new(r, opts).serializable_record }
else
- serializable_record[association] = self.class.new(records, opts).serializable_record
+ self.class.new(records, opts).serializable_record
end
- end
end
+
+ record
end
def serialize
diff --git a/activerecord/lib/active_record/serializers/json_serializer.rb b/activerecord/lib/active_record/serializers/json_serializer.rb
index 1fd65ed006..67e2b2abb3 100644
--- a/activerecord/lib/active_record/serializers/json_serializer.rb
+++ b/activerecord/lib/active_record/serializers/json_serializer.rb
@@ -1,8 +1,11 @@
+require 'active_support/json'
+
module ActiveRecord #:nodoc:
module Serialization
- def self.included(base)
- base.cattr_accessor :include_root_in_json, :instance_writer => false
- base.extend ClassMethods
+ extend ActiveSupport::Concern
+
+ included do
+ cattr_accessor :include_root_in_json, :instance_writer => false
end
# Returns a JSON string representing the model. Some configuration is
@@ -72,10 +75,11 @@ module ActiveRecord #:nodoc:
# {"comments": [{"body": "Don't think too hard"}],
# "title": "So I was thinking"}]}
def to_json(options = {})
+ json = JsonSerializer.new(self, options).to_s
if include_root_in_json
- "{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}"
+ "{#{self.class.json_class_name}:#{json}}"
else
- JsonSerializer.new(self, options).to_s
+ json
end
end
@@ -84,9 +88,15 @@ module ActiveRecord #:nodoc:
self
end
+ private
+ # For compatibility with ActiveSupport::JSON.encode
+ def rails_to_json(options, *args)
+ to_json(options)
+ end
+
class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
def serialize
- serializable_record.to_json
+ ActiveSupport::JSON.encode(serializable_record)
end
end
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index fa75874603..4eaf9531e2 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/conversions'
+
module ActiveRecord #:nodoc:
module Serialization
# Builds an XML document to represent the model. Some configuration is
@@ -165,8 +167,9 @@ module ActiveRecord #:nodoc:
class XmlSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
def builder
@builder ||= begin
+ require 'builder' unless defined? ::Builder
options[:indent] ||= 2
- builder = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
+ builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
unless options[:skip_instruct]
builder.instruct!
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index 21471da419..9dda3361d8 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -295,7 +295,7 @@ module ActiveRecord
def set_session(env, sid, session_data)
Base.silence do
- record = env[SESSION_RECORD_KEY] ||= find_session(sid)
+ record = get_session_model(env, sid)
record.data = session_data
return false unless record.save
@@ -309,6 +309,14 @@ module ActiveRecord
return true
end
+
+ def get_session_model(env, sid)
+ if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
+ env[SESSION_RECORD_KEY] = find_session(sid)
+ else
+ env[SESSION_RECORD_KEY] ||= find_session(sid)
+ end
+ end
def find_session(id)
@@session_class.find_by_session_id(id) ||
diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb
index 8c6abaaccb..2dfe2c09ea 100644
--- a/activerecord/lib/active_record/test_case.rb
+++ b/activerecord/lib/active_record/test_case.rb
@@ -20,7 +20,7 @@ module ActiveRecord
patterns_to_match.each do |pattern|
failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
end
- assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found."
+ assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
end
def assert_queries(num = 1)
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index d9e1ef351f..da075dabd3 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -8,12 +8,14 @@ module ActiveRecord
# Timestamps are in the local timezone by default but you can use UTC by setting
# <tt>ActiveRecord::Base.default_timezone = :utc</tt>
module Timestamp
- def self.included(base) #:nodoc:
- base.alias_method_chain :create, :timestamps
- base.alias_method_chain :update, :timestamps
+ extend ActiveSupport::Concern
- base.class_inheritable_accessor :record_timestamps, :instance_writer => false
- base.record_timestamps = true
+ included do
+ alias_method_chain :create, :timestamps
+ alias_method_chain :update, :timestamps
+
+ class_inheritable_accessor :record_timestamps, :instance_writer => false
+ self.record_timestamps = true
end
# Saves the record with the updated_at/on attributes set to the current time.
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index b059eb7f6f..4f8ccdd40e 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -3,16 +3,14 @@ require 'thread'
module ActiveRecord
# See ActiveRecord::Transactions::ClassMethods for documentation.
module Transactions
+ extend ActiveSupport::Concern
+
class TransactionError < ActiveRecordError # :nodoc:
end
- def self.included(base)
- base.extend(ClassMethods)
-
- base.class_eval do
- [:destroy, :save, :save!].each do |method|
- alias_method_chain method, :transactions
- end
+ included do
+ [:destroy, :save, :save!].each do |method|
+ alias_method_chain method, :transactions
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 744205df1b..04f4ed47ac 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/integer/even_odd'
+
module ActiveRecord
# Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
# +record+ method to retrieve the record which did not validate.
@@ -98,17 +100,18 @@ module ActiveRecord
end
module Validations
- def self.included(base) # :nodoc:
- base.send :include, ActiveModel::Validations
- base.extend ClassMethods
- base.send :include, InstanceMethods
+ extend ActiveSupport::Concern
- base.define_callbacks :validate_on_create, :validate_on_update
+ depends_on ActiveSupport::Callbacks
+ depends_on ActiveModel::Validations
- base.class_eval do
- alias_method_chain :save, :validation
- alias_method_chain :save!, :validation
- end
+ included do
+ include Validations::InstanceMethods
+
+ alias_method_chain :save, :validation
+ alias_method_chain :save!, :validation
+
+ define_callbacks :validate_on_create, :validate_on_update
end
module ClassMethods
@@ -197,7 +200,6 @@ module ActiveRecord
respond_to?(attribute.to_sym) ? send(attribute.to_sym) : self[attribute.to_sym]
end
end
-
end
end