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.rb1
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb6
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/enum.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb17
-rw-r--r--activerecord/lib/active_record/railties/databases.rake6
-rw-r--r--activerecord/lib/active_record/relation.rb2
-rw-r--r--activerecord/lib/active_record/relation/merger.rb3
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb14
-rw-r--r--activerecord/lib/active_record/scoping.rb28
-rw-r--r--activerecord/lib/active_record/scoping/default.rb4
-rw-r--r--activerecord/lib/active_record/secure_token.rb25
14 files changed, 57 insertions, 61 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 462b3066ab..f6d8e8a342 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1164,6 +1164,7 @@ module ActiveRecord
# Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
# Note that this operation instantly fires update SQL without waiting for the save or update call on the
# parent object, unless the parent object is a new record.
+ # This will also run validations and callbacks of associated object(s).
# [collection.delete(object, ...)]
# Removes one or more objects from the collection by setting their foreign keys to +NULL+.
# Objects will be in addition destroyed if they're associated with <tt>dependent: :destroy</tt>,
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index a6ad09a38a..be65cf318c 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -74,9 +74,8 @@ module ActiveRecord
value = foreign_klass.base_class.name
column = klass.columns_hash[reflection.type.to_s]
- substitute = klass.connection.substitute_at(column)
binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
- constraint = constraint.and table[reflection.type].eq substitute
+ constraint = constraint.and table[reflection.type].eq(Arel::Nodes::BindParam.new)
end
joins << table.create_join(table, table.create_on(constraint), join_type)
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 0ac5e80119..c4f525336b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -297,14 +297,15 @@ module ActiveRecord
# Inserts the given fixture into the table. Overridden in adapters that require
# something beyond a simple insert (eg. Oracle).
def insert_fixture(fixture, table_name)
- columns = schema_cache.columns_hash(table_name)
+ fixture = fixture.stringify_keys
+ columns = schema_cache.columns_hash(table_name)
binds = fixture.map do |name, value|
if column = columns[name]
type = lookup_cast_type_from_column(column)
Relation::QueryAttribute.new(name, value, type)
else
- raise Fixture::FixtureError, %(table "#{table_name}" has no column named "#{name}".)
+ raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
end
end
key_list = fixture.keys.map { |name| quote_column_name(name) }
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index dce34a208f..d9b42d4283 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -311,12 +311,6 @@ module ActiveRecord
{}
end
- # Returns a bind substitution value given a bind +column+
- # NOTE: The column param is currently being used by the sqlserver-adapter
- def substitute_at(column, _unused = 0)
- Arel::Nodes::BindParam.new
- end
-
# REFERENTIAL INTEGRITY ====================================
# Override to turn off referential integrity while executing <tt>&block</tt>.
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 1250f8a3c3..475a298467 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -275,7 +275,7 @@ module ActiveRecord
def relation # :nodoc:
relation = Relation.create(self, arel_table, predicate_builder)
- if finder_needs_type_condition?
+ if finder_needs_type_condition? && !ignore_default_scope?
relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
else
relation
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 04519f4dc3..8655f68308 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -187,7 +187,7 @@ module ActiveRecord
# scope :active, -> { where status: 0 }
klass.send(:detect_enum_conflict!, name, value_method_name, true)
- klass.scope value_method_name, -> { klass.where name => value }
+ klass.scope value_method_name, -> { where(name => value) }
end
end
defined_enums[name.to_s] = enum_values
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index f699e83ab3..4a6e9c12fe 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -145,7 +145,7 @@ module ActiveRecord
class NoEnvironmentInSchemaError < MigrationError #:nodoc:
def initialize
- msg = "Environment data not found in the schema. To resolve this issue, run: \n\n\tbin/rake db:migrate"
+ msg = "Environment data not found in the schema. To resolve this issue, run: \n\n\tbin/rake db:environment:set"
if defined?(Rails.env)
super("#{msg} RAILS_ENV=#{::Rails.env}")
else
@@ -157,7 +157,7 @@ module ActiveRecord
class ProtectedEnvironmentError < ActiveRecordError #:nodoc:
def initialize(env = "production")
msg = "You are attempting to run a destructive action against your '#{env}' database\n"
- msg << "if you are sure you want to continue, run the same command with the environment variable\n"
+ msg << "If you are sure you want to continue, run the same command with the environment variable\n"
msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
super(msg)
end
@@ -165,10 +165,15 @@ module ActiveRecord
class EnvironmentMismatchError < ActiveRecordError
def initialize(current: nil, stored: nil)
- msg = "You are attempting to modify a database that was last run in #{ stored } environment.\n"
- msg << "You are running in #{ current } environment."
- msg << "if you are sure you want to continue, run the same command with the environment variable\n"
- msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
+ msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
+ msg << "You are running in `#{ current }` environment."
+ msg << "If you are sure you want to continue, first set the environment using:\n\n"
+ msg << "\tbin/rake db:environment:set"
+ if defined?(Rails.env)
+ super("#{msg} RAILS_ENV=#{::Rails.env}")
+ else
+ super(msg)
+ end
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index ead9407391..d81d6b54b3 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -1,6 +1,12 @@
require 'active_record'
db_namespace = namespace :db do
+ desc "Set the environment value for the database"
+ task "environment:set" => [:environment, :load_config] do
+ ActiveRecord::InternalMetadata.create_table
+ ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
+ end
+
task :check_protected_environments => [:environment, :load_config] do
ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 6aa490908b..032b8d4c5d 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -99,7 +99,7 @@ module ActiveRecord
end
substitutes = values.map do |(arel_attr, _)|
- [arel_attr, connection.substitute_at(klass.columns_hash[arel_attr.name])]
+ [arel_attr, Arel::Nodes::BindParam.new]
end
[substitutes, binds]
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index cb971eb255..396638d74d 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -141,6 +141,9 @@ module ActiveRecord
end
def merge_single_values
+ if relation.from_clause.empty?
+ relation.from_clause = other.from_clause
+ end
relation.lock_value ||= other.lock_value
unless other.create_with_value.blank?
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 5635b4215b..716b1e8505 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -659,8 +659,10 @@ module ActiveRecord
end
def or!(other) # :nodoc:
- unless structurally_compatible_for_or?(other)
- raise ArgumentError, 'Relation passed to #or must be structurally compatible'
+ incompatible_values = structurally_incompatible_values_for_or(other)
+
+ unless incompatible_values.empty?
+ raise ArgumentError, "Relation passed to #or must be structurally compatible. Incompatible values: #{incompatible_values}"
end
self.where_clause = self.where_clause.or(other.where_clause)
@@ -1189,10 +1191,10 @@ module ActiveRecord
end
end
- def structurally_compatible_for_or?(other)
- Relation::SINGLE_VALUE_METHODS.all? { |m| send("#{m}_value") == other.send("#{m}_value") } &&
- (Relation::MULTI_VALUE_METHODS - [:extending]).all? { |m| send("#{m}_values") == other.send("#{m}_values") } &&
- (Relation::CLAUSE_METHODS - [:having, :where]).all? { |m| send("#{m}_clause") == other.send("#{m}_clause") }
+ def structurally_incompatible_values_for_or(other)
+ Relation::SINGLE_VALUE_METHODS.reject { |m| send("#{m}_value") == other.send("#{m}_value") } +
+ (Relation::MULTI_VALUE_METHODS - [:extending]).reject { |m| send("#{m}_values") == other.send("#{m}_values") } +
+ (Relation::CLAUSE_METHODS - [:having, :where]).reject { |m| send("#{m}_clause") == other.send("#{m}_clause") }
end
def new_where_clause
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
index e395970dc6..7794af8ca4 100644
--- a/activerecord/lib/active_record/scoping.rb
+++ b/activerecord/lib/active_record/scoping.rb
@@ -11,11 +11,11 @@ module ActiveRecord
module ClassMethods
def current_scope #:nodoc:
- ScopeRegistry.value_for(:current_scope, self.to_s)
+ ScopeRegistry.value_for(:current_scope, self)
end
def current_scope=(scope) #:nodoc:
- ScopeRegistry.set_value_for(:current_scope, self.to_s, scope)
+ ScopeRegistry.set_value_for(:current_scope, self, scope)
end
# Collects attributes from scopes that should be applied when creating
@@ -53,18 +53,18 @@ module ActiveRecord
# following code:
#
# registry = ActiveRecord::Scoping::ScopeRegistry
- # registry.set_value_for(:current_scope, "Board", some_new_scope)
+ # registry.set_value_for(:current_scope, Board, some_new_scope)
#
# Now when you run:
#
- # registry.value_for(:current_scope, "Board")
+ # registry.value_for(:current_scope, Board)
#
# You will obtain whatever was defined in +some_new_scope+. The #value_for
# and #set_value_for methods are delegated to the current ScopeRegistry
# object, so the above example code can also be called as:
#
# ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
- # "Board", some_new_scope)
+ # Board, some_new_scope)
class ScopeRegistry # :nodoc:
extend ActiveSupport::PerThreadRegistry
@@ -74,16 +74,22 @@ module ActiveRecord
@registry = Hash.new { |hash, key| hash[key] = {} }
end
- # Obtains the value for a given +scope_name+ and +variable_name+.
- def value_for(scope_type, variable_name)
+ # Obtains the value for a given +scope_type+ and +model+.
+ def value_for(scope_type, model)
raise_invalid_scope_type!(scope_type)
- @registry[scope_type][variable_name]
+ klass = model
+ base = model.base_class
+ while klass <= base
+ value = @registry[scope_type][klass.name]
+ return value if value
+ klass = klass.superclass
+ end
end
- # Sets the +value+ for a given +scope_type+ and +variable_name+.
- def set_value_for(scope_type, variable_name, value)
+ # Sets the +value+ for a given +scope_type+ and +model+.
+ def set_value_for(scope_type, model, value)
raise_invalid_scope_type!(scope_type)
- @registry[scope_type][variable_name] = value
+ @registry[scope_type][model.name] = value
end
private
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index cdcb73382f..8baf3b8044 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -122,11 +122,11 @@ module ActiveRecord
end
def ignore_default_scope? # :nodoc:
- ScopeRegistry.value_for(:ignore_default_scope, self)
+ ScopeRegistry.value_for(:ignore_default_scope, base_class)
end
def ignore_default_scope=(ignore) # :nodoc:
- ScopeRegistry.set_value_for(:ignore_default_scope, self, ignore)
+ ScopeRegistry.set_value_for(:ignore_default_scope, base_class, ignore)
end
# The ignore_default_scope flag is used to prevent an infinite recursion
diff --git a/activerecord/lib/active_record/secure_token.rb b/activerecord/lib/active_record/secure_token.rb
index f10f7a1515..8abda2ac49 100644
--- a/activerecord/lib/active_record/secure_token.rb
+++ b/activerecord/lib/active_record/secure_token.rb
@@ -20,35 +20,14 @@ module ActiveRecord
#
# <tt>SecureRandom::base58</tt> is used to generate the 24-character unique token, so collisions are highly unlikely.
#
- # A secure token can also be only created given a condition, for example if a user should only have an
- # auto-generated invitation token if the user was invited:
- #
- # # Schema: User(token:string, invited:boolean)
- # class User < ActiveRecord::Base
- # has_secure_token if: :invited?
- # end
- #
- # user = User.new(invited: true)
- # user.save
- # user.token # => "pX27zsMN2ViQKta1bGfLmVJE"
- #
- # user = User.new(invited: false)
- # user.save
- # user.token # => nil
- #
- # The secure token creation supports all the options a `before_create` does - like +:if+ and +:unless+.
- #
# Note that it's still possible to generate a race condition in the database in the same way that
# {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
# You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
- def has_secure_token(attribute = :token, **before_create_options)
+ def has_secure_token(attribute = :token)
# Load securerandom only when has_secure_token is used.
require 'active_support/core_ext/securerandom'
-
define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
- before_create(before_create_options) do
- self.send("#{attribute}=", self.class.generate_unique_secure_token) unless self.send("#{attribute}?")
- end
+ before_create { self.send("#{attribute}=", self.class.generate_unique_secure_token) unless self.send("#{attribute}?")}
end
def generate_unique_secure_token