aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG.md5
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb1
-rw-r--r--actionpack/test/controller/filters_test.rb25
-rw-r--r--actionpack/test/dispatch/routing_test.rb12
-rw-r--r--activemodel/lib/active_model/naming.rb2
-rw-r--r--activemodel/test/cases/serializers/json_serialization_test.rb4
-rw-r--r--activerecord/CHANGELOG.md19
-rw-r--r--activerecord/lib/active_record/associations.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb37
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb3
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb4
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb14
-rw-r--r--activerecord/lib/active_record/transactions.rb39
-rw-r--r--activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb89
-rw-r--r--activerecord/test/cases/fixtures_test.rb20
-rw-r--r--activerecord/test/cases/relations_test.rb11
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb5
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb60
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb10
-rw-r--r--guides/source/association_basics.md3
-rw-r--r--railties/lib/rails/paths.rb2
27 files changed, 344 insertions, 49 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 8f36af94b4..8685cd6495 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Deprecate AbstractController#skip_action_callback in favor of individual skip_callback methods
+ (which can be made to raise an error if no callback was removed).
+
+ *Iain Beeston*
+
* Alias the `ActionDispatch::Request#uuid` method to `ActionDispatch::Request#request_id`.
Due to implementation, `config.log_tags = [:request_id]` also works in substitute
for `config.log_tags = [:uuid]`.
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index 69f490b327..f4fd1db36c 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -63,6 +63,7 @@ module AbstractController
# impossible to skip a callback defined using an anonymous proc
# using #skip_action_callback
def skip_action_callback(*names)
+ ActiveSupport::Deprecation.warn('`skip_action_callback` is deprecated and will be removed in the next major version of Rails. Please use skip_before_action, skip_after_action or skip_around_action instead.')
skip_before_action(*names)
skip_after_action(*names)
skip_around_action(*names)
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index b9fb6be4e3..26b94f0db8 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -26,7 +26,6 @@ class ActionController::Base
end
class FilterTest < ActionController::TestCase
-
class TestController < ActionController::Base
before_action :ensure_login
after_action :clean_up
@@ -967,8 +966,15 @@ class ControllerWithAllTypesOfFilters < PostsController
end
class ControllerWithTwoLessFilters < ControllerWithAllTypesOfFilters
- skip_action_callback :around_again
- skip_action_callback :after
+ skip_around_action :around_again
+ skip_after_action :after
+end
+
+class SkipFilterUsingSkipActionCallback < ControllerWithAllTypesOfFilters
+ ActiveSupport::Deprecation.silence do
+ skip_action_callback :around_again
+ skip_action_callback :after
+ end
end
class YieldingAroundFiltersTest < ActionController::TestCase
@@ -1055,6 +1061,19 @@ class YieldingAroundFiltersTest < ActionController::TestCase
assert_equal 3, controller.instance_variable_get(:@try)
end
+ def test_skipping_with_skip_action_callback
+ test_process(SkipFilterUsingSkipActionCallback,'no_raise')
+ assert_equal 'before around (before yield) around (after yield)', assigns['ran_filter'].join(' ')
+ end
+
+ def test_deprecated_skip_action_callback
+ assert_deprecated do
+ Class.new(PostsController) do
+ skip_action_callback :clean_up
+ end
+ end
+ end
+
protected
def test_process(controller, action = "show")
@controller = controller.is_a?(Class) ? controller.new : controller
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index deb289bd57..d65d2e8e8f 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -3489,13 +3489,19 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
def test_head_fetch_with_mount_on_root
draw do
get '/home' => 'test#index'
- mount lambda { |env| [404, {"Content-Type" => "text/html"}, ["testing"]] }, at: '/'
+ mount lambda { |env| [200, {}, [env['REQUEST_METHOD']]] }, at: '/'
end
+
+ # HEAD request matches `get /home` rather than the lower-precedence
+ # Rack app mounted at `/`
head '/home'
assert_response :success
+ assert_equal 'test#index', @response.body
- head '/'
- assert_response :not_found
+ # But the Rack app can still respond to its own HEAD requests.
+ head '/foobar'
+ assert_response :ok
+ assert_equal 'HEAD', @response.body
end
private
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 2bc3eeaa19..22010b517c 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -130,7 +130,7 @@ module ActiveModel
#
# Equivalent to +to_s+.
delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
- :to_str, to: :name
+ :to_str, :as_json, to: :name
# Returns a new ActiveModel::Name instance. By default, the +namespace+
# and +name+ option will take the namespace and name of the given class
diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb
index e2eb91eeb0..d765a47636 100644
--- a/activemodel/test/cases/serializers/json_serialization_test.rb
+++ b/activemodel/test/cases/serializers/json_serialization_test.rb
@@ -195,4 +195,8 @@ class JsonSerializationTest < ActiveModel::TestCase
assert_no_match %r{"awesome":}, json
assert_no_match %r{"preferences":}, json
end
+
+ test "Class.model_name should be json encodable" do
+ assert_match %r{"Contact"}, Contact.model_name.to_json
+ end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 51c047479f..6cdf9ebd9b 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,22 @@
+* PostgreSQL, no longer disables user triggers if system triggers can't be
+ disabled. Disabling user triggers does not fulfill what the method promises.
+ Rails currently requires superuser privileges for this method.
+
+ If you absolutely rely on this behavior, consider patching
+ `disable_referential_integrity`.
+
+ *Yves Senn*
+
+* Restore aborted transaction state when `disable_referential_integrity` fails
+ due to missing permissions.
+
+ *Toby Ovod-Everett*, *Yves Senn*
+
+* PostgreSQL, print warning message if `disable_referential_integrity` fails
+ due to missing permissions.
+
+ *Andrey Nering*, *Yves Senn*
+
* Allow `:limit` option for MySQL bigint primary key support.
Example:
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 0b33ee881b..cecb2dbd0b 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1019,7 +1019,7 @@ module ActiveRecord
# can affect what it does.
#
# Note that <tt>:dependent</tt> option is ignored for +has_one+ <tt>:through</tt> associations.
- #
+ #
# === Delete or destroy?
#
# +has_many+ and +has_and_belongs_to_many+ associations have the methods <tt>destroy</tt>,
@@ -1529,8 +1529,6 @@ module ActiveRecord
# NOTE: <tt>required</tt> is set to <tt>true</tt> by default and is deprecated. If
# you don't want to have association presence validated, use <tt>optional: true</tt>.
#
- #
- #
# Option examples:
# belongs_to :firm, foreign_key: "client_of"
# belongs_to :person, primary_key: "name", foreign_key: "person_name"
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 0438c95bd7..d42f9a894b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -770,7 +770,7 @@ module ActiveRecord
# # Check a foreign key exists
# foreign_key_exists?(:accounts, :branches)
#
- # # Check a foreign key on specified column exists
+ # # Check a foreign key on a specified column exists
# foreign_key_exists?(:accounts, column: :owner_id)
#
# # Check a foreign key with a custom name exists
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index 11440e30d4..3a1e4a4a88 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -77,6 +77,10 @@ module ActiveRecord
@state.set_state(:committed)
end
+ def before_commit_records
+ records.uniq.each(&:before_committed!)
+ end
+
def commit_records
ite = records.uniq
while record = ite.shift
@@ -159,13 +163,15 @@ module ActiveRecord
def commit_transaction
inner_transaction = @stack.pop
- inner_transaction.commit
if current_transaction.joinable?
+ inner_transaction.commit
inner_transaction.records.each do |r|
r.add_to_transaction
end
else
+ inner_transaction.before_commit_records
+ inner_transaction.commit
inner_transaction.commit_records
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index c307189980..ae42e8ef8d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -478,7 +478,6 @@ module ActiveRecord
message = "#{e.class.name}: #{e.message.force_encoding sql.encoding}: #{sql}"
end
- @logger.error message if @logger
exception = translate_exception(e, message)
exception.set_backtrace e.backtrace
exception
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 8f53794be0..98009da2bd 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -11,6 +11,10 @@ module ActiveRecord
options[:auto_increment] = true if type == :bigint
super
end
+ end
+
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
+ include ColumnMethods
def new_column_definition(name, type, options) # :nodoc:
column = super
@@ -23,10 +27,6 @@ module ActiveRecord
end
end
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
- include ColumnMethods
- end
-
class Table < ActiveRecord::ConnectionAdapters::Table
include ColumnMethods
end
@@ -98,6 +98,7 @@ module ActiveRecord
def prepare_column_options(column)
spec = super
spec.delete(:precision) if /time/ === column.sql_type && column.precision == 0
+ spec.delete(:limit) if :boolean === column.type
spec
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb b/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb
index 52b307c432..c1835380f9 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb
@@ -8,20 +8,39 @@ module ActiveRecord
def disable_referential_integrity # :nodoc:
if supports_disable_referential_integrity?
+ original_exception = nil
+
begin
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
- rescue
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER USER" }.join(";"))
+ transaction(requires_new: true) do
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
+ end
+ rescue => e
+ original_exception = e
end
- end
- yield
- ensure
- if supports_disable_referential_integrity?
+
+ begin
+ yield
+ rescue ActiveRecord::InvalidForeignKey => e
+ warn <<-WARNING
+WARNING: Rails was not able to disable referential integrity.
+
+This is most likely caused due to missing permissions.
+Rails needs superuser privileges to disable referential integrity.
+
+ cause: #{original_exception.try(:message)}
+
+ WARNING
+ raise e
+ end
+
begin
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
+ transaction(require_new: true) do
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
+ end
rescue
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER USER" }.join(";"))
end
+ else
+ yield
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 400b586c95..7e184dd510 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -121,6 +121,7 @@ module ActiveRecord
@config = config
@visitor = Arel::Visitors::SQLite.new self
+ @quoted_column_names = {}
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
@prepared_statements = true
@@ -240,7 +241,7 @@ module ActiveRecord
end
def quote_column_name(name) #:nodoc:
- %Q("#{name.to_s.gsub('"', '""')}")
+ @quoted_column_names[name] ||= %Q("#{name.to_s.gsub('"', '""')}")
end
#--
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index de48681746..d41872d767 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -488,7 +488,7 @@ module ActiveRecord
end
def has_transactional_callbacks? # :nodoc:
- !_rollback_callbacks.empty? || !_commit_callbacks.empty?
+ !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
end
# Updates the attributes on this particular ActiveRecord object so that
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 739be524da..75b0e1e08d 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -708,6 +708,10 @@ module ActiveRecord
def lhs_key
@association.through_reflection.foreign_key
end
+
+ def join_table
+ @association.through_reflection.table_name
+ end
end
private
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index a58d8355aa..a09437b4b0 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -93,7 +93,7 @@ module ActiveRecord
self.class.primary_key => id,
lock_col => previous_lock_value,
).update_all(
- attribute_names.map do |name|
+ attributes_for_update(attribute_names).map do |name|
[name, _read_attribute(name)]
end.to_h
)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 7514401072..69ce5cdc2a 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -999,11 +999,15 @@ module ActiveRecord
end
def arel_columns(columns)
- columns.map do |field|
- if (Symbol === field || String === field) && columns_hash.key?(field.to_s)
- arel_table[field]
- else
- field
+ if from_clause.value
+ columns
+ else
+ columns.map do |field|
+ if (Symbol === field || String === field) && columns_hash.key?(field.to_s)
+ arel_table[field]
+ else
+ field
+ end
end
end
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index dd405c7796..598defce50 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -7,6 +7,10 @@ module ActiveRecord
included do
define_callbacks :commit, :rollback,
+ :before_commit,
+ :before_commit_without_transaction_enrollment,
+ :commit_without_transaction_enrollment,
+ :rollback_without_transaction_enrollment,
scope: [:kind, :name]
end
@@ -206,6 +210,11 @@ module ActiveRecord
connection.transaction(options, &block)
end
+ def before_commit(*args, &block) # :nodoc:
+ set_options_for_callbacks!(args)
+ set_callback(:before_commit, :before, *args, &block)
+ end
+
# This callback is called after a record has been created, updated, or destroyed.
#
# You can specify that the callback should only be fired by a certain action with
@@ -233,6 +242,21 @@ module ActiveRecord
set_callback(:rollback, :after, *args, &block)
end
+ def before_commit_without_transaction_enrollment(*args, &block) # :nodoc:
+ set_options_for_callbacks!(args)
+ set_callback(:before_commit_without_transaction_enrollment, :before, *args, &block)
+ end
+
+ def after_commit_without_transaction_enrollment(*args, &block) # :nodoc:
+ set_options_for_callbacks!(args)
+ set_callback(:commit_without_transaction_enrollment, :after, *args, &block)
+ end
+
+ def after_rollback_without_transaction_enrollment(*args, &block) # :nodoc:
+ set_options_for_callbacks!(args)
+ set_callback(:rollback_without_transaction_enrollment, :after, *args, &block)
+ end
+
def raise_in_transactional_callbacks
ActiveSupport::Deprecation.warn('ActiveRecord::Base.raise_in_transactional_callbacks is deprecated and will be removed without replacement.')
true
@@ -296,12 +320,20 @@ module ActiveRecord
clear_transaction_record_state
end
+ def before_committed! # :nodoc:
+ _run_before_commit_without_transaction_enrollment_callbacks
+ _run_before_commit_callbacks
+ end
+
# Call the +after_commit+ callbacks.
#
# Ensure that it is not called if the object was never persisted (failed create),
# but call it after the commit of a destroyed object.
def committed!(should_run_callbacks: true) #:nodoc:
- _run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
+ if should_run_callbacks && destroyed? || persisted?
+ _run_commit_without_transaction_enrollment_callbacks
+ _run_commit_callbacks
+ end
ensure
force_clear_transaction_record_state
end
@@ -309,7 +341,10 @@ module ActiveRecord
# Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record
# state should be rolled back to the beginning or just to the last savepoint.
def rolledback!(force_restore_state: false, should_run_callbacks: true) #:nodoc:
- _run_rollback_callbacks if should_run_callbacks
+ if should_run_callbacks
+ _run_rollback_without_transaction_enrollment_callbacks
+ _run_rollback_callbacks
+ end
ensure
restore_transaction_record_state(force_restore_state)
clear_transaction_record_state
diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
new file mode 100644
index 0000000000..98291f1bbf
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
@@ -0,0 +1,89 @@
+require 'cases/helper'
+require 'support/connection_helper'
+
+class PostgreSQLReferentialIntegrityTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ include ConnectionHelper
+
+ module MissingSuperuserPrivileges
+ def execute(sql)
+ if sql.match(/DISABLE TRIGGER ALL/) || sql.match(/ENABLE TRIGGER ALL/)
+ super "BROKEN;" rescue nil # put transaction in broken state
+ raise ActiveRecord::StatementInvalid, 'PG::InsufficientPrivilege'
+ else
+ super
+ end
+ end
+ end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def teardown
+ reset_connection
+ if ActiveRecord::Base.connection.is_a?(MissingSuperuserPrivileges)
+ raise "MissingSuperuserPrivileges patch was not removed"
+ end
+ end
+
+ def test_should_reraise_invalid_foreign_key_exception_and_show_warning
+ @connection.extend MissingSuperuserPrivileges
+
+ warning = capture(:stderr) do
+ e = assert_raises(ActiveRecord::InvalidForeignKey) do
+ @connection.disable_referential_integrity do
+ raise ActiveRecord::InvalidForeignKey, 'Should be re-raised'
+ end
+ end
+ assert_equal 'Should be re-raised', e.message
+ end
+ assert_match (/WARNING: Rails was not able to disable referential integrity/), warning
+ assert_match (/cause: PG::InsufficientPrivilege/), warning
+ end
+
+ def test_does_not_print_warning_if_no_invalid_foreign_key_exception_was_raised
+ @connection.extend MissingSuperuserPrivileges
+
+ warning = capture(:stderr) do
+ e = assert_raises(ActiveRecord::StatementInvalid) do
+ @connection.disable_referential_integrity do
+ raise ActiveRecord::StatementInvalid, 'Should be re-raised'
+ end
+ end
+ assert_equal 'Should be re-raised', e.message
+ end
+ assert warning.blank?, "expected no warnings but got:\n#{warning}"
+ end
+
+ def test_does_not_break_transactions
+ @connection.extend MissingSuperuserPrivileges
+
+ @connection.transaction do
+ @connection.disable_referential_integrity do
+ assert_transaction_is_not_broken
+ end
+ assert_transaction_is_not_broken
+ end
+ end
+
+ def test_does_not_break_nested_transactions
+ @connection.extend MissingSuperuserPrivileges
+
+ @connection.transaction do
+ @connection.transaction(requires_new: true) do
+ @connection.disable_referential_integrity do
+ assert_transaction_is_not_broken
+ end
+ end
+ assert_transaction_is_not_broken
+ end
+ end
+
+ private
+
+ def assert_transaction_is_not_broken
+ assert_equal "1", @connection.select_value("SELECT 1")
+ end
+end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index f41245dfd2..7ef2ebc998 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -273,7 +273,7 @@ class HasManyThroughFixture < ActiveSupport::TestCase
Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
end
- def test_has_many_through
+ def test_has_many_through_with_default_table_name
pt = make_model "ParrotTreasure"
parrot = make_model "Parrot"
treasure = make_model "Treasure"
@@ -292,6 +292,24 @@ class HasManyThroughFixture < ActiveSupport::TestCase
assert_equal load_has_and_belongs_to_many['parrots_treasures'], rows['parrots_treasures']
end
+ def test_has_many_through_with_renamed_table
+ pt = make_model "ParrotTreasure"
+ parrot = make_model "Parrot"
+ treasure = make_model "Treasure"
+
+ pt.belongs_to :parrot, :class => parrot
+ pt.belongs_to :treasure, :class => treasure
+
+ parrot.has_many :parrot_treasures, :class => pt
+ parrot.has_many :treasures, :through => :parrot_treasures
+
+ parrots = File.join FIXTURES_ROOT, 'parrots'
+
+ fs = ActiveRecord::FixtureSet.new parrot.connection, "parrots", parrot, parrots
+ rows = fs.table_rows
+ assert_equal load_has_and_belongs_to_many['parrots_treasures'], rows['parrot_treasures']
+ end
+
def load_has_and_belongs_to_many
parrot = make_model "Parrot"
parrot.has_and_belongs_to_many :treasures
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index dec3a37f42..0cf44388fa 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -157,6 +157,17 @@ class RelationTest < ActiveRecord::TestCase
end
end
+ def test_select_with_subquery_in_from_does_not_use_original_table_name
+ relation = Comment.group(:type).select('COUNT(post_id) AS post_count, type')
+ subquery = Comment.from(relation).select('type','post_count')
+ assert_equal(relation.map(&:post_count).sort,subquery.map(&:post_count).sort)
+ end
+
+ def test_group_with_subquery_in_from_does_not_use_original_table_name
+ relation = Comment.group(:type).select('COUNT(post_id) AS post_count,type')
+ subquery = Comment.from(relation).group('type').average("post_count")
+ assert_equal(relation.map(&:post_count).sort,subquery.values.sort)
+ end
def test_finding_with_conditions
assert_equal ["David"], Author.where(:name => 'David').map(&:name)
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 7d8d6421a9..513f65f707 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -225,6 +225,11 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{t\.text\s+"long_text",\s+limit: 4294967295$}, output
end
+ def test_schema_does_not_include_limit_for_emulated_mysql_boolean_fields
+ output = standard_dump
+ assert_no_match %r{t\.boolean\s+"has_fun",.+limit: 1}, output
+ end
+
def test_schema_dumps_index_type
output = standard_dump
assert_match %r{add_index "key_tests", \["awesome"\], name: "index_key_tests_on_awesome", type: :fulltext}, output
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index f185cda263..e868022fed 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -400,3 +400,63 @@ class CallbacksOnMultipleActionsTest < ActiveRecord::TestCase
assert_equal [:update_and_destroy, :create_and_destroy], topic.history
end
end
+
+
+class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
+
+ class TopicWithoutTransactionalEnrollmentCallbacks < ActiveRecord::Base
+ self.table_name = :topics
+
+ before_commit_without_transaction_enrollment { |r| r.history << :before_commit }
+ after_commit_without_transaction_enrollment { |r| r.history << :after_commit }
+ after_rollback_without_transaction_enrollment { |r| r.history << :rollback }
+
+ def history
+ @history ||= []
+ end
+ end
+
+ def setup
+ @topic = TopicWithoutTransactionalEnrollmentCallbacks.create!
+ end
+
+ def test_commit_does_not_run_transactions_callbacks_without_enrollment
+ @topic.transaction do
+ @topic.content = 'foo'
+ @topic.save!
+ end
+ assert @topic.history.empty?
+ end
+
+ def test_commit_run_transactions_callbacks_with_explicit_enrollment
+ @topic.transaction do
+ 2.times do
+ @topic.content = 'foo'
+ @topic.save!
+ end
+ @topic.class.connection.add_transaction_record(@topic)
+ end
+ assert_equal [:before_commit, :after_commit], @topic.history
+ end
+
+ def test_rollback_does_not_run_transactions_callbacks_without_enrollment
+ @topic.transaction do
+ @topic.content = 'foo'
+ @topic.save!
+ raise ActiveRecord::Rollback
+ end
+ assert @topic.history.empty?
+ end
+
+ def test_rollback_run_transactions_callbacks_with_explicit_enrollment
+ @topic.transaction do
+ 2.times do
+ @topic.content = 'foo'
+ @topic.save!
+ end
+ @topic.class.connection.add_transaction_record(@topic)
+ raise ActiveRecord::Rollback
+ end
+ assert_equal [:rollback], @topic.history
+ end
+end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 77d2f7fda2..f84be0e7f4 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -88,16 +88,6 @@ _SQL
end
end
- begin
- execute <<_SQL
- CREATE TABLE postgresql_xml_data_type (
- id SERIAL PRIMARY KEY,
- data xml
- );
-_SQL
- rescue #This version of PostgreSQL either has no XML support or is was not compiled with XML support: skipping table
- end
-
# This table is to verify if the :limit option is being ignored for text and binary columns
create_table :limitless_fields, force: true do |t|
t.binary :binary, limit: 100_000
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 0079049d44..d6b2e75e1e 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -959,7 +959,8 @@ If you set the `:validate` option to `true`, then associated objects will be val
##### `:optional`
-If you set the `:optional` option to `true`, then associated object will be validated for presence. By default, this is `false`: associated objects will be validated for presence.
+If you set the `:optional` option to `true`, then the presence of the associated
+object won't be validated. By default, this option is set to `false`.
#### Scopes for `belongs_to`
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 5458036219..ebcaaaba46 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -7,7 +7,7 @@ module Rails
# root = Root.new "/rails"
# root.add "app/controllers", eager_load: true
#
- # The command above creates a new root object and add "app/controllers" as a path.
+ # The command above creates a new root object and adds "app/controllers" as a path.
# This means we can get a <tt>Rails::Paths::Path</tt> object back like below:
#
# path = root["app/controllers"]