aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/aggregations.rb2
-rw-r--r--activerecord/lib/active_record/associations.rb76
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb36
-rw-r--r--activerecord/lib/active_record/associations/association.rb15
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb4
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb13
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb134
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb42
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb10
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb11
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb13
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/join_helper.rb3
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb5
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb3
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb7
-rw-r--r--activerecord/lib/active_record/autosave_association.rb2
-rw-r--r--activerecord/lib/active_record/base.rb127
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb35
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb28
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb138
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb4
-rw-r--r--activerecord/lib/active_record/counter_cache.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb16
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb2
-rw-r--r--activerecord/lib/active_record/locking/pessimistic.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb18
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb28
-rw-r--r--activerecord/lib/active_record/named_scope.rb21
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb28
-rw-r--r--activerecord/lib/active_record/persistence.rb14
-rw-r--r--activerecord/lib/active_record/query_cache.rb8
-rw-r--r--activerecord/lib/active_record/railtie.rb4
-rw-r--r--activerecord/lib/active_record/railties/databases.rake20
-rw-r--r--activerecord/lib/active_record/reflection.rb54
-rw-r--r--activerecord/lib/active_record/relation.rb52
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb28
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb32
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb2
-rw-r--r--activerecord/lib/active_record/result.rb2
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/serialization.rb8
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb2
-rw-r--r--activerecord/lib/active_record/session_store.rb18
-rw-r--r--activerecord/lib/active_record/test_case.rb19
-rw-r--r--activerecord/lib/active_record/transactions.rb2
-rw-r--r--activerecord/lib/active_record/validations.rb2
-rw-r--r--activerecord/lib/active_record/validations/associated.rb4
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb4
-rw-r--r--activerecord/lib/active_record/version.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb6
61 files changed, 623 insertions, 579 deletions
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 90d3b58c78..81ddbba51e 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -176,7 +176,7 @@ module ActiveRecord
# order in which mappings are defined determine the order in which attributes are sent to the
# value class constructor.
# * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
- # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
+ # attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
# mapped attributes.
# This defaults to +false+.
# * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 4979113b0b..2cbfa42718 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -76,12 +76,6 @@ module ActiveRecord
end
end
- class HasAndBelongsToManyAssociationWithPrimaryKeyError < ActiveRecordError #:nodoc:
- def initialize(reflection)
- super("Primary key is not allowed in a has_and_belongs_to_many join table (#{reflection.options[:join_table]}).")
- end
- end
-
class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
def initialize(reflection)
super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
@@ -458,20 +452,20 @@ module ActiveRecord
# end
#
# Some extensions can only be made to work with knowledge of the association's internals.
- # Extensions can access relevant state using the following methods (where 'items' is the
+ # Extensions can access relevant state using the following methods (where +items+ is the
# name of the association):
#
- # * +record.association(:items).owner+ - Returns the object the association is part of.
- # * +record.association(:items).reflection+ - Returns the reflection object that describes the association.
- # * +record.association(:items).target+ - Returns the associated object for +belongs_to+ and +has_one+, or
+ # * <tt>record.association(:items).owner</tt> - Returns the object the association is part of.
+ # * <tt>record.association(:items).reflection</tt> - Returns the reflection object that describes the association.
+ # * <tt>record.association(:items).target</tt> - Returns the associated object for +belongs_to+ and +has_one+, or
# the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
#
# === Association Join Models
#
# Has Many associations can be configured with the <tt>:through</tt> option to use an
- # explicit join model to retrieve the data. This operates similarly to a
- # +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
- # callbacks, and extra attributes on the join model. Consider the following schema:
+ # explicit join model to retrieve the data. This operates similarly to a
+ # +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
+ # callbacks, and extra attributes on the join model. Consider the following schema:
#
# class Author < ActiveRecord::Base
# has_many :authorships
@@ -528,7 +522,7 @@ module ActiveRecord
# @group.avatars # selects all avatars by going through the User join model.
#
# An important caveat with going through +has_one+ or +has_many+ associations on the
- # join model is that these associations are *read-only*. For example, the following
+ # join model is that these associations are *read-only*. For example, the following
# would not work following the previous example:
#
# @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
@@ -596,7 +590,7 @@ module ActiveRecord
# === Polymorphic Associations
#
# Polymorphic associations on models are not restricted on what types of models they
- # can be associated with. Rather, they specify an interface that a +has_many+ association
+ # can be associated with. Rather, they specify an interface that a +has_many+ association
# must adhere to.
#
# class Asset < ActiveRecord::Base
@@ -610,7 +604,7 @@ module ActiveRecord
# @asset.attachable = @post
#
# This works by using a type column in addition to a foreign key to specify the associated
- # record. In the Asset example, you'd need an +attachable_id+ integer column and an
+ # record. In the Asset example, you'd need an +attachable_id+ integer column and an
# +attachable_type+ string column.
#
# Using polymorphic associations in combination with single table inheritance (STI) is
@@ -666,7 +660,7 @@ module ActiveRecord
#
# Consider the following loop using the class above:
#
- # for post in Post.all
+ # Post.all.each do |post|
# puts "Post: " + post.title
# puts "Written by: " + post.author.name
# puts "Last comment on: " + post.comments.first.created_on
@@ -675,7 +669,7 @@ module ActiveRecord
# To iterate over these one hundred posts, we'll generate 201 database queries. Let's
# first just optimize it for retrieving the author:
#
- # for post in Post.find(:all, :include => :author)
+ # Post.find(:all, :include => :author).each do |post|
#
# This references the name of the +belongs_to+ association that also used the <tt>:author</tt>
# symbol. After loading the posts, find will collect the +author_id+ from each one and load
@@ -684,7 +678,7 @@ module ActiveRecord
#
# We can improve upon the situation further by referencing both associations in the finder with:
#
- # for post in Post.find(:all, :include => [ :author, :comments ])
+ # Post.find(:all, :include => [ :author, :comments ]).each do |post|
#
# This will load all comments with a single query. This reduces the total number of queries
# to 3. More generally the number of queries will be 1 plus the number of associations
@@ -692,7 +686,7 @@ module ActiveRecord
#
# To include a deep hierarchy of associations, use a hash:
#
- # for post in Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ])
+ # Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ]).each do |post|
#
# That'll grab not only all the comments but all their authors and gravatar pictures.
# You can mix and match symbols, arrays and hashes in any combination to describe the
@@ -741,7 +735,7 @@ module ActiveRecord
# Picture.find(:first, :include => :most_recent_comments).most_recent_comments # => returns all associated comments.
#
# When eager loaded, conditions are interpolated in the context of the model class, not
- # the model instance. Conditions are lazily interpolated before the actual model exists.
+ # the model instance. Conditions are lazily interpolated before the actual model exists.
#
# Eager loading is supported with polymorphic associations.
#
@@ -765,7 +759,7 @@ module ActiveRecord
# == Table Aliasing
#
# Active Record uses table aliasing in the case that a table is referenced multiple times
- # in a join. If a table is referenced only once, the standard table name is used. The
+ # in a join. If a table is referenced only once, the standard table name is used. The
# second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
# Indexes are appended for any more successive uses of the table name.
#
@@ -847,7 +841,7 @@ module ActiveRecord
# == Bi-directional associations
#
# When you specify an association there is usually an association on the associated model
- # that specifies the same relationship in reverse. For example, with the following models:
+ # that specifies the same relationship in reverse. For example, with the following models:
#
# class Dungeon < ActiveRecord::Base
# has_many :traps
@@ -864,9 +858,9 @@ module ActiveRecord
#
# The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are
# the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+
- # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
+ # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
# Active Record doesn't know anything about these inverse relationships and so no object
- # loading optimisation is possible. For example:
+ # loading optimization is possible. For example:
#
# d = Dungeon.first
# t = d.traps.first
@@ -876,8 +870,8 @@ module ActiveRecord
#
# The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to
# the same object data from the database, but are actually different in-memory copies
- # of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
- # Active Record about inverse relationships and it will optimise object loading. For
+ # of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
+ # Active Record about inverse relationships and it will optimise object loading. For
# example, if we changed our model definitions to:
#
# class Dungeon < ActiveRecord::Base
@@ -1060,7 +1054,7 @@ module ActiveRecord
# specify it with this option.
# [:conditions]
# Specify the conditions that the associated objects must meet in order to be included as a +WHERE+
- # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from
+ # SQL fragment, such as <tt>price > 5 AND name LIKE 'B%'</tt>. Record creations from
# the association are scoped if a hash is used.
# <tt>has_many :posts, :conditions => {:published => true}</tt> will create published
# posts with <tt>@blog.posts.create</tt> or <tt>@blog.posts.build</tt>.
@@ -1075,10 +1069,11 @@ module ActiveRecord
# Specify the method that returns the primary key used for the association. By default this is +id+.
# [:dependent]
# If set to <tt>:destroy</tt> all the associated objects are destroyed
- # alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
- # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
+ # alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
+ # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
# objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. If set to
- # <tt>:restrict</tt> this object cannot be deleted if it has any associated object.
+ # <tt>:restrict</tt> this object raises an <tt>ActiveRecord::DeleteRestrictionError</tt> exception and
+ # cannot be deleted if it has any associated objects.
#
# If using with the <tt>:through</tt> option, the association on the join model must be
# a +belongs_to+, and the records which get deleted are the join records, rather than
@@ -1227,7 +1222,8 @@ module ActiveRecord
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
# If set to <tt>:nullify</tt>, the associated object's foreign key is set to +NULL+.
- # Also, association is assigned.
+ # Also, association is assigned. If set to <tt>:restrict</tt> this object raises an
+ # <tt>ActiveRecord::DeleteRestrictionError</tt> exception and cannot be deleted if it has any associated object.
# [:foreign_key]
# Specify the foreign key used for the association. By default this is guessed to be the name
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
@@ -1243,7 +1239,7 @@ module ActiveRecord
# you want to do a join but not include the joined columns. Do not forget to include the
# primary and foreign keys, otherwise it will raise an error.
# [:through]
- # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt>,
+ # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt>,
# <tt>:primary_key</tt>, and <tt>:foreign_key</tt> are ignored, as the association uses the
# source reflection. You can only use a <tt>:through</tt> query through a <tt>has_one</tt>
# or <tt>belongs_to</tt> association on the join model.
@@ -1265,7 +1261,7 @@ module ActiveRecord
# By default, only save the associated object if it's a new record.
# [:inverse_of]
# Specifies the name of the <tt>belongs_to</tt> association on the associated object
- # that is the inverse of this <tt>has_one</tt> association. Does not work in combination
+ # that is the inverse of this <tt>has_one</tt> association. Does not work in combination
# with <tt>:through</tt> or <tt>:as</tt> options.
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
#
@@ -1383,7 +1379,7 @@ module ActiveRecord
# will be updated with the current time in addition to the updated_at/on attribute.
# [:inverse_of]
# Specifies the name of the <tt>has_one</tt> or <tt>has_many</tt> association on the associated
- # object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
+ # object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
# combination with the <tt>:polymorphic</tt> options.
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
#
@@ -1403,15 +1399,15 @@ module ActiveRecord
end
# Specifies a many-to-many relationship with another class. This associates two classes via an
- # intermediate join table. Unless the join table is explicitly specified as an option, it is
+ # intermediate join table. Unless the join table is explicitly specified as an option, it is
# guessed using the lexical order of the class names. So a join between Developer and Project
# will give the default join table name of "developers_projects" because "D" outranks "P".
- # Note that this precedence is calculated using the <tt><</tt> operator for String. This
+ # Note that this precedence is calculated using the <tt><</tt> operator for String. This
# means that if the strings are of different lengths, and the strings are equal when compared
# up to the shortest length, then the longer string is considered of higher
- # lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
+ # lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
# to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
- # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
+ # but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
# custom <tt>:join_table</tt> option if you need to.
#
# The join table should not have a primary key or a model associated with it. You must manually generate the
@@ -1513,7 +1509,7 @@ module ActiveRecord
# the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
# [:conditions]
# Specify the conditions that the associated object must meet in order to be included as a +WHERE+
- # SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are
+ # SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are
# scoped if a hash is used.
# <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
# or <tt>@blog.posts.build</tt>.
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index bd98cf2f46..92ed844a2e 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -9,7 +9,7 @@ module ActiveRecord
# table_joins is an array of arel joins which might conflict with the aliases we assign here
def initialize(table_joins = [])
- @aliases = Hash.new
+ @aliases = Hash.new { |h,k| h[k] = initial_count_for(k) }
@table_joins = table_joins
end
@@ -26,8 +26,6 @@ module ActiveRecord
def aliased_name_for(table_name, aliased_name = nil)
aliased_name ||= table_name
- initialize_count_for(table_name) if aliases[table_name].nil?
-
if aliases[table_name].zero?
# If it's zero, we can have our table_name
aliases[table_name] = 1
@@ -36,8 +34,6 @@ module ActiveRecord
# Otherwise, we need to use an alias
aliased_name = connection.table_alias_for(aliased_name)
- initialize_count_for(aliased_name) if aliases[aliased_name].nil?
-
# Update the count
aliases[aliased_name] += 1
@@ -49,32 +45,24 @@ module ActiveRecord
end
end
- def pluralize(table_name, base)
- base.pluralize_table_names ? table_name.to_s.pluralize : table_name.to_s
- end
-
private
- def initialize_count_for(name)
- aliases[name] = 0
-
- unless Arel::Table === table_joins
- # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
- quoted_name = connection.quote_table_name(name).downcase
+ def initial_count_for(name)
+ return 0 if Arel::Table === table_joins
- aliases[name] += table_joins.map { |join|
- # Table names + table aliases
- join.left.downcase.scan(
- /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
- ).size
- }.sum
- end
+ # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
+ quoted_name = connection.quote_table_name(name).downcase
- aliases[name]
+ table_joins.map { |join|
+ # Table names + table aliases
+ join.left.downcase.scan(
+ /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
+ ).size
+ }.sum
end
def truncate(name)
- name[0..connection.table_alias_length-3]
+ name.slice(0, connection.table_alias_length - 2)
end
def connection
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 687b668634..daadc8aa81 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -141,7 +141,7 @@ module ActiveRecord
@target ||= find_target
end
end
- loaded!
+ loaded! unless loaded?
target
rescue ActiveRecord::RecordNotFound
reset
@@ -177,9 +177,7 @@ module ActiveRecord
# Sets the owner attributes on the given record
def set_owner_attributes(record)
- if owner.persisted?
- creation_attributes.each { |key, value| record[key] = value }
- end
+ creation_attributes.each { |key, value| record[key] = value }
end
# Should be true if there is a foreign key present on the owner which
@@ -226,6 +224,15 @@ module ActiveRecord
def association_class
@reflection.klass
end
+
+ def build_record(attributes, options)
+ reflection.build_association(attributes, options) do |record|
+ record.assign_attributes(
+ create_scope.except(*record.changed),
+ :without_protection => true
+ )
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 94847bc2ae..9e6d9e73c5 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -60,7 +60,7 @@ module ActiveRecord
scope = scope.joins(join(
join_table,
- table[reflection.active_record_primary_key].
+ table[reflection.association_primary_key].
eq(join_table[reflection.association_foreign_key])
))
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 4b48757da7..d7632b2ea9 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -39,10 +39,6 @@ module ActiveRecord::Associations::Builder
model.send(:undecorated_table_name, model.to_s),
model.send(:undecorated_table_name, reflection.class_name)
)
-
- if model.connection.supports_primary_key? && (model.connection.primary_key(reflection.options[:join_table]) rescue false)
- raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection)
- end
end
# Generates a join table name from two provided table names.
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index 638a2ec72a..0cbbba041a 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -13,19 +13,6 @@ module ActiveRecord::Associations::Builder
private
- def define_readers
- super
- name = self.name
-
- model.redefine_method("#{name}_loaded?") do
- ActiveSupport::Deprecation.warn(
- "Calling obj.#{name}_loaded? is deprecated. Please use " \
- "obj.association(:#{name}).loaded? instead."
- )
- association(name).loaded?
- end
- end
-
def define_constructors
name = self.name
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 902ad8cb64..c15ee18e53 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -78,10 +78,14 @@ module ActiveRecord
end
def find(*args)
- if options[:finder_sql]
- find_by_scan(*args)
+ if block_given?
+ load_target.find(*args) { |*block_args| yield(*block_args) }
else
- scoped.find(*args)
+ if options[:finder_sql]
+ find_by_scan(*args)
+ else
+ scoped.find(*args)
+ end
end
end
@@ -104,44 +108,23 @@ module ActiveRecord
end
def create(attributes = {}, options = {}, &block)
- unless owner.persisted?
- raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
- end
-
- if attributes.is_a?(Array)
- attributes.collect { |attr| create(attr, options, &block) }
- else
- transaction do
- add_to_target(build_record(attributes, options)) do |record|
- yield(record) if block_given?
- insert_record(record)
- end
- end
- end
+ create_record(attributes, options, &block)
end
- def create!(attrs = {}, options = {}, &block)
- record = create(attrs, options, &block)
- Array.wrap(record).each(&:save!)
- record
+ def create!(attributes = {}, options = {}, &block)
+ create_record(attributes, options, true, &block)
end
- # Add +records+ to this association. Returns +self+ so method calls may be chained.
+ # Add +records+ to this association. Returns +self+ so method calls may be chained.
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
def concat(*records)
- result = true
load_target if owner.new_record?
- transaction do
- records.flatten.each do |record|
- raise_on_type_mismatch(record)
- add_to_target(record) do |r|
- result &&= insert_record(record) unless owner.new_record?
- end
- end
+ if owner.new_record?
+ concat_records(records)
+ else
+ transaction { concat_records(records) }
end
-
- result && records
end
# Starts a transaction in the association class's database connection.
@@ -310,14 +293,10 @@ module ActiveRecord
other_array.each { |val| raise_on_type_mismatch(val) }
original_target = load_target.dup
- transaction do
- delete(target - other_array)
-
- unless concat(other_array - target)
- @target = original_target
- raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
- "new records could not be saved."
- end
+ if owner.new_record?
+ replace_records(other_array, original_target)
+ else
+ transaction { replace_records(other_array, original_target) }
end
end
@@ -402,9 +381,13 @@ module ActiveRecord
return memory if persisted.empty?
persisted.map! do |record|
- mem_record = memory.delete(record)
+ # Unfortunately we cannot simply do memory.delete(record) since on 1.8 this returns
+ # record rather than memory.at(memory.index(record)). The behaviour is fixed in 1.9.
+ mem_index = memory.index(record)
+
+ if mem_index
+ mem_record = memory.delete_at(mem_index)
- if mem_record
(record.attribute_names - mem_record.changes.keys).each do |name|
mem_record[name] = record[name]
end
@@ -418,8 +401,25 @@ module ActiveRecord
persisted + memory
end
+ def create_record(attributes, options, raise = false, &block)
+ unless owner.persisted?
+ raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
+ end
+
+ if attributes.is_a?(Array)
+ attributes.collect { |attr| create_record(attr, options, raise, &block) }
+ else
+ transaction do
+ add_to_target(build_record(attributes, options)) do |record|
+ yield(record) if block_given?
+ insert_record(record, true, raise)
+ end
+ end
+ end
+ end
+
# Do the relevant stuff to insert the given record into the association collection.
- def insert_record(record, validate = true)
+ def insert_record(record, validate = true, raise = false)
raise NotImplementedError
end
@@ -427,26 +427,25 @@ module ActiveRecord
scoped.scope_for_create.stringify_keys
end
- def build_record(attributes, options)
- record = reflection.build_association(attributes, options)
- record.assign_attributes(create_scope.except(*record.changed), :without_protection => true)
- record.assign_attributes(attributes, options)
- record
- end
-
def delete_or_destroy(records, method)
records = records.flatten
records.each { |record| raise_on_type_mismatch(record) }
existing_records = records.reject { |r| r.new_record? }
- transaction do
- records.each { |record| callback(:before_remove, record) }
+ if existing_records.empty?
+ remove_records(existing_records, records, method)
+ else
+ transaction { remove_records(existing_records, records, method) }
+ end
+ end
- delete_records(existing_records, method) if existing_records.any?
- records.each { |record| target.delete(record) }
+ def remove_records(existing_records, records, method)
+ records.each { |record| callback(:before_remove, record) }
- records.each { |record| callback(:after_remove, record) }
- end
+ delete_records(existing_records, method) if existing_records.any?
+ records.each { |record| target.delete(record) }
+
+ records.each { |record| callback(:after_remove, record) }
end
# Delete the given records from the association, using one of the methods :destroy,
@@ -455,6 +454,29 @@ module ActiveRecord
raise NotImplementedError
end
+ def replace_records(new_target, original_target)
+ delete(target - new_target)
+
+ unless concat(new_target - target)
+ @target = original_target
+ raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
+ "new records could not be saved."
+ end
+ end
+
+ def concat_records(records)
+ result = true
+
+ records.flatten.each do |record|
+ raise_on_type_mismatch(record)
+ add_to_target(record) do |r|
+ result &&= insert_record(record) unless owner.new_record?
+ end
+ end
+
+ result && records
+ end
+
def callback(method, record)
callbacks_for(method).each do |callback|
case callback
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 8415942a1a..7f7dec467a 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -56,16 +56,18 @@ module ActiveRecord
Array.wrap(association.options[:extend]).each { |ext| proxy_extend(ext) }
end
- def respond_to?(*args)
+ alias_method :new, :build
+
+ def respond_to?(name, include_private = false)
super ||
- (load_target && target.respond_to?(*args)) ||
- @association.klass.respond_to?(*args)
+ (load_target && target.respond_to?(name, include_private)) ||
+ @association.klass.respond_to?(name, include_private)
end
def method_missing(method, *args, &block)
match = DynamicFinderMatch.match(method)
if match && match.instantiator?
- record = send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r|
+ send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r|
@association.send :set_owner_attributes, r
@association.send :add_to_target, r
yield(r) if block_given?
@@ -115,38 +117,6 @@ module ActiveRecord
@association.reload
self
end
-
- def new(*args, &block)
- if @association.is_a?(HasManyThroughAssociation)
- @association.build(*args, &block)
- else
- method_missing(:new, *args, &block)
- end
- end
-
- def proxy_owner
- ActiveSupport::Deprecation.warn(
- "Calling record.#{@association.reflection.name}.proxy_owner is deprecated. Please use " \
- "record.association(:#{@association.reflection.name}).owner instead."
- )
- @association.owner
- end
-
- def proxy_target
- ActiveSupport::Deprecation.warn(
- "Calling record.#{@association.reflection.name}.proxy_target is deprecated. Please use " \
- "record.association(:#{@association.reflection.name}).target instead."
- )
- @association.target
- end
-
- def proxy_reflection
- ActiveSupport::Deprecation.warn(
- "Calling record.#{@association.reflection.name}.proxy_reflection is deprecated. Please use " \
- "record.association(:#{@association.reflection.name}).reflection instead."
- )
- @association.reflection
- end
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 217213808b..f7ce70db1a 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
@@ -9,8 +9,14 @@ module ActiveRecord
super
end
- def insert_record(record, validate = true)
- return if record.new_record? && !record.save(:validate => validate)
+ def insert_record(record, validate = true, raise = false)
+ if record.new_record?
+ if raise
+ record.save!(:validate => validate)
+ else
+ return unless record.save(:validate => validate)
+ end
+ end
if options[:insert_sql]
owner.connection.insert(interpolate(options[:insert_sql], record))
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 78c5c4b870..50ee60284c 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -7,9 +7,14 @@ module ActiveRecord
# is provided by its child HasManyThroughAssociation.
class HasManyAssociation < CollectionAssociation #:nodoc:
- def insert_record(record, validate = true)
+ def insert_record(record, validate = true, raise = false)
set_owner_attributes(record)
- record.save(:validate => validate)
+
+ if raise
+ record.save!(:validate => validate)
+ else
+ record.save(:validate => validate)
+ end
end
private
@@ -18,7 +23,7 @@ module ActiveRecord
#
# If the association has a counter cache it gets that value. Otherwise
# it will attempt to do a count via SQL, bounded to <tt>:limit</tt> if
- # there's one. Some configuration options like :group make it impossible
+ # there's one. Some configuration options like :group make it impossible
# to do an SQL count, in those cases the array count will be used.
#
# That does not depend on whether the collection has already been loaded
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 7708228d23..2e818dca5d 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -6,8 +6,6 @@ module ActiveRecord
class HasManyThroughAssociation < HasManyAssociation #:nodoc:
include ThroughAssociation
- alias_method :new, :build
-
# 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
@@ -33,9 +31,16 @@ module ActiveRecord
super
end
- def insert_record(record, validate = true)
+ def insert_record(record, validate = true, raise = false)
ensure_not_nested
- return if record.new_record? && !record.save(:validate => validate)
+
+ if record.new_record?
+ if raise
+ record.save!(:validate => validate)
+ else
+ return unless record.save(:validate => validate)
+ end
+ end
through_record(record).save!
update_counter(1)
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 2f3a6e71f1..2131edbc20 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -14,12 +14,12 @@ module ActiveRecord
end
if record
- set_inverse_instance(record)
set_owner_attributes(record)
+ set_inverse_instance(record)
if owner.persisted? && save && !record.save
nullify_owner_attributes(record)
- set_owner_attributes(target)
+ set_owner_attributes(target) if target
raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
end
end
diff --git a/activerecord/lib/active_record/associations/join_helper.rb b/activerecord/lib/active_record/associations/join_helper.rb
index 87e33891a5..f83138195c 100644
--- a/activerecord/lib/active_record/associations/join_helper.rb
+++ b/activerecord/lib/active_record/associations/join_helper.rb
@@ -32,8 +32,7 @@ module ActiveRecord
end
def table_alias_for(reflection, join = false)
- name = alias_tracker.pluralize(reflection.name, reflection.active_record)
- name << "_#{alias_suffix}"
+ name = "#{reflection.plural_name}_#{alias_suffix}"
name << "_join" if join
name
end
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 7256dd5288..779f8164cc 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -68,7 +68,8 @@ module ActiveRecord
private
def associated_records_by_owner
- owner_keys = owners.map { |owner| owner[owner_key_name] }.compact.uniq
+ owners_map = owners_by_key
+ owner_keys = owners_map.keys.compact
if klass.nil? || owner_keys.empty?
records = []
@@ -84,7 +85,7 @@ module ActiveRecord
records.each do |record|
owner_key = record[association_key_name].to_s
- owners_by_key[owner_key].each do |owner|
+ owners_map[owner_key].each do |owner|
records_by_owner[owner] << record
end
end
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index ce1f2a5543..6b010064d5 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -26,8 +26,7 @@ module ActiveRecord
end
def build(attributes = {}, options = {})
- record = reflection.build_association(attributes, options)
- record.assign_attributes(create_scope.except(*record.changed), :without_protection => true)
+ record = build_record(attributes, options)
yield(record) if block_given?
set_new_record(record)
record
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index 53c5c3cedf..81172179e0 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -16,7 +16,7 @@ module ActiveRecord
chain[1..-1].each do |reflection|
scope = scope.merge(
reflection.klass.scoped.with_default_scope.
- except(:select, :create_with)
+ except(:select, :create_with, :includes)
)
end
scope
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 5833c65893..d0687ed0b6 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -61,7 +61,7 @@ module ActiveRecord
end
end
- def respond_to?(*args)
+ def respond_to?(name, include_private = false)
self.class.define_attribute_methods unless self.class.attribute_methods_generated?
super
end
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 5f06452247..e3f221c773 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -48,7 +48,7 @@ module ActiveRecord
end
attr_accessor :original_primary_key
-
+
# Attribute writer for the primary key column
def primary_key=(value)
@quoted_primary_key = nil
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index aef99e3129..9a50a20fbc 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -58,7 +58,7 @@ module ActiveRecord
generated_attribute_methods.module_eval("def _#{attr_name}; #{access_code}; end; alias #{attr_name} _#{attr_name}", __FILE__, __LINE__)
end
- # Define an attribute reader method. Cope with nil column.
+ # Define an attribute reader method. Cope with nil column.
# method_name is the same as attr_name except when a non-standard primary key is used,
# we still define #id as an accessor for the key
def define_read_method(method_name, attr_name, column)
@@ -99,8 +99,9 @@ module ActiveRecord
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
- if respond_to? "_#{attr_name}"
- send "_#{attr_name}" if @attributes.has_key?(attr_name.to_s)
+ method = "_#{attr_name}"
+ if respond_to? method
+ send method if @attributes.has_key?(attr_name.to_s)
else
_read_attribute attr_name
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 48dbe0838a..036d514a03 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -161,7 +161,7 @@ module ActiveRecord
#
# For performance reasons, we don't check whether to validate at runtime.
# However the validation and callback methods are lazy and those methods
- # get created when they are invoked for the very first time. However,
+ # get created when they are invoked for the very first time. However,
# this can change, for instance, when using nested attributes, which is
# called _after_ the association has been defined. Since we don't want
# the callbacks to get defined multiple times, there are guards that
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index fa33f02ae0..eb4a16ecf5 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -655,8 +655,8 @@ module ActiveRecord #:nodoc:
def set_table_name(value = nil, &block)
@quoted_table_name = nil
define_attr_method :table_name, value, &block
+ @arel_table = nil
- @arel_table = Arel::Table.new(table_name, arel_engine)
@relation = Relation.new(self, arel_table)
end
alias :table_name= :set_table_name
@@ -709,6 +709,12 @@ module ActiveRecord #:nodoc:
connection_pool.columns_hash[table_name]
end
+ # Returns a hash where the keys are column names and the values are
+ # default values when instantiating the AR object for this table.
+ def column_defaults
+ connection_pool.column_defaults[table_name]
+ end
+
# Returns an array of column names as strings.
def column_names
@column_names ||= columns.map { |column| column.name }
@@ -888,7 +894,7 @@ module ActiveRecord #:nodoc:
end
def arel_table
- Arel::Table.new(table_name, arel_engine)
+ @arel_table ||= Arel::Table.new(table_name, arel_engine)
end
def arel_engine
@@ -1204,11 +1210,11 @@ MSG
end
def current_scope #:nodoc:
- Thread.current[:"#{self}_current_scope"]
+ Thread.current["#{self}_current_scope"]
end
def current_scope=(scope) #:nodoc:
- Thread.current[:"#{self}_current_scope"] = scope
+ Thread.current["#{self}_current_scope"] = scope
end
# Use this macro in your model to set a default scope for all operations on
@@ -1261,11 +1267,14 @@ MSG
self.default_scopes = default_scopes + [scope]
end
+ # The @ignore_default_scope flag is used to prevent an infinite recursion situation where
+ # a default scope references a scope which has a default scope which references a scope...
def build_default_scope #:nodoc:
+ return if defined?(@ignore_default_scope) && @ignore_default_scope
+ @ignore_default_scope = true
+
if method(:default_scope).owner != Base.singleton_class
- # Use relation.scoping to ensure we ignore whatever the current value of
- # self.current_scope may be.
- relation.scoping { default_scope }
+ default_scope
elsif default_scopes.any?
default_scopes.inject(relation) do |default_scope, scope|
if scope.is_a?(Hash)
@@ -1277,6 +1286,8 @@ MSG
end
end
end
+ ensure
+ @ignore_default_scope = false
end
# Returns the class type of the record using the current module as a prefix. So descendants of
@@ -1299,7 +1310,6 @@ MSG
rescue NameError => e
# We don't want to swallow NoMethodError < NameError errors
raise e unless e.instance_of?(NameError)
- rescue ArgumentError
end
end
@@ -1523,6 +1533,7 @@ MSG
@marked_for_destruction = false
@previously_changed = {}
@changed_attributes = {}
+ @relation = nil
ensure_proper_type
set_serialized_attributes
@@ -1531,9 +1542,8 @@ MSG
assign_attributes(attributes, options) if attributes
- result = yield self if block_given?
+ yield self if block_given?
run_callbacks :initialize
- result
end
# Populate +coder+ with attributes about this record that should be
@@ -1564,6 +1574,7 @@ MSG
# post.title # => 'hello world'
def init_with(coder)
@attributes = coder['attributes']
+ @relation = nil
set_serialized_attributes
@@ -1653,9 +1664,6 @@ MSG
# If any attributes are protected by either +attr_protected+ or
# +attr_accessible+ then only settable attributes will be assigned.
#
- # The +guard_protected_attributes+ argument is now deprecated, use
- # the +assign_attributes+ method if you want to bypass mass-assignment security.
- #
# class User < ActiveRecord::Base
# attr_protected :is_admin
# end
@@ -1664,20 +1672,10 @@ MSG
# user.attributes = { :username => 'Phusion', :is_admin => true }
# user.username # => "Phusion"
# user.is_admin? # => false
- def attributes=(new_attributes, guard_protected_attributes = nil)
- unless guard_protected_attributes.nil?
- message = "the use of 'guard_protected_attributes' will be removed from the next major release of rails, " +
- "if you want to bypass mass-assignment security then look into using assign_attributes"
- ActiveSupport::Deprecation.warn(message)
- end
-
+ def attributes=(new_attributes)
return unless new_attributes.is_a?(Hash)
- if guard_protected_attributes == false
- assign_attributes(new_attributes, :without_protection => true)
- else
- assign_attributes(new_attributes)
- end
+ assign_attributes(new_attributes)
end
# Allows you to set all the attributes for a particular mass-assignment
@@ -1711,12 +1709,11 @@ MSG
return unless new_attributes
attributes = new_attributes.stringify_keys
- role = options[:as] || :default
-
multi_parameter_attributes = []
+ @mass_assignment_options = options
unless options[:without_protection]
- attributes = sanitize_for_mass_assignment(attributes, role)
+ attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role)
end
attributes.each do |k, v|
@@ -1729,6 +1726,7 @@ MSG
end
end
+ @mass_assignment_options = nil
assign_multiparameter_attributes(multi_parameter_attributes)
end
@@ -1783,16 +1781,12 @@ MSG
# Note also that destroying a record preserves its ID in the model instance, so deleted
# models are still comparable.
def ==(comparison_object)
- comparison_object.equal?(self) ||
+ super ||
comparison_object.instance_of?(self.class) &&
id.present? &&
comparison_object.id == id
end
-
- # Delegates to ==
- def eql?(comparison_object)
- self == comparison_object
- end
+ alias :eql? :==
# Delegates to id in order to allow two records of the same type and id to work with something like:
# [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
@@ -1810,6 +1804,15 @@ MSG
@attributes.frozen?
end
+ # Allows sort on objects
+ def <=>(other_object)
+ if other_object.is_a?(self.class)
+ self.to_key <=> other_object.to_key
+ else
+ nil
+ end
+ end
+
# Backport dup from 1.9 so that initialize_dup() gets called
unless Object.respond_to?(:initialize_dup)
def dup # :nodoc:
@@ -1861,12 +1864,16 @@ MSG
# Returns the contents of the record as a nicely formatted string.
def inspect
- attributes_as_nice_string = self.class.column_names.collect { |name|
- if has_attribute?(name)
- "#{name}: #{attribute_for_inspect(name)}"
- end
- }.compact.join(", ")
- "#<#{self.class} #{attributes_as_nice_string}>"
+ inspection = if @attributes
+ self.class.column_names.collect { |name|
+ if has_attribute?(name)
+ "#{name}: #{attribute_for_inspect(name)}"
+ end
+ }.compact.join(", ")
+ else
+ "not initialized"
+ end
+ "#<#{self.class} #{inspection}>"
end
protected
@@ -1884,12 +1891,33 @@ MSG
value
end
+ def mass_assignment_options
+ @mass_assignment_options ||= {}
+ end
+
+ def mass_assignment_role
+ mass_assignment_options[:as] || :default
+ end
+
private
+ # Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
+ # of the array, and then rescues from the possible NoMethodError. If those elements are
+ # ActiveRecord::Base's, then this triggers the various method_missing's that we have,
+ # which significantly impacts upon performance.
+ #
+ # So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
+ #
+ # See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
+ def to_ary # :nodoc:
+ nil
+ end
+
def set_serialized_attributes
- (@attributes.keys & self.class.serialized_attributes.keys).each do |key|
- coder = self.class.serialized_attributes[key]
- @attributes[key] = coder.load @attributes[key]
+ sattrs = self.class.serialized_attributes
+
+ sattrs.each do |key, coder|
+ @attributes[key] = coder.load @attributes[key] if @attributes.key?(key)
end
end
@@ -1899,8 +1927,9 @@ MSG
# do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
# No such attribute would be set for objects of the Message class in that example.
def ensure_proper_type
- unless self.class.descends_from_active_record?
- write_attribute(self.class.inheritance_column, self.class.sti_name)
+ klass = self.class
+ if klass.finder_needs_type_condition?
+ write_attribute(klass.inheritance_column, klass.sti_name)
end
end
@@ -2007,7 +2036,7 @@ MSG
set_values = (1..3).collect{|position| values_hash_from_param[position].blank? ? 1 : values_hash_from_param[position]}
begin
Date.new(*set_values)
- rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
+ rescue ArgumentError # if Date.new raises an exception on an invalid date
instantiate_time_object(name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
end
end
@@ -2074,8 +2103,10 @@ MSG
end
def populate_with_current_scope_attributes
- self.class.scoped.scope_for_create.each do |att,value|
- respond_to?("#{att}=") && send("#{att}=", value)
+ return unless self.class.scope_attributes?
+
+ self.class.scope_attributes.each do |att,value|
+ send("#{att}=", value) if respond_to?("#{att}=")
end
end
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 3fdbc9fce5..ddfdb05297 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -60,6 +60,7 @@ module ActiveRecord
attr_accessor :automatic_reconnect
attr_reader :spec, :connections
attr_reader :columns, :columns_hash, :primary_keys, :tables
+ attr_reader :column_defaults
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
# object which describes database connection information (e.g. adapter,
@@ -106,6 +107,12 @@ module ActiveRecord
}]
end
+ @column_defaults = Hash.new do |h, table_name|
+ h[table_name] = Hash[columns[table_name].map { |col|
+ [col.name, col.default]
+ }]
+ end
+
@primary_keys = Hash.new do |h, table_name|
h[table_name] = with_connection do |conn|
table_exists?(table_name) ? conn.primary_key(table_name) : 'id'
@@ -119,6 +126,7 @@ module ActiveRecord
with_connection do |conn|
conn.tables.each { |table| @tables[table] = true }
+ @tables[name] = true if !@tables.key?(name) && conn.table_exists?(name)
end
@tables.key? name
@@ -132,6 +140,7 @@ module ActiveRecord
def clear_cache!
@columns.clear
@columns_hash.clear
+ @column_defaults.clear
@tables.clear
end
@@ -139,6 +148,7 @@ module ActiveRecord
def clear_table_cache!(table_name)
@columns.delete table_name
@columns_hash.delete table_name
+ @column_defaults.delete table_name
@primary_keys.delete table_name
end
@@ -165,7 +175,7 @@ module ActiveRecord
checkin conn if conn
end
- # If a connection already exists yield it to the block. If no connection
+ # 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
@@ -425,6 +435,14 @@ module ActiveRecord
@testing = testing
end
+ def method_missing(method_sym, *arguments, &block)
+ @body.send(method_sym, *arguments, &block)
+ end
+
+ def respond_to?(method_sym, include_private = false)
+ super || @body.respond_to?(method_sym)
+ end
+
def each(&block)
body.each(&block)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index b3eb23bbb3..777ef15dfc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/module/deprecation'
-
module ActiveRecord
module ConnectionAdapters # :nodoc:
module DatabaseStatements
@@ -42,7 +40,7 @@ module ActiveRecord
undef_method :execute
# Executes +sql+ statement in the context of this connection using
- # +binds+ as the bind substitutes. +name+ is logged along with
+ # +binds+ as the bind substitutes. +name+ is logged along with
# the executed +sql+ statement.
def exec_query(sql, name = 'SQL', binds = [])
end
@@ -245,38 +243,13 @@ module ActiveRecord
# done if the transaction block raises an exception or returns false.
def rollback_db_transaction() end
- # Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL
- # fragment that has the same semantics as LIMIT and OFFSET.
- #
- # +options+ must be a Hash which contains a +:limit+ option
- # and an +:offset+ option.
- #
- # This method *modifies* the +sql+ parameter.
- #
- # This method is deprecated!! Stop using it!
- #
- # ===== Examples
- # add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50})
- # generates
- # SELECT * FROM suppliers LIMIT 10 OFFSET 50
- def add_limit_offset!(sql, options)
- if limit = options[:limit]
- sql << " LIMIT #{sanitize_limit(limit)}"
- end
- if offset = options[:offset]
- sql << " OFFSET #{offset.to_i}"
- end
- sql
- end
- deprecate :add_limit_offset!
-
def default_sequence_name(table, column)
nil
end
# Set the sequence to the max value of the table's column.
def reset_sequence!(table, column, sequence = nil)
- # Do nothing by default. Implement for PostgreSQL, Oracle, ...
+ # Do nothing by default. Implement for PostgreSQL, Oracle, ...
end
# Inserts the given fixture into the table. Overridden in adapters that require
@@ -308,10 +281,10 @@ module ActiveRecord
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
#
# The +limit+ may be anything that can evaluate to a string via #to_s. It
- # should look like an integer, or a comma-delimited list of integers, or
+ # should look like an integer, or a comma-delimited list of integers, or
# an Arel SQL literal.
#
- # Returns Integer and Arel::Nodes::SqlLiteral limits as is.
+ # Returns Integer and Arel::Nodes::SqlLiteral limits as is.
# Returns the sanitized limit parameter, either as an integer, or as a
# string which contains a comma-delimited list of integers.
def sanitize_limit(limit)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 70a8f6bb58..82f564e41d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -328,7 +328,7 @@ module ActiveRecord
end
# Checks to see if a column exists. See SchemaStatements#column_exists?
- def column_exists?(column_name, type = nil, options = nil)
+ def column_exists?(column_name, type = nil, options = {})
@base.column_exists?(@table_name, column_name, type, options)
end
@@ -386,13 +386,13 @@ module ActiveRecord
# Removes the given index from the table.
#
# ===== Examples
- # ====== Remove the suppliers_name_index in the suppliers table
- # t.remove_index :name
- # ====== Remove the index named accounts_branch_id_index in the accounts table
+ # ====== Remove the index_table_name_on_column in the table_name table
+ # t.remove_index :column
+ # ====== Remove the index named index_table_name_on_branch_id in the table_name table
# t.remove_index :column => :branch_id
- # ====== Remove the index named accounts_branch_id_party_id_index in the accounts table
+ # ====== Remove the index named index_table_name_on_branch_id_and_party_id in the table_name table
# t.remove_index :column => [:branch_id, :party_id]
- # ====== Remove the index named by_branch_party in the accounts table
+ # ====== Remove the index named by_branch_party in the table_name table
# t.remove_index :name => :by_branch_party
def remove_index(options = {})
@base.remove_index(@table_name, options)
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 b4969c29d2..8e3ba1297e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -4,7 +4,7 @@ module ActiveRecord
module ConnectionAdapters # :nodoc:
module SchemaStatements
# Returns a Hash of mappings from the abstract data types to the native
- # database types. See TableDefinition#column for details on the recognized
+ # database types. See TableDefinition#column for details on the recognized
# abstract data types.
def native_database_types
{}
@@ -78,7 +78,7 @@ module ActiveRecord
# Creates a new table with the name +table_name+. +table_name+ may either
# be a String or a Symbol.
#
- # There are two ways to work with +create_table+. You can use the block
+ # There are two ways to work with +create_table+. You can use the block
# form or the regular form, like this:
#
# === Block form
@@ -161,7 +161,7 @@ module ActiveRecord
yield td if block_given?
if options[:force] && table_exists?(table_name)
- drop_table(table_name, options)
+ drop_table(table_name)
end
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
@@ -253,7 +253,7 @@ module ActiveRecord
end
# Drops a table from the database.
- def drop_table(table_name, options = {})
+ def drop_table(table_name)
execute "DROP TABLE #{quote_table_name(table_name)}"
end
@@ -299,7 +299,7 @@ module ActiveRecord
raise NotImplementedError, "rename_column is not implemented"
end
- # Adds a new index to the table. +column_name+ can be a single Symbol, or
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
# an Array of Symbols.
#
# The index will be named after the table and the first column name,
@@ -346,11 +346,11 @@ module ActiveRecord
# Remove the given index from the table.
#
- # Remove the suppliers_name_index in the suppliers table.
- # remove_index :suppliers, :name
- # Remove the index named accounts_branch_id_index in the accounts table.
+ # Remove the index_accounts_on_column in the accounts table.
+ # remove_index :accounts, :column
+ # Remove the index named index_accounts_on_branch_id in the accounts table.
# remove_index :accounts, :column => :branch_id
- # Remove the index named accounts_branch_id_party_id_index in the accounts table.
+ # Remove the index named index_accounts_on_branch_id_and_party_id in the accounts table.
# remove_index :accounts, :column => [:branch_id, :party_id]
# Remove the index named by_branch_party in the accounts table.
# remove_index :accounts, :name => :by_branch_party
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 3eddb69e73..a7856539b7 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -1,3 +1,5 @@
+require 'set'
+
module ActiveRecord
# :stopdoc:
module ConnectionAdapters
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index bbaac29e5a..d6c167ad36 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -1,6 +1,6 @@
# encoding: utf-8
-gem 'mysql2', '~> 0.3.0'
+gem 'mysql2', '~> 0.3.6'
require 'mysql2'
module ActiveRecord
@@ -347,19 +347,6 @@ module ActiveRecord
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
end
- def add_limit_offset!(sql, options)
- limit, offset = options[:limit], options[:offset]
- if limit && offset
- sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
- elsif limit
- sql << " LIMIT #{sanitize_limit(limit)}"
- elsif offset
- sql << " OFFSET #{offset.to_i}"
- end
- sql
- end
- deprecate :add_limit_offset!
-
# SCHEMA STATEMENTS ========================================
def structure_dump
@@ -440,10 +427,6 @@ module ActiveRecord
tables(nil, schema).include? table
end
- def drop_table(table_name, options = {})
- super(table_name, options)
- end
-
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil)
indexes = []
@@ -582,11 +565,6 @@ module ActiveRecord
pk_and_sequence && pk_and_sequence.first
end
- def case_sensitive_equality_operator
- "= BINARY"
- end
- deprecate :case_sensitive_equality_operator
-
def case_sensitive_modifier(node)
Arel::Nodes::Bin.new(node)
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index a3704fe54a..9e6cb13cca 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -406,7 +406,7 @@ module ActiveRecord
def exec_without_stmt(sql, name = 'SQL') # :nodoc:
# Some queries, like SHOW CREATE TABLE don't work through the prepared
- # statement API. For those queries, we need to use this method. :'(
+ # statement API. For those queries, we need to use this method. :'(
log(sql, name) do
result = @connection.query(sql)
cols = []
@@ -487,19 +487,6 @@ module ActiveRecord
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
end
- def add_limit_offset!(sql, options) #:nodoc:
- limit, offset = options[:limit], options[:offset]
- if limit && offset
- sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
- elsif limit
- sql << " LIMIT #{sanitize_limit(limit)}"
- elsif offset
- sql << " OFFSET #{offset.to_i}"
- end
- sql
- end
- deprecate :add_limit_offset!
-
# SCHEMA STATEMENTS ========================================
def structure_dump #:nodoc:
@@ -581,10 +568,6 @@ module ActiveRecord
tables(nil, schema).include? table
end
- def drop_table(table_name, options = {})
- super(table_name, options)
- end
-
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil)#:nodoc:
indexes = []
@@ -712,11 +695,6 @@ module ActiveRecord
pk_and_sequence && pk_and_sequence.first
end
- def case_sensitive_equality_operator
- "= BINARY"
- end
- deprecate :case_sensitive_equality_operator
-
def case_sensitive_modifier(node)
Arel::Nodes::Bin.new(node)
end
@@ -729,7 +707,7 @@ module ActiveRecord
def quoted_columns_for_index(column_names, options = {})
length = options[:length] if options.is_a?(Hash)
- quoted_column_names = case length
+ case length
when Hash
column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
when Fixnum
@@ -832,7 +810,7 @@ module ActiveRecord
stmt.execute(*binds.map { |col, val| type_cast(val, col) })
rescue Mysql::Error => e
# Older versions of MySQL leave the prepared statement in a bad
- # place when an error occurs. To support older mysql versions, we
+ # place when an error occurs. To support older mysql versions, we
# need to close the statement and delete the statement from the
# cache.
stmt.close
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index f94d52e20e..29dd0a9ea3 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -198,7 +198,7 @@ module ActiveRecord
# * <tt>:password</tt> - Defaults to nothing.
# * <tt>:database</tt> - The name of the database. No default, must be provided.
# * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
- # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
+ # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
# * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
# <encoding></tt> call on the connection.
# * <tt>:min_messages</tt> - An optional client min messages that is used in a
@@ -466,10 +466,11 @@ module ActiveRecord
# Executes an INSERT query and returns the new record's ID
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- # Extract the table from the insert sql. Yuck.
- _, table = extract_schema_and_table(sql.split(" ", 4)[2])
-
- pk ||= primary_key(table)
+ unless pk
+ # Extract the table from the insert sql. Yuck.
+ table_ref = extract_table_ref_from_insert_sql(sql)
+ pk = primary_key(table_ref) if table_ref
+ end
if pk
select_value("#{sql} RETURNING #{quote_column_name(pk)}")
@@ -565,9 +566,9 @@ module ActiveRecord
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
unless pk
- _, table = extract_schema_and_table(sql.split(" ", 4)[2])
-
- pk = primary_key(table)
+ # Extract the table from the insert sql. Yuck.
+ table_ref = extract_table_ref_from_insert_sql(sql)
+ pk = primary_key(table_ref) if table_ref
end
sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
@@ -618,7 +619,7 @@ module ActiveRecord
create_database(name)
end
- # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
+ # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
# <tt>:encoding</tt>, <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
# <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
#
@@ -665,34 +666,33 @@ module ActiveRecord
SQL
end
+ # Returns true if table exists.
+ # If the schema is not specified as part of +name+ then it will only find tables within
+ # the current schema search path (regardless of permissions to access tables in other schemas)
def table_exists?(name)
schema, table = extract_schema_and_table(name.to_s)
+ return false unless table
- binds = [[nil, table.gsub(/(^"|"$)/,'')]]
+ binds = [[nil, table]]
binds << [nil, schema] if schema
exec_query(<<-SQL, 'SCHEMA', binds).rows.first[0].to_i > 0
- SELECT COUNT(*)
- FROM pg_tables
- WHERE tablename = $1
- #{schema ? "AND schemaname = $2" : ''}
+ SELECT COUNT(*)
+ FROM pg_class c
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind in ('v','r')
+ AND c.relname = $1
+ AND n.nspname = #{schema ? '$2' : 'ANY (current_schemas(false))'}
SQL
end
- # Extracts the table and schema name from +name+
- def extract_schema_and_table(name)
- schema, table = name.split('.', 2)
-
- unless table # A table was provided without a schema
- table = schema
- schema = nil
- end
-
- if name =~ /^"/ # Handle quoted table names
- table = name
- schema = nil
- end
- [schema, table]
+ # Returns true if schema exists.
+ def schema_exists?(name)
+ exec_query(<<-SQL, 'SCHEMA', [[nil, name]]).rows.first[0].to_i > 0
+ SELECT COUNT(*)
+ FROM pg_namespace
+ WHERE nspname = $1
+ SQL
end
# Returns an array of indexes for the given table.
@@ -742,6 +742,11 @@ module ActiveRecord
query('select current_database()')[0][0]
end
+ # Returns the current schema name.
+ def current_schema
+ query('SELECT current_schema', 'SCHEMA')[0][0]
+ end
+
# Returns the current database encoding format.
def encoding
query(<<-end_sql)[0][0]
@@ -805,7 +810,7 @@ module ActiveRecord
end
if pk && sequence
- quoted_sequence = quote_column_name(sequence)
+ quoted_sequence = quote_table_name(sequence)
select_value <<-end_sql, 'Reset sequence'
SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
@@ -818,18 +823,25 @@ module ActiveRecord
# First try looking for a sequence with a dependency on the
# given table's primary key.
result = exec_query(<<-end_sql, 'SCHEMA').rows.first
- SELECT attr.attname, seq.relname
+ SELECT attr.attname, ns.nspname, seq.relname
FROM pg_class seq
INNER JOIN pg_depend dep ON seq.oid = dep.objid
INNER JOIN pg_attribute attr ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
+ INNER JOIN pg_namespace ns ON seq.relnamespace = ns.oid
WHERE seq.relkind = 'S'
AND cons.contype = 'p'
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
end_sql
# [primary_key, sequence]
- [result.first, result.last]
+ if result.second == 'public' then
+ sequence = result.last
+ else
+ sequence = result.second+'.'+result.last
+ end
+
+ [result.first, sequence]
rescue
nil
end
@@ -928,7 +940,7 @@ module ActiveRecord
# Construct a clean list of column names from the ORDER BY clause, removing
# any ASC/DESC modifiers
- order_columns = orders.collect { |s| s =~ /^(.+)\s+(ASC|DESC)\s*$/i ? $1 : s }
+ order_columns = orders.collect { |s| s.gsub(/\s+(ASC|DESC)\s*/i, '') }
order_columns.delete_if { |c| c.blank? }
order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
@@ -953,27 +965,27 @@ module ActiveRecord
end
private
- def exec_no_cache(sql, binds)
- @connection.async_exec(sql)
- end
-
- def exec_cache(sql, binds)
- unless @statements.key? sql
- nextkey = "a#{@statements.length + 1}"
- @connection.prepare nextkey, sql
- @statements[sql] = nextkey
+ def exec_no_cache(sql, binds)
+ @connection.async_exec(sql)
end
- key = @statements[sql]
+ def exec_cache(sql, binds)
+ unless @statements.key? sql
+ nextkey = "a#{@statements.length + 1}"
+ @connection.prepare nextkey, sql
+ @statements[sql] = nextkey
+ end
- # Clear the queue
- @connection.get_last_result
- @connection.send_query_prepared(key, binds.map { |col, val|
- type_cast(val, col)
- })
- @connection.block
- @connection.get_last_result
- end
+ key = @statements[sql]
+
+ # Clear the queue
+ @connection.get_last_result
+ @connection.send_query_prepared(key, binds.map { |col, val|
+ type_cast(val, col)
+ })
+ @connection.block
+ @connection.get_last_result
+ end
# The internal PostgreSQL identifier of the money data type.
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
@@ -1073,9 +1085,29 @@ module ActiveRecord
end
end
- def table_definition
- TableDefinition.new(self)
- end
+ # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
+ # +schema_name+ is nil if not specified in +name+.
+ # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
+ # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
+ #
+ # * <tt>table_name</tt>
+ # * <tt>"table.name"</tt>
+ # * <tt>schema_name.table_name</tt>
+ # * <tt>schema_name."table.name"</tt>
+ # * <tt>"schema.name"."table name"</tt>
+ def extract_schema_and_table(name)
+ table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
+ [schema, table]
+ end
+
+ def extract_table_ref_from_insert_sql(sql)
+ sql[/into\s+([^\(]*).*values\s*\(/i]
+ $1.strip if $1
+ end
+
+ def table_definition
+ TableDefinition.new(self)
+ end
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 3c6f52e0fa..724b2e6d9c 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -102,6 +102,10 @@ module ActiveRecord
# Clears the prepared statements cache.
def clear_cache!
+ @statements.values.map { |hash| hash[:stmt] }.each { |stmt|
+ stmt.close unless stmt.closed?
+ }
+
@statements.clear
end
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index 7839f03848..4d387565d9 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -2,7 +2,7 @@ module ActiveRecord
# = Active Record Counter Cache
module CounterCache
# Resets one or more counter caches to their correct value using an SQL
- # count query. This is useful when adding new counter caches, or if the
+ # count query. This is useful when adding new counter caches, or if the
# counter has been corrupted or modified directly by SQL.
#
# ==== Parameters
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 1f31722a7c..819743a361 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -12,7 +12,6 @@ require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/logger'
require 'active_support/ordered_hash'
-require 'active_support/core_ext/module/deprecation'
require 'active_record/fixtures/file'
if defined? ActiveRecord
@@ -54,14 +53,14 @@ class FixturesFileNotFound < StandardError; end
# name: Google
# url: http://www.google.com
#
-# This YAML fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and is followed by an
-# indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
+# This YAML fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and is followed by an
+# indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
# pleasure.
#
# Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
# See http://yaml.org/type/omap.html
-# for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table.
-# This is commonly needed for tree structures. Example:
+# for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table.
+# This is commonly needed for tree structures. Example:
#
# --- !omap
# - parent:
@@ -75,7 +74,7 @@ class FixturesFileNotFound < StandardError; end
#
# = Using fixtures in testcases
#
-# Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
+# Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
# fixtures, but first let's take a look at a sample unit test:
#
# require 'test_helper'
@@ -393,9 +392,6 @@ class FixturesFileNotFound < StandardError; end
#
# Any fixture labeled "DEFAULTS" is safely ignored.
-Fixture = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Fixture', 'ActiveRecord::Fixture')
-Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Fixtures', 'ActiveRecord::Fixtures')
-
module ActiveRecord
class Fixtures
MAX_ID = 2 ** 30 - 1
@@ -558,7 +554,7 @@ module ActiveRecord
fixtures.size
end
- # Return a hash of rows to be inserted. The key is the table, the value is
+ # Return a hash of rows to be inserted. The key is the table, the value is
# a list of rows to insert to that table.
def table_rows
now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 3afa257a76..6cfce6e573 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -73,7 +73,7 @@ module ActiveRecord
# <tt>locking_enabled?</tt> at this point as
# <tt>@attributes</tt> may not have been initialized yet.
- if lock_optimistically && result.include?(self.class.locking_column)
+ if result.key?(self.class.locking_column) && lock_optimistically
result[self.class.locking_column] ||= 0
end
diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb
index 4c4c1bf5a1..66994e4797 100644
--- a/activerecord/lib/active_record/locking/pessimistic.rb
+++ b/activerecord/lib/active_record/locking/pessimistic.rb
@@ -14,7 +14,7 @@ module ActiveRecord
# Account.transaction do
# # select * from accounts where name = 'shugo' limit 1 for update
# shugo = Account.where("name = 'shugo'").lock(true).first
- # yuko = Account.where("name = 'shugo'").lock(true).first
+ # yuko = Account.where("name = 'yuko'").lock(true).first
# shugo.balance -= 100
# shugo.save!
# yuko.balance += 100
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 640111096d..c459846264 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -53,7 +53,7 @@ module ActiveRecord
#
# This migration will add a boolean flag to the accounts table and remove it
# if you're backing out of the migration. It shows how all migrations have
- # two class methods +up+ and +down+ that describes the transformations
+ # two methods +up+ and +down+ that describes the transformations
# required to implement or remove the migration. These methods can consist
# of both the migration specific methods like add_column and remove_column,
# but may also contain regular Ruby code for generating data needed for the
@@ -66,9 +66,9 @@ module ActiveRecord
# create_table :system_settings do |t|
# t.string :name
# t.string :label
- # t.text :value
+ # t.text :value
# t.string :type
- # t.integer :position
+ # t.integer :position
# end
#
# SystemSetting.create :name => "notice",
@@ -116,8 +116,10 @@ module ActiveRecord
# with the name of the column. Other options include
# <tt>:name</tt> and <tt>:unique</tt> (e.g.
# <tt>{ :name => "users_name_index", :unique => true }</tt>).
- # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified
- # by +index_name+.
+ # * <tt>remove_index(table_name, :column => column_name)</tt>: Removes the index
+ # specified by +column_name+.
+ # * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
+ # specified by +index_name+.
#
# == Irreversible transformations
#
@@ -179,7 +181,7 @@ module ActiveRecord
#
# class RemoveEmptyTags < ActiveRecord::Migration
# def up
- # Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
+ # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
# end
#
# def down
@@ -225,7 +227,7 @@ module ActiveRecord
# def up
# add_column :people, :salary, :integer
# Person.reset_column_information
- # Person.find(:all).each do |p|
+ # Person.all.each do |p|
# p.update_attribute :salary, SalaryCalculator.compute(p)
# end
# end
@@ -245,7 +247,7 @@ module ActiveRecord
# def up
# ...
# say_with_time "Updating salaries..." do
- # Person.find(:all).each do |p|
+ # Person.all.each do |p|
# p.update_attribute :salary, SalaryCalculator.compute(p)
# end
# end
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index c9d57ce812..2eeff7e36f 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -1,12 +1,12 @@
module ActiveRecord
class Migration
- # ActiveRecord::Migration::CommandRecorder records commands done during
- # a migration and knows how to reverse those commands. The CommandRecorder
+ # <tt>ActiveRecord::Migration::CommandRecorder</tt> records commands done during
+ # a migration and knows how to reverse those commands. The CommandRecorder
# knows how to invert the following commands:
#
# * add_column
# * add_index
- # * add_timestamp
+ # * add_timestamps
# * create_table
# * remove_timestamps
# * rename_column
@@ -20,21 +20,21 @@ module ActiveRecord
@delegate = delegate
end
- # record +command+. +command+ should be a method name and arguments.
+ # record +command+. +command+ should be a method name and arguments.
# For example:
#
- # recorder.record(:method_name, [:arg1, arg2])
+ # recorder.record(:method_name, [:arg1, :arg2])
def record(*command)
@commands << command
end
# Returns a list that represents commands that are the inverse of the
- # commands stored in +commands+. For example:
+ # commands stored in +commands+. For example:
#
# recorder.record(:rename_table, [:old, :new])
# recorder.inverse # => [:rename_table, [:new, :old]]
#
- # This method will raise an IrreversibleMigration exception if it cannot
+ # This method will raise an +IrreversibleMigration+ exception if it cannot
# invert the +commands+.
def inverse
@commands.reverse.map { |name, args|
@@ -48,11 +48,11 @@ module ActiveRecord
super || delegate.respond_to?(*args)
end
- [:create_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default].each do |method|
+ [:create_table, :change_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default].each do |method|
class_eval <<-EOV, __FILE__, __LINE__ + 1
- def #{method}(*args)
- record(:"#{method}", args)
- end
+ def #{method}(*args) # def create_table(*args)
+ record(:"#{method}", args) # record(:create_table, args)
+ end # end
EOV
end
@@ -79,8 +79,10 @@ module ActiveRecord
end
def invert_add_index(args)
- table, columns, _ = *args
- [:remove_index, [table, {:column => columns}]]
+ table, columns, options = *args
+ index_name = options.try(:[], :name)
+ options_hash = index_name ? {:name => index_name} : {:column => columns}
+ [:remove_index, [table, options_hash]]
end
def invert_remove_timestamps(args)
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 588f52be44..0313abe456 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -17,7 +17,7 @@ module ActiveRecord
# posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
#
# fruits = Fruit.scoped
- # fruits = fruits.where(:colour => 'red') if options[:red_only]
+ # fruits = fruits.where(:color => 'red') if options[:red_only]
# fruits = fruits.limit(10) if limited?
#
# Anonymous \scopes tend to be useful when procedurally generating complex
@@ -40,6 +40,25 @@ module ActiveRecord
end
end
+ ##
+ # Collects attributes from scopes that should be applied when creating
+ # an AR instance for the particular class this is called on.
+ def scope_attributes # :nodoc:
+ if current_scope
+ current_scope.scope_for_create
+ else
+ scope = relation.clone
+ scope.default_scoped = true
+ scope.scope_for_create
+ end
+ end
+
+ ##
+ # Are there default attributes associated with this scope?
+ def scope_attributes? # :nodoc:
+ current_scope || default_scopes.any?
+ end
+
# Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query,
# such as <tt>where(:color => :red).select('shirts.*').includes(:washing_instructions)</tt>.
#
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 08b27b6a8e..53617059d0 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -277,14 +277,14 @@ module ActiveRecord
type = (reflection.collection? ? :collection : :one_to_one)
# def pirate_attributes=(attributes)
- # assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
+ # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
# end
class_eval <<-eoruby, __FILE__, __LINE__ + 1
if method_defined?(:#{association_name}_attributes=)
remove_method(:#{association_name}_attributes=)
end
def #{association_name}_attributes=(attributes)
- assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
+ assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options)
end
eoruby
else
@@ -319,21 +319,21 @@ module ActiveRecord
# If the given attributes include a matching <tt>:id</tt> attribute, or
# update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
# then the existing record will be marked for destruction.
- def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
+ def assign_nested_attributes_for_one_to_one_association(association_name, attributes, assignment_opts = {})
options = self.nested_attributes_options[association_name]
attributes = attributes.with_indifferent_access
if (options[:update_only] || !attributes['id'].blank?) && (record = send(association_name)) &&
(options[:update_only] || record.id.to_s == attributes['id'].to_s)
- assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
+ assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy], assignment_opts) unless call_reject_if(association_name, attributes)
- elsif attributes['id'].present?
+ elsif attributes['id'].present? && !assignment_opts[:without_protection]
raise_nested_attributes_record_not_found(association_name, attributes['id'])
elsif !reject_new_record?(association_name, attributes)
method = "build_#{association_name}"
if respond_to?(method)
- send(method, attributes.except(*UNASSIGNABLE_KEYS))
+ send(method, attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
else
raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
end
@@ -367,7 +367,7 @@ module ActiveRecord
# { :name => 'John' },
# { :id => '2', :_destroy => true }
# ])
- def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
+ def assign_nested_attributes_for_collection_association(association_name, attributes_collection, assignment_opts = {})
options = self.nested_attributes_options[association_name]
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
@@ -401,7 +401,7 @@ module ActiveRecord
if attributes['id'].blank?
unless reject_new_record?(association_name, attributes)
- association.build(attributes.except(*UNASSIGNABLE_KEYS))
+ association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
end
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
unless association.loaded? || call_reject_if(association_name, attributes)
@@ -418,8 +418,10 @@ module ActiveRecord
end
if !call_reject_if(association_name, attributes)
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy], assignment_opts)
end
+ elsif assignment_opts[:without_protection]
+ association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
else
raise_nested_attributes_record_not_found(association_name, attributes['id'])
end
@@ -428,8 +430,8 @@ module ActiveRecord
# Updates a record with the +attributes+ or marks it for destruction if
# +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
- def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
- record.attributes = attributes.except(*UNASSIGNABLE_KEYS)
+ def assign_to_or_mark_for_destruction(record, attributes, allow_destroy, assignment_opts)
+ record.assign_attributes(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
end
@@ -458,5 +460,9 @@ module ActiveRecord
def raise_nested_attributes_record_not_found(association_name, record_id)
raise RecordNotFound, "Couldn't find #{self.class.reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
end
+
+ def unassignable_keys(assignment_opts)
+ assignment_opts[:without_protection] ? UNASSIGNABLE_KEYS - %w[id] : UNASSIGNABLE_KEYS
+ end
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index b9041f44d8..f425118f59 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -33,7 +33,11 @@ module ActiveRecord
# +save+ returns +false+. See ActiveRecord::Callbacks for further
# details.
def save(*)
- create_or_update
+ begin
+ create_or_update
+ rescue ActiveRecord::RecordInvalid
+ false
+ end
end
# Saves the model.
@@ -133,6 +137,8 @@ module ActiveRecord
# * Callbacks are skipped.
# * updated_at/updated_on column is not updated if that column is available.
#
+ # Raises an +ActiveRecordError+ when called on new objects, or when the +name+
+ # attribute is marked as readonly.
def update_column(name, value)
name = name.to_s
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
@@ -273,7 +279,7 @@ module ActiveRecord
@changed_attributes.except!(*changes.keys)
primary_key = self.class.primary_key
- self.class.update_all(changes, { primary_key => self[primary_key] }) == 1
+ self.class.unscoped.update_all(changes, { primary_key => self[primary_key] }) == 1
end
end
@@ -313,9 +319,7 @@ module ActiveRecord
# that a new instance, or one populated from a passed-in Hash, still has all the attributes
# that instances loaded from the database would.
def attributes_from_column_definition
- Hash[self.class.columns.map do |column|
- [column.name, column.default]
- end]
+ self.class.column_defaults.dup
end
end
end
diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb
index 4e61671473..e485901440 100644
--- a/activerecord/lib/active_record/query_cache.rb
+++ b/activerecord/lib/active_record/query_cache.rb
@@ -33,6 +33,14 @@ module ActiveRecord
@target = target
end
+ def method_missing(method_sym, *arguments, &block)
+ @target.send(method_sym, *arguments, &block)
+ end
+
+ def respond_to?(method_sym, include_private = false)
+ super || @target.respond_to?(method_sym)
+ end
+
def each(&block)
@target.each(&block)
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index bae2ded244..47133e77e8 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -29,8 +29,8 @@ module ActiveRecord
# When loading console, force ActiveRecord::Base to be loaded
# to avoid cross references when loading a constant for the
# first time. Also, make it output to STDERR.
- console do |sandbox|
- require "active_record/railties/console_sandbox" if sandbox
+ console do |app|
+ require "active_record/railties/console_sandbox" if app.sandbox?
ActiveRecord::Base.logger = Logger.new(STDERR)
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 2c7298e220..399f3c5d03 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -369,7 +369,7 @@ db_namespace = namespace :db do
ENV['PGPASSWORD'] = abcs[Rails.env]['password'].to_s if abcs[Rails.env]['password']
search_path = abcs[Rails.env]['schema_search_path']
unless search_path.blank?
- search_path = search_path.split(",").map{|search_path| "--schema=#{search_path.strip}" }.join(" ")
+ search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ")
end
`pg_dump -i -U "#{abcs[Rails.env]['username']}" -s -x -O -f db/#{Rails.env}_structure.sql #{search_path} #{abcs[Rails.env]['database']}`
raise 'Error dumping database' if $?.exitstatus == 1
@@ -377,8 +377,7 @@ db_namespace = namespace :db do
dbfile = abcs[Rails.env]['database'] || abcs[Rails.env]['dbfile']
`sqlite3 #{dbfile} .schema > db/#{Rails.env}_structure.sql`
when 'sqlserver'
- `scptxfr /s #{abcs[Rails.env]['host']} /d #{abcs[Rails.env]['database']} /I /f db\\#{Rails.env}_structure.sql /q /A /r`
- `scptxfr /s #{abcs[Rails.env]['host']} /d #{abcs[Rails.env]['database']} /I /F db\ /q /A /r`
+ `smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f db\\#{Rails.env}_structure.sql -A -U`
when "firebird"
set_firebird_env(abcs[Rails.env])
db_string = firebird_db_string(abcs[Rails.env])
@@ -423,7 +422,7 @@ db_namespace = namespace :db do
dbfile = abcs['test']['database'] || abcs['test']['dbfile']
`sqlite3 #{dbfile} < #{Rails.root}/db/#{Rails.env}_structure.sql`
when 'sqlserver'
- `osql -E -S #{abcs['test']['host']} -d #{abcs['test']['database']} -i db\\#{Rails.env}_structure.sql`
+ `sqlcmd -S #{abcs['test']['host']} -d #{abcs['test']['database']} -U #{abcs['test']['username']} -P #{abcs['test']['password']} -i db\\#{Rails.env}_structure.sql`
when 'oci', 'oracle'
ActiveRecord::Base.establish_connection(:test)
IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split(";\n\n").each do |ddl|
@@ -453,9 +452,11 @@ db_namespace = namespace :db do
dbfile = abcs['test']['database'] || abcs['test']['dbfile']
File.delete(dbfile) if File.exist?(dbfile)
when 'sqlserver'
- dropfkscript = "#{abcs['test']['host']}.#{abcs['test']['database']}.DP1".gsub(/\\/,'-')
- `osql -E -S #{abcs['test']['host']} -d #{abcs['test']['database']} -i db\\#{dropfkscript}`
- `osql -E -S #{abcs['test']['host']} -d #{abcs['test']['database']} -i db\\#{Rails.env}_structure.sql`
+ test = abcs.deep_dup['test']
+ test_database = test['database']
+ test['database'] = 'master'
+ ActiveRecord::Base.establish_connection(test)
+ ActiveRecord::Base.connection.recreate_database!(test_database)
when "oci", "oracle"
ActiveRecord::Base.establish_connection(:test)
ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
@@ -481,8 +482,7 @@ db_namespace = namespace :db do
# desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
task :create => :environment do
raise 'Task unavailable to this database (no migration support)' unless ActiveRecord::Base.connection.supports_migrations?
- require 'rails/generators'
- Rails::Generators.configure!
+ Rails.application.load_generators
require 'rails/generators/rails/session_migration/session_migration_generator'
Rails::Generators::SessionMigrationGenerator.start [ ENV['MIGRATION'] || 'add_sessions_table' ]
end
@@ -499,7 +499,7 @@ namespace :railties do
# desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2"
task :migrations => :'db:load_config' do
to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map {|n| n.strip }
- railties = {}
+ railties = ActiveSupport::OrderedHash.new
Rails.application.railties.all do |railtie|
next unless to_load == :all || to_load.include?(railtie.railtie_name)
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index b1bb1b0f7f..a2324039cf 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -1,5 +1,4 @@
require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/module/deprecation'
require 'active_support/core_ext/object/inclusion'
module ActiveRecord
@@ -81,12 +80,6 @@ module ActiveRecord
# Abstract base class for AggregateReflection and AssociationReflection. Objects of
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
class MacroReflection
- attr_reader :active_record
-
- def initialize(macro, name, options, active_record)
- @macro, @name, @options, @active_record = macro, name, options, active_record
- end
-
# Returns the name of the macro.
#
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:balance</tt>
@@ -105,6 +98,19 @@ module ActiveRecord
# <tt>has_many :clients</tt> returns +{}+
attr_reader :options
+ attr_reader :active_record
+
+ attr_reader :plural_name # :nodoc:
+
+ def initialize(macro, name, options, active_record)
+ @macro = macro
+ @name = name
+ @options = options
+ @active_record = active_record
+ @plural_name = active_record.pluralize_table_names ?
+ name.to_s.pluralize : name.to_s
+ end
+
# Returns the class for the macro.
#
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money class
@@ -124,7 +130,11 @@ module ActiveRecord
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
# and +other_aggregation+ has an options hash assigned to it.
def ==(other_aggregation)
- other_aggregation.kind_of?(self.class) && name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record
+ super ||
+ other_aggregation.kind_of?(self.class) &&
+ name == other_aggregation.name &&
+ other_aggregation.options &&
+ active_record == other_aggregation.active_record
end
def sanitized_conditions #:nodoc:
@@ -169,25 +179,8 @@ module ActiveRecord
# Returns a new, unsaved instance of the associated class. +options+ will
# be passed to the class's constructor.
- def build_association(*options)
- klass.new(*options)
- end
-
- # Creates a new instance of the associated class, and immediately saves it
- # with ActiveRecord::Base#save. +options+ will be passed to the class's
- # creation method. Returns the newly created object.
- def create_association(*options)
- klass.create(*options)
- end
-
- # Creates a new instance of the associated class, and immediately saves it
- # with ActiveRecord::Base#save!. +options+ will be passed to the class's
- # creation method. If the created record doesn't pass validations, then an
- # exception will be raised.
- #
- # Returns the newly created object.
- def create_association!(*options)
- klass.create!(*options)
+ def build_association(*options, &block)
+ klass.new(*options, &block)
end
def table_name
@@ -202,11 +195,6 @@ module ActiveRecord
@foreign_key ||= options[:foreign_key] || derive_foreign_key
end
- def primary_key_name
- foreign_key
- end
- deprecate :primary_key_name => :foreign_key
-
def foreign_type
@foreign_type ||= options[:foreign_type] || "#{name}_type"
end
@@ -379,7 +367,7 @@ module ActiveRecord
delegate :foreign_key, :foreign_type, :association_foreign_key,
:active_record_primary_key, :type, :to => :source_reflection
- # Gets the source of the through reflection. It checks both a singularized
+ # Gets the source of the through reflection. It checks both a singularized
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
#
# class Post < ActiveRecord::Base
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index ae9afad48a..9cd146b857 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -6,13 +6,13 @@ module ActiveRecord
JoinOperation = Struct.new(:relation, :join_class, :on)
ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind]
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from, :reorder]
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reorder, :reverse_order]
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
# These are explicitly delegated to improve performance (avoids method_missing)
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :to => :klass
+ delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :column_hash,:to => :klass
attr_reader :table, :klass, :loaded
attr_accessor :extensions, :default_scoped
@@ -29,6 +29,7 @@ module ActiveRecord
SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
(ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])}
@extensions = []
+ @create_with_value = {}
end
def insert(values)
@@ -102,24 +103,30 @@ module ActiveRecord
def to_a
return @records if loaded?
- @records = if @readonly_value.nil? && !@klass.locking_enabled?
- eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql, @bind_values)
- else
- IdentityMap.without do
+ default_scoped = with_default_scope
+
+ if default_scoped.equal?(self)
+ @records = if @readonly_value.nil? && !@klass.locking_enabled?
eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql, @bind_values)
+ else
+ IdentityMap.without do
+ eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql, @bind_values)
+ end
end
- end
- preload = @preload_values
- preload += @includes_values unless eager_loading?
- preload.each do |associations|
- ActiveRecord::Associations::Preloader.new(@records, associations).run
- end
+ preload = @preload_values
+ preload += @includes_values unless eager_loading?
+ preload.each do |associations|
+ ActiveRecord::Associations::Preloader.new(@records, associations).run
+ end
- # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
- # are JOINS and no explicit SELECT.
- readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
- @records.each { |record| record.readonly! } if readonly
+ # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
+ # are JOINS and no explicit SELECT.
+ readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
+ @records.each { |record| record.readonly! } if readonly
+ else
+ @records = default_scoped.to_a
+ end
@loaded = true
@records
@@ -287,7 +294,7 @@ module ActiveRecord
end
# Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
# less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
#
# This essentially finds the object (or multiple objects) with the given id, creates a new object
@@ -316,7 +323,7 @@ module ActiveRecord
# Deletes the records matching +conditions+ without instantiating the records first, and hence not
# calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
# goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
- # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
+ # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
# the number of rows affected.
#
# ==== Parameters
@@ -397,7 +404,7 @@ module ActiveRecord
end
def scope_for_create
- @scope_for_create ||= where_values_hash.merge(@create_with_value || {})
+ @scope_for_create ||= where_values_hash.merge(create_with_value)
end
def eager_loading?
@@ -418,9 +425,10 @@ module ActiveRecord
end
def with_default_scope #:nodoc:
- if default_scoped?
- default_scope = @klass.send(:build_default_scope)
- default_scope ? default_scope.merge(self) : self
+ if default_scoped? && default_scope = klass.send(:build_default_scope)
+ default_scope = default_scope.merge(self)
+ default_scope.default_scoped = false
+ default_scope
else
self
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 0fcae92d51..0ac821b2d7 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -66,7 +66,7 @@ module ActiveRecord
calculate(:average, column_name, options)
end
- # Calculates the minimum value on a given column. The value is returned
+ # Calculates the minimum value on a given column. The value is returned
# with the same data type of the column, or +nil+ if there's no row. See
# +calculate+ for examples with options.
#
@@ -89,11 +89,15 @@ module ActiveRecord
# +calculate+ for examples with options.
#
# Person.sum('age') # => 4562
- def sum(column_name, options = {})
- calculate(:sum, column_name, options)
+ def sum(*args)
+ if block_given?
+ self.to_a.sum(*args) {|*block_args| yield(*block_args)}
+ else
+ calculate(:sum, *args)
+ end
end
- # This calculates aggregate values in the given column. Methods for count, sum, average,
+ # This calculates aggregate values in the given column. Methods for count, sum, average,
# minimum, and maximum have been added as shortcuts. Options such as <tt>:conditions</tt>,
# <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
#
@@ -101,7 +105,7 @@ module ActiveRecord
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
# for AVG, and the given column's type for everything else.
# * Grouped values: This returns an ordered hash of the values and groups them by the
- # <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
+ # <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
#
# values = Person.maximum(:age, :group => 'last_name')
# puts values["Drake"]
@@ -119,7 +123,7 @@ module ActiveRecord
# Options:
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
# See conditions in the intro to ActiveRecord::Base.
- # * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
+ # * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
# the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
# * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
# (Rarely needed).
@@ -146,10 +150,16 @@ module ActiveRecord
if options.except(:distinct).present?
apply_finder_options(options.except(:distinct)).calculate(operation, column_name, :distinct => options[:distinct])
else
- if eager_loading? || (includes_values.present? && references_eager_loaded_tables?)
- construct_relation_for_association_calculations.calculate(operation, column_name, options)
+ relation = with_default_scope
+
+ if relation.equal?(self)
+ if eager_loading? || (includes_values.present? && references_eager_loaded_tables?)
+ construct_relation_for_association_calculations.calculate(operation, column_name, options)
+ else
+ perform_calculation(operation, column_name, options)
+ end
else
- perform_calculation(operation, column_name, options)
+ relation.calculate(operation, column_name, options)
end
end
rescue ThrowResult
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 12d0a0a370..422d89060b 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -83,7 +83,7 @@ module ActiveRecord
#
# Example for find with a lock: Imagine two concurrent transactions:
# each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
- # in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
+ # in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
# transaction has to wait until the first is finished; we get the
# expected <tt>person.visits == 4</tt>.
#
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 94aa999715..ec2e4d4dcd 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -9,7 +9,7 @@ module ActiveRecord
:select_values, :group_values, :order_values, :joins_values,
:where_values, :having_values, :bind_values,
:limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value,
- :from_value, :reorder_value
+ :from_value, :reorder_value, :reverse_order_value
def includes(*args)
args.reject! {|a| a.blank? }
@@ -137,7 +137,7 @@ module ActiveRecord
def create_with(value)
relation = clone
- relation.create_with_value = value && (@create_with_value || {}).merge(value)
+ relation.create_with_value = value ? create_with_value.merge(value) : {}
relation
end
@@ -158,13 +158,9 @@ module ActiveRecord
end
def reverse_order
- order_clause = arel.order_clauses
-
- order = order_clause.empty? ?
- "#{table_name}.#{primary_key} DESC" :
- reverse_sql_order(order_clause).join(', ')
-
- except(:order).order(Arel.sql(order))
+ relation = clone
+ relation.reverse_order_value = !relation.reverse_order_value
+ relation
end
def arel
@@ -186,6 +182,7 @@ module ActiveRecord
arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
order = @reorder_value ? @reorder_value : @order_values
+ order = reverse_sql_order(order) if @reverse_order_value
arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty?
build_select(arel, @select_values.uniq)
@@ -306,9 +303,20 @@ module ActiveRecord
end
def reverse_sql_order(order_query)
- order_query.join(', ').split(',').collect do |s|
- s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
- end
+ order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
+
+ order_query.map do |o|
+ case o
+ when Arel::Nodes::Ascending, Arel::Nodes::Descending
+ o.reverse
+ when String, Symbol
+ o.to_s.split(',').collect do |s|
+ s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
+ end
+ else
+ o
+ end
+ end.flatten
end
def array_of_strings?(o)
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 69706b5ead..ba882beca9 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -55,7 +55,7 @@ module ActiveRecord
merged_relation.lock_value = r.lock_value unless merged_relation.lock_value
- merged_relation = merged_relation.create_with(r.create_with_value) if r.create_with_value
+ merged_relation = merged_relation.create_with(r.create_with_value) unless r.create_with_value.empty?
# Apply scope extension modules
merged_relation.send :apply_modules, r.extensions
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index 243012f88c..9ceab2eabc 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -1,7 +1,7 @@
module ActiveRecord
###
# This class encapsulates a Result returned from calling +exec_query+ on any
- # database connection adapter. For example:
+ # database connection adapter. For example:
#
# x = ActiveRecord::Base.connection.exec_query('SELECT * FROM foo')
# x # => #<ActiveRecord::Result:0xdeadbeef>
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index a893c0ad85..19585f6214 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -106,7 +106,7 @@ HEADER
spec = {}
spec[:name] = column.name.inspect
- # AR has an optimisation which handles zero-scale decimals as integers. This
+ # AR has an optimization which handles zero-scale decimals as integers. This
# code ensures that the dumper still dumps the column as a decimal.
spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
'decimal'
diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb
index be4354ce6a..e3185a9c5a 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -31,9 +31,6 @@ module ActiveRecord #:nodoc:
def serializable_add_includes(options = {})
return unless include_associations = options.delete(:include)
- base_only_or_except = { :except => options[:except],
- :only => options[:only] }
-
include_has_options = include_associations.is_a?(Hash)
associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
@@ -46,9 +43,8 @@ module ActiveRecord #:nodoc:
end
if records
- association_options = include_has_options ? include_associations[association] : base_only_or_except
- opts = options.merge(association_options)
- yield(association, records, opts)
+ association_options = include_has_options ? include_associations[association] : {}
+ yield(association, records, association_options)
end
end
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index 8c4adf7116..f8e6cf958c 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -75,7 +75,7 @@ module ActiveRecord #:nodoc:
# </firm>
#
# Additionally, the record being serialized will be passed to a Proc's second
- # parameter. This allows for ad hoc additions to the resultant document that
+ # parameter. This allows for ad hoc additions to the resultant document that
# incorporate the context of the record being serialized. And by leveraging the
# closure created by a Proc, to_xml can be used to add elements that normally fall
# outside of the scope of the model -- for example, generating and appending URLs
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index c3e976002e..929559c3ba 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -1,7 +1,7 @@
module ActiveRecord
# = Active Record Session Store
#
- # A session store backed by an Active Record class. A default class is
+ # A session store backed by an Active Record class. A default class is
# provided, but any object duck-typing to an Active Record Session class
# with text +session_id+ and +data+ attributes is sufficient.
#
@@ -23,7 +23,7 @@ module ActiveRecord
# ActiveRecord::SessionStore::Session.data_column_name = 'legacy_session_data'
#
# Note that setting the primary key to the +session_id+ frees you from
- # having a separate +id+ column if you don't want it. However, you must
+ # having a separate +id+ column if you don't want it. However, you must
# set <tt>session.model.id = session.session_id</tt> by hand! A before filter
# on ApplicationController is a good place.
#
@@ -46,7 +46,7 @@ module ActiveRecord
# save
# destroy
#
- # The example SqlBypass class is a generic SQL session store. You may
+ # The example SqlBypass class is a generic SQL session store. You may
# use it as a basis for high-performance database-specific stores.
class SessionStore < ActionDispatch::Session::AbstractStore
module ClassMethods # :nodoc:
@@ -79,7 +79,7 @@ module ActiveRecord
##
# :singleton-method:
- # Customizable data column name. Defaults to 'data'.
+ # Customizable data column name. Defaults to 'data'.
cattr_accessor :data_column_name
self.data_column_name = 'data'
@@ -161,12 +161,12 @@ module ActiveRecord
end
# A barebones session store which duck-types with the default session
- # store but bypasses Active Record and issues SQL directly. This is
+ # store but bypasses Active Record and issues SQL directly. This is
# an example session model class meant as a basis for your own classes.
#
# The database connection, table name, and session id and data columns
- # are configurable class attributes. Marshaling and unmarshaling
- # are implemented as class methods that you may override. By default,
+ # are configurable class attributes. Marshaling and unmarshaling
+ # are implemented as class methods that you may override. By default,
# marshaling data is
#
# ActiveSupport::Base64.encode64(Marshal.dump(data))
@@ -176,7 +176,7 @@ module ActiveRecord
# Marshal.load(ActiveSupport::Base64.decode64(data))
#
# This marshaling behavior is intended to store the widest range of
- # binary session data in a +text+ column. For higher performance,
+ # binary session data in a +text+ column. For higher performance,
# store in a +blob+ column instead and forgo the Base64 encoding.
class SqlBypass
extend ClassMethods
@@ -286,7 +286,7 @@ module ActiveRecord
end
end
- # The class used for session storage. Defaults to
+ # The class used for session storage. Defaults to
# ActiveRecord::SessionStore::Session
cattr_accessor :session_class
self.session_class = Session
diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb
index 0d47eb3338..ffe9b08dce 100644
--- a/activerecord/lib/active_record/test_case.rb
+++ b/activerecord/lib/active_record/test_case.rb
@@ -13,7 +13,7 @@ module ActiveRecord
ActiveRecord::IdentityMap.clear
end
- # Backport skip to Ruby 1.8. test/unit doesn't support it, so just
+ # Backport skip to Ruby 1.8. test/unit doesn't support it, so just
# make it a noop.
unless instance_methods.map(&:to_s).include?("skip")
def skip(message)
@@ -31,27 +31,30 @@ module ActiveRecord
end
def assert_sql(*patterns_to_match)
- $queries_executed = []
+ ActiveRecord::SQLCounter.log = []
yield
- $queries_executed
+ ActiveRecord::SQLCounter.log
ensure
failed_patterns = []
patterns_to_match.each do |pattern|
- failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
+ failed_patterns << pattern unless ActiveRecord::SQLCounter.log.any?{ |sql| pattern === sql }
end
- assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
+ assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
end
def assert_queries(num = 1)
- $queries_executed = []
+ ActiveRecord::SQLCounter.log = []
yield
ensure
- %w{ BEGIN COMMIT }.each { |x| $queries_executed.delete(x) }
- assert_equal num, $queries_executed.size, "#{$queries_executed.size} instead of #{num} queries were executed.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
+ assert_equal num, ActiveRecord::SQLCounter.log.size, "#{ActiveRecord::SQLCounter.log.size} instead of #{num} queries were executed.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
end
def assert_no_queries(&block)
+ prev_ignored_sql = ActiveRecord::SQLCounter.ignored_sql
+ ActiveRecord::SQLCounter.ignored_sql = []
assert_queries(0, &block)
+ ensure
+ ActiveRecord::SQLCounter.ignored_sql = prev_ignored_sql
end
def with_kcode(kcode)
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index d363f36108..ae97a3f3ca 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -165,7 +165,7 @@ module ActiveRecord
# writing, the only database that we're aware of that supports true nested
# transactions, is MS-SQL. Because of this, Active Record emulates nested
# transactions by using savepoints on MySQL and PostgreSQL. See
- # http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
+ # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
# for more information about savepoints.
#
# === Callbacks
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 59b6876135..4b075183c3 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -1,7 +1,7 @@
module ActiveRecord
# = Active Record RecordInvalid
#
- # Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
+ # 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.
#
# begin
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index 3a783aeb00..5df85304a2 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -37,10 +37,10 @@ module ActiveRecord
# validation contexts by default (+nil+), other options are <tt>:create</tt>
# and <tt>:update</tt>.
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
def validates_associated(*attr_names)
validates_with AssociatedValidator, _merge_attributes(attr_names)
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 4db4105389..484b1d369b 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -83,7 +83,7 @@ module ActiveRecord
# validates_uniqueness_of :user_name, :scope => :account_id
# end
#
- # Or even multiple scope parameters. For example, making sure that a teacher can only be on the schedule once
+ # Or even multiple scope parameters. For example, making sure that a teacher can only be on the schedule once
# per semester for a particular class.
#
# class TeacherSchedule < ActiveRecord::Base
@@ -105,7 +105,7 @@ module ActiveRecord
# The method, proc or string should return or evaluate to a true or false value.
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or
- # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method, proc or string should
+ # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method, proc or string should
# return or evaluate to a true or false value.
#
# === Concurrency and integrity
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index 43fe1cdee5..838aa8fb1e 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -1,9 +1,9 @@
module ActiveRecord
module VERSION #:nodoc:
MAJOR = 3
- MINOR = 1
+ MINOR = 2
TINY = 0
- PRE = "rc1"
+ PRE = "beta"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb
index 8f0bf1ef0d..9ea3248513 100644
--- a/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb
@@ -1,5 +1,5 @@
class <%= migration_class_name %> < ActiveRecord::Migration
- def up
+ def change
create_table :<%= session_table_name %> do |t|
t.string :session_id, :null => false
t.text :data
@@ -9,8 +9,4 @@ class <%= migration_class_name %> < ActiveRecord::Migration
add_index :<%= session_table_name %>, :session_id
add_index :<%= session_table_name %>, :updated_at
end
-
- def down
- drop_table :<%= session_table_name %>
- end
end