aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md90
-rw-r--r--activerecord/README.rdoc3
-rw-r--r--activerecord/lib/active_record/associations/association.rb9
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb6
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb12
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb12
-rw-r--r--activerecord/lib/active_record/callbacks.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb49
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb17
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb35
-rw-r--r--activerecord/lib/active_record/enum.rb2
-rw-r--r--activerecord/lib/active_record/inheritance.rb21
-rw-r--r--activerecord/lib/active_record/migration.rb18
-rw-r--r--activerecord/lib/active_record/model_schema.rb3
-rw-r--r--activerecord/lib/active_record/persistence.rb13
-rw-r--r--activerecord/lib/active_record/reflection.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb5
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb16
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb28
-rw-r--r--activerecord/lib/active_record/relation/record_fetch_warning.rb2
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb10
-rw-r--r--activerecord/lib/active_record/schema_migration.rb2
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb4
-rw-r--r--activerecord/lib/active_record/touch_later.rb16
-rw-r--r--activerecord/lib/active_record/transactions.rb26
-rw-r--r--activerecord/lib/active_record/type_caster.rb2
-rw-r--r--activerecord/lib/active_record/type_caster/connection.rb2
-rw-r--r--activerecord/lib/active_record/type_caster/map.rb2
-rw-r--r--activerecord/lib/active_record/validations/associated.rb10
-rw-r--r--activerecord/test/cases/adapter_test.rb25
-rw-r--r--activerecord/test/cases/adapters/mysql/active_schema_test.rb10
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb18
-rw-r--r--activerecord/test/cases/adapters/mysql/schema_test.rb8
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb10
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb18
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb24
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb152
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb26
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb12
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb21
-rw-r--r--activerecord/test/cases/attributes_test.rb13
-rw-r--r--activerecord/test/cases/base_test.rb13
-rw-r--r--activerecord/test/cases/connection_management_test.rb6
-rw-r--r--activerecord/test/cases/date_time_precision_test.rb37
-rw-r--r--activerecord/test/cases/finder_test.rb91
-rw-r--r--activerecord/test/cases/forbidden_attributes_protection_test.rb72
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/inheritance_test.rb45
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb54
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb4
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb24
-rw-r--r--activerecord/test/cases/migration/postgresql_geometric_types_test.rb93
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb2
-rw-r--r--activerecord/test/cases/migration/rename_table_test.rb4
-rw-r--r--activerecord/test/cases/migration_test.rb38
-rw-r--r--activerecord/test/cases/migrator_test.rb4
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb35
-rw-r--r--activerecord/test/cases/persistence_test.rb13
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb2
-rw-r--r--activerecord/test/cases/primary_keys_test.rb2
-rw-r--r--activerecord/test/cases/relation/delegation_test.rb8
-rw-r--r--activerecord/test/cases/relation_test.rb7
-rw-r--r--activerecord/test/cases/sanitize_test.rb94
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb32
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb4
-rw-r--r--activerecord/test/cases/time_precision_test.rb34
-rw-r--r--activerecord/test/cases/touch_later_test.rb8
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb6
-rw-r--r--activerecord/test/cases/validations/association_validation_test.rb12
-rw-r--r--activerecord/test/cases/view_test.rb14
-rw-r--r--activerecord/test/models/bulb.rb7
-rw-r--r--activerecord/test/models/car.rb1
-rw-r--r--activerecord/test/schema/schema.rb4
-rw-r--r--activerecord/test/support/schema_dumping_helper.rb2
83 files changed, 1036 insertions, 599 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index c0e3840b40..56a3232ee9 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,72 @@
+* Introduce after_{create,update,delete}_commit callbacks.
+
+ Before:
+
+ after_commit :add_to_index_later, on: :create
+ after_commit :update_in_index_later, on: :update
+ after_commit :remove_from_index_later, on: :destroy
+
+ After:
+
+ after_create_commit :add_to_index_later
+ after_update_commit :update_in_index_later
+ after_destroy_commit :remove_from_index_later
+
+ Fixes #22515.
+
+ *Genadi Samokovarov*
+
+* Respect the column default values for `inheritance_column` when
+ instantiating records through the base class.
+
+ Fixes #17121.
+
+ Example:
+
+ # The schema of BaseModel has `t.string :type, default: 'SubType'`
+ subtype = BaseModel.new
+ assert_equals SubType, subtype.class
+
+ *Kuldeep Aggarwal*
+
+* Fix `rake db:structure:dump` on Postgres when multiple schemas are used.
+
+ Fixes #22346.
+
+ *Nick Muerdter*, *ckoenig*
+
+* Add schema dumping support for PostgreSQL geometric data types.
+
+ *Ryuta Kamizono*
+
+* Except keys of `build_record`'s argument from `create_scope` in `initialize_attributes`.
+
+ Fixes #21893.
+
+ *Yuichiro Kaneko*
+
+* Deprecate `connection.tables` on the SQLite3 and MySQL adapters.
+ Also deprecate passing arguments to `#tables`.
+ And deprecate `table_exists?`.
+
+ The `#tables` method of some adapters (mysql, mysql2, sqlite3) would return
+ both tables and views while others (postgresql) just return tables. To make
+ their behavior consistent, `#tables` will return only tables in the future.
+
+ The `#table_exists?` method would check both tables and views. To make
+ their behavior consistent with `#tables`, `#table_exists?` will check only
+ tables in the future.
+
+ *Yuichiro Kaneko*
+
+* Improve support for non Active Record objects on `validates_associated`
+
+ Skipping `marked_for_destruction?` when the associated object does not responds
+ to it make easier to validate virtual associations built on top of Active Model
+ objects and/or serialized objects that implement a `valid?` instance method.
+
+ *Kassio Borges*, *Lucas Mazza*
+
* Change connection management middleware to return a new response with
a body proxy, rather than mutating the original.
@@ -179,9 +248,9 @@
Example:
- config.generators do |g|
- g.orm :active_record, primary_key_type: :uuid
- end
+ config.generators do |g|
+ g.orm :active_record, primary_key_type: :uuid
+ end
*Jon McCartie*
@@ -257,10 +326,10 @@
To load the fixtures file `accounts.yml` as the `User` model, use:
- _fixture:
- model_class: User
- david:
- name: David
+ _fixture:
+ model_class: User
+ david:
+ name: David
Fixes #9516.
@@ -381,6 +450,13 @@
*Wojciech Wnętrzak*
+* Instantiating an AR model with `ActionController::Parameters` now raises
+ an `ActiveModel::ForbiddenAttributesError` if the parameters include a
+ `type` field that has not been explicitly permitted. Previously, the
+ `type` field was simply ignored in the same situation.
+
+ *Prem Sichanugrist*
+
* PostgreSQL, `create_schema`, `drop_schema` and `rename_table` now quote
schema names.
diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc
index 3eac8cc422..7eb4e9db1a 100644
--- a/activerecord/README.rdoc
+++ b/activerecord/README.rdoc
@@ -188,7 +188,7 @@ Admit the Database:
The latest version of Active Record can be installed with RubyGems:
- % gem install activerecord
+ $ gem install activerecord
Source code can be downloaded as part of the Rails project on GitHub:
@@ -215,4 +215,3 @@ Bug reports can be filed for the Ruby on Rails project here:
Feature requests should be discussed on the rails-core mailing list here:
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
-
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index c7b396f3d4..d64ab64c99 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -163,9 +163,12 @@ module ActiveRecord
@reflection = @owner.class._reflect_on_association(reflection_name)
end
- def initialize_attributes(record) #:nodoc:
+ def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
+ except_from_scope_attributes ||= {}
skip_assign = [reflection.foreign_key, reflection.type].compact
- attributes = create_scope.except(*(record.changed - skip_assign))
+ assigned_keys = record.changed
+ assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
+ attributes = create_scope.except(*(assigned_keys - skip_assign))
record.assign_attributes(attributes)
set_inverse_instance(record)
end
@@ -248,7 +251,7 @@ module ActiveRecord
def build_record(attributes)
reflection.build_association(attributes) do |record|
- initialize_attributes(record)
+ initialize_attributes(record, attributes)
end
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index dae468ba54..f02d146e89 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -106,8 +106,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
touch = reflection.options[:touch]
callback = lambda { |record|
- touch_method = touching_delayed_records? ? :touch : :touch_later
- BelongsTo.touch_record(record, foreign_key, n, touch, touch_method)
+ BelongsTo.touch_record(record, foreign_key, n, touch, belongs_to_touch_method)
}
model.after_save callback, if: :changed?
@@ -116,8 +115,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
def self.add_destroy_callbacks(model, reflection)
- name = reflection.name
- model.after_destroy lambda { |o| o.association(name).handle_dependency }
+ model.after_destroy lambda { |o| o.association(reflection.name).handle_dependency }
end
def self.define_validations(model, reflection)
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index f32dddb8f0..473b80a658 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -414,12 +414,16 @@ module ActiveRecord
def replace_on_target(record, index, skip_callbacks)
callback(:before_add, record) unless skip_callbacks
+
+ was_loaded = loaded?
yield(record) if block_given?
- if index
- @target[index] = record
- else
- @target << record
+ unless !was_loaded && loaded?
+ if index
+ @target[index] = record
+ else
+ @target << record
+ end
end
callback(:after_add, record) unless skip_callbacks
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index 9f183c3e7e..0e4e951269 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -32,7 +32,7 @@ module ActiveRecord
@alias_cache[node][column]
end
- class Table < Struct.new(:node, :columns)
+ class Table < Struct.new(:node, :columns) # :nodoc:
def table
Arel::Nodes::TableAlias.new node.table, node.aliased_table_name
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 4ae585d3f5..423a93964e 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -191,6 +191,18 @@ module ActiveRecord
end
end
+ # Returns true if the given attribute exists, otherwise false.
+ #
+ # class Person < ActiveRecord::Base
+ # end
+ #
+ # Person.has_attribute?('name') # => true
+ # Person.has_attribute?(:age) # => true
+ # Person.has_attribute?(:nothing) # => false
+ def has_attribute?(attr_name)
+ attribute_types.key?(attr_name.to_s)
+ end
+
# Returns the column object for the named attribute.
# Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
# named attribute does not exist.
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index cfd8cbda67..4058affec3 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -208,12 +208,12 @@ module ActiveRecord
#
# Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
# callback (+log_children+ in this case) should be executed before the children get destroyed by the
- # <tt>dependent: destroy</tt> option.
+ # <tt>dependent: :destroy</tt> option.
#
# Let's look at the code below:
#
# class Topic < ActiveRecord::Base
- # has_many :children, dependent: destroy
+ # has_many :children, dependent: :destroy
#
# before_destroy :log_children
#
@@ -228,7 +228,7 @@ module ActiveRecord
# You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
#
# class Topic < ActiveRecord::Base
- # has_many :children, dependent: destroy
+ # has_many :children, dependent: :destroy
#
# before_destroy :log_children, prepend: true
#
@@ -238,7 +238,7 @@ module ActiveRecord
# end
# end
#
- # This way, the +before_destroy+ gets executed before the <tt>dependent: destroy</tt> is called, and the data is still available.
+ # This way, the +before_destroy+ gets executed before the <tt>dependent: :destroy</tt> is called, and the data is still available.
#
# == \Transactions
#
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 486b7b6d25..ccd2899489 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -197,7 +197,7 @@ module ActiveRecord
elapsed = Time.now - t0
if elapsed >= timeout
- msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
+ msg = 'could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use' %
[timeout, elapsed]
raise ConnectionTimeoutError, msg
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 abf0124562..159cbcb85a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -206,14 +206,13 @@ module ActiveRecord
include ColumnMethods
attr_accessor :indexes
- attr_reader :name, :temporary, :options, :as, :foreign_keys, :native
+ attr_reader :name, :temporary, :options, :as, :foreign_keys
- def initialize(types, name, temporary, options, as = nil)
+ def initialize(name, temporary, options, as = nil)
@columns_hash = {}
@indexes = {}
@foreign_keys = {}
@primary_keys = nil
- @native = types
@temporary = temporary
@options = options
@as = as
@@ -362,11 +361,8 @@ module ActiveRecord
def new_column_definition(name, type, options) # :nodoc:
type = aliased_types(type.to_s, type)
column = create_column_definition name, type
- limit = options.fetch(:limit) do
- native[type][:limit] if native[type].is_a?(Hash)
- end
- column.limit = limit
+ column.limit = options[:limit]
column.precision = options[:precision]
column.scale = options[:scale]
column.default = options[:default]
@@ -627,11 +623,6 @@ module ActiveRecord
def foreign_key_exists?(*args) # :nodoc:
@base.foreign_key_exists?(name, *args)
end
-
- private
- def native
- @base.native_database_types
- end
end
end
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 d5f8dbc8fc..b50d28862c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -263,7 +263,7 @@ module ActiveRecord
yield td if block_given?
- if options[:force] && table_exists?(table_name)
+ if options[:force] && data_source_exists?(table_name)
drop_table(table_name, options)
end
@@ -1088,7 +1088,7 @@ module ActiveRecord
if index_name.length > max_index_length
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
end
- if table_exists?(table_name) && index_name_exists?(table_name, index_name, false)
+ if data_source_exists?(table_name) && index_name_exists?(table_name, index_name, false)
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
end
index_columns = quoted_columns_for_index(column_names, options).join(", ")
@@ -1168,7 +1168,7 @@ module ActiveRecord
private
def create_table_definition(name, temporary = false, options = nil, as = nil)
- TableDefinition.new native_database_types, name, temporary, options, as
+ TableDefinition.new(name, temporary, options, as)
end
def create_alter_table(name)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 55910865e5..4b6912c616 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -95,14 +95,15 @@ module ActiveRecord
attr_reader :prepared_statements
- def initialize(connection, logger = nil, pool = nil) #:nodoc:
+ def initialize(connection, logger = nil, config = {}) # :nodoc:
super()
@connection = connection
@owner = nil
@instrumenter = ActiveSupport::Notifications.instrumenter
@logger = logger
- @pool = pool
+ @config = config
+ @pool = nil
@schema_cache = SchemaCache.new self
@visitor = nil
@prepared_statements = false
@@ -289,14 +290,14 @@ module ActiveRecord
# locks
#
# Return true if we got the lock, otherwise false
- def get_advisory_lock(key) # :nodoc:
+ def get_advisory_lock(lock_id) # :nodoc:
end
# This is meant to be implemented by the adapters that support advisory
# locks.
#
# Return true if we released the lock, otherwise false
- def release_advisory_lock(key) # :nodoc:
+ def release_advisory_lock(lock_id) # :nodoc:
end
# A list of extensions, to be filled in by adapters that support them.
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 3e3bbc267b..25ba42e5c9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -134,9 +134,7 @@ module ActiveRecord
time: { name: "time" },
date: { name: "date" },
binary: { name: "blob" },
- blob: { name: "blob" },
boolean: { name: "tinyint", limit: 1 },
- bigint: { name: "bigint" },
json: { name: "json" },
}
@@ -145,8 +143,7 @@ module ActiveRecord
# FIXME: Make the first parameter more similar for the two adapters
def initialize(connection, logger, connection_options, config)
- super(connection, logger)
- @connection_options, @config = connection_options, config
+ super(connection, logger, config)
@quoted_column_names, @quoted_table_names = {}, {}
@visitor = Arel::Visitors::MySQL.new self
@@ -226,12 +223,12 @@ module ActiveRecord
version >= '5.0.0'
end
- def get_advisory_lock(key, timeout = 0) # :nodoc:
- select_value("SELECT GET_LOCK('#{key}', #{timeout});").to_s == '1'
+ def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
+ select_value("SELECT GET_LOCK('#{lock_name}', #{timeout});").to_s == '1'
end
- def release_advisory_lock(key) # :nodoc:
- select_value("SELECT RELEASE_LOCK('#{key}')").to_s == '1'
+ def release_advisory_lock(lock_name) # :nodoc:
+ select_value("SELECT RELEASE_LOCK('#{lock_name}')").to_s == '1'
end
def native_database_types
@@ -497,18 +494,43 @@ module ActiveRecord
end
def tables(name = nil) # :nodoc:
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ #tables currently returns both tables and views.
+ This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
+ Use #data_sources instead.
+ MSG
+
+ if name
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing arguments to #tables is deprecated without replacement.
+ MSG
+ end
+
+ data_sources
+ end
+
+ def data_sources
sql = "SELECT table_name FROM information_schema.tables "
sql << "WHERE table_schema = #{quote(@config[:database])}"
select_values(sql, 'SCHEMA')
end
- alias data_sources tables
def truncate(table_name, name = nil)
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
end
def table_exists?(table_name)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ #table_exists? currently checks both tables and views.
+ This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
+ Use #data_source_exists? instead.
+ MSG
+
+ data_source_exists?(table_name)
+ end
+
+ def data_source_exists?(table_name)
return false unless table_name.present?
schema, name = table_name.to_s.split('.', 2)
@@ -519,7 +541,6 @@ module ActiveRecord
select_values(sql, 'SCHEMA').any?
end
- alias data_source_exists? table_exists?
def views # :nodoc:
select_values("SHOW FULL TABLES WHERE table_type = 'VIEW'", 'SCHEMA')
@@ -566,10 +587,8 @@ module ActiveRecord
sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
execute_and_free(sql, 'SCHEMA') do |result|
each_hash(result).map do |field|
- field_name = set_field_encoding(field[:Field])
- sql_type = field[:Type]
- type_metadata = fetch_type_metadata(sql_type, field[:Extra])
- new_column(field_name, field[:Default], type_metadata, field[:Null] == "YES", nil, field[:Collation])
+ type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
+ new_column(field[:Field], field[:Default], type_metadata, field[:Null] == "YES", nil, field[:Collation])
end
end
end
@@ -1019,7 +1038,7 @@ module ActiveRecord
end
def create_table_definition(name, temporary = false, options = nil, as = nil) # :nodoc:
- MySQL::TableDefinition.new(native_database_types, name, temporary, options, as)
+ MySQL::TableDefinition.new(name, temporary, options, as)
end
def integer_to_sql(limit) # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 773ecbe126..7ca597859d 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -16,8 +16,7 @@ module ActiveRecord
end
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)
+ ConnectionAdapters::Mysql2Adapter.new(client, logger, nil, config)
rescue Mysql2::Error => error
if error.message.include?("Unknown database")
raise ActiveRecord::NoDatabaseError
@@ -185,10 +184,6 @@ module ActiveRecord
def full_version
@full_version ||= @connection.server_info[:version]
end
-
- def set_field_encoding field_name
- field_name
- end
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 89d18ee14e..76f1b91e6b 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -82,6 +82,7 @@ module ActiveRecord
super
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
@client_encoding = nil
+ @connection_options = connection_options
connect
end
@@ -104,6 +105,11 @@ module ActiveRecord
end
end
+ def new_column(field, default, sql_type_metadata = nil, null = true, default_function = nil, collation = nil) # :nodoc:
+ field = set_field_encoding(field)
+ super
+ end
+
def error_number(exception) # :nodoc:
exception.errno if exception.respond_to?(:errno)
end
@@ -463,7 +469,7 @@ module ActiveRecord
@full_version ||= @connection.server_info
end
- def set_field_encoding field_name
+ def set_field_encoding(field_name)
field_name.force_encoding(client_encoding)
if internal_enc = Encoding.default_internal
field_name = field_name.encode!(internal_enc)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index aaf5b2898b..a48d64f7bd 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -70,6 +70,12 @@ module ActiveRecord
# Returns the list of all tables in the schema search path.
def tables(name = nil)
+ if name
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing arguments to #tables is deprecated without replacement.
+ MSG
+ end
+
select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", 'SCHEMA')
end
@@ -87,6 +93,16 @@ module ActiveRecord
# If the schema is not specified as part of +name+ then it will only find tables within
# the current schema search path (regardless of permissions to access tables in other schemas)
def table_exists?(name)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ #table_exists? currently checks both tables and views.
+ This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
+ Use #data_source_exists? instead.
+ MSG
+
+ data_source_exists?(name)
+ end
+
+ def data_source_exists?(name)
name = Utils.extract_schema_qualified_name(name.to_s)
return false unless name.identifier
@@ -99,7 +115,6 @@ module ActiveRecord
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
SQL
end
- alias data_source_exists? table_exists?
def views # :nodoc:
select_values(<<-SQL, 'SCHEMA')
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index ed6ab8235f..aa43854d01 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -72,7 +72,6 @@ module ActiveRecord
NATIVE_DATABASE_TYPES = {
primary_key: "serial primary key",
- bigserial: "bigserial",
string: { name: "character varying" },
text: { name: "text" },
integer: { name: "integer" },
@@ -89,7 +88,6 @@ module ActiveRecord
int8range: { name: "int8range" },
binary: { name: "bytea" },
boolean: { name: "boolean" },
- bigint: { name: "bigint" },
xml: { name: "xml" },
tsvector: { name: "tsvector" },
hstore: { name: "hstore" },
@@ -102,6 +100,12 @@ module ActiveRecord
ltree: { name: "ltree" },
citext: { name: "citext" },
point: { name: "point" },
+ line: { name: "line" },
+ lseg: { name: "lseg" },
+ box: { name: "box" },
+ path: { name: "path" },
+ polygon: { name: "polygon" },
+ circle: { name: "circle" },
bit: { name: "bit" },
bit_varying: { name: "bit varying" },
money: { name: "money" },
@@ -188,7 +192,7 @@ module ActiveRecord
# Initializes and connects a PostgreSQL adapter.
def initialize(connection, logger, connection_parameters, config)
- super(connection, logger)
+ super(connection, logger, config)
@visitor = Arel::Visitors::PostgreSQL.new self
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@@ -198,7 +202,7 @@ module ActiveRecord
@prepared_statements = false
end
- @connection_parameters, @config = connection_parameters, config
+ @connection_parameters = connection_parameters
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
@local_tz = nil
@@ -306,18 +310,18 @@ module ActiveRecord
postgresql_version >= 90300
end
- def get_advisory_lock(key) # :nodoc:
- unless key.is_a?(Integer) && key.bit_length <= 63
- raise(ArgumentError, "Postgres requires advisory lock keys to be a signed 64 bit integer")
+ def get_advisory_lock(lock_id) # :nodoc:
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
+ raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
end
- select_value("SELECT pg_try_advisory_lock(#{key});")
+ select_value("SELECT pg_try_advisory_lock(#{lock_id});")
end
- def release_advisory_lock(key) # :nodoc:
- unless key.is_a?(Integer) && key.bit_length <= 63
- raise(ArgumentError, "Postgres requires advisory lock keys to be a signed 64 bit integer")
+ def release_advisory_lock(lock_id) # :nodoc:
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
+ raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
end
- select_value("SELECT pg_advisory_unlock(#{key})")
+ select_value("SELECT pg_advisory_unlock(#{lock_id})")
end
def enable_extension(name)
@@ -457,15 +461,15 @@ module ActiveRecord
m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
m.register_type 'citext', OID::SpecializedString.new(:citext)
m.register_type 'ltree', OID::SpecializedString.new(:ltree)
+ m.register_type 'line', OID::SpecializedString.new(:line)
+ m.register_type 'lseg', OID::SpecializedString.new(:lseg)
+ m.register_type 'box', OID::SpecializedString.new(:box)
+ m.register_type 'path', OID::SpecializedString.new(:path)
+ m.register_type 'polygon', OID::SpecializedString.new(:polygon)
+ m.register_type 'circle', OID::SpecializedString.new(:circle)
# FIXME: why are we keeping these types as strings?
m.alias_type 'interval', 'varchar'
- m.alias_type 'path', 'varchar'
- m.alias_type 'line', 'varchar'
- m.alias_type 'polygon', 'varchar'
- m.alias_type 'circle', 'varchar'
- m.alias_type 'lseg', 'varchar'
- m.alias_type 'box', 'varchar'
register_class_with_precision m, 'time', Type::Time
register_class_with_precision m, 'timestamp', OID::DateTime
@@ -736,7 +740,7 @@ module ActiveRecord
end
def create_table_definition(name, temporary = false, options = nil, as = nil) # :nodoc:
- PostgreSQL::TableDefinition.new native_database_types, name, temporary, options, as
+ PostgreSQL::TableDefinition.new(name, temporary, options, as)
end
def can_perform_case_insensitive_comparison_for?(column)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 32fe275bfb..163cbb875f 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -78,11 +78,10 @@ module ActiveRecord
end
def initialize(connection, logger, connection_options, config)
- super(connection, logger)
+ super(connection, logger, config)
@active = nil
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
- @config = config
@visitor = Arel::Visitors::SQLite.new self
@quoted_column_names = {}
@@ -130,6 +129,10 @@ module ActiveRecord
true
end
+ def supports_datetime_with_precision?
+ true
+ end
+
def active?
@active != false
end
@@ -312,11 +315,36 @@ module ActiveRecord
# SCHEMA STATEMENTS ========================================
def tables(name = nil) # :nodoc:
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ #tables currently returns both tables and views.
+ This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
+ Use #data_sources instead.
+ MSG
+
+ if name
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing arguments to #tables is deprecated without replacement.
+ MSG
+ end
+
+ data_sources
+ end
+
+ def data_sources
select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", 'SCHEMA')
end
- alias data_sources tables
def table_exists?(table_name)
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ #table_exists? currently checks both tables and views.
+ This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
+ Use #data_source_exists? instead.
+ MSG
+
+ data_source_exists?(table_name)
+ end
+
+ def data_source_exists?(table_name)
return false unless table_name.present?
sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
@@ -324,7 +352,6 @@ module ActiveRecord
select_values(sql, 'SCHEMA').any?
end
- alias data_source_exists? table_exists?
def views # :nodoc:
select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", 'SCHEMA')
diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb
index 8fba6fcc35..7ded96f8fb 100644
--- a/activerecord/lib/active_record/enum.rb
+++ b/activerecord/lib/active_record/enum.rb
@@ -104,7 +104,7 @@ module ActiveRecord
super
end
- class EnumType < Type::Value
+ class EnumType < Type::Value # :nodoc:
def initialize(name, mapping)
@name = name
@mapping = mapping
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 589c70db0d..6259c4cd33 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -51,8 +51,8 @@ module ActiveRecord
end
attrs = args.first
- if subclass_from_attributes?(attrs)
- subclass = subclass_from_attributes(attrs)
+ if has_attribute?(inheritance_column)
+ subclass = subclass_from_attributes(attrs) || subclass_from_attributes(column_defaults)
end
if subclass && subclass != self
@@ -163,7 +163,7 @@ module ActiveRecord
end
def using_single_table_inheritance?(record)
- record[inheritance_column].present? && columns_hash.include?(inheritance_column)
+ record[inheritance_column].present? && has_attribute?(inheritance_column)
end
def find_sti_class(type_name)
@@ -195,17 +195,14 @@ module ActiveRecord
# Detect the subclass from the inheritance column of attrs. If the inheritance column value
# is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
- # If this is a StrongParameters hash, and access to inheritance_column is not permitted,
- # this will ignore the inheritance column and return nil
- def subclass_from_attributes?(attrs)
- attribute_names.include?(inheritance_column) && attrs.is_a?(Hash)
- end
-
def subclass_from_attributes(attrs)
- subclass_name = attrs.with_indifferent_access[inheritance_column]
+ attrs = attrs.to_h if attrs.respond_to?(:permitted?)
+ if attrs.is_a?(Hash)
+ subclass_name = attrs.with_indifferent_access[inheritance_column]
- if subclass_name.present?
- find_sti_class(subclass_name)
+ if subclass_name.present?
+ find_sti_class(subclass_name)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 1e870d4c44..ca2537cdc3 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -967,10 +967,12 @@ module ActiveRecord
end
def get_all_versions(connection = Base.connection)
- if connection.table_exists?(schema_migrations_table_name)
- SchemaMigration.all.map { |x| x.version.to_i }.sort
- else
- []
+ ActiveSupport::Deprecation.silence do
+ if connection.table_exists?(schema_migrations_table_name)
+ SchemaMigration.all.map { |x| x.version.to_i }.sort
+ else
+ []
+ end
end
end
@@ -1200,17 +1202,17 @@ module ActiveRecord
end
def with_advisory_lock
- key = generate_migrator_advisory_lock_key
- got_lock = Base.connection.get_advisory_lock(key)
+ lock_id = generate_migrator_advisory_lock_id
+ got_lock = Base.connection.get_advisory_lock(lock_id)
raise ConcurrentMigrationError unless got_lock
load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
yield
ensure
- Base.connection.release_advisory_lock(key) if got_lock
+ Base.connection.release_advisory_lock(lock_id) if got_lock
end
MIGRATOR_SALT = 2053462845
- def generate_migrator_advisory_lock_key
+ def generate_migrator_advisory_lock_id
db_name_hash = Zlib.crc32(Base.connection.current_database)
MIGRATOR_SALT * db_name_hash
end
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index a9bd094a66..e3f304b0af 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -339,6 +339,9 @@ module ActiveRecord
@columns = nil
@columns_hash = nil
@attribute_names = nil
+ direct_descendants.each do |descendant|
+ descendant.send(:reload_schema_from_cache)
+ end
end
# Guesses the table name, but does not decorate it with prefix and suffix information.
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 94316d5249..9e566031b8 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -298,6 +298,7 @@ module ActiveRecord
# * \Validations are skipped.
# * \Callbacks are skipped.
# * +updated_at+/+updated_on+ are not updated.
+ # * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
#
# This method raises an ActiveRecord::ActiveRecordError when called on new
# objects, or when at least one of the attributes is marked as readonly.
@@ -358,6 +359,14 @@ module ActiveRecord
# if the predicate returns +true+ the attribute will become +false+. This
# method toggles directly the underlying value without calling any setter.
# Returns +self+.
+ #
+ # Example:
+ #
+ # user = User.first
+ # user.banned? # => false
+ # user.toggle(:banned)
+ # user.banned? # => true
+ #
def toggle(attribute)
self[attribute] = !public_send("#{attribute}?")
self
@@ -558,5 +567,9 @@ module ActiveRecord
ensure
@_association_destroy_exception = nil
end
+
+ def belongs_to_touch_method
+ :touch
+ end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 5b9d45d871..a549b28f16 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -371,7 +371,7 @@ module ActiveRecord
end
def foreign_key
- @foreign_key ||= options[:foreign_key] || derive_foreign_key
+ @foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
end
def association_foreign_key
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index f100476374..2cf19c76c5 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -347,9 +347,8 @@ module ActiveRecord
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
# statement and sends it straight to the database. It does not instantiate the involved models and it does not
- # trigger Active Record callbacks or validations. Values passed to #update_all will not go through
- # Active Record's type-casting behavior. It should receive only values that can be passed as-is to the SQL
- # database.
+ # trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
+ # Active Record's normal type casting and serialization.
#
# ==== Parameters
#
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 27de313d05..e4e5d63006 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -36,13 +36,8 @@ 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.
- BLACKLISTED_ARRAY_METHODS = [
- :compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
- :shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
- :keep_if, :pop, :shift, :delete_at, :select!
- ].to_set # :nodoc:
-
- delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a
+ delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join,
+ :[], :&, :|, :+, :-, :sample, :shuffle, :reverse, :compact, to: :to_a
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
:connection, :columns_hash, :to => :klass
@@ -114,21 +109,14 @@ module ActiveRecord
def respond_to?(method, include_private = false)
super || @klass.respond_to?(method, include_private) ||
- array_delegable?(method) ||
arel.respond_to?(method, include_private)
end
protected
- def array_delegable?(method)
- Array.method_defined?(method) && BLACKLISTED_ARRAY_METHODS.exclude?(method)
- end
-
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
scoping { @klass.public_send(method, *args, &block) }
- elsif array_delegable?(method)
- to_a.public_send(method, *args, &block)
elsif arel.respond_to?(method)
arel.public_send(method, *args, &block)
else
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 2dc52982c9..dbecb842b5 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -13,6 +13,8 @@ module ActiveRecord
# WhereChain objects act as placeholder for queries in which #where does not have any parameter.
# In this case, #where must be chained with #not to return a new relation.
class WhereChain
+ include ActiveModel::ForbiddenAttributesProtection
+
def initialize(scope)
@scope = scope
end
@@ -41,6 +43,8 @@ module ActiveRecord
# User.where.not(name: "Jon", role: "admin")
# # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
def not(opts, *rest)
+ opts = sanitize_forbidden_attributes(opts)
+
where_clause = @scope.send(:where_clause_factory).build(opts, rest)
@scope.references!(PredicateBuilder.references(opts)) if Hash === opts
@@ -407,10 +411,30 @@ module ActiveRecord
self
end
- # Performs a joins on +args+:
+ # Performs a joins on +args+. The given symbol(s) should match the name of
+ # the association(s).
#
# User.joins(:posts)
- # # SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
+ # # SELECT "users".*
+ # # FROM "users"
+ # # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
+ #
+ # Multiple joins:
+ #
+ # User.joins(:posts, :account)
+ # # SELECT "users".*
+ # # FROM "users"
+ # # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
+ # # INNER JOIN "accounts" ON "accounts"."id" = "users"."account_id"
+ #
+ # Nested joins:
+ #
+ # User.joins(posts: [:comments])
+ # # SELECT "users".*
+ # # FROM "users"
+ # # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
+ # # INNER JOIN "comments" "comments_posts"
+ # # ON "comments_posts"."post_id" = "posts"."id"
#
# You can use strings in order to customize your joins:
#
diff --git a/activerecord/lib/active_record/relation/record_fetch_warning.rb b/activerecord/lib/active_record/relation/record_fetch_warning.rb
index 14e1bf89fa..0a1814b3dd 100644
--- a/activerecord/lib/active_record/relation/record_fetch_warning.rb
+++ b/activerecord/lib/active_record/relation/record_fetch_warning.rb
@@ -23,11 +23,13 @@ module ActiveRecord
end
end
+ # :stopdoc:
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
payload = args.last
QueryRegistry.queries << payload[:sql]
end
+ # :startdoc:
class QueryRegistry # :nodoc:
extend ActiveSupport::PerThreadRegistry
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 5c3318651a..67d7f83cb4 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -12,6 +12,7 @@ module ActiveRecord
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
# Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
+ #
# Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
# # Performs a single join query with both where conditions.
#
@@ -37,11 +38,14 @@ module ActiveRecord
end
def merge!(other) # :nodoc:
- if !other.is_a?(Relation) && other.respond_to?(:to_proc)
+ if other.is_a?(Hash)
+ Relation::HashMerger.new(self, other).merge
+ elsif other.is_a?(Relation)
+ Relation::Merger.new(self, other).merge
+ elsif other.respond_to?(:to_proc)
instance_exec(&other)
else
- klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
- klass.new(self, other).merge
+ raise ArgumentError, "#{other.inspect} is not an ActiveRecord::Relation"
end
end
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index b384529e75..51b9b17395 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -21,7 +21,7 @@ module ActiveRecord
end
def table_exists?
- connection.table_exists?(table_name)
+ ActiveSupport::Deprecation.silence { connection.table_exists?(table_name) }
end
def create_table(limit=nil)
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index cd7d949239..8b4874044c 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -56,9 +56,9 @@ module ActiveRecord
args = ['-s', '-x', '-O', '-f', filename]
unless search_path.blank?
- args << search_path.split(',').map do |part|
+ args += search_path.split(',').map do |part|
"--schema=#{part.strip}"
- end.join(' ')
+ end
end
args << configuration['database']
run_cmd('pg_dump', args, 'dumping')
diff --git a/activerecord/lib/active_record/touch_later.rb b/activerecord/lib/active_record/touch_later.rb
index 4352a0ffea..9a80a63e28 100644
--- a/activerecord/lib/active_record/touch_later.rb
+++ b/activerecord/lib/active_record/touch_later.rb
@@ -16,6 +16,13 @@ module ActiveRecord
surreptitiously_touch @_defer_touch_attrs
self.class.connection.add_transaction_record self
+
+ # touch the parents as we are not calling the after_save callbacks
+ self.class.reflect_on_all_associations(:belongs_to).each do |r|
+ if touch = r.options[:touch]
+ ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, r.foreign_key, r.name, touch, :touch_later)
+ end
+ end
end
def touch(*names, time: nil) # :nodoc:
@@ -26,6 +33,7 @@ module ActiveRecord
end
private
+
def surreptitiously_touch(attrs)
attrs.each { |attr| write_attribute attr, @_touch_time }
clear_attribute_changes attrs
@@ -33,9 +41,8 @@ module ActiveRecord
def touch_deferred_attributes
if has_defer_touch_attrs? && persisted?
- @_touching_delayed_records = true
touch(*@_defer_touch_attrs, time: @_touch_time)
- @_touching_delayed_records, @_defer_touch_attrs, @_touch_time = nil, nil, nil
+ @_defer_touch_attrs, @_touch_time = nil, nil
end
end
@@ -43,8 +50,9 @@ module ActiveRecord
defined?(@_defer_touch_attrs) && @_defer_touch_attrs.present?
end
- def touching_delayed_records?
- defined?(@_touching_delayed_records) && @_touching_delayed_records
+ def belongs_to_touch_method
+ :touch_later
end
+
end
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 8de82feae3..38ab1f3fc6 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -233,6 +233,24 @@ module ActiveRecord
set_callback(:commit, :after, *args, &block)
end
+ # Shortcut for +after_commit :hook, on: :create+.
+ def after_create_commit(*args, &block)
+ set_options_for_callbacks!(args, on: :create)
+ set_callback(:commit, :after, *args, &block)
+ end
+
+ # Shortcut for +after_commit :hook, on: :update+.
+ def after_update_commit(*args, &block)
+ set_options_for_callbacks!(args, on: :update)
+ set_callback(:commit, :after, *args, &block)
+ end
+
+ # Shortcut for +after_commit :hook, on: :destroy+.
+ def after_destroy_commit(*args, &block)
+ set_options_for_callbacks!(args, on: :destroy)
+ set_callback(:commit, :after, *args, &block)
+ end
+
# This callback is called after a create, update, or destroy are rolled back.
#
# Please check the documentation of #after_commit for options.
@@ -268,9 +286,11 @@ module ActiveRecord
private
- def set_options_for_callbacks!(args)
- options = args.last
- if options.is_a?(Hash) && options[:on]
+ def set_options_for_callbacks!(args, enforced_options = {})
+ options = args.extract_options!.merge!(enforced_options)
+ args << options
+
+ if options[:on]
fire_on = Array(options[:on])
assert_valid_transaction_action(fire_on)
options[:if] = Array(options[:if])
diff --git a/activerecord/lib/active_record/type_caster.rb b/activerecord/lib/active_record/type_caster.rb
index 63ba10c289..accc339d00 100644
--- a/activerecord/lib/active_record/type_caster.rb
+++ b/activerecord/lib/active_record/type_caster.rb
@@ -2,6 +2,6 @@ require 'active_record/type_caster/map'
require 'active_record/type_caster/connection'
module ActiveRecord
- module TypeCaster
+ module TypeCaster # :nodoc:
end
end
diff --git a/activerecord/lib/active_record/type_caster/connection.rb b/activerecord/lib/active_record/type_caster/connection.rb
index 868d08ed44..7ed8dcc313 100644
--- a/activerecord/lib/active_record/type_caster/connection.rb
+++ b/activerecord/lib/active_record/type_caster/connection.rb
@@ -1,6 +1,6 @@
module ActiveRecord
module TypeCaster
- class Connection
+ class Connection # :nodoc:
def initialize(klass, table_name)
@klass = klass
@table_name = table_name
diff --git a/activerecord/lib/active_record/type_caster/map.rb b/activerecord/lib/active_record/type_caster/map.rb
index 4b1941351c..3a367b3999 100644
--- a/activerecord/lib/active_record/type_caster/map.rb
+++ b/activerecord/lib/active_record/type_caster/map.rb
@@ -1,6 +1,6 @@
module ActiveRecord
module TypeCaster
- class Map
+ class Map # :nodoc:
def initialize(types)
@types = types
end
diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb
index 32fbaf0a91..b14db85167 100644
--- a/activerecord/lib/active_record/validations/associated.rb
+++ b/activerecord/lib/active_record/validations/associated.rb
@@ -2,10 +2,16 @@ module ActiveRecord
module Validations
class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
def validate_each(record, attribute, value)
- if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?}.any?
- record.errors.add(attribute, :invalid, options.merge(:value => value))
+ if Array(value).reject { |r| valid_object?(r) }.any?
+ record.errors.add(attribute, :invalid, options.merge(value: value))
end
end
+
+ private
+
+ def valid_object?(record)
+ (record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?
+ end
end
module ClassMethods
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 77c47f12d5..abe1ea7c90 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -23,7 +23,8 @@ module ActiveRecord
end
def test_tables
- tables = @connection.tables
+ tables = nil
+ ActiveSupport::Deprecation.silence { tables = @connection.tables }
assert tables.include?("accounts")
assert tables.include?("authors")
assert tables.include?("tasks")
@@ -31,9 +32,15 @@ module ActiveRecord
end
def test_table_exists?
- assert @connection.table_exists?("accounts")
- assert !@connection.table_exists?("nonexistingtable")
- assert !@connection.table_exists?(nil)
+ ActiveSupport::Deprecation.silence do
+ assert @connection.table_exists?("accounts")
+ assert !@connection.table_exists?("nonexistingtable")
+ assert !@connection.table_exists?(nil)
+ end
+ end
+
+ def test_table_exists_checking_both_tables_and_views_is_deprecated
+ assert_deprecated { @connection.table_exists?("accounts") }
end
def test_data_sources
@@ -246,6 +253,16 @@ module ActiveRecord
assert_not_nil error.cause
end
end
+
+ if current_adapter?(:MysqlAdapter, :Mysql2Adapter, :SQLite3Adapter)
+ def test_tables_returning_both_tables_and_views_is_deprecated
+ assert_deprecated { @connection.tables }
+ end
+ end
+
+ def test_passing_arguments_to_tables_is_deprecated
+ assert_deprecated { @connection.tables(:books) }
+ end
end
class AdapterTestWithoutTransaction < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
index 0b5c9e1798..8a7a0bb25d 100644
--- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
@@ -16,8 +16,8 @@ class MysqlActiveSchemaTest < ActiveRecord::MysqlTestCase
end
def test_add_index
- # add_index calls table_exists? and index_name_exists? which can't work since execute is stubbed
- def (ActiveRecord::Base.connection).table_exists?(*); true; end
+ # add_index calls data_source_exists? and index_name_exists? which can't work since execute is stubbed
+ def (ActiveRecord::Base.connection).data_source_exists?(*); true; end
def (ActiveRecord::Base.connection).index_name_exists?(*); false; end
expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`) "
@@ -60,7 +60,7 @@ class MysqlActiveSchemaTest < ActiveRecord::MysqlTestCase
end
def test_index_in_create
- def (ActiveRecord::Base.connection).table_exists?(*); false; end
+ def (ActiveRecord::Base.connection).data_source_exists?(*); false; end
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
expected = "CREATE TABLE `people` (#{type} INDEX `index_people_on_last_name` (`last_name`) ) ENGINE=InnoDB"
@@ -78,7 +78,7 @@ class MysqlActiveSchemaTest < ActiveRecord::MysqlTestCase
end
def test_index_in_bulk_change
- def (ActiveRecord::Base.connection).table_exists?(*); true; end
+ def (ActiveRecord::Base.connection).data_source_exists?(*); true; end
def (ActiveRecord::Base.connection).index_name_exists?(*); false; end
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
@@ -152,7 +152,7 @@ class MysqlActiveSchemaTest < ActiveRecord::MysqlTestCase
end
def test_indexes_in_create
- ActiveRecord::Base.connection.stubs(:table_exists?).with(:temp).returns(false)
+ ActiveRecord::Base.connection.stubs(:data_source_exists?).with(:temp).returns(false)
ActiveRecord::Base.connection.stubs(:index_name_exists?).with(:index_temp_on_zip).returns(false)
expected = "CREATE TEMPORARY TABLE `temp` ( INDEX `index_temp_on_zip` (`zip`) ) ENGINE=InnoDB AS SELECT id, name, zip FROM a_really_complicated_query"
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index 75653ee9af..390dd15b92 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -171,31 +171,31 @@ class MysqlConnectionTest < ActiveRecord::MysqlTestCase
end
def test_get_and_release_advisory_lock
- key = "test_key"
+ lock_name = "test_lock_name"
- got_lock = @connection.get_advisory_lock(key)
+ got_lock = @connection.get_advisory_lock(lock_name)
assert got_lock, "get_advisory_lock should have returned true but it didn't"
- assert_equal test_lock_free(key), false,
+ assert_equal test_lock_free(lock_name), false,
"expected the test advisory lock to be held but it wasn't"
- released_lock = @connection.release_advisory_lock(key)
+ released_lock = @connection.release_advisory_lock(lock_name)
assert released_lock, "expected release_advisory_lock to return true but it didn't"
- assert test_lock_free(key), 'expected the test key to be available after releasing'
+ assert test_lock_free(lock_name), 'expected the test lock to be available after releasing'
end
def test_release_non_existent_advisory_lock
- fake_key = "fake_key"
- released_non_existent_lock = @connection.release_advisory_lock(fake_key)
+ lock_name = "fake_lock_name"
+ released_non_existent_lock = @connection.release_advisory_lock(lock_name)
assert_equal released_non_existent_lock, false,
'expected release_advisory_lock to return false when there was no lock to release'
end
protected
- def test_lock_free(key)
- @connection.select_value("SELECT IS_FREE_LOCK('#{key}');") == '1'
+ def test_lock_free(lock_name)
+ @connection.select_value("SELECT IS_FREE_LOCK('#{lock_name}');") == '1'
end
private
diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb
index 04bcf0d839..14dbdd375b 100644
--- a/activerecord/test/cases/adapters/mysql/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/schema_test.rb
@@ -59,13 +59,13 @@ module ActiveRecord
assert_equal 'id', @omgpost.primary_key
end
- def test_table_exists?
+ def test_data_source_exists?
name = @omgpost.table_name
- assert @connection.table_exists?(name), "#{name} table should exist"
+ assert @connection.data_source_exists?(name), "#{name} data_source should exist"
end
- def test_table_exists_wrong_schema
- assert(!@connection.table_exists?("#{@db_name}.zomg"), "table should not exist")
+ def test_data_source_exists_wrong_schema
+ assert(!@connection.data_source_exists?("#{@db_name}.zomg"), "data_source should not exist")
end
def test_dump_indexes
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index 31dc69a45b..99f97c7914 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -16,8 +16,8 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
end
def test_add_index
- # add_index calls table_exists? and index_name_exists? which can't work since execute is stubbed
- def (ActiveRecord::Base.connection).table_exists?(*); true; end
+ # add_index calls data_source_exists? and index_name_exists? which can't work since execute is stubbed
+ def (ActiveRecord::Base.connection).data_source_exists?(*); true; end
def (ActiveRecord::Base.connection).index_name_exists?(*); false; end
expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`) "
@@ -60,7 +60,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
end
def test_index_in_create
- def (ActiveRecord::Base.connection).table_exists?(*); false; end
+ def (ActiveRecord::Base.connection).data_source_exists?(*); false; end
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
expected = "CREATE TABLE `people` (#{type} INDEX `index_people_on_last_name` (`last_name`) ) ENGINE=InnoDB"
@@ -78,7 +78,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
end
def test_index_in_bulk_change
- def (ActiveRecord::Base.connection).table_exists?(*); true; end
+ def (ActiveRecord::Base.connection).data_source_exists?(*); true; end
def (ActiveRecord::Base.connection).index_name_exists?(*); false; end
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
@@ -152,7 +152,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
end
def test_indexes_in_create
- ActiveRecord::Base.connection.stubs(:table_exists?).with(:temp).returns(false)
+ ActiveRecord::Base.connection.stubs(:data_source_exists?).with(:temp).returns(false)
ActiveRecord::Base.connection.stubs(:index_name_exists?).with(:index_temp_on_zip).returns(false)
expected = "CREATE TEMPORARY TABLE `temp` ( INDEX `index_temp_on_zip` (`zip`) ) ENGINE=InnoDB AS SELECT id, name, zip FROM a_really_complicated_query"
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 71c4028675..507d024bb6 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -133,30 +133,30 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
end
def test_get_and_release_advisory_lock
- key = "test_key"
+ lock_name = "test_lock_name"
- got_lock = @connection.get_advisory_lock(key)
+ got_lock = @connection.get_advisory_lock(lock_name)
assert got_lock, "get_advisory_lock should have returned true but it didn't"
- assert_equal test_lock_free(key), false,
+ assert_equal test_lock_free(lock_name), false,
"expected the test advisory lock to be held but it wasn't"
- released_lock = @connection.release_advisory_lock(key)
+ released_lock = @connection.release_advisory_lock(lock_name)
assert released_lock, "expected release_advisory_lock to return true but it didn't"
- assert test_lock_free(key), 'expected the test key to be available after releasing'
+ assert test_lock_free(lock_name), 'expected the test lock to be available after releasing'
end
def test_release_non_existent_advisory_lock
- fake_key = "fake_key"
- released_non_existent_lock = @connection.release_advisory_lock(fake_key)
+ lock_name = "fake_lock_name"
+ released_non_existent_lock = @connection.release_advisory_lock(lock_name)
assert_equal released_non_existent_lock, false,
'expected release_advisory_lock to return false when there was no lock to release'
end
protected
- def test_lock_free(key)
- @connection.select_value("SELECT IS_FREE_LOCK('#{key}');") == 1
+ def test_lock_free(lock_name)
+ @connection.select_value("SELECT IS_FREE_LOCK('#{lock_name}');") == 1
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index 0d9f07917c..43957791b1 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -59,13 +59,13 @@ module ActiveRecord
assert_equal 'id', @omgpost.primary_key
end
- def test_table_exists?
+ def test_data_source_exists?
name = @omgpost.table_name
- assert @connection.table_exists?(name), "#{name} table should exist"
+ assert @connection.data_source_exists?(name), "#{name} data_source should exist"
end
- def test_table_exists_wrong_schema
- assert(!@connection.table_exists?("#{@db_name}.zomg"), "table should not exist")
+ def test_data_source_exists_wrong_schema
+ assert(!@connection.data_source_exists?("#{@db_name}.zomg"), "data_source should not exist")
end
def test_dump_indexes
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index b12beb91de..d559de3e28 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -90,7 +90,7 @@ module ActiveRecord
end
def test_tables_logs_name
- @connection.tables('hello')
+ ActiveSupport::Deprecation.silence { @connection.tables('hello') }
assert_equal 'SCHEMA', @subscriber.logged[0][1]
end
@@ -100,7 +100,7 @@ module ActiveRecord
end
def test_table_exists_logs_name
- @connection.table_exists?('items')
+ ActiveSupport::Deprecation.silence { @connection.table_exists?('items') }
assert_equal 'SCHEMA', @subscriber.logged[0][1]
end
@@ -211,33 +211,33 @@ module ActiveRecord
end
def test_get_and_release_advisory_lock
- key = 5295901941911233559
+ lock_id = 5295901941911233559
list_advisory_locks = <<-SQL
SELECT locktype,
- (classid::bigint << 32) | objid::bigint AS lock_key
+ (classid::bigint << 32) | objid::bigint AS lock_id
FROM pg_locks
WHERE locktype = 'advisory'
SQL
- got_lock = @connection.get_advisory_lock(key)
+ got_lock = @connection.get_advisory_lock(lock_id)
assert got_lock, "get_advisory_lock should have returned true but it didn't"
- advisory_lock = @connection.query(list_advisory_locks).find {|l| l[1] == key}
+ advisory_lock = @connection.query(list_advisory_locks).find {|l| l[1] == lock_id}
assert advisory_lock,
- "expected to find an advisory lock with key #{key} but there wasn't one"
+ "expected to find an advisory lock with lock_id #{lock_id} but there wasn't one"
- released_lock = @connection.release_advisory_lock(key)
+ released_lock = @connection.release_advisory_lock(lock_id)
assert released_lock, "expected release_advisory_lock to return true but it didn't"
- advisory_locks = @connection.query(list_advisory_locks).select {|l| l[1] == key}
+ advisory_locks = @connection.query(list_advisory_locks).select {|l| l[1] == lock_id}
assert_empty advisory_locks,
- "expected to have released advisory lock with key #{key} but it was still held"
+ "expected to have released advisory lock with lock_id #{lock_id} but it was still held"
end
def test_release_non_existent_advisory_lock
- fake_key = 2940075057017742022
+ fake_lock_id = 2940075057017742022
with_warning_suppression do
- released_non_existent_lock = @connection.release_advisory_lock(fake_key)
+ released_non_existent_lock = @connection.release_advisory_lock(fake_lock_id)
assert_equal released_non_existent_lock, false,
'expected release_advisory_lock to return false when there was no lock to release'
end
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
index 0baf985654..3b97cb4ad4 100644
--- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -167,16 +167,18 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
end
class PostgresqlGeometricTest < ActiveRecord::PostgreSQLTestCase
+ include SchemaDumpingHelper
+
class PostgresqlGeometric < ActiveRecord::Base; end
setup do
@connection = ActiveRecord::Base.connection
@connection.create_table("postgresql_geometrics") do |t|
- t.column :a_line_segment, :lseg
- t.column :a_box, :box
- t.column :a_path, :path
- t.column :a_polygon, :polygon
- t.column :a_circle, :circle
+ t.lseg :a_line_segment
+ t.box :a_box
+ t.path :a_path
+ t.polygon :a_polygon
+ t.circle :a_circle
end
end
@@ -233,4 +235,144 @@ class PostgresqlGeometricTest < ActiveRecord::PostgreSQLTestCase
objs = PostgresqlGeometric.find_by_sql "SELECT isclosed(a_path) FROM postgresql_geometrics ORDER BY id ASC"
assert_equal [false, true], objs.map(&:isclosed)
end
+
+ def test_schema_dumping
+ output = dump_table_schema("postgresql_geometrics")
+ assert_match %r{t\.lseg\s+"a_line_segment"$}, output
+ assert_match %r{t\.box\s+"a_box"$}, output
+ assert_match %r{t\.path\s+"a_path"$}, output
+ assert_match %r{t\.polygon\s+"a_polygon"$}, output
+ assert_match %r{t\.circle\s+"a_circle"$}, output
+ end
+end
+
+class PostgreSQLGeometricLineTest < ActiveRecord::PostgreSQLTestCase
+ include SchemaDumpingHelper
+
+ class PostgresqlLine < ActiveRecord::Base; end
+
+ setup do
+ unless ActiveRecord::Base.connection.send(:postgresql_version) >= 90400
+ skip("line type is not fully implemented")
+ end
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table("postgresql_lines") do |t|
+ t.line :a_line
+ end
+ end
+
+ teardown do
+ if defined?(@connection)
+ @connection.drop_table 'postgresql_lines', if_exists: true
+ end
+ end
+
+ def test_geometric_line_type
+ g = PostgresqlLine.new(
+ a_line: '{2.0, 3, 5.5}'
+ )
+ g.save!
+
+ h = PostgresqlLine.find(g.id)
+ assert_equal '{2,3,5.5}', h.a_line
+ end
+
+ def test_alternative_format_line_type
+ g = PostgresqlLine.new(
+ a_line: '(2.0, 3), (4.0, 6.0)'
+ )
+ g.save!
+
+ h = PostgresqlLine.find(g.id)
+ assert_equal '{1.5,-1,0}', h.a_line
+ end
+
+ def test_schema_dumping_for_line_type
+ output = dump_table_schema("postgresql_lines")
+ assert_match %r{t\.line\s+"a_line"$}, output
+ end
+end
+
+class PostgreSQLGeometricTypesTest < ActiveRecord::PostgreSQLTestCase
+ attr_reader :connection, :table_name
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ @table_name = :testings
+ end
+
+ def test_creating_column_with_point_type
+ connection.create_table(table_name) do |t|
+ t.point :foo_point
+ end
+
+ assert_column_exists(:foo_point)
+ assert_type_correct(:foo_point, :point)
+ end
+
+ def test_creating_column_with_line_type
+ connection.create_table(table_name) do |t|
+ t.line :foo_line
+ end
+
+ assert_column_exists(:foo_line)
+ assert_type_correct(:foo_line, :line)
+ end
+
+ def test_creating_column_with_lseg_type
+ connection.create_table(table_name) do |t|
+ t.lseg :foo_lseg
+ end
+
+ assert_column_exists(:foo_lseg)
+ assert_type_correct(:foo_lseg, :lseg)
+ end
+
+ def test_creating_column_with_box_type
+ connection.create_table(table_name) do |t|
+ t.box :foo_box
+ end
+
+ assert_column_exists(:foo_box)
+ assert_type_correct(:foo_box, :box)
+ end
+
+ def test_creating_column_with_path_type
+ connection.create_table(table_name) do |t|
+ t.path :foo_path
+ end
+
+ assert_column_exists(:foo_path)
+ assert_type_correct(:foo_path, :path)
+ end
+
+ def test_creating_column_with_polygon_type
+ connection.create_table(table_name) do |t|
+ t.polygon :foo_polygon
+ end
+
+ assert_column_exists(:foo_polygon)
+ assert_type_correct(:foo_polygon, :polygon)
+ end
+
+ def test_creating_column_with_circle_type
+ connection.create_table(table_name) do |t|
+ t.circle :foo_circle
+ end
+
+ assert_column_exists(:foo_circle)
+ assert_type_correct(:foo_circle, :circle)
+ end
+
+ private
+
+ def assert_column_exists(column_name)
+ assert connection.column_exists?(table_name, column_name)
+ end
+
+ def assert_type_correct(column_name, type)
+ column = connection.columns(table_name).find { |c| c.name == column_name.to_s }
+ assert_equal type, column.type
+ end
end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 93e98ec872..7c9169f6e2 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -184,42 +184,42 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
@connection.exec_query("alter table developers drop column zomg", 'sql', []) if altered
end
- def test_table_exists?
+ def test_data_source_exists?
[Thing1, Thing2, Thing3, Thing4].each do |klass|
name = klass.table_name
- assert @connection.table_exists?(name), "'#{name}' table should exist"
+ assert @connection.data_source_exists?(name), "'#{name}' data_source should exist"
end
end
- def test_table_exists_when_on_schema_search_path
+ def test_data_source_exists_when_on_schema_search_path
with_schema_search_path(SCHEMA_NAME) do
- assert(@connection.table_exists?(TABLE_NAME), "table should exist and be found")
+ assert(@connection.data_source_exists?(TABLE_NAME), "data_source should exist and be found")
end
end
- def test_table_exists_when_not_on_schema_search_path
+ def test_data_source_exists_when_not_on_schema_search_path
with_schema_search_path('PUBLIC') do
- assert(!@connection.table_exists?(TABLE_NAME), "table exists but should not be found")
+ assert(!@connection.data_source_exists?(TABLE_NAME), "data_source exists but should not be found")
end
end
- def test_table_exists_wrong_schema
- assert(!@connection.table_exists?("foo.things"), "table should not exist")
+ def test_data_source_exists_wrong_schema
+ assert(!@connection.data_source_exists?("foo.things"), "data_source should not exist")
end
- def test_table_exists_quoted_names
+ def test_data_source_exists_quoted_names
[ %("#{SCHEMA_NAME}"."#{TABLE_NAME}"), %(#{SCHEMA_NAME}."#{TABLE_NAME}"), %(#{SCHEMA_NAME}."#{TABLE_NAME}")].each do |given|
- assert(@connection.table_exists?(given), "table should exist when specified as #{given}")
+ assert(@connection.data_source_exists?(given), "data_source should exist when specified as #{given}")
end
with_schema_search_path(SCHEMA_NAME) do
given = %("#{TABLE_NAME}")
- assert(@connection.table_exists?(given), "table should exist when specified as #{given}")
+ assert(@connection.data_source_exists?(given), "data_source should exist when specified as #{given}")
end
end
- def test_table_exists_quoted_table
+ def test_data_source_exists_quoted_table
with_schema_search_path(SCHEMA_NAME) do
- assert(@connection.table_exists?('"things.table"'), "table should exist")
+ assert(@connection.data_source_exists?('"things.table"'), "data_source should exist")
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 640df31e2e..a2fd1177a6 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -284,9 +284,9 @@ module ActiveRecord
def test_tables
with_example_table do
- assert_equal %w{ ex }, @conn.tables
+ ActiveSupport::Deprecation.silence { assert_equal %w{ ex }, @conn.tables }
with_example_table 'id integer PRIMARY KEY AUTOINCREMENT, number integer', 'people' do
- assert_equal %w{ ex people }.sort, @conn.tables.sort
+ ActiveSupport::Deprecation.silence { assert_equal %w{ ex people }.sort, @conn.tables.sort }
end
end
end
@@ -297,7 +297,9 @@ module ActiveRecord
WHERE type IN ('table','view') AND name <> 'sqlite_sequence'
SQL
assert_logged [[sql.squish, 'SCHEMA', []]] do
- @conn.tables('hello')
+ ActiveSupport::Deprecation.silence do
+ @conn.tables('hello')
+ end
end
end
@@ -316,7 +318,9 @@ module ActiveRecord
WHERE type IN ('table','view') AND name <> 'sqlite_sequence' AND name = 'ex'
SQL
assert_logged [[sql.squish, 'SCHEMA', []]] do
- assert @conn.table_exists?('ex')
+ ActiveSupport::Deprecation.silence do
+ assert @conn.table_exists?('ex')
+ end
end
end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index eb94870a35..ad157582a4 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -203,9 +203,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
bulb = car.bulbs.create
assert_equal 'defaulty', bulb.name
+ end
+
+ def test_build_and_create_from_association_should_respect_passed_attributes_over_default_scope
+ car = Car.create(name: 'honda')
- bulb = car.bulbs.create(:name => 'exotic')
+ bulb = car.bulbs.build(name: 'exotic')
assert_equal 'exotic', bulb.name
+
+ bulb = car.bulbs.create(name: 'exotic')
+ assert_equal 'exotic', bulb.name
+
+ bulb = car.awesome_bulbs.build(frickinawesome: false)
+ assert_equal false, bulb.frickinawesome
+
+ bulb = car.awesome_bulbs.create(frickinawesome: false)
+ assert_equal false, bulb.frickinawesome
end
def test_build_from_association_should_respect_scope
@@ -2335,6 +2348,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [first_bulb, second_bulb], car.bulbs
end
+ test 'double insertion of new object to association when same association used in the after create callback of a new object' do
+ car = Car.create!
+ car.bulbs << TrickyBulb.new
+ assert_equal 1, car.bulbs.size
+ end
+
def test_association_force_reload_with_only_true_is_deprecated
company = Company.find(1)
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index 264b275181..2991ca8b76 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -172,5 +172,18 @@ module ActiveRecord
assert_equal int_range, klass.type_for_attribute("my_int_range")
end
end
+
+ test "attributes added after subclasses load are inherited" do
+ parent = Class.new(ActiveRecord::Base) do
+ self.table_name = "topics"
+ end
+
+ child = Class.new(parent)
+ child.new # => force a schema load
+
+ parent.attribute(:foo, Type::Value.new)
+
+ assert_equal(:bar, child.new(foo: :bar).foo)
+ end
end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index d961f4710e..3a9d60a79f 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1345,6 +1345,19 @@ class BasicsTest < ActiveRecord::TestCase
Company.attribute_names
end
+ def test_has_attribute
+ assert Company.has_attribute?('id')
+ assert Company.has_attribute?('type')
+ assert Company.has_attribute?('name')
+ assert_not Company.has_attribute?('lastname')
+ assert_not Company.has_attribute?('age')
+ end
+
+ def test_has_attribute_with_symbol
+ assert Company.has_attribute?(:id)
+ assert_not Company.has_attribute?(:age)
+ end
+
def test_attribute_names_on_table_not_exists
assert_equal [], NonExistentTable.attribute_names
end
diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb
index cf8c4c688a..d43668e57c 100644
--- a/activerecord/test/cases/connection_management_test.rb
+++ b/activerecord/test/cases/connection_management_test.rb
@@ -92,14 +92,14 @@ module ActiveRecord
app = lambda { |_| [200, {}, body] }
response_body = ConnectionManagement.new(app).call(@env)[2]
assert response_body.respond_to?(:to_path)
- assert_equal response_body.to_path, "/path"
+ assert_equal "/path", response_body.to_path
end
test "doesn't mutate the original response" do
original_response = [200, {}, 'hi']
app = lambda { |_| original_response }
- response_body = ConnectionManagement.new(app).call(@env)[2]
- assert_equal original_response.last, 'hi'
+ ConnectionManagement.new(app).call(@env)[2]
+ assert_equal 'hi', original_response.last
end
end
end
diff --git a/activerecord/test/cases/date_time_precision_test.rb b/activerecord/test/cases/date_time_precision_test.rb
index 698f1b852e..e996d142a2 100644
--- a/activerecord/test/cases/date_time_precision_test.rb
+++ b/activerecord/test/cases/date_time_precision_test.rb
@@ -10,6 +10,7 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
setup do
@connection = ActiveRecord::Base.connection
+ Foo.reset_column_information
end
teardown do
@@ -20,24 +21,24 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
@connection.create_table(:foos, force: true)
@connection.add_column :foos, :created_at, :datetime, precision: 0
@connection.add_column :foos, :updated_at, :datetime, precision: 5
- assert_equal 0, activerecord_column_option('foos', 'created_at', 'precision')
- assert_equal 5, activerecord_column_option('foos', 'updated_at', 'precision')
+ assert_equal 0, Foo.columns_hash['created_at'].precision
+ assert_equal 5, Foo.columns_hash['updated_at'].precision
end
def test_timestamps_helper_with_custom_precision
@connection.create_table(:foos, force: true) do |t|
t.timestamps precision: 4
end
- assert_equal 4, activerecord_column_option('foos', 'created_at', 'precision')
- assert_equal 4, activerecord_column_option('foos', 'updated_at', 'precision')
+ assert_equal 4, Foo.columns_hash['created_at'].precision
+ assert_equal 4, Foo.columns_hash['updated_at'].precision
end
def test_passing_precision_to_datetime_does_not_set_limit
@connection.create_table(:foos, force: true) do |t|
t.timestamps precision: 4
end
- assert_nil activerecord_column_option('foos', 'created_at', 'limit')
- assert_nil activerecord_column_option('foos', 'updated_at', 'limit')
+ assert_nil Foo.columns_hash['created_at'].limit
+ assert_nil Foo.columns_hash['updated_at'].limit
end
def test_invalid_datetime_precision_raises_error
@@ -48,14 +49,6 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
end
end
- def test_database_agrees_with_activerecord_about_precision
- @connection.create_table(:foos, force: true) do |t|
- t.timestamps precision: 4
- end
- assert_equal 4, database_datetime_precision('foos', 'created_at')
- assert_equal 4, database_datetime_precision('foos', 'updated_at')
- end
-
def test_formatting_datetime_according_to_precision
@connection.create_table(:foos, force: true) do |t|
t.datetime :created_at, precision: 0
@@ -91,21 +84,5 @@ class DateTimePrecisionTest < ActiveRecord::TestCase
end
end
- private
-
- def database_datetime_precision(table_name, column_name)
- results = @connection.exec_query("SELECT column_name, datetime_precision FROM information_schema.columns WHERE table_name = '#{table_name}'")
- result = results.find do |result_hash|
- result_hash["column_name"] == column_name
- end
- result && result["datetime_precision"].to_i
- end
-
- def activerecord_column_option(tablename, column_name, option)
- result = @connection.columns(tablename).find do |column|
- column.name == column_name
- end
- result && result.send(option)
- end
end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 6686ce012d..91214da048 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -706,96 +706,13 @@ class FinderTest < ActiveRecord::TestCase
assert Company.where(["name = :name", {name: "37signals' go'es agains"}]).first
end
- def test_bind_arity
- assert_nothing_raised { bind '' }
- assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
-
- assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
- assert_nothing_raised { bind '?', 1 }
- assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
- end
-
def test_named_bind_variables
- assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
- assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
-
- assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
-
assert_kind_of Firm, Company.where(["name = :name", { name: "37signals" }]).first
assert_nil Company.where(["name = :name", { name: "37signals!" }]).first
assert_nil Company.where(["name = :name", { name: "37signals!' OR 1=1" }]).first
assert_kind_of Time, Topic.where(["id = :id", { id: 1 }]).first.written_on
end
- def test_named_bind_arity
- assert_nothing_raised { bind "name = :name", { name: "37signals" } }
- assert_nothing_raised { bind "name = :name", { name: "37signals", id: 1 } }
- assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "name = :name", { id: 1 } }
- end
-
- class SimpleEnumerable
- include Enumerable
-
- def initialize(ary)
- @ary = ary
- end
-
- def each(&b)
- @ary.each(&b)
- end
- end
-
- def test_bind_enumerable
- quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})
-
- assert_equal '1,2,3', bind('?', [1, 2, 3])
- assert_equal quoted_abc, bind('?', %w(a b c))
-
- assert_equal '1,2,3', bind(':a', :a => [1, 2, 3])
- assert_equal quoted_abc, bind(':a', :a => %w(a b c)) # '
-
- assert_equal '1,2,3', bind('?', SimpleEnumerable.new([1, 2, 3]))
- assert_equal quoted_abc, bind('?', SimpleEnumerable.new(%w(a b c)))
-
- assert_equal '1,2,3', bind(':a', :a => SimpleEnumerable.new([1, 2, 3]))
- assert_equal quoted_abc, bind(':a', :a => SimpleEnumerable.new(%w(a b c))) # '
- end
-
- def test_bind_empty_enumerable
- quoted_nil = ActiveRecord::Base.connection.quote(nil)
- assert_equal quoted_nil, bind('?', [])
- assert_equal " in (#{quoted_nil})", bind(' in (?)', [])
- assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', [])
- end
-
- def test_bind_empty_string
- quoted_empty = ActiveRecord::Base.connection.quote('')
- assert_equal quoted_empty, bind('?', '')
- end
-
- def test_bind_chars
- quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
- quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
- assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
- assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
- assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
- assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
- end
-
- def test_bind_record
- o = Struct.new(:quoted_id).new(1)
- assert_equal '1', bind('?', o)
-
- os = [o] * 3
- assert_equal '1,1,1', bind('?', os)
- end
-
- def test_named_bind_with_postgresql_type_casts
- l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
- assert_nothing_raised(&l)
- assert_equal "#{ActiveRecord::Base.connection.quote('10')}::integer '2009-01-01'::date", l.call
- end
-
def test_string_sanitation
assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
assert_equal "'something; select table'", ActiveRecord::Base.sanitize("something; select table")
@@ -1136,14 +1053,6 @@ class FinderTest < ActiveRecord::TestCase
end
protected
- def bind(statement, *vars)
- if vars.first.is_a?(Hash)
- ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
- else
- ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
- end
- end
-
def table_with_custom_primary_key
yield(Class.new(Toy) do
def self.name
diff --git a/activerecord/test/cases/forbidden_attributes_protection_test.rb b/activerecord/test/cases/forbidden_attributes_protection_test.rb
index f4e7646f03..91921469b8 100644
--- a/activerecord/test/cases/forbidden_attributes_protection_test.rb
+++ b/activerecord/test/cases/forbidden_attributes_protection_test.rb
@@ -1,14 +1,20 @@
require 'cases/helper'
require 'active_support/core_ext/hash/indifferent_access'
-require 'models/person'
+
require 'models/company'
+require 'models/person'
+require 'models/ship'
+require 'models/ship_part'
+require 'models/treasure'
-class ProtectedParams < ActiveSupport::HashWithIndifferentAccess
+class ProtectedParams
attr_accessor :permitted
alias :permitted? :permitted
+ delegate :keys, :key?, :has_key?, :empty?, to: :@parameters
+
def initialize(attributes)
- super(attributes)
+ @parameters = attributes.with_indifferent_access
@permitted = false
end
@@ -17,6 +23,18 @@ class ProtectedParams < ActiveSupport::HashWithIndifferentAccess
self
end
+ def [](key)
+ @parameters[key]
+ end
+
+ def to_h
+ @parameters
+ end
+
+ def stringify_keys
+ dup
+ end
+
def dup
super.tap do |duplicate|
duplicate.instance_variable_set :@permitted, @permitted
@@ -75,6 +93,13 @@ class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
end
end
+ def test_create_with_works_with_permitted_params
+ params = ProtectedParams.new(first_name: 'Guille').permit!
+
+ person = Person.create_with(params).create!
+ assert_equal 'Guille', person.first_name
+ end
+
def test_create_with_works_with_params_values
params = ProtectedParams.new(first_name: 'Guille')
@@ -90,10 +115,51 @@ class ForbiddenAttributesProtectionTest < ActiveRecord::TestCase
end
end
+ def test_where_works_with_permitted_params
+ params = ProtectedParams.new(first_name: 'Guille').permit!
+
+ person = Person.where(params).create!
+ assert_equal 'Guille', person.first_name
+ end
+
def test_where_works_with_params_values
params = ProtectedParams.new(first_name: 'Guille')
person = Person.where(first_name: params[:first_name]).create!
assert_equal 'Guille', person.first_name
end
+
+ def test_where_not_checks_permitted
+ params = ProtectedParams.new(first_name: 'Guille', gender: 'm')
+
+ assert_raises(ActiveModel::ForbiddenAttributesError) do
+ Person.where().not(params)
+ end
+ end
+
+ def test_where_not_works_with_permitted_params
+ params = ProtectedParams.new(first_name: 'Guille').permit!
+ Person.create!(params)
+ assert_empty Person.where.not(params).select {|p| p.first_name == 'Guille' }
+ end
+
+ def test_strong_params_style_objects_work_with_singular_associations
+ params = ProtectedParams.new( name: "Stern", ship_attributes: ProtectedParams.new(name: "The Black Rock").permit!).permit!
+ part = ShipPart.new(params)
+
+ assert_equal "Stern", part.name
+ assert_equal "The Black Rock", part.ship.name
+ end
+
+ def test_strong_params_style_objects_work_with_collection_associations
+ params = ProtectedParams.new(
+ trinkets_attributes: ProtectedParams.new(
+ "0" => ProtectedParams.new(name: "Necklace").permit!,
+ "1" => ProtectedParams.new(name: "Spoon").permit! ) ).permit!
+ part = ShipPart.new(params)
+
+ assert_equal "Necklace", part.trinkets[0].name
+ assert_equal "Spoon", part.trinkets[1].name
+ end
+
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index d82a3040fc..07dbc8a53f 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -47,7 +47,7 @@ def in_memory_db?
end
def subsecond_precision_supported?
- !current_adapter?(:MysqlAdapter, :Mysql2Adapter) || ActiveRecord::Base.connection.version >= '5.6.4'
+ ActiveRecord::Base.connection.supports_datetime_with_precision?
end
def mysql_enforcing_gtid_consistency?
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index 52e3734dd0..03bce547da 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -478,4 +478,49 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase
product = Shop::Product.new(:type => phone)
assert product.save
end
+
+ def test_inheritance_new_with_subclass_as_default
+ original_type = Company.columns_hash["type"].default
+ ActiveRecord::Base.connection.change_column_default :companies, :type, 'Firm'
+ Company.reset_column_information
+
+ firm = Company.new # without arguments
+ assert_equal 'Firm', firm.type
+ assert_instance_of Firm, firm
+
+ firm = Company.new(firm_name: 'Shri Hans Plastic') # with arguments
+ assert_equal 'Firm', firm.type
+ assert_instance_of Firm, firm
+
+ firm = Company.new(type: 'Client') # overwrite the default type
+ assert_equal 'Client', firm.type
+ assert_instance_of Client, firm
+ ensure
+ ActiveRecord::Base.connection.change_column_default :companies, :type, original_type
+ Company.reset_column_information
+ end
+end
+
+class InheritanceAttributeTest < ActiveRecord::TestCase
+
+ class Company < ActiveRecord::Base
+ self.table_name = 'companies'
+ attribute :type, :string, default: "InheritanceAttributeTest::Startup"
+ end
+
+ class Startup < Company
+ end
+
+ class Empire < Company
+ end
+
+ def test_inheritance_new_with_subclass_as_default
+ startup = Company.new # without arguments
+ assert_equal 'InheritanceAttributeTest::Startup', startup.type
+ assert_instance_of Startup, startup
+
+ empire = Company.new(type: 'InheritanceAttributeTest::Empire') # without arguments
+ assert_equal 'InheritanceAttributeTest::Empire', empire.type
+ assert_instance_of Empire, empire
+ end
end
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index 84b0ff8fcb..0da58040c8 100644
--- a/activerecord/test/cases/invertible_migration_test.rb
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -157,8 +157,10 @@ module ActiveRecord
teardown do
%w[horses new_horses].each do |table|
- if ActiveRecord::Base.connection.table_exists?(table)
- ActiveRecord::Base.connection.drop_table(table)
+ ActiveSupport::Deprecation.silence do
+ if ActiveRecord::Base.connection.table_exists?(table)
+ ActiveRecord::Base.connection.drop_table(table)
+ end
end
end
ActiveRecord::Migration.verbose = @verbose_was
@@ -189,14 +191,14 @@ module ActiveRecord
def test_migrate_up
migration = InvertibleMigration.new
migration.migrate(:up)
- assert migration.connection.table_exists?("horses"), "horses should exist"
+ ActiveSupport::Deprecation.silence { assert migration.connection.table_exists?("horses"), "horses should exist" }
end
def test_migrate_down
migration = InvertibleMigration.new
migration.migrate :up
migration.migrate :down
- assert !migration.connection.table_exists?("horses")
+ ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") }
end
def test_migrate_revert
@@ -204,11 +206,11 @@ module ActiveRecord
revert = InvertibleRevertMigration.new
migration.migrate :up
revert.migrate :up
- assert !migration.connection.table_exists?("horses")
+ ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") }
revert.migrate :down
- assert migration.connection.table_exists?("horses")
+ ActiveSupport::Deprecation.silence { assert migration.connection.table_exists?("horses") }
migration.migrate :down
- assert !migration.connection.table_exists?("horses")
+ ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") }
end
def test_migrate_revert_by_part
@@ -216,18 +218,24 @@ module ActiveRecord
received = []
migration = InvertibleByPartsMigration.new
migration.test = ->(dir){
- assert migration.connection.table_exists?("horses")
- assert migration.connection.table_exists?("new_horses")
+ ActiveSupport::Deprecation.silence do
+ assert migration.connection.table_exists?("horses")
+ assert migration.connection.table_exists?("new_horses")
+ end
received << dir
}
migration.migrate :up
assert_equal [:both, :up], received
- assert !migration.connection.table_exists?("horses")
- assert migration.connection.table_exists?("new_horses")
+ ActiveSupport::Deprecation.silence do
+ assert !migration.connection.table_exists?("horses")
+ assert migration.connection.table_exists?("new_horses")
+ end
migration.migrate :down
assert_equal [:both, :up, :both, :down], received
- assert migration.connection.table_exists?("horses")
- assert !migration.connection.table_exists?("new_horses")
+ ActiveSupport::Deprecation.silence do
+ assert migration.connection.table_exists?("horses")
+ assert !migration.connection.table_exists?("new_horses")
+ end
end
def test_migrate_revert_whole_migration
@@ -236,20 +244,20 @@ module ActiveRecord
revert = RevertWholeMigration.new(klass)
migration.migrate :up
revert.migrate :up
- assert !migration.connection.table_exists?("horses")
+ ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") }
revert.migrate :down
- assert migration.connection.table_exists?("horses")
+ ActiveSupport::Deprecation.silence { assert migration.connection.table_exists?("horses") }
migration.migrate :down
- assert !migration.connection.table_exists?("horses")
+ ActiveSupport::Deprecation.silence { assert !migration.connection.table_exists?("horses") }
end
end
def test_migrate_nested_revert_whole_migration
revert = NestedRevertWholeMigration.new(InvertibleRevertMigration)
revert.migrate :down
- assert revert.connection.table_exists?("horses")
+ ActiveSupport::Deprecation.silence { assert revert.connection.table_exists?("horses") }
revert.migrate :up
- assert !revert.connection.table_exists?("horses")
+ ActiveSupport::Deprecation.silence { assert !revert.connection.table_exists?("horses") }
end
def test_migrate_revert_change_column_default
@@ -314,24 +322,24 @@ module ActiveRecord
def test_legacy_up
LegacyMigration.migrate :up
- assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist"
+ ActiveSupport::Deprecation.silence { assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist" }
end
def test_legacy_down
LegacyMigration.migrate :up
LegacyMigration.migrate :down
- assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist"
+ ActiveSupport::Deprecation.silence { assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist" }
end
def test_up
LegacyMigration.up
- assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist"
+ ActiveSupport::Deprecation.silence { assert ActiveRecord::Base.connection.table_exists?("horses"), "horses should exist" }
end
def test_down
LegacyMigration.up
LegacyMigration.down
- assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist"
+ ActiveSupport::Deprecation.silence { assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist" }
end
def test_migrate_down_with_table_name_prefix
@@ -340,7 +348,7 @@ module ActiveRecord
migration = InvertibleMigration.new
migration.migrate(:up)
assert_nothing_raised { migration.migrate(:down) }
- assert !ActiveRecord::Base.connection.table_exists?("p_horses_s"), "p_horses_s should not exist"
+ ActiveSupport::Deprecation.silence { assert !ActiveRecord::Base.connection.table_exists?("p_horses_s"), "p_horses_s should not exist" }
ensure
ActiveRecord::Base.table_name_prefix = ActiveRecord::Base.table_name_suffix = ''
end
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index 83e50048ec..2ff9cf8cf5 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -405,9 +405,9 @@ module ActiveRecord
def test_drop_table_if_exists
connection.create_table(:testings)
- assert connection.table_exists?(:testings)
+ ActiveSupport::Deprecation.silence { assert connection.table_exists?(:testings) }
connection.drop_table(:testings, if_exists: true)
- assert_not connection.table_exists?(:testings)
+ ActiveSupport::Deprecation.silence { assert_not connection.table_exists?(:testings) }
end
def test_drop_table_if_exists_nothing_raised
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
index 8fd08fe4ce..0a7b57455c 100644
--- a/activerecord/test/cases/migration/create_join_table_test.rb
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -12,7 +12,9 @@ module ActiveRecord
teardown do
%w(artists_musics musics_videos catalog).each do |table_name|
- connection.drop_table table_name if connection.tables.include?(table_name)
+ ActiveSupport::Deprecation.silence do
+ connection.drop_table table_name if connection.table_exists?(table_name)
+ end
end
end
@@ -82,62 +84,62 @@ module ActiveRecord
connection.create_join_table :artists, :musics
connection.drop_join_table :artists, :musics
- assert !connection.tables.include?('artists_musics')
+ ActiveSupport::Deprecation.silence { assert !connection.table_exists?('artists_musics') }
end
def test_drop_join_table_with_strings
connection.create_join_table :artists, :musics
connection.drop_join_table 'artists', 'musics'
- assert !connection.tables.include?('artists_musics')
+ ActiveSupport::Deprecation.silence { assert !connection.table_exists?('artists_musics') }
end
def test_drop_join_table_with_the_proper_order
connection.create_join_table :videos, :musics
connection.drop_join_table :videos, :musics
- assert !connection.tables.include?('musics_videos')
+ ActiveSupport::Deprecation.silence { assert !connection.table_exists?('musics_videos') }
end
def test_drop_join_table_with_the_table_name
connection.create_join_table :artists, :musics, table_name: :catalog
connection.drop_join_table :artists, :musics, table_name: :catalog
- assert !connection.tables.include?('catalog')
+ ActiveSupport::Deprecation.silence { assert !connection.table_exists?('catalog') }
end
def test_drop_join_table_with_the_table_name_as_string
connection.create_join_table :artists, :musics, table_name: 'catalog'
connection.drop_join_table :artists, :musics, table_name: 'catalog'
- assert !connection.tables.include?('catalog')
+ ActiveSupport::Deprecation.silence { assert !connection.table_exists?('catalog') }
end
def test_drop_join_table_with_column_options
connection.create_join_table :artists, :musics, column_options: {null: true}
connection.drop_join_table :artists, :musics, column_options: {null: true}
- assert !connection.tables.include?('artists_musics')
+ ActiveSupport::Deprecation.silence { assert !connection.table_exists?('artists_musics') }
end
def test_create_and_drop_join_table_with_common_prefix
with_table_cleanup do
connection.create_join_table 'audio_artists', 'audio_musics'
- assert_includes connection.tables, 'audio_artists_musics'
+ ActiveSupport::Deprecation.silence { assert connection.table_exists?('audio_artists_musics') }
connection.drop_join_table 'audio_artists', 'audio_musics'
- assert !connection.tables.include?('audio_artists_musics'), "Should have dropped join table, but didn't"
+ ActiveSupport::Deprecation.silence { assert !connection.table_exists?('audio_artists_musics'), "Should have dropped join table, but didn't" }
end
end
private
def with_table_cleanup
- tables_before = connection.tables
+ tables_before = connection.data_sources
yield
ensure
- tables_after = connection.tables - tables_before
+ tables_after = connection.data_sources - tables_before
tables_after.each do |table|
connection.drop_table table
diff --git a/activerecord/test/cases/migration/postgresql_geometric_types_test.rb b/activerecord/test/cases/migration/postgresql_geometric_types_test.rb
deleted file mode 100644
index e4772905bb..0000000000
--- a/activerecord/test/cases/migration/postgresql_geometric_types_test.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-require 'cases/helper'
-
-module ActiveRecord
- class Migration
- class PostgreSQLGeometricTypesTest < ActiveRecord::TestCase
- attr_reader :connection, :table_name
-
- def setup
- super
- @connection = ActiveRecord::Base.connection
- @table_name = :testings
- end
-
- if current_adapter?(:PostgreSQLAdapter)
- def test_creating_column_with_point_type
- connection.create_table(table_name) do |t|
- t.point :foo_point
- end
-
- assert_column_exists(:foo_point)
- assert_type_correct(:foo_point, :point)
- end
-
- def test_creating_column_with_line_type
- connection.create_table(table_name) do |t|
- t.line :foo_line
- end
-
- assert_column_exists(:foo_line)
- assert_type_correct(:foo_line, :line)
- end
-
- def test_creating_column_with_lseg_type
- connection.create_table(table_name) do |t|
- t.lseg :foo_lseg
- end
-
- assert_column_exists(:foo_lseg)
- assert_type_correct(:foo_lseg, :lseg)
- end
-
- def test_creating_column_with_box_type
- connection.create_table(table_name) do |t|
- t.box :foo_box
- end
-
- assert_column_exists(:foo_box)
- assert_type_correct(:foo_box, :box)
- end
-
- def test_creating_column_with_path_type
- connection.create_table(table_name) do |t|
- t.path :foo_path
- end
-
- assert_column_exists(:foo_path)
- assert_type_correct(:foo_path, :path)
- end
-
- def test_creating_column_with_polygon_type
- connection.create_table(table_name) do |t|
- t.polygon :foo_polygon
- end
-
- assert_column_exists(:foo_polygon)
- assert_type_correct(:foo_polygon, :polygon)
- end
-
- def test_creating_column_with_circle_type
- connection.create_table(table_name) do |t|
- t.circle :foo_circle
- end
-
- assert_column_exists(:foo_circle)
- assert_type_correct(:foo_circle, :circle)
- end
- end
-
- private
- def assert_column_exists(column_name)
- columns = connection.columns(table_name)
- assert columns.map(&:name).include?(column_name.to_s)
- end
-
- def assert_type_correct(column_name, type)
- columns = connection.columns(table_name)
- column = columns.select{ |c| c.name == column_name.to_s }.first
- assert_equal type.to_s, column.sql_type
- end
-
- end
- end
-end \ No newline at end of file
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index 84ec657398..edbc8abe4d 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -164,7 +164,7 @@ class ReferencesWithoutForeignKeySupportTest < ActiveRecord::TestCase
t.references :testing_parent, foreign_key: true
end
- assert_includes @connection.tables, "testings"
+ assert_includes @connection.data_sources, "testings"
end
end
end
diff --git a/activerecord/test/cases/migration/rename_table_test.rb b/activerecord/test/cases/migration/rename_table_test.rb
index 6d742d3f2f..8eb027d53f 100644
--- a/activerecord/test/cases/migration/rename_table_test.rb
+++ b/activerecord/test/cases/migration/rename_table_test.rb
@@ -15,7 +15,7 @@ module ActiveRecord
end
def teardown
- rename_table :octopi, :test_models if connection.table_exists? :octopi
+ ActiveSupport::Deprecation.silence { rename_table :octopi, :test_models if connection.table_exists? :octopi }
super
end
@@ -83,7 +83,7 @@ module ActiveRecord
enable_extension!('uuid-ossp', connection)
connection.create_table :cats, id: :uuid
assert_nothing_raised { rename_table :cats, :felines }
- assert connection.table_exists? :felines
+ ActiveSupport::Deprecation.silence { assert connection.table_exists? :felines }
ensure
disable_extension!('uuid-ossp', connection)
connection.drop_table :cats, if_exists: true
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 15a0e0516d..c3c204cf9f 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -524,33 +524,33 @@ class MigrationTest < ActiveRecord::TestCase
end
if ActiveRecord::Base.connection.supports_advisory_locks?
- def test_migrator_generates_valid_lock_key
+ def test_migrator_generates_valid_lock_id
migration = Class.new(ActiveRecord::Migration).new
migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
- lock_key = migrator.send(:generate_migrator_advisory_lock_key)
+ lock_id = migrator.send(:generate_migrator_advisory_lock_id)
- assert ActiveRecord::Base.connection.get_advisory_lock(lock_key),
- "the Migrator should have generated a valid lock key, but it didn't"
- assert ActiveRecord::Base.connection.release_advisory_lock(lock_key),
- "the Migrator should have generated a valid lock key, but it didn't"
+ assert ActiveRecord::Base.connection.get_advisory_lock(lock_id),
+ "the Migrator should have generated a valid lock id, but it didn't"
+ assert ActiveRecord::Base.connection.release_advisory_lock(lock_id),
+ "the Migrator should have generated a valid lock id, but it didn't"
end
- def test_generate_migrator_advisory_lock_key
+ def test_generate_migrator_advisory_lock_id
# It is important we are consistent with how we generate this so that
# exclusive locking works across migrator versions
migration = Class.new(ActiveRecord::Migration).new
migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
- lock_key = migrator.send(:generate_migrator_advisory_lock_key)
+ lock_id = migrator.send(:generate_migrator_advisory_lock_id)
current_database = ActiveRecord::Base.connection.current_database
salt = ActiveRecord::Migrator::MIGRATOR_SALT
- expected_key = Zlib.crc32(current_database) * salt
+ expected_id = Zlib.crc32(current_database) * salt
- assert lock_key == expected_key, "expected lock key generated by the migrator to be #{expected_key}, but it was #{lock_key} instead"
- assert lock_key.is_a?(Fixnum), "expected lock key to be a Fixnum, but it wasn't"
- assert lock_key.bit_length <= 63, "lock key must be a signed integer of max 63 bits magnitude"
+ assert lock_id == expected_id, "expected lock id generated by the migrator to be #{expected_id}, but it was #{lock_id} instead"
+ assert lock_id.is_a?(Fixnum), "expected lock id to be a Fixnum, but it wasn't"
+ assert lock_id.bit_length <= 63, "lock id must be a signed integer of max 63 bits magnitude"
end
def test_migrator_one_up_with_unavailable_lock
@@ -564,9 +564,9 @@ class MigrationTest < ActiveRecord::TestCase
}.new
migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
- lock_key = migrator.send(:generate_migrator_advisory_lock_key)
+ lock_id = migrator.send(:generate_migrator_advisory_lock_id)
- with_another_process_holding_lock(lock_key) do
+ with_another_process_holding_lock(lock_id) do
assert_raise(ActiveRecord::ConcurrentMigrationError) { migrator.migrate }
end
@@ -585,9 +585,9 @@ class MigrationTest < ActiveRecord::TestCase
}.new
migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
- lock_key = migrator.send(:generate_migrator_advisory_lock_key)
+ lock_id = migrator.send(:generate_migrator_advisory_lock_id)
- with_another_process_holding_lock(lock_key) do
+ with_another_process_holding_lock(lock_id) do
assert_raise(ActiveRecord::ConcurrentMigrationError) { migrator.run }
end
@@ -606,18 +606,18 @@ class MigrationTest < ActiveRecord::TestCase
}
end
- def with_another_process_holding_lock(lock_key)
+ def with_another_process_holding_lock(lock_id)
thread_lock = Concurrent::CountDownLatch.new
test_terminated = Concurrent::CountDownLatch.new
other_process = Thread.new do
begin
conn = ActiveRecord::Base.connection_pool.checkout
- conn.get_advisory_lock(lock_key)
+ conn.get_advisory_lock(lock_id)
thread_lock.count_down
test_terminated.wait # hold the lock open until we tested everything
ensure
- conn.release_advisory_lock(lock_key)
+ conn.release_advisory_lock(lock_id)
ActiveRecord::Base.connection_pool.checkin(conn)
end
end
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index 2ff6938e7b..dbf088f455 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -313,9 +313,9 @@ class MigratorTest < ActiveRecord::TestCase
_, migrator = migrator_class(3)
ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
- assert_not ActiveRecord::Base.connection.table_exists?('schema_migrations')
+ ActiveSupport::Deprecation.silence { assert_not ActiveRecord::Base.connection.table_exists?('schema_migrations') }
migrator.migrate("valid", 1)
- assert ActiveRecord::Base.connection.table_exists?('schema_migrations')
+ ActiveSupport::Deprecation.silence { assert ActiveRecord::Base.connection.table_exists?('schema_migrations') }
end
def test_migrator_forward
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 93cb631a04..0b700afcb4 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -1068,39 +1068,4 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR
assert_not part.valid?
assert_equal ["Ship name can't be blank"], part.errors.full_messages
end
-
- class ProtectedParameters
- def initialize(hash)
- @hash = hash
- end
-
- def permitted?
- true
- end
-
- def [](key)
- @hash[key]
- end
-
- def to_h
- @hash
- end
- end
-
- test "strong params style objects can be assigned for singular associations" do
- params = { name: "Stern", ship_attributes:
- ProtectedParameters.new(name: "The Black Rock") }
- part = ShipPart.new(params)
-
- assert_equal "Stern", part.name
- assert_equal "The Black Rock", part.ship.name
- end
-
- test "strong params style objects can be assigned for collection associations" do
- params = { trinkets_attributes: ProtectedParameters.new("0" => ProtectedParameters.new(name: "Necklace"), "1" => ProtectedParameters.new(name: "Spoon")) }
- part = ShipPart.new(params)
-
- assert_equal "Necklace", part.trinkets[0].name
- assert_equal "Spoon", part.trinkets[1].name
- end
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index acc3103ac6..b2e59e3970 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -966,4 +966,17 @@ class PersistenceTest < ActiveRecord::TestCase
widget.reset_column_information
end
end
+
+ def test_reset_column_information_resets_children
+ child = Class.new(Topic)
+ child.new # force schema to load
+
+ ActiveRecord::Base.connection.add_column(:topics, :foo, :string)
+ Topic.reset_column_information
+
+ assert_equal "bar", child.new(foo: :bar).foo
+ ensure
+ ActiveRecord::Base.connection.remove_column(:topics, :foo)
+ Topic.reset_column_information
+ end
end
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index daa3271777..bca50dd008 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -44,7 +44,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase
conn = ActiveRecord::Base.connection_pool.checkout
ActiveRecord::Base.connection_pool.checkin conn
@connection_count += 1
- ActiveRecord::Base.connection.tables
+ ActiveRecord::Base.connection.data_sources
rescue ActiveRecord::ConnectionTimeoutError
@timed_out += 1
end
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 344822c883..d883784553 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -224,7 +224,7 @@ class PrimaryKeyAnyTypeTest < ActiveRecord::TestCase
end
teardown do
- @connection.drop_table(:barcodes) if @connection.table_exists? :barcodes
+ @connection.drop_table(:barcodes, if_exists: true)
end
def test_any_type_primary_key
diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb
index 989f4e1e5d..f0e07e0731 100644
--- a/activerecord/test/cases/relation/delegation_test.rb
+++ b/activerecord/test/cases/relation/delegation_test.rb
@@ -27,7 +27,7 @@ module ActiveRecord
module DelegationWhitelistBlacklistTests
ARRAY_DELEGATES = [
- :+, :-, :|, :&, :[],
+ :+, :-, :|, :&, :[], :shuffle,
:all?, :collect, :compact, :detect, :each, :each_cons, :each_with_index,
:exclude?, :find_all, :flat_map, :group_by, :include?, :length,
:map, :none?, :one?, :partition, :reject, :reverse,
@@ -40,12 +40,6 @@ module ActiveRecord
assert_respond_to target, method
end
end
-
- ActiveRecord::Delegation::BLACKLISTED_ARRAY_METHODS.each do |method|
- define_method "test_#{method}_is_not_delegated_to_Array" do
- assert_raises(NoMethodError) { call_method(target, method) }
- end
- end
end
class DelegationAssociationTest < DelegationTest
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 8794bc8043..f46d414b95 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -235,6 +235,13 @@ module ActiveRecord
assert_equal 3, relation.where(id: post.id).pluck(:id).size
end
+ def test_merge_raises_with_invalid_argument
+ assert_raises ArgumentError do
+ relation = Relation.new(FakeKlass, :b, nil)
+ relation.merge(true)
+ end
+ end
+
def test_respond_to_for_non_selected_element
post = Post.select(:title).first
assert_equal false, post.respond_to?(:body), "post should not respond_to?(:body) since invoking it raises exception"
diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb
index aa5c04dd06..239f63d27b 100644
--- a/activerecord/test/cases/sanitize_test.rb
+++ b/activerecord/test/cases/sanitize_test.rb
@@ -79,4 +79,98 @@ class SanitizeTest < ActiveRecord::TestCase
searchable_post.search("20% _reduction_!").to_a
end
end
+
+ def test_bind_arity
+ assert_nothing_raised { bind '' }
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
+
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
+ assert_nothing_raised { bind '?', 1 }
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
+ end
+
+ def test_named_bind_variables
+ assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
+ assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
+
+ assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
+ end
+
+ def test_named_bind_arity
+ assert_nothing_raised { bind "name = :name", { name: "37signals" } }
+ assert_nothing_raised { bind "name = :name", { name: "37signals", id: 1 } }
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "name = :name", { id: 1 } }
+ end
+
+ class SimpleEnumerable
+ include Enumerable
+
+ def initialize(ary)
+ @ary = ary
+ end
+
+ def each(&b)
+ @ary.each(&b)
+ end
+ end
+
+ def test_bind_enumerable
+ quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})
+
+ assert_equal '1,2,3', bind('?', [1, 2, 3])
+ assert_equal quoted_abc, bind('?', %w(a b c))
+
+ assert_equal '1,2,3', bind(':a', :a => [1, 2, 3])
+ assert_equal quoted_abc, bind(':a', :a => %w(a b c)) # '
+
+ assert_equal '1,2,3', bind('?', SimpleEnumerable.new([1, 2, 3]))
+ assert_equal quoted_abc, bind('?', SimpleEnumerable.new(%w(a b c)))
+
+ assert_equal '1,2,3', bind(':a', :a => SimpleEnumerable.new([1, 2, 3]))
+ assert_equal quoted_abc, bind(':a', :a => SimpleEnumerable.new(%w(a b c))) # '
+ end
+
+ def test_bind_empty_enumerable
+ quoted_nil = ActiveRecord::Base.connection.quote(nil)
+ assert_equal quoted_nil, bind('?', [])
+ assert_equal " in (#{quoted_nil})", bind(' in (?)', [])
+ assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', [])
+ end
+
+ def test_bind_empty_string
+ quoted_empty = ActiveRecord::Base.connection.quote('')
+ assert_equal quoted_empty, bind('?', '')
+ end
+
+ def test_bind_chars
+ quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
+ quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
+ assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
+ assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
+ end
+
+ def test_bind_record
+ o = Struct.new(:quoted_id).new(1)
+ assert_equal '1', bind('?', o)
+
+ os = [o] * 3
+ assert_equal '1,1,1', bind('?', os)
+ end
+
+ def test_named_bind_with_postgresql_type_casts
+ l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
+ assert_nothing_raised(&l)
+ assert_equal "#{ActiveRecord::Base.connection.quote('10')}::integer '2009-01-01'::date", l.call
+ end
+
+ private
+ def bind(statement, *vars)
+ if vars.first.is_a?(Hash)
+ ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
+ else
+ ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
+ end
+ end
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 2a2c2bc8d0..43f133b12d 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -353,6 +353,38 @@ class SchemaDumperTest < ActiveRecord::TestCase
ActiveRecord::Base.table_name_suffix = ActiveRecord::Base.table_name_prefix = ''
$stdout = original
end
+
+ def test_schema_dump_with_table_name_prefix_and_ignoring_tables
+ original, $stdout = $stdout, StringIO.new
+
+ create_cat_migration = Class.new(ActiveRecord::Migration) do
+ def change
+ create_table("cats") do |t|
+ end
+ create_table("omg_cats") do |t|
+ end
+ end
+ end
+
+ original_table_name_prefix = ActiveRecord::Base.table_name_prefix
+ original_schema_dumper_ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
+ ActiveRecord::Base.table_name_prefix = 'omg_'
+ ActiveRecord::SchemaDumper.ignore_tables = ["cats"]
+ migration = create_cat_migration.new
+ migration.migrate(:up)
+
+ stream = StringIO.new
+ output = ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream).string
+
+ assert_match %r{create_table "omg_cats"}, output
+ refute_match %r{create_table "cats"}, output
+ ensure
+ migration.migrate(:down)
+ ActiveRecord::Base.table_name_prefix = original_table_name_prefix
+ ActiveRecord::SchemaDumper.ignore_tables = original_schema_dumper_ignore_tables
+
+ $stdout = original
+ end
end
class SchemaDumperDefaultsTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index c31f94b2f2..ba53f340ae 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -212,7 +212,7 @@ module ActiveRecord
def test_structure_dump_with_schema_search_path
@configuration['schema_search_path'] = 'foo,bar'
- Kernel.expects(:system).with('pg_dump', '-s', '-x', '-O', '-f', @filename, '--schema=foo --schema=bar', 'my-app-db').returns(true)
+ Kernel.expects(:system).with('pg_dump', '-s', '-x', '-O', '-f', @filename, '--schema=foo', '--schema=bar', 'my-app-db').returns(true)
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
end
@@ -228,7 +228,7 @@ module ActiveRecord
end
def test_structure_dump_with_dump_schemas_string
- Kernel.expects(:system).with("pg_dump", '-s', '-x', '-O', '-f', @filename, '--schema=foo --schema=bar', "my-app-db").returns(true)
+ Kernel.expects(:system).with("pg_dump", '-s', '-x', '-O', '-f', @filename, '--schema=foo', '--schema=bar', "my-app-db").returns(true)
with_dump_schemas('foo,bar') do
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
diff --git a/activerecord/test/cases/time_precision_test.rb b/activerecord/test/cases/time_precision_test.rb
index ff7a81fe60..3b6e4dcc2b 100644
--- a/activerecord/test/cases/time_precision_test.rb
+++ b/activerecord/test/cases/time_precision_test.rb
@@ -10,6 +10,7 @@ class TimePrecisionTest < ActiveRecord::TestCase
setup do
@connection = ActiveRecord::Base.connection
+ Foo.reset_column_information
end
teardown do
@@ -20,8 +21,8 @@ class TimePrecisionTest < ActiveRecord::TestCase
@connection.create_table(:foos, force: true)
@connection.add_column :foos, :start, :time, precision: 3
@connection.add_column :foos, :finish, :time, precision: 6
- assert_equal 3, activerecord_column_option('foos', 'start', 'precision')
- assert_equal 6, activerecord_column_option('foos', 'finish', 'precision')
+ assert_equal 3, Foo.columns_hash['start'].precision
+ assert_equal 6, Foo.columns_hash['finish'].precision
end
def test_passing_precision_to_time_does_not_set_limit
@@ -29,8 +30,8 @@ class TimePrecisionTest < ActiveRecord::TestCase
t.time :start, precision: 3
t.time :finish, precision: 6
end
- assert_nil activerecord_column_option('foos', 'start', 'limit')
- assert_nil activerecord_column_option('foos', 'finish', 'limit')
+ assert_nil Foo.columns_hash['start'].limit
+ assert_nil Foo.columns_hash['finish'].limit
end
def test_invalid_time_precision_raises_error
@@ -42,15 +43,6 @@ class TimePrecisionTest < ActiveRecord::TestCase
end
end
- def test_database_agrees_with_activerecord_about_precision
- @connection.create_table(:foos, force: true) do |t|
- t.time :start, precision: 2
- t.time :finish, precision: 4
- end
- assert_equal 2, database_datetime_precision('foos', 'start')
- assert_equal 4, database_datetime_precision('foos', 'finish')
- end
-
def test_formatting_time_according_to_precision
@connection.create_table(:foos, force: true) do |t|
t.time :start, precision: 0
@@ -88,21 +80,5 @@ class TimePrecisionTest < ActiveRecord::TestCase
end
end
- private
-
- def database_datetime_precision(table_name, column_name)
- results = @connection.exec_query("SELECT column_name, datetime_precision FROM information_schema.columns WHERE table_name = '#{table_name}'")
- result = results.find do |result_hash|
- result_hash["column_name"] == column_name
- end
- result && result["datetime_precision"].to_i
- end
-
- def activerecord_column_option(tablename, column_name, option)
- result = @connection.columns(tablename).find do |column|
- column.name == column_name
- end
- result && result.send(option)
- end
end
end
diff --git a/activerecord/test/cases/touch_later_test.rb b/activerecord/test/cases/touch_later_test.rb
index 7058f4fbe2..b47769eed7 100644
--- a/activerecord/test/cases/touch_later_test.rb
+++ b/activerecord/test/cases/touch_later_test.rb
@@ -95,16 +95,14 @@ class TouchLaterTest < ActiveRecord::TestCase
end
def test_touching_three_deep
- skip "Pending from #19324"
-
previous_tree_updated_at = trees(:root).updated_at
previous_grandparent_updated_at = nodes(:grandparent).updated_at
previous_parent_updated_at = nodes(:parent_a).updated_at
previous_child_updated_at = nodes(:child_one_of_a).updated_at
- travel 5.seconds
-
- Node.create! parent: nodes(:child_one_of_a), tree: trees(:root)
+ travel 5.seconds do
+ Node.create! parent: nodes(:child_one_of_a), tree: trees(:root)
+ end
assert_not_equal nodes(:child_one_of_a).reload.updated_at, previous_child_updated_at
assert_not_equal nodes(:parent_a).reload.updated_at, previous_parent_updated_at
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index f2229939c8..637f89196e 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -35,9 +35,9 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
has_many :replies, class_name: "ReplyWithCallbacks", foreign_key: "parent_id"
after_commit { |record| record.do_after_commit(nil) }
- after_commit(on: :create) { |record| record.do_after_commit(:create) }
- after_commit(on: :update) { |record| record.do_after_commit(:update) }
- after_commit(on: :destroy) { |record| record.do_after_commit(:destroy) }
+ after_create_commit { |record| record.do_after_commit(:create) }
+ after_update_commit { |record| record.do_after_commit(:update) }
+ after_destroy_commit { |record| record.do_after_commit(:destroy) }
after_rollback { |record| record.do_after_rollback(nil) }
after_rollback(on: :create) { |record| record.do_after_rollback(:create) }
after_rollback(on: :update) { |record| record.do_after_rollback(:update) }
diff --git a/activerecord/test/cases/validations/association_validation_test.rb b/activerecord/test/cases/validations/association_validation_test.rb
index bff5ffa65e..584a3dc0d8 100644
--- a/activerecord/test/cases/validations/association_validation_test.rb
+++ b/activerecord/test/cases/validations/association_validation_test.rb
@@ -45,6 +45,18 @@ class AssociationValidationTest < ActiveRecord::TestCase
assert t.valid?
end
+ def test_validates_associated_without_marked_for_destruction
+ reply = Class.new do
+ def valid?
+ true
+ end
+ end
+ Topic.validates_associated(:replies)
+ t = Topic.new
+ t.define_singleton_method(:replies) { [reply.new] }
+ assert t.valid?
+ end
+
def test_validates_associated_with_custom_message_using_quotes
Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
Topic.validates_presence_of :content
diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb
index e80d8bd584..d50ae74e35 100644
--- a/activerecord/test/cases/view_test.rb
+++ b/activerecord/test/cases/view_test.rb
@@ -45,7 +45,7 @@ module ViewBehavior
def test_table_exists
view_name = Ebook.table_name
# TODO: switch this assertion around once we changed #tables to not return views.
- assert @connection.table_exists?(view_name), "'#{view_name}' table should exist"
+ ActiveSupport::Deprecation.silence { assert @connection.table_exists?(view_name), "'#{view_name}' table should exist" }
end
def test_views_ara_valid_data_sources
@@ -87,7 +87,7 @@ class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
end
def drop_view(name)
- @connection.execute "DROP VIEW #{name}" if @connection.table_exists? name
+ @connection.execute "DROP VIEW #{name}" if @connection.view_exists? name
end
end
@@ -106,7 +106,7 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
end
teardown do
- @connection.execute "DROP VIEW paperbacks" if @connection.table_exists? "paperbacks"
+ @connection.execute "DROP VIEW paperbacks" if @connection.view_exists? "paperbacks"
end
def test_reading
@@ -125,7 +125,8 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
def test_table_exists
view_name = Paperback.table_name
- assert @connection.table_exists?(view_name), "'#{view_name}' table should exist"
+ # TODO: switch this assertion around once we changed #tables to not return views.
+ ActiveSupport::Deprecation.silence { assert @connection.table_exists?(view_name), "'#{view_name}' table should exist" }
end
def test_column_definitions
@@ -167,7 +168,7 @@ class UpdateableViewTest < ActiveRecord::TestCase
end
teardown do
- @connection.execute "DROP VIEW printed_books" if @connection.table_exists? "printed_books"
+ @connection.execute "DROP VIEW printed_books" if @connection.view_exists? "printed_books"
end
def test_update_record
@@ -209,8 +210,7 @@ class MaterializedViewTest < ActiveRecord::PostgreSQLTestCase
end
def drop_view(name)
- @connection.execute "DROP MATERIALIZED VIEW #{name}" if @connection.table_exists? name
-
+ @connection.execute "DROP MATERIALIZED VIEW #{name}" if @connection.view_exists? name
end
end
end
diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb
index a6e83fe353..dc0296305a 100644
--- a/activerecord/test/models/bulb.rb
+++ b/activerecord/test/models/bulb.rb
@@ -1,6 +1,7 @@
class Bulb < ActiveRecord::Base
default_scope { where(:name => 'defaulty') }
belongs_to :car, :touch => true
+ scope :awesome, -> { where(frickinawesome: true) }
attr_reader :scope_after_initialize, :attributes_after_initialize
@@ -49,3 +50,9 @@ class FailedBulb < Bulb
throw(:abort)
end
end
+
+class TrickyBulb < Bulb
+ after_create do |record|
+ record.car.bulbs.to_a
+ end
+end
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index 81263b79d1..778c22b1f6 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -4,6 +4,7 @@ class Car < ActiveRecord::Base
has_many :funky_bulbs, class_name: 'FunkyBulb', dependent: :destroy
has_many :failed_bulbs, class_name: 'FailedBulb', dependent: :destroy
has_many :foo_bulbs, -> { where(:name => 'foo') }, :class_name => "Bulb"
+ has_many :awesome_bulbs, -> { awesome }, class_name: "Bulb"
has_one :bulb
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 66a1f5aa8a..025184f63a 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -114,7 +114,7 @@ ActiveRecord::Schema.define do
create_table :bulbs, force: true do |t|
t.integer :car_id
t.string :name
- t.boolean :frickinawesome
+ t.boolean :frickinawesome, default: false
t.string :color
end
@@ -356,7 +356,7 @@ ActiveRecord::Schema.define do
t.column :key, :string
end
- create_table :guitar, force: true do |t|
+ create_table :guitars, force: true do |t|
t.string :color
end
diff --git a/activerecord/test/support/schema_dumping_helper.rb b/activerecord/test/support/schema_dumping_helper.rb
index 2d1651454d..666c1b6a14 100644
--- a/activerecord/test/support/schema_dumping_helper.rb
+++ b/activerecord/test/support/schema_dumping_helper.rb
@@ -1,7 +1,7 @@
module SchemaDumpingHelper
def dump_table_schema(table, connection = ActiveRecord::Base.connection)
old_ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
- ActiveRecord::SchemaDumper.ignore_tables = connection.tables - [table]
+ ActiveRecord::SchemaDumper.ignore_tables = connection.data_sources - [table]
stream = StringIO.new
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
stream.string