aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations/association.rb3
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb59
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb7
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb60
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb8
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb24
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb12
-rw-r--r--activerecord/lib/active_record/associations/builder/has_many.rb4
-rw-r--r--activerecord/lib/active_record/associations/builder/has_one.rb6
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb8
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb6
-rw-r--r--activerecord/lib/active_record/associations/preloader/singular_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/singular_association.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb35
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb101
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb17
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb12
-rw-r--r--activerecord/lib/active_record/connection_handling.rb11
-rw-r--r--activerecord/lib/active_record/counter_cache.rb8
-rw-r--r--activerecord/lib/active_record/dynamic_matchers.rb9
-rw-r--r--activerecord/lib/active_record/enum.rb12
-rw-r--r--activerecord/lib/active_record/errors.rb14
-rw-r--r--activerecord/lib/active_record/fixtures.rb2
-rw-r--r--activerecord/lib/active_record/integration.rb6
-rw-r--r--activerecord/lib/active_record/null_relation.rb6
-rw-r--r--activerecord/lib/active_record/railtie.rb23
-rw-r--r--activerecord/lib/active_record/railties/databases.rake17
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb37
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb37
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb18
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb17
-rw-r--r--activerecord/lib/active_record/sanitization.rb9
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb19
-rw-r--r--activerecord/lib/active_record/version.rb2
46 files changed, 432 insertions, 249 deletions
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index ce7d41cf54..67ea489b22 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -104,11 +104,12 @@ module ActiveRecord
# Set the inverse association, if possible
def set_inverse_instance(record)
- if record && invertible_for?(record)
+ if invertible_for?(record)
inverse = record.association(inverse_reflection_for(record).name)
inverse.target = owner
inverse.inversed = true
end
+ record
end
# Returns the class of the target. belongs_to polymorphic overrides this to look at the
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index e1fa5225b5..8272a5584c 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -8,13 +8,16 @@ module ActiveRecord
end
def replace(record)
- raise_on_type_mismatch!(record) if record
-
- update_counters(record)
- replace_keys(record)
- set_inverse_instance(record)
-
- @updated = true if record
+ if record
+ raise_on_type_mismatch!(record)
+ update_counters(record)
+ replace_keys(record)
+ set_inverse_instance(record)
+ @updated = true
+ else
+ decrement_counters
+ remove_keys
+ end
self.target = record
end
@@ -34,35 +37,41 @@ module ActiveRecord
!loaded? && foreign_key_present? && klass
end
- def update_counters(record)
+ def with_cache_name
counter_cache_name = reflection.counter_cache_column
+ return unless counter_cache_name && owner.persisted?
+ yield counter_cache_name
+ end
- if counter_cache_name && owner.persisted? && different_target?(record)
- if record
- record.class.increment_counter(counter_cache_name, record.id)
- end
+ def update_counters(record)
+ with_cache_name do |name|
+ return unless different_target? record
+ record.class.increment_counter(name, record.id)
+ decrement_counter name
+ end
+ end
- if foreign_key_present?
- klass.decrement_counter(counter_cache_name, target_id)
- end
+ def decrement_counters
+ with_cache_name { |name| decrement_counter name }
+ end
+
+ def decrement_counter counter_cache_name
+ if foreign_key_present?
+ klass.decrement_counter(counter_cache_name, target_id)
end
end
# Checks whether record is different to the current target, without loading it
def different_target?(record)
- if record.nil?
- owner[reflection.foreign_key]
- else
- record.id != owner[reflection.foreign_key]
- end
+ record.id != owner[reflection.foreign_key]
end
def replace_keys(record)
- if record
- owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)]
- else
- owner[reflection.foreign_key] = nil
- end
+ owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)]
+ end
+
+ def remove_keys
+ owner[reflection.foreign_key] = nil
end
def foreign_key_present?
diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
index eae5eed3a1..b710cf6bdb 100644
--- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
@@ -11,7 +11,12 @@ module ActiveRecord
def replace_keys(record)
super
- owner[reflection.foreign_type] = record && record.class.base_class.name
+ owner[reflection.foreign_type] = record.class.base_class.name
+ end
+
+ def remove_keys
+ super
+ owner[reflection.foreign_type] = nil
end
def different_target?(record)
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index 37ba1c73b1..3911d1b520 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/module/attribute_accessors'
+
# This is the parent Association class which defines the variables
# used by all associations.
#
@@ -13,65 +15,67 @@ module ActiveRecord::Associations::Builder
class Association #:nodoc:
class << self
attr_accessor :extensions
+ # TODO: This class accessor is needed to make activerecord-deprecated_finders work.
+ # We can move it to a constant in 5.0.
+ attr_accessor :valid_options
end
self.extensions = []
- VALID_OPTIONS = [:class_name, :class, :foreign_key, :validate]
+ self.valid_options = [:class_name, :class, :foreign_key, :validate]
+
+ attr_reader :name, :scope, :options
def self.build(model, name, scope, options, &block)
- extension = define_extensions model, name, &block
- reflection = create_reflection model, name, scope, options, extension
+ builder = create_builder model, name, scope, options, &block
+ reflection = builder.build(model)
define_accessors model, reflection
define_callbacks model, reflection
+ builder.define_extensions model
reflection
end
- def self.create_reflection(model, name, scope, options, extension = nil)
+ def self.create_builder(model, name, scope, options, &block)
raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
+ new(model, name, scope, options, &block)
+ end
+
+ def initialize(model, name, scope, options)
+ # TODO: Move this to create_builder as soon we drop support to activerecord-deprecated_finders.
if scope.is_a?(Hash)
options = scope
scope = nil
end
- validate_options(options)
+ # TODO: Remove this model argument as soon we drop support to activerecord-deprecated_finders.
+ @name = name
+ @scope = scope
+ @options = options
- scope = build_scope(scope, extension)
-
- ActiveRecord::Reflection.create(macro, name, scope, options, model)
- end
-
- def self.build_scope(scope, extension)
- new_scope = scope
+ validate_options
if scope && scope.arity == 0
- new_scope = proc { instance_exec(&scope) }
- end
-
- if extension
- new_scope = wrap_scope new_scope, extension
+ @scope = proc { instance_exec(&scope) }
end
-
- new_scope
end
- def self.wrap_scope(scope, extension)
- scope
+ def build(model)
+ ActiveRecord::Reflection.create(macro, name, scope, options, model)
end
- def self.macro
+ def macro
raise NotImplementedError
end
- def self.valid_options(options)
- VALID_OPTIONS + Association.extensions.flat_map(&:valid_options)
+ def valid_options
+ Association.valid_options + Association.extensions.flat_map(&:valid_options)
end
- def self.validate_options(options)
- options.assert_valid_keys(valid_options(options))
+ def validate_options
+ options.assert_valid_keys(valid_options)
end
- def self.define_extensions(model, name)
+ def define_extensions(model)
end
def self.define_callbacks(model, reflection)
@@ -114,6 +118,8 @@ module ActiveRecord::Associations::Builder
raise NotImplementedError
end
+ private
+
def self.add_before_destroy_callbacks(model, reflection)
unless valid_dependent_options.include? reflection.options[:dependent]
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{reflection.options[:dependent]}"
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index e8e36e7cd0..5ccaa55a32 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -1,10 +1,10 @@
module ActiveRecord::Associations::Builder
class BelongsTo < SingularAssociation #:nodoc:
- def self.macro
+ def macro
:belongs_to
end
- def self.valid_options(options)
+ def valid_options
super + [:foreign_type, :polymorphic, :touch, :counter_cache]
end
@@ -23,6 +23,8 @@ module ActiveRecord::Associations::Builder
add_counter_cache_methods mixin
end
+ private
+
def self.add_counter_cache_methods(mixin)
return if mixin.method_defined? :belongs_to_counter_cache_after_create
@@ -110,7 +112,7 @@ module ActiveRecord::Associations::Builder
end
record = o.send name
- unless record.nil? || record.new_record?
+ if record && record.persisted?
if touch != true
record.touch touch
else
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index 2ff67f904d..bc15a49996 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -7,11 +7,22 @@ module ActiveRecord::Associations::Builder
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
- def self.valid_options(options)
+ def valid_options
super + [:table_name, :before_add,
:after_add, :before_remove, :after_remove, :extend]
end
+ attr_reader :block_extension
+
+ def initialize(model, name, scope, options)
+ super
+ @mod = nil
+ if block_given?
+ @mod = Module.new(&Proc.new)
+ @scope = wrap_scope @scope, @mod
+ end
+ end
+
def self.define_callbacks(model, reflection)
super
name = reflection.name
@@ -21,11 +32,10 @@ module ActiveRecord::Associations::Builder
}
end
- def self.define_extensions(model, name)
- if block_given?
+ def define_extensions(model)
+ if @mod
extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
- extension = Module.new(&Proc.new)
- model.parent.const_set(extension_module_name, extension)
+ model.parent.const_set(extension_module_name, @mod)
end
end
@@ -68,7 +78,9 @@ module ActiveRecord::Associations::Builder
CODE
end
- def self.wrap_scope(scope, mod)
+ private
+
+ def wrap_scope(scope, mod)
if scope
proc { |owner| instance_exec(owner, &scope).extending(mod) }
else
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 1c9c04b044..e472277374 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
@@ -20,7 +20,7 @@ module ActiveRecord::Associations::Builder
def self.build(lhs_class, name, options)
if options[:join_table]
- KnownTable.new options[:join_table]
+ KnownTable.new options[:join_table].to_s
else
class_name = options.fetch(:class_name) {
name.to_s.camelize.singularize
@@ -84,11 +84,11 @@ module ActiveRecord::Associations::Builder
middle_name = [lhs_model.name.downcase.pluralize,
association_name].join('_').gsub(/::/, '_').to_sym
middle_options = middle_options join_model
-
- HasMany.create_reflection(lhs_model,
- middle_name,
- nil,
- middle_options)
+ hm_builder = HasMany.create_builder(lhs_model,
+ middle_name,
+ nil,
+ middle_options)
+ hm_builder.build lhs_model
end
private
diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb
index 227184cd19..7909b93622 100644
--- a/activerecord/lib/active_record/associations/builder/has_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_many.rb
@@ -1,10 +1,10 @@
module ActiveRecord::Associations::Builder
class HasMany < CollectionAssociation #:nodoc:
- def self.macro
+ def macro
:has_many
end
- def self.valid_options(options)
+ def valid_options
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache]
end
diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb
index 064a3c8b51..f359efd496 100644
--- a/activerecord/lib/active_record/associations/builder/has_one.rb
+++ b/activerecord/lib/active_record/associations/builder/has_one.rb
@@ -1,10 +1,10 @@
module ActiveRecord::Associations::Builder
class HasOne < SingularAssociation #:nodoc:
- def self.macro
+ def macro
:has_one
end
- def self.valid_options(options)
+ def valid_options
valid = super + [:order, :as]
valid += [:through, :source, :source_type] if options[:through]
valid
@@ -14,6 +14,8 @@ module ActiveRecord::Associations::Builder
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
end
+ private
+
def self.add_before_destroy_callbacks(model, reflection)
super unless reflection.options[:through]
end
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index a6e8300642..e655c389a6 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -2,7 +2,7 @@
module ActiveRecord::Associations::Builder
class SingularAssociation < Association #:nodoc:
- def self.valid_options(options)
+ def valid_options
super + [:remote, :dependent, :primary_key, :inverse_of]
end
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 62f23f54f9..e9ed5d9979 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -4,7 +4,7 @@ module ActiveRecord
#
# CollectionAssociation is an abstract class that provides common stuff to
# ease the implementation of association proxies that represent
- # collections. See the class hierarchy in AssociationProxy.
+ # collections. See the class hierarchy in Association.
#
# CollectionAssociation:
# HasManyAssociation => has_many
@@ -193,7 +193,11 @@ module ActiveRecord
# Count all records using SQL. Construct options and pass them with
# scope to the target class's +count+.
- def count(column_name = nil)
+ def count(column_name = nil, count_options = {})
+ # TODO: Remove count_options argument as soon we remove support to
+ # activerecord-deprecated_finders.
+ column_name, count_options = nil, column_name if column_name.is_a?(Hash)
+
relation = scope
if association_scope.distinct_value
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 0b37ecf5b7..0dabe256e3 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -669,8 +669,10 @@ module ActiveRecord
# # #<Pet id: 2, name: "Spook", person_id: 1>,
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
# # ]
- def count(column_name = nil)
- @association.count(column_name)
+ def count(column_name = nil, options = {})
+ # TODO: Remove options argument as soon we remove support to
+ # activerecord-deprecated_finders.
+ @association.count(column_name, options)
end
# Returns the size of the collection. If the collection hasn't been loaded,
diff --git a/activerecord/lib/active_record/associations/preloader/singular_association.rb b/activerecord/lib/active_record/associations/preloader/singular_association.rb
index 2b5cfda8ce..f60647a81e 100644
--- a/activerecord/lib/active_record/associations/preloader/singular_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/singular_association.rb
@@ -11,7 +11,7 @@ module ActiveRecord
association = owner.association(reflection.name)
association.target = record
- association.set_inverse_instance(record)
+ association.set_inverse_instance(record) if record
end
end
diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb
index 02dc464536..e4500af5b2 100644
--- a/activerecord/lib/active_record/associations/singular_association.rb
+++ b/activerecord/lib/active_record/associations/singular_association.rb
@@ -39,7 +39,9 @@ module ActiveRecord
end
def find_target
- scope.first.tap { |record| set_inverse_instance(record) }
+ if record = scope.first
+ set_inverse_instance record
+ end
end
def replace(record)
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 217fc52dd5..73761520f7 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -330,7 +330,7 @@ module ActiveRecord
end
# 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)). It raises
+ # "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
#
# Alias for the <tt>read_attribute</tt> method.
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index c152a246b5..d01e9aea59 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -102,7 +102,7 @@ module ActiveRecord
end
# 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
+ # it has been typecast (for example, "2004-12-12" in a date column is cast
# to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
# If it's cached, just return it
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 cfdcae7f63..cebe741daa 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -86,7 +86,7 @@ module ActiveRecord
end
end
- # Return the number of threads currently waiting on this
+ # Returns the number of threads currently waiting on this
# queue.
def num_waiting
synchronize do
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 1a323b6a9c..e196df079b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -346,7 +346,7 @@ module ActiveRecord
protected
- # Return a subquery for the given key using the join information.
+ # Returns a subquery for the given key using the join information.
def subquery_for(key, select)
subselect = select.clone
subselect.projections = [key]
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 552a22d28a..75501852ed 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -43,7 +43,9 @@ module ActiveRecord
# SQLite does not understand dates, so this method will convert a Date
# to a String.
def type_cast(value, column)
- return value.id if value.respond_to?(:quoted_id)
+ if value.respond_to?(:quoted_id) && value.respond_to?(:id)
+ return value.id
+ end
case value
when String, ActiveSupport::Multibyte::Chars
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
index 7c330a2f25..a51691bfa8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -34,9 +34,10 @@ module ActiveRecord
def visit_TableDefinition(o)
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
- create_sql << "#{quote_table_name(o.name)} ("
- create_sql << o.columns.map { |c| accept c }.join(', ')
- create_sql << ") #{o.options}"
+ create_sql << "#{quote_table_name(o.name)} "
+ create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " unless o.as
+ create_sql << "#{o.options}"
+ create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
create_sql
end
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 063b19871a..c39bf15e83 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -49,14 +49,15 @@ module ActiveRecord
# An array of ColumnDefinition objects, representing the column changes
# that have been defined.
attr_accessor :indexes
- attr_reader :name, :temporary, :options
+ attr_reader :name, :temporary, :options, :as
- def initialize(types, name, temporary, options)
+ def initialize(types, name, temporary, options, as = nil)
@columns_hash = {}
@indexes = {}
@native = types
@temporary = temporary
@options = options
+ @as = as
@name = name
end
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 51a6929dab..00383bad3b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -131,6 +131,9 @@ module ActiveRecord
# [<tt>:force</tt>]
# Set to true to drop the table before creating it.
# Defaults to false.
+ # [<tt>:as</tt>]
+ # SQL to use to generate the table. When this option is used, the block is
+ # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
#
# ====== Add a backend specific option to the generated SQL (MySQL)
#
@@ -169,19 +172,31 @@ module ActiveRecord
# supplier_id int
# )
#
+ # ====== Create a temporary table based on a query
+ #
+ # create_table(:long_query, temporary: true,
+ # as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
+ #
+ # generates:
+ #
+ # CREATE TEMPORARY TABLE long_query AS
+ # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
+ #
# See also TableDefinition#column for details on how to create columns.
def create_table(table_name, options = {})
- td = create_table_definition table_name, options[:temporary], options[:options]
+ td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
- unless options[:id] == false
- pk = options.fetch(:primary_key) {
- Base.get_primary_key table_name.to_s.singularize
- }
+ if !options[:as]
+ unless options[:id] == false
+ pk = options.fetch(:primary_key) {
+ Base.get_primary_key table_name.to_s.singularize
+ }
- td.primary_key pk, options.fetch(:id, :primary_key), options
- end
+ td.primary_key pk, options.fetch(:id, :primary_key), options
+ end
- yield td if block_given?
+ yield td if block_given?
+ end
if options[:force] && table_exists?(table_name)
drop_table(table_name, options)
@@ -826,8 +841,8 @@ module ActiveRecord
end
private
- def create_table_definition(name, temporary, options)
- TableDefinition.new native_database_types, name, temporary, options
+ def create_table_definition(name, temporary, options, as = nil)
+ TableDefinition.new native_database_types, name, temporary, options, as
end
def create_alter_table(name)
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 1b9f865666..7768c24967 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -303,12 +303,6 @@ module ActiveRecord
else
log(sql, name) { @connection.query(sql) }
end
- rescue ActiveRecord::StatementInvalid => exception
- if exception.message.split(":").first =~ /Packets out of order/
- raise ActiveRecord::StatementInvalid.new("'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings.", exception.original_exception)
- else
- raise
- end
end
# MysqlAdapter has to free a result after using it, so we use this method to write
@@ -492,6 +486,10 @@ module ActiveRecord
rename_table_indexes(table_name, new_name)
end
+ def drop_table(table_name, options = {})
+ execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}"
+ end
+
def rename_index(table_name, old_name, new_name)
if (version[0] == 5 && version[1] >= 7) || version[0] >= 6
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 64fc9e95d8..7e16156408 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -16,38 +16,24 @@ module ActiveRecord
##
# Builds a ConnectionSpecification from user input
class Resolver # :nodoc:
- attr_reader :config, :klass, :configurations
+ attr_reader :configurations
- def initialize(config, configurations)
- @config = config
+ def initialize(configurations)
@configurations = configurations
end
- def spec
- case config
- when nil
- raise AdapterNotSpecified unless defined?(Rails.env)
- resolve_string_connection Rails.env
- when Symbol, String
- resolve_string_connection config.to_s
- when Hash
- resolve_hash_connection config
- end
- end
-
- private
- def resolve_string_connection(spec) # :nodoc:
- hash = configurations.fetch(spec) do |k|
- connection_url_to_hash(k)
+ def resolve(config)
+ if config
+ resolve_connection config
+ elsif defined?(Rails.env)
+ resolve_env_connection Rails.env.to_sym
+ else
+ raise AdapterNotSpecified
end
-
- raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash
-
- resolve_hash_connection hash
end
- def resolve_hash_connection(spec) # :nodoc:
- spec = spec.symbolize_keys
+ def spec(config)
+ spec = resolve(config).symbolize_keys
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
@@ -61,20 +47,64 @@ module ActiveRecord
end
adapter_method = "#{spec[:adapter]}_connection"
-
ConnectionSpecification.new(spec, adapter_method)
end
- def connection_url_to_hash(url) # :nodoc:
- config = URI.parse url
+ private
+
+ def resolve_connection(spec) #:nodoc:
+ case spec
+ when Symbol, String
+ resolve_env_connection spec
+ when Hash
+ resolve_hash_connection spec
+ end
+ end
+
+ def resolve_env_connection(spec) # :nodoc:
+ # Rails has historically accepted a string to mean either
+ # an environment key or a url spec, so we have deprecated
+ # this ambiguous behaviour and in the future this function
+ # can be removed in favor of resolve_string_connection and
+ # resolve_symbol_connection.
+ if config = configurations[spec.to_s]
+ if spec.is_a?(String)
+ ActiveSupport::Deprecation.warn "Passing a string to ActiveRecord::Base.establish_connection " \
+ "for a configuration lookup is deprecated, please pass a symbol (#{spec.to_sym.inspect}) instead"
+ end
+ resolve_connection(config)
+ elsif spec.is_a?(String)
+ resolve_string_connection(spec)
+ else
+ raise(AdapterNotSpecified, "#{spec} database is not configured")
+ end
+ end
+
+ def resolve_hash_connection(spec) # :nodoc:
+ spec
+ end
+
+ def resolve_string_connection(spec) # :nodoc:
+ config = URI.parse spec
adapter = config.scheme
adapter = "postgresql" if adapter == "postgres"
- spec = { :adapter => adapter,
- :username => config.user,
- :password => config.password,
- :port => config.port,
- :database => config.path.sub(%r{^/},""),
- :host => config.host }
+
+ database = if adapter == 'sqlite3'
+ if '/:memory:' == config.path
+ ':memory:'
+ else
+ config.path
+ end
+ else
+ config.path.sub(%r{^/},"")
+ end
+
+ spec = { "adapter" => adapter,
+ "username" => config.user,
+ "password" => config.password,
+ "port" => config.port,
+ "database" => database,
+ "host" => config.host }
spec.reject!{ |_,value| value.blank? }
@@ -83,8 +113,7 @@ module ActiveRecord
spec.map { |key,value| spec[key] = uri_parser.unescape(value) if value.is_a?(String) }
if config.query
- options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
-
+ options = Hash[config.query.split("&").map{ |pair| pair.split("=") }]
spec.merge!(options)
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index e790f731ea..6d8e994654 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -18,6 +18,12 @@ module ActiveRecord
client = Mysql2::Client.new(config)
options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
+ rescue Mysql2::Error => error
+ if error.message.include?("Unknown database")
+ raise ActiveRecord::NoDatabaseError.new(error.message)
+ else
+ raise error
+ end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index c4dcba0501..7dbaa272a3 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -34,6 +34,12 @@ module ActiveRecord
default_flags |= Mysql::CLIENT_FOUND_ROWS if Mysql.const_defined?(:CLIENT_FOUND_ROWS)
options = [host, username, password, database, port, socket, default_flags]
ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config)
+ rescue Mysql::Error => error
+ if error.message.include?("Unknown database")
+ raise ActiveRecord::NoDatabaseError.new(error.message)
+ else
+ raise error
+ end
end
end
@@ -419,14 +425,19 @@ module ActiveRecord
if result
types = {}
+ fields = []
result.fetch_fields.each { |field|
+ field_name = field.name
+ fields << field_name
+
if field.decimals > 0
- types[field.name] = Fields::Decimal.new
+ types[field_name] = Fields::Decimal.new
else
- types[field.name] = Fields.find_type field
+ types[field_name] = Fields.find_type field
end
}
- result_set = ActiveRecord::Result.new(types.keys, result.to_a, types)
+
+ result_set = ActiveRecord::Result.new(fields, result.to_a, types)
result.free
else
result_set = ActiveRecord::Result.new([], [])
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index bf34f2bdae..35ce881302 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -144,7 +144,7 @@ module ActiveRecord
def quote_and_escape(value)
case value
- when "NULL"
+ when "NULL", Numeric
value
else
"\"#{value.gsub(/"/,"\\\"")}\""
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index d23a24589c..11a5eba464 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -46,7 +46,7 @@ module ActiveRecord
# PostgreSQL-specific extensions to column definitions in a table.
class PostgreSQLColumn < Column #:nodoc:
attr_accessor :array
- # Instantiates a new PostgreSQL column definition in a table.
+
def initialize(name, default, oid_type, sql_type = nil, null = true)
@oid_type = oid_type
default_value = self.class.extract_value_from_default(default)
@@ -62,6 +62,14 @@ module ActiveRecord
@default_function = default if has_default_function?(default_value, default)
end
+ def number?
+ !array && super
+ end
+
+ def text?
+ !array && super
+ end
+
# :stopdoc:
class << self
include ConnectionAdapters::PostgreSQLColumn::Cast
@@ -865,6 +873,12 @@ module ActiveRecord
PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
configure_connection
+ rescue ::PG::Error => error
+ if error.message.include?("does not exist")
+ raise ActiveRecord::NoDatabaseError.new(error.message)
+ else
+ raise error
+ end
end
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -969,12 +983,12 @@ module ActiveRecord
end
def extract_table_ref_from_insert_sql(sql)
- sql[/into\s+([^\(]*).*values\s*\(/i]
+ sql[/into\s+([^\(]*).*values\s*\(/im]
$1.strip if $1
end
- def create_table_definition(name, temporary, options)
- TableDefinition.new native_database_types, name, temporary, options
+ def create_table_definition(name, temporary, options, as = nil)
+ TableDefinition.new native_database_types, name, temporary, options, as
end
def update_table_definition(table_name, base)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 2cf1015f2c..92bb70ba53 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -31,6 +31,12 @@ module ActiveRecord
db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
ConnectionAdapters::SQLite3Adapter.new(db, logger, config)
+ rescue Errno::ENOENT => error
+ if error.message.include?("No such file or directory")
+ raise ActiveRecord::NoDatabaseError.new(error.message)
+ else
+ raise error
+ end
end
end
@@ -586,7 +592,11 @@ module ActiveRecord
def translate_exception(exception, message)
case exception.message
- when /column(s)? .* (is|are) not unique/
+ # SQLite 3.8.2 returns a newly formatted error message:
+ # UNIQUE constraint failed: *table_name*.*column_name*
+ # Older versions of SQLite return:
+ # column *column_name* is not unique
+ when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
RecordNotUnique.new(message, exception)
else
super
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index a1943dfcb0..c4afadbd9b 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -32,11 +32,18 @@ module ActiveRecord
# "postgres://myuser:mypass@localhost/somedatabase"
# )
#
+ # In case <tt>ActiveRecord::Base.configurations</tt> is set (Rails
+ # automatically loads the contents of config/database.yml into it),
+ # a symbol can also be given as argument, representing a key in the
+ # configuration hash:
+ #
+ # ActiveRecord::Base.establish_connection(:production)
+ #
# The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
# may be returned on an error.
def establish_connection(spec = ENV["DATABASE_URL"])
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new spec, configurations
- spec = resolver.spec
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations
+ spec = resolver.spec(spec)
unless respond_to?(spec.adapter_method)
raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index 7e3bef9431..dcbdf75627 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -82,10 +82,10 @@ module ActiveRecord
# Increment a numeric field by one, via a direct SQL update.
#
- # This method is used primarily for maintaining counter_cache columns used to
- # store aggregate values. For example, a DiscussionBoard may cache posts_count
- # and comments_count to avoid running an SQL query to calculate the number of
- # posts and comments there are each time it is displayed.
+ # This method is used primarily for maintaining counter_cache columns that are
+ # used to store aggregate values. For example, a DiscussionBoard may cache
+ # posts_count and comments_count to avoid running an SQL query to calculate the
+ # number of posts and comments there are, each time it is displayed.
#
# ==== Parameters
#
diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb
index e650ebcf64..5caab09038 100644
--- a/activerecord/lib/active_record/dynamic_matchers.rb
+++ b/activerecord/lib/active_record/dynamic_matchers.rb
@@ -84,13 +84,18 @@ module ActiveRecord
"#{finder}(#{attributes_hash})"
end
+ # The parameters in the signature may have reserved Ruby words, in order
+ # to prevent errors, we start each param name with `_`.
+ #
# Extended in activerecord-deprecated_finders
def signature
- attribute_names.join(', ')
+ attribute_names.map { |name| "_#{name}" }.join(', ')
end
+ # Given that the parameters starts with `_`, the finder needs to use the
+ # same parameter name.
def attributes_hash
- "{" + attribute_names.map { |name| ":#{name} => #{name}" }.join(',') + "}"
+ "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(',') + "}"
end
def finder
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 837989aaa7..2f8439892b 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -55,12 +55,12 @@ module ActiveRecord
def enum(definitions)
klass = self
definitions.each do |name, values|
- # DIRECTION = { }
+ # STATUS = { }
enum_values = _enum_methods_module.const_set name.to_s.upcase, ActiveSupport::HashWithIndifferentAccess.new
name = name.to_sym
_enum_methods_module.module_eval do
- # def direction=(value) self[:direction] = DIRECTION[value] end
+ # def status=(value) self[:status] = STATUS[value] end
define_method("#{name}=") { |value|
unless enum_values.has_key?(value)
raise ArgumentError, "'#{value}' is not a valid #{name}"
@@ -68,20 +68,20 @@ module ActiveRecord
self[name] = enum_values[value]
}
- # def direction() DIRECTION.key self[:direction] end
+ # def status() STATUS.key self[:status] end
define_method(name) { enum_values.key self[name] }
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
pairs.each do |value, i|
enum_values[value] = i
- # scope :incoming, -> { where direction: 0 }
+ # scope :active, -> { where status: 0 }
klass.scope value, -> { klass.where name => i }
- # def incoming?() direction == 0 end
+ # def active?() status == 0 end
define_method("#{value}?") { self[name] == i }
- # def incoming! update! direction: :incoming end
+ # def active!() update! status: :active end
define_method("#{value}!") { update! name => value }
end
end
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 7e38719811..7f6228131f 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -94,6 +94,18 @@ module ActiveRecord
class PreparedStatementInvalid < ActiveRecordError
end
+ # Raised when a given database does not exist
+ class NoDatabaseError < ActiveRecordError
+ def initialize(message)
+ super extend_message(message)
+ end
+
+ # can be over written to add additional error information.
+ def extend_message(message)
+ message
+ end
+ end
+
# Raised on attempt to save stale record. Record is stale when it's being saved in another query after
# instantiation, for example, when two users edit the same wiki page and one starts editing and saves
# the page before the other.
@@ -188,7 +200,7 @@ module ActiveRecord
end
end
- # Raised when a primary key is needed, but there is not one specified in the schema or model.
+ # Raised when a primary key is needed, but not specified in the schema or model.
class UnknownPrimaryKey < ActiveRecordError
attr_reader :model
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index d241788a9b..a7a54483bc 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -604,7 +604,7 @@ module ActiveRecord
}
end
- # Return a hash of rows to be inserted. The key is the table, the value is
+ # Returns 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 = config.default_timezone == :utc ? Time.now.utc : Time.now
diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb
index 27576b1e61..31e2518540 100644
--- a/activerecord/lib/active_record/integration.rb
+++ b/activerecord/lib/active_record/integration.rb
@@ -98,8 +98,10 @@ module ActiveRecord
super()
else
define_method :to_param do
- if (default = super()) && (result = send(method_name).to_s).present?
- "#{default}-#{result.squish.truncate(20, separator: /\s/, omission: nil).parameterize}"
+ if (default = super()) &&
+ (result = send(method_name).to_s).present? &&
+ (param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
+ "#{default}-#{param}"
else
default
end
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
index 080b20134d..5b255c3fe5 100644
--- a/activerecord/lib/active_record/null_relation.rb
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -50,8 +50,10 @@ module ActiveRecord
0
end
- def calculate(_operation, _column_name)
- if _operation == :count
+ def calculate(operation, _column_name, _options = {})
+ # TODO: Remove _options argument as soon we remove support to
+ # activerecord-deprecated_finders.
+ if operation == :count
0
else
nil
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index eef08aea88..2c796f97e6 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -43,11 +43,24 @@ module ActiveRecord
namespace :db do
task :load_config do
ActiveRecord::Tasks::DatabaseTasks.db_dir = Rails.application.config.paths["db"].first
- ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration
ActiveRecord::Tasks::DatabaseTasks.migrations_paths = Rails.application.paths['db/migrate'].to_a
ActiveRecord::Tasks::DatabaseTasks.fixtures_path = File.join Rails.root, 'test', 'fixtures'
ActiveRecord::Tasks::DatabaseTasks.root = Rails.root
+ configuration = if ENV["DATABASE_URL"]
+ { Rails.env => ENV["DATABASE_URL"] }
+ else
+ Rails.application.config.database_configuration || {}
+ end
+
+ resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(configuration)
+
+ configuration.each do |key, value|
+ configuration[key] = resolver.resolve(value) if value
+ end
+
+ ActiveRecord::Tasks::DatabaseTasks.database_configuration = configuration
+
if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH)
if engine.paths['db/migrate'].existent
ActiveRecord::Tasks::DatabaseTasks.migrations_paths += engine.paths['db/migrate'].to_a
@@ -122,6 +135,14 @@ module ActiveRecord
# and then establishes the connection.
initializer "active_record.initialize_database" do |app|
ActiveSupport.on_load(:active_record) do
+
+ class ActiveRecord::NoDatabaseError
+ def extend_message(message)
+ message << "Run `$ bin/rake db:create db:migrate` to create your database"
+ message
+ end
+ end
+
self.configurations = app.config.database_configuration || {}
establish_connection
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 52b3d3e5e6..90dfc177e5 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -14,11 +14,7 @@ db_namespace = namespace :db do
desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all databases in the config)'
task :create => [:load_config] do
- if ENV['DATABASE_URL']
- ActiveRecord::Tasks::DatabaseTasks.create_database_url
- else
- ActiveRecord::Tasks::DatabaseTasks.create_current
- end
+ ActiveRecord::Tasks::DatabaseTasks.create_current
end
namespace :drop do
@@ -29,11 +25,7 @@ db_namespace = namespace :db do
desc 'Drops the database using DATABASE_URL or the current Rails.env (use db:drop:all to drop all databases)'
task :drop => [:load_config] do
- if ENV['DATABASE_URL']
- ActiveRecord::Tasks::DatabaseTasks.drop_database_url
- else
- ActiveRecord::Tasks::DatabaseTasks.drop_current
- end
+ ActiveRecord::Tasks::DatabaseTasks.drop_current
end
desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
@@ -343,7 +335,7 @@ db_namespace = namespace :db do
end
# desc "Recreate the test database from a fresh schema"
- task :clone do
+ task :clone => :environment do
case ActiveRecord::Base.schema_format
when :ruby
db_namespace["test:clone_schema"].invoke
@@ -364,7 +356,7 @@ db_namespace = namespace :db do
end
# desc 'Check for pending migrations and load the test schema'
- task :prepare => :load_config do
+ task :prepare => [:environment, :load_config] do
unless ActiveRecord::Base.configurations.blank?
db_namespace['test:load'].invoke
end
@@ -401,4 +393,3 @@ namespace :railties do
end
task 'test:prepare' => ['db:test:prepare', 'db:test:load', 'db:abort_if_pending_migrations']
-
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index cf24d10a8d..45ffb99868 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -19,16 +19,21 @@ module ActiveRecord
#
# Person.group(:city).count
# # => { 'Rome' => 5, 'Paris' => 3 }
- def count(column_name = nil)
- calculate(:count, column_name)
+ def count(column_name = nil, options = {})
+ # TODO: Remove options argument as soon we remove support to
+ # activerecord-deprecated_finders.
+ column_name, options = nil, column_name if column_name.is_a?(Hash)
+ calculate(:count, column_name, options)
end
# Calculates the average value on a given column. Returns +nil+ if there's
# no row. See +calculate+ for examples with options.
#
# Person.average(:age) # => 35.8
- def average(column_name)
- calculate(:average, column_name)
+ def average(column_name, options = {})
+ # TODO: Remove options argument as soon we remove support to
+ # activerecord-deprecated_finders.
+ calculate(:average, column_name, options)
end
# Calculates the minimum value on a given column. The value is returned
@@ -36,8 +41,10 @@ module ActiveRecord
# +calculate+ for examples with options.
#
# Person.minimum(:age) # => 7
- def minimum(column_name)
- calculate(:minimum, column_name)
+ def minimum(column_name, options = {})
+ # TODO: Remove options argument as soon we remove support to
+ # activerecord-deprecated_finders.
+ calculate(:minimum, column_name, options)
end
# Calculates the maximum value on a given column. The value is returned
@@ -45,8 +52,10 @@ module ActiveRecord
# +calculate+ for examples with options.
#
# Person.maximum(:age) # => 93
- def maximum(column_name)
- calculate(:maximum, column_name)
+ def maximum(column_name, options = {})
+ # TODO: Remove options argument as soon we remove support to
+ # activerecord-deprecated_finders.
+ calculate(:maximum, column_name, options)
end
# Calculates the sum of values on a given column. The value is returned
@@ -89,15 +98,17 @@ module ActiveRecord
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
#
# Person.sum("2 * age")
- def calculate(operation, column_name)
+ def calculate(operation, column_name, options = {})
+ # TODO: Remove options argument as soon we remove support to
+ # activerecord-deprecated_finders.
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
column_name = attribute_alias(column_name)
end
if has_include?(column_name)
- construct_relation_for_association_calculations.calculate(operation, column_name)
+ construct_relation_for_association_calculations.calculate(operation, column_name, options)
else
- perform_calculation(operation, column_name)
+ perform_calculation(operation, column_name, options)
end
end
@@ -180,7 +191,9 @@ module ActiveRecord
eager_loading? || (includes_values.present? && (column_name || references_eager_loaded_tables?))
end
- def perform_calculation(operation, column_name)
+ def perform_calculation(operation, column_name, options = {})
+ # TODO: Remove options argument as soon we remove support to
+ # activerecord-deprecated_finders.
operation = operation.to_s.downcase
# If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 1e15bddcdf..21beed332f 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -1,3 +1,4 @@
+require 'set'
require 'active_support/concern'
require 'active_support/deprecation'
@@ -36,7 +37,14 @@ module ActiveRecord
# may vary depending on the klass of a relation, so we create a subclass of Relation
# for each different klass, and the delegations are compiled into that subclass only.
- delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
+ BLACKLISTED_ARRAY_METHODS = [
+ :compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
+ :shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
+ :keep_if, :pop, :shift, :delete_at, :compact
+ ].to_set # :nodoc:
+
+ delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, to: :to_a
+
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
:connection, :columns_hash, :to => :klass
@@ -64,7 +72,7 @@ module ActiveRecord
RUBY
else
define_method method do |*args, &block|
- scoping { @klass.send(method, *args, &block) }
+ scoping { @klass.public_send(method, *args, &block) }
end
end
end
@@ -83,13 +91,10 @@ module ActiveRecord
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
self.class.delegate_to_scoped_klass(method)
- scoping { @klass.send(method, *args, &block) }
- elsif array_delegable?(method)
- self.class.delegate method, :to => :to_a
- to_a.send(method, *args, &block)
+ scoping { @klass.public_send(method, *args, &block) }
elsif arel.respond_to?(method)
self.class.delegate method, :to => :arel
- arel.send(method, *args, &block)
+ arel.public_send(method, *args, &block)
else
super
end
@@ -109,30 +114,24 @@ module ActiveRecord
end
def respond_to?(method, include_private = false)
- super || array_delegable?(method) ||
- @klass.respond_to?(method, include_private) ||
+ super || @klass.respond_to?(method, include_private) ||
+ array_delegable?(method) ||
arel.respond_to?(method, include_private)
end
protected
def array_delegable?(method)
- defined = Array.method_defined?(method)
- if defined && method.to_s.ends_with?('!')
- ActiveSupport::Deprecation.warn(
- "Association will no longer delegate #{method} to #to_a as of Rails 4.2. You instead must first call #to_a on the association to expose the array to be acted on."
- )
- end
- defined
+ Array.method_defined?(method) && BLACKLISTED_ARRAY_METHODS.exclude?(method)
end
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
- scoping { @klass.send(method, *args, &block) }
+ scoping { @klass.public_send(method, *args, &block) }
elsif array_delegable?(method)
- to_a.send(method, *args, &block)
+ to_a.public_send(method, *args, &block)
elsif arel.respond_to?(method)
- arel.send(method, *args, &block)
+ arel.public_send(method, *args, &block)
else
super
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index c60cd27a83..1252af7635 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -55,9 +55,9 @@ module ActiveRecord
#
# For polymorphic relationships, find the foreign key and type:
# PriceEstimate.where(estimate_of: treasure)
- if klass && value.is_a?(Base) && reflection = klass.reflect_on_association(column.to_sym)
- if reflection.polymorphic?
- queries << build(table[reflection.foreign_type], value.class.base_class)
+ if klass && reflection = klass.reflect_on_association(column.to_sym)
+ if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
+ queries << build(table[reflection.foreign_type], base_class)
end
column = reflection.foreign_key
@@ -67,6 +67,18 @@ module ActiveRecord
queries
end
+ def self.polymorphic_base_class_from_value(value)
+ case value
+ when Relation
+ value.klass.base_class
+ when Array
+ val = value.compact.first
+ val.class.base_class if val.is_a?(Base)
+ when Base
+ value.class.base_class
+ end
+ end
+
def self.references(attributes)
attributes.map do |key, value|
if value.is_a?(Hash)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index cacc787eba..3d0709266a 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -427,7 +427,7 @@ module ActiveRecord
# === string
#
# A single string, without additional arguments, is passed to the query
- # constructor as a SQL fragment, and used in the where clause of the query.
+ # constructor as an SQL fragment, and used in the where clause of the query.
#
# Client.where("orders_count = '2'")
# # SELECT * from clients where orders_count = '2';
@@ -631,12 +631,11 @@ module ActiveRecord
self
end
- # Returns a chainable relation with zero records, specifically an
- # instance of the <tt>ActiveRecord::NullRelation</tt> class.
+ # Returns a chainable relation with zero records.
#
- # The returned <tt>ActiveRecord::NullRelation</tt> inherits from Relation and implements the
- # Null Object pattern. It is an object with defined null behavior and always returns an empty
- # array of records without querying the database.
+ # The returned relation implements the Null Object pattern. It is an
+ # object with defined null behavior and always returns an empty array of
+ # records without querying the database.
#
# Any subsequent condition chained to the returned relation will continue
# generating an empty relation and will not fire any query to the database.
@@ -656,7 +655,7 @@ module ActiveRecord
# when 'Reviewer'
# Post.published
# when 'Bad User'
- # Post.none # => returning [] instead breaks the previous code
+ # Post.none # It can't be chained if [] is returned.
# end
# end
#
@@ -983,8 +982,10 @@ module ActiveRecord
end
def build_select(arel, selects)
- unless selects.empty?
+ if !selects.empty?
arel.project(*selects)
+ elsif from_value
+ arel.project(Arel.star)
else
arel.project(@klass.arel_table[Arel.star])
end
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index cab8fd745a..dacaec26b7 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -100,8 +100,9 @@ module ActiveRecord
# { status: nil, group_id: 1 }
# # => "status = NULL , group_id = 1"
def sanitize_sql_hash_for_assignment(attrs, table)
+ c = connection
attrs.map do |attr, value|
- "#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value)}"
+ "#{c.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, c, columns_hash[attr.to_s])}"
end.join(', ')
end
@@ -152,8 +153,10 @@ module ActiveRecord
end
end
- def quote_bound_value(value, c = connection) #:nodoc:
- if value.respond_to?(:map) && !value.acts_like?(:string)
+ def quote_bound_value(value, c = connection, column = nil) #:nodoc:
+ if column
+ c.quote(value, column)
+ elsif value.respond_to?(:map) && !value.acts_like?(:string)
if value.respond_to?(:empty?) && value.empty?
c.quote(nil)
else
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index be7d496d15..06592eece2 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -56,11 +56,7 @@ module ActiveRecord
if options.has_key?(:config)
@current_config = options[:config]
else
- @current_config ||= if ENV['DATABASE_URL']
- database_url_config
- else
- ActiveRecord::Base.configurations[options[:env]]
- end
+ @current_config ||= ActiveRecord::Base.configurations[options[:env]]
end
end
@@ -85,10 +81,6 @@ module ActiveRecord
ActiveRecord::Base.establish_connection environment
end
- def create_database_url
- create database_url_config
- end
-
def drop(*arguments)
configuration = arguments.first
class_for_adapter(configuration['adapter']).new(*arguments).drop
@@ -107,10 +99,6 @@ module ActiveRecord
}
end
- def drop_database_url
- drop database_url_config
- end
-
def charset_current(environment = env)
charset ActiveRecord::Base.configurations[environment]
end
@@ -165,11 +153,6 @@ module ActiveRecord
private
- def database_url_config
- @database_url_config ||=
- ConnectionAdapters::ConnectionSpecification::Resolver.new(ENV["DATABASE_URL"], {}).spec.config.stringify_keys
- end
-
def class_for_adapter(adapter)
key = @tasks.keys.detect { |pattern| adapter[pattern] }
unless key
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index de5fd05468..863c3ebe4d 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -1,7 +1,7 @@
module ActiveRecord
# Returns the version of the currently loaded ActiveRecord as a Gem::Version
def self.version
- Gem::Version.new "4.1.0.beta"
+ Gem::Version.new "4.1.0.beta1"
end
module VERSION #:nodoc: