aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/associations.rb8
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb31
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb32
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb52
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb21
-rw-r--r--activerecord/lib/active_record/errors.rb10
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb14
-rw-r--r--activerecord/lib/active_record/model.rb4
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb4
-rw-r--r--activerecord/lib/active_record/railties/databases.rake17
-rw-r--r--activerecord/lib/active_record/relation.rb11
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb12
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb37
-rw-r--r--activerecord/lib/active_record/scoping/default.rb8
-rw-r--r--activerecord/lib/active_record/store.rb9
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb27
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb4
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb4
23 files changed, 236 insertions, 84 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 68f8bbeb1c..a62fce4756 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1167,6 +1167,8 @@ module ActiveRecord
# If true, always save the associated objects or destroy them if marked for destruction,
# when saving the parent object. If false, never save or destroy the associated objects.
# By default, only save associated objects that are new records.
+ #
+ # Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
# [:inverse_of]
# Specifies the name of the <tt>belongs_to</tt> association on the associated object
# that is the inverse of this <tt>has_many</tt> association. Does not work in combination
@@ -1288,6 +1290,8 @@ module ActiveRecord
# If true, always save the associated object or destroy it if marked for destruction,
# when saving the parent object. If false, never save or destroy the associated object.
# By default, only save the associated object if it's a new record.
+ #
+ # Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
# [: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
@@ -1404,6 +1408,8 @@ module ActiveRecord
# saving the parent object.
# If false, never save or destroy the associated object.
# By default, only save the associated object if it's a new record.
+ #
+ # Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
# [:touch]
# If true, the associated object will be touched (the updated_at/on attributes set to now)
# when this record is either saved or destroyed. If you specify a symbol, that attribute
@@ -1589,6 +1595,8 @@ module ActiveRecord
# If false, never save or destroy the associated objects.
# By default, only save associated objects that are new records.
#
+ # Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
+ #
# Option examples:
# has_and_belongs_to_many :projects
# has_and_belongs_to_many :projects, :include => [ :milestones, :manager ]
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index ab6d253ef8..5b41f72e52 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -158,11 +158,12 @@ module ActiveRecord
begin
send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
rescue => ex
- errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name}", ex, name)
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
end
end
unless errors.empty?
- raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
+ error_descriptions = errors.map { |ex| ex.message }.join(",")
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
end
end
@@ -180,15 +181,27 @@ module ActiveRecord
end
def read_time_parameter_value(name, values_hash_from_param)
- # If Date bits were not provided, error
- raise "Missing Parameter" if [1,2,3].any?{|position| !values_hash_from_param.has_key?(position)}
- max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
- # If Date bits were provided but blank, then return nil
- return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
+ # If column is a :time (and not :date or :timestamp) there is no need to validate if
+ # there are year/month/day fields
+ if column_for_attribute(name).type == :time
+ # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
+ {1 => 1970, 2 => 1, 3 => 1}.each do |key,value|
+ values_hash_from_param[key] ||= value
+ end
+ else
+ # else column is a timestamp, so if Date bits were not provided, error
+ if missing_parameter = [1,2,3].detect{ |position| !values_hash_from_param.has_key?(position) }
+ raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter}i)")
+ end
+
+ # If Date bits were provided but blank, then return nil
+ return nil if (1..3).any? { |position| values_hash_from_param[position].blank? }
+ end
- set_values = (1..max_position).collect{|position| values_hash_from_param[position] }
+ max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
+ set_values = (1..max_position).collect{ |position| values_hash_from_param[position] }
# If Time bits are not there, then default to 0
- (3..5).each {|i| set_values[i] = set_values[i].blank? ? 0 : set_values[i]}
+ (3..5).each { |i| set_values[i] = set_values[i].blank? ? 0 : set_values[i] }
instantiate_time_object(name, set_values)
end
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 4af4d28b74..49ab3ab808 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -6,7 +6,7 @@ module ActiveRecord
included do
# Returns a hash of all the attributes that have been specified for serialization as
# keys and their class restriction as values.
- class_attribute :serialized_attributes
+ class_attribute :serialized_attributes, instance_writer: false
self.serialized_attributes = {}
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 3b4537aab4..be6fda95b4 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -56,7 +56,7 @@ module ActiveRecord
end
def select_all(arel, name = nil, binds = [])
- if @query_cache_enabled
+ if @query_cache_enabled && !locked?(arel)
sql = to_sql(arel, binds)
cache_sql(sql, binds) { super(sql, name, binds) }
else
@@ -65,6 +65,7 @@ module ActiveRecord
end
private
+
def cache_sql(sql, binds)
result =
if @query_cache[sql].key?(binds)
@@ -83,6 +84,10 @@ module ActiveRecord
result.collect { |row| row.dup }
end
end
+
+ def locked?(arel)
+ arel.respond_to?(:locked) && arel.locked
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 6f9f0399db..60a9eee7c7 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -31,7 +31,7 @@ module ActiveRecord
# BigDecimals need to be put in a non-normalized form and quoted.
when nil then "NULL"
when BigDecimal then value.to_s('F')
- when Numeric then value.to_s
+ when Numeric, ActiveSupport::Duration then value.to_s
when Date, Time then "'#{quoted_date(value)}'"
when Symbol then "'#{quote_string(value.to_s)}'"
when Class then "'#{value.to_s}'"
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 df78ba6c5a..ef17dfbbc5 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -259,7 +259,7 @@ module ActiveRecord
end # end
EOV
end
-
+
# Adds index options to the indexes hash, keyed by column name
# This is primarily used to track indexes that need to be created after the table
#
@@ -282,7 +282,7 @@ module ActiveRecord
index_options = options.delete(:index)
args.each do |col|
column("#{col}_id", :integer, options)
- column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
+ column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
index(polymorphic ? %w(id type).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
end
end
@@ -441,17 +441,13 @@ module ActiveRecord
# Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
# <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
#
- # t.references(:goat)
- # t.references(:goat, :polymorphic => true)
- # t.belongs_to(:goat)
+ # t.references(:user)
+ # t.belongs_to(:supplier, polymorphic: true)
+ #
def references(*args)
options = args.extract_options!
- polymorphic = options.delete(:polymorphic)
- index_options = options.delete(:index)
- args.each do |col|
- @base.add_column(@table_name, "#{col}_id", :integer, options)
- @base.add_column(@table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
- @base.add_index(@table_name, polymorphic ? %w(id type).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
+ args.each do |ref_name|
+ @base.add_reference(@table_name, ref_name, options)
end
end
alias :belongs_to :references
@@ -459,18 +455,16 @@ module ActiveRecord
# Removes a reference. Optionally removes a +type+ column.
# <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
#
- # t.remove_references(:goat)
- # t.remove_references(:goat, :polymorphic => true)
- # t.remove_belongs_to(:goat)
+ # t.remove_references(:user)
+ # t.remove_belongs_to(:supplier, polymorphic: true)
+ #
def remove_references(*args)
options = args.extract_options!
- polymorphic = options.delete(:polymorphic)
- args.each do |col|
- @base.remove_column(@table_name, "#{col}_id")
- @base.remove_column(@table_name, "#{col}_type") unless polymorphic.nil?
+ args.each do |ref_name|
+ @base.remove_reference(@table_name, ref_name, options)
end
end
- alias :remove_belongs_to :remove_references
+ alias :remove_belongs_to :remove_references
# Adds a column or columns of a specified type
#
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 f5794a4e54..3c3f01223c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -57,7 +57,6 @@ module ActiveRecord
# Checks to see if a column exists in a given table.
#
- # === Examples
# # Check a column exists
# column_exists?(:suppliers, :name)
#
@@ -65,13 +64,18 @@ module ActiveRecord
# column_exists?(:suppliers, :name, :string)
#
# # Check a column exists with a specific definition
- # column_exists?(:suppliers, :name, :string, :limit => 100)
+ # column_exists?(:suppliers, :name, :string, limit: 100)
+ # column_exists?(:suppliers, :name, :string, default: 'default')
+ # column_exists?(:suppliers, :name, :string, null: false)
+ # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
def column_exists?(table_name, column_name, type = nil, options = {})
columns(table_name).any?{ |c| c.name == column_name.to_s &&
- (!type || c.type == type) &&
- (!options[:limit] || c.limit == options[:limit]) &&
- (!options[:precision] || c.precision == options[:precision]) &&
- (!options[:scale] || c.scale == options[:scale]) }
+ (!type || c.type == type) &&
+ (!options.key?(:limit) || c.limit == options[:limit]) &&
+ (!options.key?(:precision) || c.precision == options[:precision]) &&
+ (!options.key?(:scale) || c.scale == options[:scale]) &&
+ (!options.key?(:default) || c.default == options[:default]) &&
+ (!options.key?(:null) || c.null == options[:null]) }
end
# Creates a new table with the name +table_name+. +table_name+ may either
@@ -439,6 +443,42 @@ module ActiveRecord
indexes(table_name).detect { |i| i.name == index_name }
end
+ # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
+ # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
+ #
+ # ====== Create a user_id column
+ # add_reference(:products, :user)
+ #
+ # ====== Create a supplier_id and supplier_type columns
+ # add_belongs_to(:products, :supplier, polymorphic: true)
+ #
+ # ====== Create a supplier_id, supplier_type columns and appropriate index
+ # add_reference(:products, :supplier, polymorphic: true, index: true)
+ #
+ def add_reference(table_name, ref_name, options = {})
+ polymorphic = options.delete(:polymorphic)
+ index_options = options.delete(:index)
+ add_column(table_name, "#{ref_name}_id", :integer, options)
+ add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
+ add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
+ end
+ alias :add_belongs_to :add_reference
+
+ # Removes the reference(s). Also removes a +type+ column if one exists.
+ # <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
+ #
+ # ====== Remove the reference
+ # remove_reference(:products, :user, index: true)
+ #
+ # ====== Remove polymorphic reference
+ # remove_reference(:products, :supplier, polymorphic: true)
+ #
+ def remove_reference(table_name, ref_name, options = {})
+ remove_column(table_name, "#{ref_name}_id")
+ remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
+ end
+ alias :remove_belongs_to :remove_reference
+
# Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
# entire structure of the database.
def structure_dump
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 921278d145..df4a9d5afc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -72,6 +72,8 @@ module ActiveRecord
when /^mediumint/i; 3
when /^smallint/i; 2
when /^tinyint/i; 1
+ when /^enum\((.+)\)/i
+ $1.split(',').map{|enum| enum.strip.length - 2}.max
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 8491d42b86..dd40351a38 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -1,3 +1,5 @@
+require 'uri'
+
module ActiveRecord
module ConnectionAdapters
class ConnectionSpecification #:nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 98bcbfff6f..a57a532ba2 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -916,7 +916,8 @@ module ActiveRecord
end
# 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>:encoding</tt>, <tt>:collation</tt>, <tt>:ctype</tt>,
+ # <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
# <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
#
# Example:
@@ -933,6 +934,10 @@ module ActiveRecord
" TEMPLATE = \"#{value}\""
when :encoding
" ENCODING = '#{value}'"
+ when :collation
+ " LC_COLLATE = '#{value}'"
+ when :ctype
+ " LC_CTYPE = '#{value}'"
when :tablespace
" TABLESPACE = \"#{value}\""
when :connection_limit
@@ -1059,6 +1064,20 @@ module ActiveRecord
end_sql
end
+ # Returns the current database collation.
+ def collation
+ query(<<-end_sql, 'SCHEMA')[0][0]
+ SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
+ end_sql
+ end
+
+ # Returns the current database ctype.
+ def ctype
+ query(<<-end_sql, 'SCHEMA')[0][0]
+ SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
+ end_sql
+ end
+
# Returns an array of schema names.
def schema_names
query(<<-SQL, 'SCHEMA').flatten
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 858b667e22..5f157fde6d 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -106,13 +106,11 @@ module ActiveRecord
attr_reader :record, :attempted_action
def initialize(record, attempted_action)
+ super("Attempted to #{attempted_action} a stale object: #{record.class.name}")
@record = record
@attempted_action = attempted_action
end
- def message
- "Attempted to #{attempted_action} a stale object: #{record.class.name}"
- end
end
# Raised when association is being configured improperly or
@@ -168,9 +166,9 @@ module ActiveRecord
class AttributeAssignmentError < ActiveRecordError
attr_reader :exception, :attribute
def initialize(message, exception, attribute)
+ super(message)
@exception = exception
@attribute = attribute
- @message = message
end
end
@@ -189,12 +187,10 @@ module ActiveRecord
attr_reader :model
def initialize(model)
+ super("Unknown primary key for table #{model.table_name} in model #{model}.")
@model = model
end
- def message
- "Unknown primary key for table #{model.table_name} in model #{model}."
- end
end
class ImmutableRelation < ActiveRecordError
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index 96b62fdd61..95f4360578 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -51,13 +51,15 @@ module ActiveRecord
super || delegate.respond_to?(*args)
end
- [:create_table, :create_join_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|
+ [:create_table, :create_join_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, :add_reference, :remove_reference].each do |method|
class_eval <<-EOV, __FILE__, __LINE__ + 1
def #{method}(*args) # def create_table(*args)
record(:"#{method}", args) # record(:create_table, args)
end # end
EOV
end
+ alias :add_belongs_to :add_reference
+ alias :remove_belongs_to :remove_reference
private
@@ -102,6 +104,16 @@ module ActiveRecord
[:remove_timestamps, args]
end
+ def invert_add_reference(args)
+ [:remove_reference, args]
+ end
+ alias :invert_add_belongs_to :invert_add_reference
+
+ def invert_remove_reference(args)
+ [:add_reference, args]
+ end
+ alias :invert_remove_belongs_to :invert_remove_reference
+
# Forwards any missing method call to the \target.
def method_missing(method, *args, &block)
@delegate.send(method, *args, &block)
diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb
index 7b3d926d91..0015e3a567 100644
--- a/activerecord/lib/active_record/model.rb
+++ b/activerecord/lib/active_record/model.rb
@@ -103,7 +103,9 @@ module ActiveRecord
def abstract_class?
false
end
-
+
+ # Defines the name of the table column which will store the class name on single-table
+ # inheritance situations.
def inheritance_column
'type'
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 841681e542..7febb5539f 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -351,7 +351,7 @@ module ActiveRecord
if respond_to?(method)
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?"
+ raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
end
end
end
@@ -373,7 +373,7 @@ module ActiveRecord
# })
#
# Will update the name of the Person with ID 1, build a new associated
- # person with the name `John', and mark the associated Person with ID 2
+ # person with the name 'John', and mark the associated Person with ID 2
# for destruction.
#
# Also accepts an Array of attribute hashes:
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 1dcd0a5a32..78ecb1cdc5 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -148,13 +148,10 @@ db_namespace = namespace :db do
# desc "Retrieves the collation for the current environment's database"
task :collation => [:environment, :load_config] do
- config = ActiveRecord::Base.configurations[Rails.env || 'development']
- case config['adapter']
- when /mysql/
- ActiveRecord::Base.establish_connection(config)
- puts ActiveRecord::Base.connection.collation
- else
- $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
+ begin
+ puts ActiveRecord::Tasks::DatabaseTasks.collation_current
+ rescue NoMethodError
+ $stderr.puts 'Sorry, your database adapter is not supported yet, feel free to submit a patch'
end
end
@@ -274,7 +271,7 @@ db_namespace = namespace :db do
ENV['ISC_USER'] = config['username'].to_s if config['username']
ENV['ISC_PASSWORD'] = config['password'].to_s if config['password']
end
-
+
def firebird_db_string(config)
FireRuby::Database.db_string_for(config.symbolize_keys)
end
@@ -313,7 +310,7 @@ db_namespace = namespace :db do
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
case abcs[env]['adapter']
when /mysql/, /postgresql/, /sqlite/
- ActiveRecord::Tasks::DatabaseTasks.structure_load(abcs[Rails.env], filename)
+ ActiveRecord::Tasks::DatabaseTasks.structure_load(abcs[env], filename)
when 'sqlserver'
`sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i #{filename}`
when 'oci', 'oracle'
@@ -435,7 +432,7 @@ namespace :railties do
task :migrations => :'db:load_config' do
to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map {|n| n.strip }
railties = {}
- Rails.application.railties.all do |railtie|
+ Rails.application.railties.each do |railtie|
next unless to_load == :all || to_load.include?(railtie.railtie_name)
if railtie.respond_to?(:paths) && (path = railtie.paths['db/migrate'].first)
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index fe3aa00a74..dd1f77e925 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -492,10 +492,6 @@ module ActiveRecord
end
end
- def inspect
- to_a.inspect
- end
-
def pretty_print(q)
q.pp(self.to_a)
end
@@ -518,6 +514,13 @@ module ActiveRecord
@values.dup
end
+ def inspect
+ entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
+ entries[10] = '...' if entries.size == 11
+
+ "#<#{self.class.name} [#{entries.join(', ')}]>"
+ end
+
private
def references_eager_loaded_tables?
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index e6a67b76fe..974cd326ef 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -146,8 +146,8 @@ module ActiveRecord
to_a
end
- # Returns true if a record exists in the table that matches the +id+ or
- # conditions given, or false otherwise. The argument can take five forms:
+ # Returns +true+ if a record exists in the table that matches the +id+ or
+ # conditions given, or +false+ otherwise. The argument can take six forms:
#
# * Integer - Finds the record with this primary key.
# * String - Finds the record with a primary key corresponding to this
@@ -155,8 +155,9 @@ module ActiveRecord
# * Array - Finds the record that matches these +find+-style conditions
# (such as <tt>['color = ?', 'red']</tt>).
# * Hash - Finds the record that matches these +find+-style conditions
- # (such as <tt>{:color => 'red'}</tt>).
- # * No args - Returns false if the table is empty, true otherwise.
+ # (such as <tt>{color: 'red'}</tt>).
+ # * +false+ - Returns always +false+.
+ # * No args - Returns +false+ if the table is empty, +true+ otherwise.
#
# For more information about specifying conditions as a Hash or Array,
# see the Conditions section in the introduction to ActiveRecord::Base.
@@ -168,7 +169,8 @@ module ActiveRecord
# Person.exists?(5)
# Person.exists?('5')
# Person.exists?(['name LIKE ?', "%#{query}%"])
- # Person.exists?(:name => "David")
+ # Person.exists?(name: 'David')
+ # Person.exists?(false)
# Person.exists?
def exists?(conditions = :none)
conditions = conditions.id if ActiveRecord::Model === conditions
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index d61df216bc..e401ed37b0 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -41,6 +41,17 @@ module ActiveRecord
alias extensions extending_values
+ # Specify relationships to be included in the result set. For
+ # example:
+ #
+ # users = User.includes(:address)
+ # users.each do |user|
+ # user.address.city
+ # end
+ #
+ # allows you to access the +address+ attribute of the +User+ model without
+ # firing an additional query. This will often result in a
+ # performance improvement over a simple +join+
def includes(*args)
args.empty? ? self : spawn.includes!(*args)
end
@@ -131,6 +142,18 @@ module ActiveRecord
self
end
+ # Allows to specify a group attribute:
+ #
+ # User.group(:name)
+ # => SELECT "users".* FROM "users" GROUP BY name
+ #
+ # Returns an array with distinct records based on the `group` attribute:
+ #
+ # User.select([:id, :name])
+ # => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
+ #
+ # User.group(:name)
+ # => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
def group(*args)
args.blank? ? self : spawn.group!(*args)
end
@@ -142,6 +165,16 @@ module ActiveRecord
self
end
+ # Allows to specify an order attribute:
+ #
+ # User.order('name')
+ # => SELECT "users".* FROM "users" ORDER BY name
+ #
+ # User.order('name DESC')
+ # => SELECT "users".* FROM "users" ORDER BY name DESC
+ #
+ # User.order('name DESC, email')
+ # => SELECT "users".* FROM "users" ORDER BY name DESC, email
def order(*args)
args.blank? ? self : spawn.order!(*args)
end
@@ -415,10 +448,10 @@ module ActiveRecord
#
# Can accept other relation objects. For example:
#
- # Topic.select('title').from(Topics.approved)
+ # Topic.select('title').from(Topic.approved)
# # => SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
#
- # Topics.select('a.title').from(Topics.approved, :a)
+ # Topic.select('a.title').from(Topic.approved, :a)
# # => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
#
def from(value, subquery_name = nil)
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index af51c803a7..b35fec7920 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -31,14 +31,14 @@ module ActiveRecord
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
# }
#
- # It is recommended that you use the block form of unscoped because chaining
- # unscoped with <tt>scope</tt> does not work. Assuming that
+ # It is recommended that you use the block form of unscoped because
+ # chaining unscoped with <tt>scope</tt> does not work. Assuming that
# <tt>published</tt> is a <tt>scope</tt>, the following two statements
- # are equal: the default_scope is applied on both.
+ # are equal: the <tt>default_scope</tt> is applied on both.
#
# Post.unscoped.published
# Post.published
- def unscoped #:nodoc:
+ def unscoped
block_given? ? relation.scoping { yield } : relation
end
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index d13491502e..d836acf18f 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -43,7 +43,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :stored_attributes
+ class_attribute :stored_attributes, instance_writer: false
self.stored_attributes = {}
end
@@ -58,8 +58,11 @@ module ActiveRecord
keys.each do |key|
define_method("#{key}=") do |value|
initialize_store_attribute(store_attribute)
- send(store_attribute)[key] = value
- send :"#{store_attribute}_will_change!"
+ attribute = send(store_attribute)
+ if value != attribute[key]
+ attribute[key] = value
+ send :"#{store_attribute}_will_change!"
+ end
end
define_method(key) do
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 999b2ebc85..fb3dfc2730 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -3,13 +3,17 @@ module ActiveRecord
module DatabaseTasks # :nodoc:
extend self
- TASKS_PATTERNS = {
- /mysql/ => ActiveRecord::Tasks::MySQLDatabaseTasks,
- /postgresql/ => ActiveRecord::Tasks::PostgreSQLDatabaseTasks,
- /sqlite/ => ActiveRecord::Tasks::SQLiteDatabaseTasks
- }
LOCAL_HOSTS = ['127.0.0.1', 'localhost']
+ def register_task(pattern, task)
+ @tasks ||= {}
+ @tasks[pattern] = task
+ end
+
+ register_task(/mysql/, ActiveRecord::Tasks::MySQLDatabaseTasks)
+ register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
+ register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
+
def create(*arguments)
configuration = arguments.first
class_for_adapter(configuration['adapter']).new(*arguments).create
@@ -56,6 +60,15 @@ module ActiveRecord
class_for_adapter(configuration['adapter']).new(*arguments).charset
end
+ def collation_current(environment = Rails.env)
+ collation ActiveRecord::Base.configurations[environment]
+ end
+
+ def collation(*arguments)
+ configuration = arguments.first
+ class_for_adapter(configuration['adapter']).new(*arguments).collation
+ end
+
def purge(configuration)
class_for_adapter(configuration['adapter']).new(configuration).purge
end
@@ -75,8 +88,8 @@ module ActiveRecord
private
def class_for_adapter(adapter)
- key = TASKS_PATTERNS.keys.detect { |pattern| adapter[pattern] }
- TASKS_PATTERNS[key]
+ key = @tasks.keys.detect { |pattern| adapter[pattern] }
+ @tasks[key]
end
def each_current_configuration(environment)
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index b39cd2282f..bf62dfd5b5 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -44,6 +44,10 @@ module ActiveRecord
connection.charset
end
+ def collation
+ connection.collation
+ end
+
def structure_dump(filename)
establish_connection configuration
File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump }
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index a210392e53..ea5cb888fb 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -29,6 +29,10 @@ module ActiveRecord
connection.encoding
end
+ def collation
+ connection.collation
+ end
+
def purge
clear_active_connections!
drop