aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb5
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb10
-rw-r--r--actionpack/test/controller/test_case_test.rb2
-rw-r--r--actionpack/test/dispatch/response_test.rb2
-rw-r--r--actionpack/test/dispatch/routing/concerns_test.rb2
-rw-r--r--actionpack/test/dispatch/static_test.rb2
-rw-r--r--activerecord/CHANGELOG.md60
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb3
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb15
-rw-r--r--activerecord/lib/active_record/autosave_association.rb12
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb6
-rw-r--r--activerecord/lib/active_record/tasks/sqlite_database_tasks.rb6
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb19
-rw-r--r--activerecord/test/cases/autosave_association_test.rb36
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb2
-rw-r--r--activerecord/test/cases/tasks/sqlite_rake_test.rb2
-rw-r--r--activerecord/test/models/guitar.rb4
-rw-r--r--activerecord/test/models/tuning_peg.rb4
-rw-r--r--activerecord/test/schema/schema.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/module/remove_method.rb13
-rw-r--r--activesupport/test/core_ext/module/remove_method_test.rb20
-rw-r--r--guides/source/active_support_instrumentation.md1
-rw-r--r--guides/source/configuring.md4
-rw-r--r--guides/source/initialization.md3
-rw-r--r--railties/lib/rails/application.rb3
-rw-r--r--railties/test/application/configuration_test.rb13
-rw-r--r--railties/test/application/rake/dbs_test.rb52
32 files changed, 277 insertions, 43 deletions
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index 13795f0dd8..287550db42 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -61,7 +61,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.')
+ ActiveSupport::Deprecation.warn('`skip_action_callback` is deprecated and will be removed in Rails 5.1. Please use skip_before_action, skip_after_action or skip_around_action instead.')
skip_before_action(*names, raise: false)
skip_after_action(*names, raise: false)
skip_around_action(*names, raise: false)
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 380e9d29b4..2cada1f68a 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -522,7 +522,7 @@ module ActionController
@request.delete_header 'HTTP_COOKIE'
if @request.have_cookie_jar?
- unless @response.committed?
+ unless @request.cookie_jar.committed?
@request.cookie_jar.write(@response)
self.cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index c54efb6541..f0127aa276 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -149,7 +149,6 @@ module ActionDispatch # :nodoc:
self.body, self.status = body, status
- @blank = false
@cv = new_cond
@committed = false
@sending = false
@@ -287,12 +286,8 @@ module ActionDispatch # :nodoc:
@stream.write string
end
- EMPTY = " "
-
# Allows you to manually set or override the response body.
def body=(body)
- @blank = true if body == EMPTY
-
if body.respond_to?(:to_path)
@stream = body
else
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 7e59bb68cf..790f9ea5d2 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -131,35 +131,35 @@ module ActionDispatch
# Performs a GET request, following any subsequent redirect.
# See +request_via_redirect+ for more information.
def get_via_redirect(path, *args)
- ActiveSupport::Deprecation.warn('`get_via_redirect` is deprecated and will be removed in the next version of Rails. Please use follow_redirect! manually after the request call for the same behavior.')
+ ActiveSupport::Deprecation.warn('`get_via_redirect` is deprecated and will be removed in Rails 5.1. Please use follow_redirect! manually after the request call for the same behavior.')
request_via_redirect(:get, path, *args)
end
# Performs a POST request, following any subsequent redirect.
# See +request_via_redirect+ for more information.
def post_via_redirect(path, *args)
- ActiveSupport::Deprecation.warn('`post_via_redirect` is deprecated and will be removed in the next version of Rails. Please use follow_redirect! manually after the request call for the same behavior.')
+ ActiveSupport::Deprecation.warn('`post_via_redirect` is deprecated and will be removed in Rails 5.1. Please use follow_redirect! manually after the request call for the same behavior.')
request_via_redirect(:post, path, *args)
end
# Performs a PATCH request, following any subsequent redirect.
# See +request_via_redirect+ for more information.
def patch_via_redirect(path, *args)
- ActiveSupport::Deprecation.warn('`patch_via_redirect` is deprecated and will be removed in the next version of Rails. Please use follow_redirect! manually after the request call for the same behavior.')
+ ActiveSupport::Deprecation.warn('`patch_via_redirect` is deprecated and will be removed in Rails 5.1. Please use follow_redirect! manually after the request call for the same behavior.')
request_via_redirect(:patch, path, *args)
end
# Performs a PUT request, following any subsequent redirect.
# See +request_via_redirect+ for more information.
def put_via_redirect(path, *args)
- ActiveSupport::Deprecation.warn('`put_via_redirect` is deprecated and will be removed in the next version of Rails. Please use follow_redirect! manually after the request call for the same behavior.')
+ ActiveSupport::Deprecation.warn('`put_via_redirect` is deprecated and will be removed in Rails 5.1. Please use follow_redirect! manually after the request call for the same behavior.')
request_via_redirect(:put, path, *args)
end
# Performs a DELETE request, following any subsequent redirect.
# See +request_via_redirect+ for more information.
def delete_via_redirect(path, *args)
- ActiveSupport::Deprecation.warn('`delete_via_redirect` is deprecated and will be removed in the next version of Rails. Please use follow_redirect! manually after the request call for the same behavior.')
+ ActiveSupport::Deprecation.warn('`delete_via_redirect` is deprecated and will be removed in Rails 5.1. Please use follow_redirect! manually after the request call for the same behavior.')
request_via_redirect(:delete, path, *args)
end
end
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index 40c97abd35..f1a296682d 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -139,7 +139,7 @@ XML
def delete_cookie
cookies.delete("foo")
- head :ok
+ render plain: 'ok'
end
def test_without_body
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 126379a23c..981d820ccf 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -206,8 +206,6 @@ class ResponseTest < ActiveSupport::TestCase
end
test "read content type with charset utf-16" do
- jruby_skip "https://github.com/jruby/jruby/issues/3138"
-
original = ActionDispatch::Response.default_charset
begin
ActionDispatch::Response.default_charset = 'utf-16'
diff --git a/actionpack/test/dispatch/routing/concerns_test.rb b/actionpack/test/dispatch/routing/concerns_test.rb
index 361ceca677..7ef513b0c8 100644
--- a/actionpack/test/dispatch/routing/concerns_test.rb
+++ b/actionpack/test/dispatch/routing/concerns_test.rb
@@ -109,8 +109,6 @@ class RoutingConcernsTest < ActionDispatch::IntegrationTest
end
def test_concerns_executes_block_in_context_of_current_mapper
- jruby_skip "https://github.com/jruby/jruby/issues/3143"
-
mapper = ActionDispatch::Routing::Mapper.new(ActionDispatch::Routing::RouteSet.new)
mapper.concern :test_concern do
resources :things
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index e62ed09b0a..1da57ab50b 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -75,8 +75,6 @@ module StaticTests
end
def test_served_static_file_with_non_english_filename
- jruby_skip "Stop skipping if following bug gets fixed: " \
- "http://jira.codehaus.org/browse/JRUBY-7192"
assert_html "means hello in Japanese\n", get("/foo/#{Rack::Utils.escape("こんにちは.html")}")
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 48005eee1a..7055cb2e25 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,52 @@
+* Includes HABTM returns correct size now. It's caused by the join dependency
+ only instantiates one HABTM object because the join table hasn't a primary key.
+
+ Fixes #16032.
+
+ Examples:
+
+ before:
+
+ Project.first.salaried_developers.size # => 3
+ Project.includes(:salaried_developers).first.salaried_developers.size # => 1
+
+ after:
+
+ Project.first.salaried_developers.size # => 3
+ Project.includes(:salaried_developers).first.salaried_developers.size # => 3
+
+ *Bigxiang*
+
+* Add option to index errors in nested attributes
+
+ For models which have nested attributes, errors within those models will
+ now be indexed if :index_errors is specified when defining a
+ has_many relationship, or if its set in the global config.
+
+ Example:
+
+ class Guitar < ActiveRecord::Base
+ has_many :tuning_pegs
+ accepts_nested_attributes_for :tuning_pegs
+ end
+
+ class TuningPeg < ActiveRecord::Base
+ belongs_to :guitar
+ validates_numericality_of :pitch
+ end
+
+ - Old style
+ - `guitar.errors["tuning_pegs.pitch"] = ["is not a number"]`
+
+ - New style (if defined globally, or set in has_many_relationship)
+ - `guitar.errors["tuning_pegs[1].pitch"] = ["is not a number"]`
+
+ *Michael Probber and Terence Sun*
+
+* Exit with non-zero status for failed database rake tasks.
+
+ *Jay Hayes*
+
* Queries such as `Computer.joins(:monitor).group(:status).count` will now be
interpreted as `Computer.joins(:monitor).group('computers.status').count`
so that when `Computer` and `Monitor` have both `status` columns we don't
@@ -110,9 +159,8 @@
database.
- m = Model.create!
- m.created_at.usec == m.reload.created_at.usec
- # => false
+ m = Model.create!
+ m.created_at.usec == m.reload.created_at.usec # => false
# due to different precision in Time.now and database column
If the precision is low enough, (mysql default is 0, so it is always low
@@ -300,9 +348,9 @@
Example:
- @users = User.where("name like ?", "%Alberto%")
- @users.cache_key
- => "/users/query-5942b155a43b139f2471b872ac54251f-3-20150714212107656125000"
+ @users = User.where("name like ?", "%Alberto%")
+ @users.cache_key
+ # => "/users/query-5942b155a43b139f2471b872ac54251f-3-20150714212107656125000"
*Alberto Fernández-Capel*
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index d4f079e346..b806a2f832 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -149,7 +149,7 @@ module ActiveRecord
class HasOneThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly #:nodoc:
end
- # This error is raised when trying to eager load a poloymorphic association using a JOIN.
+ # This error is raised when trying to eager load a polymorphic association using a JOIN.
# Eager loading polymorphic associations is only possible with
# {ActiveRecord::Relation#preload}[rdoc-ref:QueryMethods#preload].
class EagerLoadPolymorphicError < ActiveRecordError
diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb
index f71b10120f..7864d4c536 100644
--- a/activerecord/lib/active_record/associations/builder/has_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_many.rb
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
end
def self.valid_options(options)
- super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type]
+ super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type, :index_errors]
end
def self.valid_dependent_options
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index ca473beea3..0e98a3b3a4 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -150,7 +150,8 @@ module ActiveRecord
message_bus.instrument('instantiation.active_record', payload) do
result_set.each { |row_hash|
- parent = parents[row_hash[primary_key]] ||= join_root.instantiate(row_hash, column_aliases)
+ parent_key = primary_key ? row_hash[primary_key] : row_hash
+ parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
}
end
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 3992a240b9..ecf6fb8643 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -54,6 +54,8 @@ module ActiveRecord
autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
end
+ NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
+
# Eager loads the named associations for the given Active Record record(s).
#
# In this description, 'association name' shall refer to the name passed
@@ -88,9 +90,6 @@ module ActiveRecord
# [ :books, :author ]
# { author: :avatar }
# [ :books, { author: :avatar } ]
-
- NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
-
def preload(records, associations, preload_scope = nil)
records = Array.wrap(records).compact.uniq
associations = Array.wrap(associations)
@@ -107,6 +106,7 @@ module ActiveRecord
private
+ # Loads all the given data into +records+ for the +association+.
def preloaders_on(association, records, scope)
case association
when Hash
@@ -132,6 +132,11 @@ module ActiveRecord
}
end
+ # Loads all the given data into +records+ for a singular +association+.
+ #
+ # Functions by instantiating a preloader class such as Preloader::HasManyThrough and
+ # call the +run+ method for each passed in class in the +records+ argument.
+ #
# Not all records have the same class, so group then preload group on the reflection
# itself so that if various subclass share the same association then we do not split
# them unnecessarily
@@ -181,6 +186,10 @@ module ActiveRecord
def self.preloaded_records; []; end
end
+ # Returns a class containing the logic needed to load preload the data
+ # and attach it to a relation. For example +Preloader::Association+ or
+ # +Preloader::HasManyThrough+. The class returned implements a `run` method
+ # that accepts a preloader.
def preloader_for(reflection, owners, rhs_klass)
return NullPreloader unless rhs_klass
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index d35bc3e794..fc12c3f45a 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -140,6 +140,8 @@ module ActiveRecord
included do
Associations::Builder::Association.extensions << AssociationBuilderExtension
+ mattr_accessor :index_nested_attribute_errors, instance_writer: false
+ self.index_nested_attribute_errors = false
end
module ClassMethods # :nodoc:
@@ -315,7 +317,7 @@ module ActiveRecord
def validate_collection_association(reflection)
if association = association_instance_get(reflection.name)
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
- records.each { |record| association_valid?(reflection, record) }
+ records.each_with_index { |record, index| association_valid?(reflection, record, index) }
end
end
end
@@ -323,14 +325,18 @@ module ActiveRecord
# Returns whether or not the association is valid and applies any errors to
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
# enabled records if they're marked_for_destruction? or destroyed.
- def association_valid?(reflection, record)
+ def association_valid?(reflection, record, index=nil)
return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
unless valid = record.valid?(validation_context)
if reflection.options[:autosave]
record.errors.each do |attribute, message|
- attribute = "#{reflection.name}.#{attribute}"
+ if index.nil? || (!reflection.options[:index_errors] && !ActiveRecord::Base.index_nested_attribute_errors)
+ attribute = "#{reflection.name}.#{attribute}"
+ else
+ attribute = "#{reflection.name}[#{index}].#{attribute}"
+ end
errors[attribute] << message
errors[attribute].uniq!
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index d940ac244a..b6f3695856 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -34,7 +34,7 @@ db_namespace = namespace :db do
end
end
- # desc "Empty the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV it defaults to purging the development and test databases."
+ # desc "Empty the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:purge:all to purge all databases in the config). Without RAILS_ENV it defaults to purging the development and test databases."
task :purge => [:load_config] do
ActiveRecord::Tasks::DatabaseTasks.purge_current
end
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index ea7927a435..c0c29a618c 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -94,8 +94,9 @@ module ActiveRecord
rescue DatabaseAlreadyExists
$stderr.puts "#{configuration['database']} already exists"
rescue Exception => error
- $stderr.puts error, *(error.backtrace)
+ $stderr.puts error
$stderr.puts "Couldn't create database for #{configuration.inspect}"
+ raise
end
def create_all
@@ -115,8 +116,9 @@ module ActiveRecord
rescue ActiveRecord::NoDatabaseError
$stderr.puts "Database '#{configuration['database']}' does not exist"
rescue Exception => error
- $stderr.puts error, *(error.backtrace)
+ $stderr.puts error
$stderr.puts "Couldn't drop #{configuration['database']}"
+ raise
end
def drop_all
diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
index 9ab64d0325..9ec3c8a94a 100644
--- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
@@ -19,11 +19,15 @@ module ActiveRecord
path = Pathname.new configuration['database']
file = path.absolute? ? path.to_s : File.join(root, path)
- FileUtils.rm(file) if File.exist?(file)
+ FileUtils.rm(file)
+ rescue Errno::ENOENT => error
+ raise NoDatabaseError.new(error.message, error)
end
def purge
drop
+ rescue NoDatabaseError
+ ensure
create
end
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 20af436e02..e9f679e6de 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -957,4 +957,23 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
projects = ProjectUnscopingDavidDefaultScope.includes(:developers).where(id: project.id)
assert_equal 1, projects.first.developers.size
end
+
+ def test_preloaded_associations_size
+ assert_equal Project.first.salaried_developers.size,
+ Project.preload(:salaried_developers).first.salaried_developers.size
+
+ assert_equal Project.includes(:salaried_developers).references(:salaried_developers).first.salaried_developers.size,
+ Project.preload(:salaried_developers).first.salaried_developers.size
+
+ # Nested HATBM
+ first_project = Developer.first.projects.first
+ preloaded_first_project =
+ Developer.preload(projects: :salaried_developers).
+ first.
+ projects.
+ detect { |p| p.id == first_project.id }
+
+ assert preloaded_first_project.salaried_developers.loaded?, true
+ assert_equal first_project.salaried_developers.size, preloaded_first_project.salaried_developers.size
+ end
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 37ec3f1106..0df8f1f798 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -24,6 +24,8 @@ require 'models/molecule'
require 'models/member'
require 'models/member_detail'
require 'models/organization'
+require 'models/guitar'
+require 'models/tuning_peg'
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def test_autosave_validation
@@ -397,6 +399,40 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
assert_not molecule.persisted?, 'Molecule should not be persisted when its electrons are invalid'
end
+ def test_errors_should_be_indexed_when_passed_as_array
+ guitar = Guitar.new
+ tuning_peg_valid = TuningPeg.new
+ tuning_peg_valid.pitch = 440.0
+ tuning_peg_invalid = TuningPeg.new
+
+ guitar.tuning_pegs = [tuning_peg_valid, tuning_peg_invalid]
+
+ assert_not tuning_peg_invalid.valid?
+ assert tuning_peg_valid.valid?
+ assert_not guitar.valid?
+ assert_equal ["is not a number"], guitar.errors["tuning_pegs[1].pitch"]
+ assert_not_equal ["is not a number"], guitar.errors["tuning_pegs.pitch"]
+ end
+
+ def test_errors_should_be_indexed_when_global_flag_is_set
+ old_attribute_config = ActiveRecord::Base.index_nested_attribute_errors
+ ActiveRecord::Base.index_nested_attribute_errors = true
+
+ molecule = Molecule.new
+ valid_electron = Electron.new(name: 'electron')
+ invalid_electron = Electron.new
+
+ molecule.electrons = [valid_electron, invalid_electron]
+
+ assert_not invalid_electron.valid?
+ assert valid_electron.valid?
+ assert_not molecule.valid?
+ assert_equal ["can't be blank"], molecule.errors["electrons[1].name"]
+ assert_not_equal ["can't be blank"], molecule.errors["electrons.name"]
+ ensure
+ ActiveRecord::Base.index_nested_attribute_errors = old_attribute_config
+ end
+
def test_valid_adding_with_nested_attributes
molecule = Molecule.new
valid_electron = Electron.new(name: 'electron')
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index c3fd0b2383..c31f94b2f2 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -60,7 +60,7 @@ module ActiveRecord
$stderr.expects(:puts).
with("Couldn't create database for #{@configuration.inspect}")
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration }
end
def test_create_when_database_exists_outputs_info_to_stderr
diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb
index 750d5e42dc..0aea0c3b38 100644
--- a/activerecord/test/cases/tasks/sqlite_rake_test.rb
+++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb
@@ -53,7 +53,7 @@ module ActiveRecord
$stderr.expects(:puts).
with("Couldn't create database for #{@configuration.inspect}")
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, '/rails/root'
+ assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration, '/rails/root' }
end
end
diff --git a/activerecord/test/models/guitar.rb b/activerecord/test/models/guitar.rb
new file mode 100644
index 0000000000..cd068ff53d
--- /dev/null
+++ b/activerecord/test/models/guitar.rb
@@ -0,0 +1,4 @@
+class Guitar < ActiveRecord::Base
+ has_many :tuning_pegs, index_errors: true
+ accepts_nested_attributes_for :tuning_pegs
+end
diff --git a/activerecord/test/models/tuning_peg.rb b/activerecord/test/models/tuning_peg.rb
new file mode 100644
index 0000000000..1252d6dc1d
--- /dev/null
+++ b/activerecord/test/models/tuning_peg.rb
@@ -0,0 +1,4 @@
+class TuningPeg < ActiveRecord::Base
+ belongs_to :guitar
+ validates_numericality_of :pitch
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 690ab1af4b..ca1757acd0 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -348,6 +348,10 @@ ActiveRecord::Schema.define do
t.column :key, :string
end
+ create_table :guitar, force: true do |t|
+ t.string :color
+ end
+
create_table :inept_wizards, force: true do |t|
t.column :name, :string, null: false
t.column :city, :string, null: false
@@ -851,6 +855,11 @@ ActiveRecord::Schema.define do
t.belongs_to :ship
end
+ create_table :tuning_pegs, force: true do |t|
+ t.integer :guitar_id
+ t.float :pitch
+ end
+
create_table :tyres, force: true do |t|
t.integer :car_id
end
diff --git a/activesupport/lib/active_support/core_ext/module/remove_method.rb b/activesupport/lib/active_support/core_ext/module/remove_method.rb
index 287ec477a0..d5ec16d68a 100644
--- a/activesupport/lib/active_support/core_ext/module/remove_method.rb
+++ b/activesupport/lib/active_support/core_ext/module/remove_method.rb
@@ -16,7 +16,20 @@ class Module
# Replaces the existing method definition, if there is one, with the passed
# block as its body.
def redefine_method(method, &block)
+ visibility = method_visibility(method)
remove_possible_method(method)
define_method(method, &block)
+ send(visibility, method)
+ end
+
+ def method_visibility(method) # :nodoc:
+ case
+ when private_method_defined?(method)
+ :private
+ when protected_method_defined?(method)
+ :protected
+ else
+ :public
+ end
end
end
diff --git a/activesupport/test/core_ext/module/remove_method_test.rb b/activesupport/test/core_ext/module/remove_method_test.rb
index 77774afb0d..0d684dc70e 100644
--- a/activesupport/test/core_ext/module/remove_method_test.rb
+++ b/activesupport/test/core_ext/module/remove_method_test.rb
@@ -7,6 +7,16 @@ module RemoveMethodTests
return 1
end
+ def do_something_protected
+ return 1
+ end
+ protected :do_something_protected
+
+ def do_something_private
+ return 1
+ end
+ private :do_something_private
+
class << self
def do_something_else
return 2
@@ -34,8 +44,16 @@ class RemoveMethodTest < ActiveSupport::TestCase
def test_redefine_method_in_an_object
RemoveMethodTests::A.class_eval{
self.redefine_method(:do_something) { return 100 }
+ self.redefine_method(:do_something_protected) { return 100 }
+ self.redefine_method(:do_something_private) { return 100 }
}
assert_equal 100, RemoveMethodTests::A.new.do_something
+ assert_equal 100, RemoveMethodTests::A.new.send(:do_something_protected)
+ assert_equal 100, RemoveMethodTests::A.new.send(:do_something_private)
+
+ assert RemoveMethodTests::A.public_method_defined? :do_something
+ assert RemoveMethodTests::A.protected_method_defined? :do_something_protected
+ assert RemoveMethodTests::A.private_method_defined? :do_something_private
end
-end \ No newline at end of file
+end
diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md
index e5a560edd0..f495acbf68 100644
--- a/guides/source/active_support_instrumentation.md
+++ b/guides/source/active_support_instrumentation.md
@@ -232,6 +232,7 @@ Active Record
| `:sql` | SQL statement |
| `:name` | Name of the operation |
| `:connection_id` | `self.object_id` |
+| `:binds` | Bind parameters |
INFO. The adapters will add their own data as well.
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index e2206667e8..e2125cae2e 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -313,6 +313,10 @@ All these configuration options are delegated to the `I18n` library.
by a query exceeds the threshold, a warning is logged. This can be used to
identify queries which might be causing memory bloat.
+* `config.active_record.index_nested_attribute_errors` allows errors for nested
+ has_many relationships to be displayed with an index as well as the error.
+ Defaults to false.
+
The MySQL adapter adds one additional configuration option:
* `ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns in a MySQL database to be booleans and is true by default.
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index 43083ebb86..ebe1cb206a 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -86,10 +86,9 @@ The `APP_PATH` constant will be used later in `rails/commands`. The `config/boot
`config/boot.rb` contains:
```ruby
-# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
-require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
+require 'bundler/setup' # Set up gems listed in the Gemfile.
```
In a standard Rails application, there's a `Gemfile` which declares all
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 7916e24af1..e81ec62a1d 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -167,6 +167,9 @@ module Rails
# team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
@caching_key_generator ||=
if secrets.secret_key_base
+ unless secrets.secret_key_base.kind_of?(String)
+ raise ArgumentError, "`secret_key_base` for #{Rails.env} environment must be a type of String, change this value in `config/secrets.yml`"
+ end
key_generator = ActiveSupport::KeyGenerator.new(secrets.secret_key_base, iterations: 1000)
ActiveSupport::CachingKeyGenerator.new(key_generator)
else
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index d96d8ded6b..ebcfcb1c3a 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -417,6 +417,19 @@ module ApplicationTests
end
end
+ test "raise when secrets.secret_key_base is not a type of string" do
+ app_file 'config/secrets.yml', <<-YAML
+ development:
+ secret_key_base: 123
+ YAML
+
+ app 'development'
+
+ assert_raise(ArgumentError) do
+ app.key_generator
+ end
+ end
+
test "prefer secrets.secret_token over config.secret_token" do
app_file 'config/initializers/secret_token.rb', <<-RUBY
Rails.application.config.secret_token = ""
diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb
index e7beab8b5e..f94d08673a 100644
--- a/railties/test/application/rake/dbs_test.rb
+++ b/railties/test/application/rake/dbs_test.rb
@@ -49,6 +49,58 @@ module ApplicationTests
db_create_and_drop database_url_db_name
end
+ def with_database_existing
+ Dir.chdir(app_path) do
+ set_database_url
+ `bin/rake db:create`
+ yield
+ `bin/rake db:drop`
+ end
+ end
+
+ test 'db:create failure because database exists' do
+ with_database_existing do
+ output = `bin/rake db:create 2>&1`
+ assert_match /already exists/, output
+ assert_equal 0, $?.exitstatus
+ end
+ end
+
+ def with_bad_permissions
+ Dir.chdir(app_path) do
+ set_database_url
+ FileUtils.chmod("-w", "db")
+ yield
+ FileUtils.chmod("+w", "db")
+ end
+ end
+
+ test 'db:create failure because bad permissions' do
+ with_bad_permissions do
+ output = `bin/rake db:create 2>&1`
+ assert_match /Couldn't create database/, output
+ assert_equal 1, $?.exitstatus
+ end
+ end
+
+ test 'db:drop failure because database does not exist' do
+ Dir.chdir(app_path) do
+ output = `bin/rake db:drop 2>&1`
+ assert_match /does not exist/, output
+ assert_equal 0, $?.exitstatus
+ end
+ end
+
+ test 'db:drop failure because bad permissions' do
+ with_database_existing do
+ with_bad_permissions do
+ output = `bin/rake db:drop 2>&1`
+ assert_match /Couldn't drop/, output
+ assert_equal 1, $?.exitstatus
+ end
+ end
+ end
+
def db_migrate_and_status(expected_database)
Dir.chdir(app_path) do
`bin/rails generate model book title:string;