aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations.rb50
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb36
-rw-r--r--activerecord/lib/active_record/associations/association.rb16
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb22
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb12
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb2
-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.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb2
-rw-r--r--activerecord/lib/active_record/autosave_association.rb2
-rw-r--r--activerecord/lib/active_record/base.rb25
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb34
-rw-r--r--activerecord/lib/active_record/errors.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb6
-rw-r--r--activerecord/lib/active_record/migration.rb10
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb16
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb8
-rw-r--r--activerecord/lib/active_record/persistence.rb7
-rw-r--r--activerecord/lib/active_record/railties/databases.rake13
-rw-r--r--activerecord/lib/active_record/reflection.rb6
-rw-r--r--activerecord/lib/active_record/relation.rb16
-rw-r--r--activerecord/lib/active_record/relation/batches.rb2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb8
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb5
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb8
27 files changed, 182 insertions, 149 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 2cbfa42718..029d7a9b15 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -191,7 +191,7 @@ module ActiveRecord
# * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
# * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
# * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
- # <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.find(:all, options),</tt>
+ # <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.all(options),</tt>
# <tt>Project#milestones.build, Project#milestones.create</tt>
# * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
# <tt>Project#categories.delete(category1)</tt>
@@ -420,7 +420,7 @@ module ActiveRecord
# end
# end
#
- # person = Account.find(:first).people.find_or_create_by_name("David Heinemeier Hansson")
+ # person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
# person.first_name # => "David"
# person.last_name # => "Heinemeier Hansson"
#
@@ -477,7 +477,7 @@ module ActiveRecord
# belongs_to :book
# end
#
- # @author = Author.find :first
+ # @author = Author.first
# @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
# @author.books # selects all books by using the Authorship join model
#
@@ -497,7 +497,7 @@ module ActiveRecord
# belongs_to :client
# end
#
- # @firm = Firm.find :first
+ # @firm = Firm.first
# @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
# @firm.invoices # selects all invoices by going through the Client join model
#
@@ -669,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:
#
- # Post.find(:all, :include => :author).each do |post|
+ # Post.includes(: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
@@ -678,7 +678,7 @@ module ActiveRecord
#
# We can improve upon the situation further by referencing both associations in the finder with:
#
- # Post.find(:all, :include => [ :author, :comments ]).each do |post|
+ # Post.includes(: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
@@ -686,7 +686,7 @@ module ActiveRecord
#
# To include a deep hierarchy of associations, use a hash:
#
- # Post.find(:all, :include => [ :author, { :comments => { :author => :gravatar } } ]).each do |post|
+ # Post.includes(: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
@@ -714,13 +714,13 @@ module ActiveRecord
# <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
#
# If you do want eager load only some members of an association it is usually more natural
- # to <tt>:include</tt> an association which has conditions defined on it:
+ # to include an association which has conditions defined on it:
#
# class Post < ActiveRecord::Base
# has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true]
# end
#
- # Post.find(:all, :include => :approved_comments)
+ # Post.includes(:approved_comments)
#
# This will load posts and eager load the +approved_comments+ association, which contains
# only those comments that have been approved.
@@ -732,7 +732,7 @@ module ActiveRecord
# has_many :most_recent_comments, :class_name => 'Comment', :order => 'id DESC', :limit => 10
# end
#
- # Picture.find(:first, :include => :most_recent_comments).most_recent_comments # => returns all associated comments.
+ # Picture.includes(:most_recent_comments).first.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.
@@ -745,7 +745,7 @@ module ActiveRecord
#
# A call that tries to eager load the addressable model
#
- # Address.find(:all, :include => :addressable)
+ # Address.includes(:addressable)
#
# This will execute one query to load the addresses and load the addressables with one
# query per addressable type.
@@ -763,43 +763,43 @@ module ActiveRecord
# 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.
#
- # Post.find :all, :joins => :comments
+ # Post.joins(:comments)
# # => SELECT ... FROM posts INNER JOIN comments ON ...
- # Post.find :all, :joins => :special_comments # STI
+ # Post.joins(:special_comments) # STI
# # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
- # Post.find :all, :joins => [:comments, :special_comments] # special_comments is the reflection name, posts is the parent table name
+ # Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name
# # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
#
# Acts as tree example:
#
- # TreeMixin.find :all, :joins => :children
+ # TreeMixin.joins(:children)
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
- # TreeMixin.find :all, :joins => {:children => :parent}
+ # TreeMixin.joins(:children => :parent)
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
# INNER JOIN parents_mixins ...
- # TreeMixin.find :all, :joins => {:children => {:parent => :children}}
+ # TreeMixin.joins(:children => {:parent => :children})
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
# INNER JOIN parents_mixins ...
# INNER JOIN mixins childrens_mixins_2
#
# Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
#
- # Post.find :all, :joins => :categories
+ # Post.joins(:categories)
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
- # Post.find :all, :joins => {:categories => :posts}
+ # Post.joins(:categories => :posts)
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
# INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
- # Post.find :all, :joins => {:categories => {:posts => :categories}}
+ # Post.joins(:categories => {:posts => :categories})
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
# INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
# INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
#
- # If you wish to specify your own custom joins using a <tt>:joins</tt> option, those table
+ # If you wish to specify your own custom joins using <tt>joins</tt> method, those table
# names will take precedence over the eager associations:
#
- # Post.find :all, :joins => :comments, :joins => "inner join comments ..."
+ # Post.joins(:comments).joins("inner join comments ...")
# # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
- # Post.find :all, :joins => [:comments, :special_comments], :joins => "inner join comments ..."
+ # Post.joins(:comments, :special_comments).joins("inner join comments ...")
# # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
# INNER JOIN comments special_comments_posts ...
# INNER JOIN comments ...
@@ -1031,7 +1031,7 @@ module ActiveRecord
# === Example
#
# Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
- # * <tt>Firm#clients</tt> (similar to <tt>Clients.find :all, :conditions => ["firm_id = ?", id]</tt>)
+ # * <tt>Firm#clients</tt> (similar to <tt>Clients.all :conditions => ["firm_id = ?", id]</tt>)
# * <tt>Firm#clients<<</tt>
# * <tt>Firm#clients.delete</tt>
# * <tt>Firm#clients=</tt>
@@ -1195,7 +1195,7 @@ module ActiveRecord
# === Example
#
# An Account class declares <tt>has_one :beneficiary</tt>, which will add:
- # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.find(:first, :conditions => "account_id = #{id}")</tt>)
+ # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.first(:conditions => "account_id = #{id}")</tt>)
# * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
# * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
# * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</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 daadc8aa81..bb519c5703 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -30,7 +30,7 @@ module ActiveRecord
@updated = false
reset
- construct_scope
+ reset_scope
end
# Returns the name of the table of the related class:
@@ -51,7 +51,7 @@ module ActiveRecord
# Reloads the \target and returns +self+ on success.
def reload
reset
- construct_scope
+ reset_scope
load_target
self unless target.nil?
end
@@ -84,21 +84,25 @@ module ActiveRecord
end
def scoped
- target_scope.merge(@association_scope)
+ target_scope.merge(association_scope)
end
- # Construct the scope for this association.
+ # The scope for this association.
#
# Note that the association_scope is merged into the target_scope only when the
# scoped method is called. This is because at that point the call may be surrounded
# by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
# actually gets built.
- def construct_scope
+ def association_scope
if klass
- @association_scope = AssociationScope.new(self).scope
+ @association_scope ||= AssociationScope.new(self).scope
end
end
+ def reset_scope
+ @association_scope = nil
+ end
+
# Set the inverse association, if possible
def set_inverse_instance(record)
if record && invertible_for?(record)
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 d7632b2ea9..30fc44b4c2 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
@@ -7,24 +7,22 @@ module ActiveRecord::Associations::Builder
def build
reflection = super
check_validity(reflection)
- define_after_destroy_method
+ define_destroy_hook
reflection
end
private
- def define_after_destroy_method
+ def define_destroy_hook
name = self.name
- model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
- def #{after_destroy_method_name}
- association(#{name.to_sym.inspect}).delete_all
- end
- eoruby
- model.after_destroy after_destroy_method_name
- end
-
- def after_destroy_method_name
- "has_and_belongs_to_many_after_destroy_for_#{name}"
+ model.send(:include, Module.new {
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def destroy_associations
+ association(#{name.to_sym.inspect}).delete_all
+ super
+ end
+ RUBY
+ })
end
# TODO: These checks should probably be moved into the Reflection, and we should not be
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 224e0095d9..3e68f973e7 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -50,7 +50,7 @@ module ActiveRecord
else
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
- scoped.select(column).except(:includes).map! do |record|
+ scoped.select(column).map! do |record|
record.send(reflection.association_primary_key)
end
end
@@ -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
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 7f7dec467a..827dfb7ccb 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -12,7 +12,7 @@ module ActiveRecord
# has_many :posts
# end
#
- # blog = Blog.find(:first)
+ # blog = Blog.first
#
# the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
# <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 6b8201973a..2131edbc20 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -19,7 +19,7 @@ module ActiveRecord
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 d094a8def3..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 = reflection.plural_name.dup
- 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 6b010064d5..a1a921bcb4 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -18,11 +18,11 @@ module ActiveRecord
end
def create(attributes = {}, options = {}, &block)
- build(attributes, options, &block).tap { |record| record.save }
+ create_record(attributes, options, &block)
end
def create!(attributes = {}, options = {}, &block)
- build(attributes, options, &block).tap { |record| record.save! }
+ create_record(attributes, options, true, &block)
end
def build(attributes = {}, options = {})
@@ -50,6 +50,15 @@ module ActiveRecord
def set_new_record(record)
replace(record)
end
+
+ def create_record(attributes, options, raise_error = false)
+ record = build_record(attributes, options)
+ yield(record) if block_given?
+ saved = record.save
+ set_new_record(record)
+ raise RecordInvalid.new(record) if !saved && raise_error
+ record
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index e3f221c773..8bd898d126 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -7,7 +7,7 @@ module ActiveRecord
# the record is not persisted? or has just been destroyed.
def to_key
key = send(self.class.primary_key)
- [key] if key
+ persisted? && key ? [key] : nil
end
module ClassMethods
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 036d514a03..085fdba639 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -347,7 +347,7 @@ module ActiveRecord
end
# reconstruct the scope now that we know the owner's id
- association.send(:construct_scope) if association.respond_to?(:construct_scope)
+ association.send(:reset_scope) if association.respond_to?(:reset_scope)
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index c2383630e0..eb4a16ecf5 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1058,7 +1058,7 @@ module ActiveRecord #:nodoc:
if match.finder?
options = arguments.extract_options!
relation = options.any? ? scoped(options) : scoped
- relation.send :find_by_attributes, match, attribute_names, *arguments
+ relation.send :find_by_attributes, match, attribute_names, *arguments, &block
elsif match.instantiator?
scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
end
@@ -1709,27 +1709,24 @@ 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|
if k.include?("(")
multi_parameter_attributes << [ k, v ]
+ elsif respond_to?("#{k}=")
+ send("#{k}=", v)
else
- method_name = "#{k}="
- if respond_to?(method_name)
- method(method_name).arity == -2 ? send(method_name, v, options) : send(method_name, v)
- else
- raise(UnknownAttributeError, "unknown attribute: #{k}")
- end
+ raise(UnknownAttributeError, "unknown attribute: #{k}")
end
end
+ @mass_assignment_options = nil
assign_multiparameter_attributes(multi_parameter_attributes)
end
@@ -1894,6 +1891,14 @@ 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
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 29dd0a9ea3..a84f73c73f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -670,7 +670,7 @@ module ActiveRecord
# 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)
+ schema, table = Utils.extract_schema_and_table(name.to_s)
return false unless table
binds = [[nil, table]]
@@ -947,6 +947,23 @@ module ActiveRecord
"DISTINCT #{columns}, #{order_columns * ', '}"
end
+ module Utils
+ # 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 self.extract_schema_and_table(name)
+ table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
+ [schema, table]
+ end
+ end
+
protected
# Returns the version of the connected PostgreSQL server.
def postgresql_version
@@ -1085,21 +1102,6 @@ module ActiveRecord
end
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
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index ea1709cb1f..ad7d8cd63c 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -87,7 +87,7 @@ module ActiveRecord
#
# For example, in
#
- # Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
+ # Location.where("lat = ? AND lng = ?", 53.7362)
#
# two placeholders are given but only one variable to fill them.
class PreparedStatementInvalid < ActiveRecordError
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 819743a361..3f36dcde14 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -149,13 +149,13 @@ class FixturesFileNotFound < StandardError; end
# self.use_transactional_fixtures = true
#
# test "godzilla" do
-# assert !Foo.find(:all).empty?
+# assert !Foo.all.empty?
# Foo.destroy_all
-# assert Foo.find(:all).empty?
+# assert Foo.all.empty?
# end
#
# test "godzilla aftermath" do
-# assert !Foo.find(:all).empty?
+# assert !Foo.all.empty?
# end
# end
#
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 3d1bc5c1e0..c459846264 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -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",
@@ -181,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
@@ -227,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
@@ -247,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 4a01313c61..2eeff7e36f 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -6,7 +6,7 @@ module ActiveRecord
#
# * add_column
# * add_index
- # * add_timestamp
+ # * add_timestamps
# * create_table
# * remove_timestamps
# * rename_column
@@ -48,7 +48,7 @@ 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) # def create_table(*args)
record(:"#{method}", args) # record(:create_table, args)
@@ -93,15 +93,11 @@ module ActiveRecord
[:remove_timestamps, args]
end
- # Record all the methods called in the +change+ method of a migration.
- # This will ensure that IrreversibleMigration is raised when the corresponding
- # invert_method does not exist while the migration is rolled back.
+ # Forwards any missing method call to the \target.
def method_missing(method, *args, &block)
- if delegate.respond_to?(method)
- delegate.send(method, *args, &block)
- else
- record(method, args)
- end
+ @delegate.send(method, *args, &block)
+ rescue NoMethodError => e
+ raise e, e.message.sub(/ for #<.*$/, " via proxy for #{@delegate}")
end
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index f51fd21077..53617059d0 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -276,15 +276,15 @@ module ActiveRecord
type = (reflection.collection? ? :collection : :one_to_one)
- # def pirate_attributes=(attributes, assignment_opts = {})
- # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, assignment_opts)
+ # def pirate_attributes=(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, assignment_opts = {})
- assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, assignment_opts)
+ def #{association_name}_attributes=(attributes)
+ assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options)
end
eoruby
else
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index f425118f59..d9cd7987b0 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -79,6 +79,8 @@ module ActiveRecord
# Deletes the record in the database and freezes this instance to reflect
# that no changes should be made (since they can't be persisted).
def destroy
+ destroy_associations
+
if persisted?
IdentityMap.remove(self) if IdentityMap.enabled?
pk = self.class.primary_key
@@ -284,6 +286,11 @@ module ActiveRecord
end
private
+
+ # A hook to be overriden by association modules.
+ def destroy_associations
+ end
+
def create_or_update
raise ReadOnlyRecord if readonly?
result = new_record? ? create : update
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 399f3c5d03..44e5520230 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -44,6 +44,12 @@ db_namespace = namespace :db do
create_database(ActiveRecord::Base.configurations[Rails.env])
end
+ def mysql_creation_options(config)
+ @charset = ENV['CHARSET'] || 'utf8'
+ @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
+ {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
+ end
+
def create_database(config)
begin
if config['adapter'] =~ /sqlite/
@@ -67,9 +73,6 @@ db_namespace = namespace :db do
rescue
case config['adapter']
when /mysql/
- @charset = ENV['CHARSET'] || 'utf8'
- @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
- creation_options = {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
if config['adapter'] =~ /jdbc/
#FIXME After Jdbcmysql gives this class
require 'active_record/railties/jdbcmysql_error'
@@ -80,7 +83,7 @@ db_namespace = namespace :db do
access_denied_error = 1045
begin
ActiveRecord::Base.establish_connection(config.merge('database' => nil))
- ActiveRecord::Base.connection.create_database(config['database'], creation_options)
+ ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
ActiveRecord::Base.establish_connection(config)
rescue error_class => sqlerr
if sqlerr.errno == access_denied_error
@@ -443,7 +446,7 @@ db_namespace = namespace :db do
case abcs['test']['adapter']
when /mysql/
ActiveRecord::Base.establish_connection(:test)
- ActiveRecord::Base.connection.recreate_database(abcs['test']['database'], abcs['test'])
+ ActiveRecord::Base.connection.recreate_database(abcs['test']['database'], mysql_creation_options(abcs['test']))
when /postgresql/
ActiveRecord::Base.clear_active_connections!
drop_database(abcs['test'])
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 894d56b3a0..a2324039cf 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -130,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:
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 9cd146b857..2d0861d5c9 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/module/delegation'
module ActiveRecord
# = Active Record Relation
@@ -249,8 +250,7 @@ module ActiveRecord
# Person.update(people.keys, people.values)
def update(id, attributes)
if id.is_a?(Array)
- idx = -1
- id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
+ id.each.with_index.map {|one_id, idx| update(one_id, attributes[idx])}
else
object = find(id)
object.update_attributes(attributes)
@@ -408,7 +408,17 @@ module ActiveRecord
end
def eager_loading?
- @should_eager_load ||= (@eager_load_values.any? || (@includes_values.any? && references_eager_loaded_tables?))
+ @should_eager_load ||=
+ @eager_load_values.any? ||
+ @includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
+ end
+
+ # Joins that are also marked for preloading. In which case we should just eager load them.
+ # Note that this is a naive implementation because we could have strings and symbols which
+ # represent the same association, but that aren't matched by this. Also, we could have
+ # nested hashes which partially match, e.g. { :a => :b } & { :a => [:b, :c] }
+ def joined_includes_values
+ @includes_values & @joins_values
end
def ==(other)
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index d52b84179f..46ab67d1cf 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -20,8 +20,6 @@ module ActiveRecord
find_in_batches(options) do |records|
records.each { |record| yield record }
end
-
- self
end
# Yields each batch of records that was found by the find +options+ as
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 9c5f603c01..0ac821b2d7 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -89,8 +89,12 @@ 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,
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index c6e8762b4a..283115a448 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -193,8 +193,8 @@ module ActiveRecord
else
relation = relation.where(table[primary_key].eq(id)) if id
end
-
- connection.select_value(relation.to_sql) ? true : false
+
+ connection.select_value(relation.to_sql, "#{name} Exists") ? true : false
end
protected
@@ -259,6 +259,7 @@ module ActiveRecord
if match.bang? && result.blank?
raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
else
+ yield(result) if block_given?
result
end
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index ec2e4d4dcd..8bd4732c0c 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -96,11 +96,11 @@ module ActiveRecord
relation
end
- def having(*args)
- return self if args.blank?
+ def having(opts, *rest)
+ return self if opts.blank?
relation = clone
- relation.having_values += build_where(*args)
+ relation.having_values += build_where(opts, rest)
relation
end
@@ -307,7 +307,7 @@ module ActiveRecord
order_query.map do |o|
case o
- when Arel::Nodes::Ascending, Arel::Nodes::Descending
+ when Arel::Nodes::Ordering
o.reverse
when String, Symbol
o.to_s.split(',').collect do |s|