aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actioncable/lib/action_cable/connection/base.rb18
-rw-r--r--actionpack/CHANGELOG.md38
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb38
-rw-r--r--actionpack/lib/action_dispatch/testing/test_response.rb6
-rw-r--r--actionpack/test/controller/integration_test.rb35
-rw-r--r--actionview/lib/action_view/dependency_tracker.rb16
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb8
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb10
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb24
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb22
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb13
-rw-r--r--activerecord/lib/active_record/querying.rb2
-rw-r--r--activerecord/lib/active_record/reflection.rb54
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb32
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb10
-rw-r--r--activesupport/CHANGELOG.md4
-rw-r--r--activesupport/lib/active_support/core_ext/array/access.rb14
-rw-r--r--activesupport/test/core_ext/array/access_test.rb2
-rw-r--r--guides/source/active_support_core_extensions.md2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml2
21 files changed, 251 insertions, 103 deletions
diff --git a/actioncable/lib/action_cable/connection/base.rb b/actioncable/lib/action_cable/connection/base.rb
index b5f898436a..e23789978c 100644
--- a/actioncable/lib/action_cable/connection/base.rb
+++ b/actioncable/lib/action_cable/connection/base.rb
@@ -185,12 +185,14 @@ module ActionCable
end
def respond_to_successful_request
+ logger.info successful_request_message
websocket.rack_response
end
def respond_to_invalid_request
close if websocket.alive?
+ logger.error invalid_request_message
logger.info finished_request_message
[ 404, { 'Content-Type' => 'text/plain' }, [ 'Page not found' ] ]
end
@@ -205,7 +207,7 @@ module ActionCable
'Started %s "%s"%s for %s at %s' % [
request.request_method,
request.filtered_path,
- websocket.possible? ? ' [WebSocket]' : '',
+ websocket.possible? ? ' [WebSocket]' : '[non-WebSocket]',
request.ip,
Time.now.to_s ]
end
@@ -213,10 +215,22 @@ module ActionCable
def finished_request_message
'Finished "%s"%s for %s at %s' % [
request.filtered_path,
- websocket.possible? ? ' [WebSocket]' : '',
+ websocket.possible? ? ' [WebSocket]' : '[non-WebSocket]',
request.ip,
Time.now.to_s ]
end
+
+ def invalid_request_message
+ 'Failed to upgrade to WebSocket (REQUEST_METHOD: %s, HTTP_CONNECTION: %s, HTTP_UPGRADE: %s)' % [
+ env["REQUEST_METHOD"], env["HTTP_CONNECTION"], env["HTTP_UPGRADE"]
+ ]
+ end
+
+ def successful_request_message
+ 'Successfully upgraded to WebSocket (REQUEST_METHOD: %s, HTTP_CONNECTION: %s, HTTP_UPGRADE: %s)' % [
+ env["REQUEST_METHOD"], env["HTTP_CONNECTION"], env["HTTP_UPGRADE"]
+ ]
+ end
end
end
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 93e598e493..bf964d06e9 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,41 @@
+* Add request encoding and response parsing to integration tests.
+
+ What previously was:
+
+ ```ruby
+ require 'test_helper'
+
+ class ApiTest < ActionDispatch::IntegrationTest
+ test 'creates articles' do
+ assert_difference -> { Article.count } do
+ post articles_path(format: :json),
+ params: { article: { title: 'Ahoy!' } }.to_json,
+ headers: { 'Content-Type' => 'application/json' }
+ end
+
+ assert_equal({ id: Article.last.id, title: 'Ahoy!' }, JSON.parse(response.body))
+ end
+ end
+ ```
+
+ Can now be written as:
+
+ ```ruby
+ require 'test_helper'
+
+ class ApiTest < ActionDispatch::IntegrationTest
+ test 'creates articles' do
+ assert_difference -> { Article.count } do
+ post articles_path, { article: { title: 'Ahoy!' } }, as: :json
+ end
+
+ assert_equal({ id: Article.last.id, title: 'Ahoy!' }, response.parsed_body)
+ end
+ end
+ ```
+
+ *Kasper Timm Hansen*
+
* Add image/svg+xml as a default mime type.
*DHH*
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 61bd39c186..742bce1ca6 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -381,6 +381,7 @@ module ActionDispatch
response = _mock_session.last_response
@response = ActionDispatch::TestResponse.from_response(response)
@response.request = @request
+ @response.response_parser = request_encoder
@html_document = nil
@url_options = nil
@@ -396,7 +397,7 @@ module ActionDispatch
class RequestEncoder # :nodoc:
@encoders = {}
- def initialize(mime_name, param_encoder, url_encoded_form = false)
+ def initialize(mime_name, param_encoder, response_parser, url_encoded_form = false)
@mime = Mime[mime_name]
unless @mime
@@ -406,7 +407,8 @@ module ActionDispatch
@url_encoded_form = url_encoded_form
@path_format = ".#{@mime.symbol}" unless @url_encoded_form
- @param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
+ @response_parser = response_parser || -> body { body }
+ @param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
end
def append_format_to(path)
@@ -422,17 +424,21 @@ module ActionDispatch
@param_encoder.call(params)
end
+ def parse_body(body)
+ @response_parser.call(body)
+ end
+
def self.encoder(name)
@encoders[name] || WWWFormEncoder
end
- def self.register_encoder(mime_name, &param_encoder)
- @encoders[mime_name] = new(mime_name, param_encoder)
+ def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil)
+ @encoders[mime_name] = new(mime_name, param_encoder, response_parser)
end
- register_encoder :json
+ register_encoder :json, response_parser: -> body { JSON.parse(body) }
- WWWFormEncoder = new(:url_encoded_form, -> params { params }, true)
+ WWWFormEncoder = new(:url_encoded_form, -> params { params }, nil, true)
end
end
@@ -696,23 +702,31 @@ module ActionDispatch
# require 'test_helper'
#
# class ApiTest < ActionDispatch::IntegrationTest
- # test "creates articles" do
+ # test 'creates articles' do
# assert_difference -> { Article.count } do
# post articles_path, params: { article: { title: 'Ahoy!' } }, as: :json
# end
#
# assert_response :success
+ # assert_equal({ id: Arcticle.last.id, title: 'Ahoy!' }, response.parsed_body)
# end
# end
#
# The `as` option sets the format to JSON, sets the content type to
# 'application/json' and encodes the parameters as JSON.
#
+ # Calling `parsed_body` on the response parses the response body as what
+ # the last request was encoded as. If the request wasn't encoded `as` something,
+ # it's the same as calling `body`.
+ #
# For any custom MIME Types you've registered, you can even add your own encoders with:
#
- # ActionDispatch::IntegrationTest.register_encoder :wibble do |params|
- # params.to_wibble
- # end
+ # ActionDispatch::IntegrationTest.register_encoder :wibble,
+ # param_encoder: -> params { params.to_wibble },
+ # response_parser: -> body { body }
+ #
+ # Where `param_encoder` defines how the params should be encoded and
+ # `response_parser` defines how the response body should be parsed.
#
# Consult the Rails Testing Guide for more.
@@ -743,8 +757,8 @@ module ActionDispatch
html_document.root
end
- def self.register_encoder(*args, &param_encoder)
- Integration::Session::RequestEncoder.register_encoder(*args, &param_encoder)
+ def self.register_encoder(*args)
+ Integration::Session::RequestEncoder.register_encoder(*args)
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb
index 4b79a90242..58d3e6eb0f 100644
--- a/actionpack/lib/action_dispatch/testing/test_response.rb
+++ b/actionpack/lib/action_dispatch/testing/test_response.rb
@@ -18,5 +18,11 @@ module ActionDispatch
# Was there a server-side error?
alias_method :error?, :server_error?
+
+ attr_writer :response_parser # :nodoc:
+
+ def parsed_body
+ @response_parser.parse_body(body)
+ end
end
end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 296bc1baad..cb524bacb2 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -1129,13 +1129,23 @@ end
class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
class FooController < ActionController::Base
- def foos
+ def foos_json
+ render json: params.permit(:foo)
+ end
+
+ def foos_wibble
render plain: 'ok'
end
end
def test_encoding_as_json
- assert_encoded_as :json, content_type: 'application/json'
+ post_to_foos as: :json do
+ assert_response :success
+ assert_match 'foos_json.json', request.path
+ assert_equal 'application/json', request.content_type
+ assert_equal({ 'foo' => 'fighters' }, request.request_parameters)
+ assert_equal({ 'foo' => 'fighters' }, response.parsed_body)
+ end
end
def test_encoding_as_without_mime_registration
@@ -1147,25 +1157,28 @@ class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
def test_registering_custom_encoder
Mime::Type.register 'text/wibble', :wibble
- ActionDispatch::IntegrationTest.register_encoder(:wibble, &:itself)
+ ActionDispatch::IntegrationTest.register_encoder(:wibble,
+ param_encoder: -> params { params })
- assert_encoded_as :wibble, content_type: 'text/wibble',
- parsed_parameters: Hash.new # Unregistered MIME Type can't be parsed
+ post_to_foos as: :wibble do
+ assert_response :success
+ assert_match 'foos_wibble.wibble', request.path
+ assert_equal 'text/wibble', request.content_type
+ assert_equal Hash.new, request.request_parameters # Unregistered MIME Type can't be parsed.
+ assert_equal 'ok', response.parsed_body
+ end
ensure
Mime::Type.unregister :wibble
end
private
- def assert_encoded_as(format, content_type:, parsed_parameters: { 'foo' => 'fighters' })
+ def post_to_foos(as:)
with_routing do |routes|
routes.draw { post ':action' => FooController }
- post '/foos', params: { foo: 'fighters' }, as: format
+ post "/foos_#{as}", params: { foo: 'fighters' }, as: as
- assert_response :success
- assert_match "foos.#{format}", request.path
- assert_equal content_type, request.content_type
- assert_equal parsed_parameters, request.request_parameters
+ yield
end
end
end
diff --git a/actionview/lib/action_view/dependency_tracker.rb b/actionview/lib/action_view/dependency_tracker.rb
index 5a4c3ea3fe..fe98b370b7 100644
--- a/actionview/lib/action_view/dependency_tracker.rb
+++ b/actionview/lib/action_view/dependency_tracker.rb
@@ -7,18 +7,20 @@ module ActionView
def self.find_dependencies(name, template, view_paths = nil)
tracker = @trackers[template.handler]
- return [] unless tracker.present?
+ return [] unless tracker
- if tracker.respond_to?(:supports_view_paths?) && tracker.supports_view_paths?
- tracker.call(name, template, view_paths)
- else
- tracker.call(name, template)
- end
+ tracker.call(name, template, view_paths)
end
def self.register_tracker(extension, tracker)
handler = Template.handler_for_extension(extension)
- @trackers[handler] = tracker
+ if tracker.respond_to?(:supports_view_paths?)
+ @trackers[handler] = tracker
+ else
+ @trackers[handler] = lambda { |name, template, _|
+ tracker.call(name, template)
+ }
+ end
end
def self.remove_tracker(handler)
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index a4cf6023e1..be96f9fa2a 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Add ActiveRecord `#second_to_last` and `#third_to_last` methods.
+
+ *Brian Christian*
+
* Added `numeric` helper into migrations.
Example:
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 9f2c7292ea..2dca6b612e 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -136,6 +136,14 @@ module ActiveRecord
first_nth_or_last(:forty_two, *args)
end
+ def third_to_last(*args)
+ first_nth_or_last(:third_to_last, *args)
+ end
+
+ def second_to_last(*args)
+ first_nth_or_last(:second_to_last, *args)
+ end
+
def last(*args)
first_nth_or_last(:last, *args)
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index fe693cfbb6..2a9627a474 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -197,6 +197,16 @@ module ActiveRecord
@association.forty_two(*args)
end
+ # Same as #first except returns only the third-to-last record.
+ def third_to_last(*args)
+ @association.third_to_last(*args)
+ end
+
+ # Same as #first except returns only the second-to-last record.
+ def second_to_last(*args)
+ @association.second_to_last(*args)
+ end
+
# Returns the last record, or the last +n+ records, from the collection.
# If the collection is empty, the first form returns +nil+, and the second
# form returns an empty array.
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 423a93964e..e902eb7531 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -34,30 +34,6 @@ module ActiveRecord
BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
- class AttributeMethodCache
- def initialize
- @module = Module.new
- @method_cache = Concurrent::Map.new
- end
-
- def [](name)
- @method_cache.compute_if_absent(name) do
- safe_name = name.unpack('h*'.freeze).first
- temp_method = "__temp__#{safe_name}"
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
- @module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
- @module.instance_method temp_method
- end
- end
-
- private
-
- # Override this method in the subclasses for method body.
- def method_body(method_name, const_name)
- raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
- end
- end
-
class GeneratedAttributeMethods < Module; end # :nodoc:
module ClassMethods
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 5197e21fa4..ab2ecaa7c5 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -1,8 +1,11 @@
module ActiveRecord
module AttributeMethods
module Read
- ReaderMethodCache = Class.new(AttributeMethodCache) {
- private
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ protected
+
# We want to generate the methods via module_eval rather than
# define_method, because define_method is slower on dispatch.
# Evaluating many similar methods may use more memory as the instruction
@@ -21,21 +24,6 @@ module ActiveRecord
# to allocate an object on each call to the attribute method.
# Making it frozen means that it doesn't get duped when used to
# key the @attributes in read_attribute.
- def method_body(method_name, const_name)
- <<-EOMETHOD
- def #{method_name}
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
- _read_attribute(name) { |n| missing_attribute(n, caller) }
- end
- EOMETHOD
- end
- }.new
-
- extend ActiveSupport::Concern
-
- module ClassMethods
- protected
-
def define_method_attribute(name)
safe_name = name.unpack('h*'.freeze).first
temp_method = "__temp__#{safe_name}"
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index bbf2a51a0e..5599b590ca 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -1,19 +1,6 @@
module ActiveRecord
module AttributeMethods
module Write
- WriterMethodCache = Class.new(AttributeMethodCache) {
- private
-
- def method_body(method_name, const_name)
- <<-EOMETHOD
- def #{method_name}(value)
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
- write_attribute(name, value)
- end
- EOMETHOD
- end
- }.new
-
extend ActiveSupport::Concern
included do
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 1f429cfd94..5259797223 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module Querying
delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, to: :all
- delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, to: :all
+ delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!, to: :all
delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all
delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, to: :all
delegate :find_by, :find_by!, to: :all
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 99c0e71f97..956fe7c51e 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -239,6 +239,10 @@ module ActiveRecord
def alias_candidate(name)
"#{plural_name}_#{name}"
end
+
+ def chain
+ collect_join_chain
+ end
end
# Base class for AggregateReflection and AssociationReflection. Objects of
@@ -421,7 +425,7 @@ module ActiveRecord
# A chain of reflections from this one back to the owner. For more see the explanation in
# ThroughReflection.
- def chain
+ def collect_join_chain
[self]
end
@@ -495,6 +499,18 @@ module ActiveRecord
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
+ def add_as_source(seed)
+ seed
+ end
+
+ def add_as_polymorphic_through(reflection, seed)
+ seed + [PolymorphicReflection.new(self, reflection)]
+ end
+
+ def add_as_through(seed)
+ seed + [self]
+ end
+
protected
def actual_source_reflection # FIXME: this is a horrible name
@@ -742,19 +758,8 @@ module ActiveRecord
# # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
# <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
#
- def chain
- @chain ||= begin
- a = source_reflection.chain
- b = through_reflection.chain.map(&:dup)
-
- if options[:source_type]
- b[0] = PolymorphicReflection.new(b[0], self)
- end
-
- chain = a + b
- chain[0] = self # Use self so we don't lose the information from :source_type
- chain
- end
+ def collect_join_chain
+ collect_join_reflections [self]
end
# This is for clearing cache on the reflection. Useful for tests that need to compare
@@ -913,6 +918,27 @@ module ActiveRecord
scope_chain
end
+ def add_as_source(seed)
+ collect_join_reflections seed
+ end
+
+ def add_as_polymorphic_through(reflection, seed)
+ collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
+ end
+
+ def add_as_through(seed)
+ collect_join_reflections(seed + [self])
+ end
+
+ def collect_join_reflections(seed)
+ a = source_reflection.add_as_source seed
+ if options[:source_type]
+ through_reflection.add_as_polymorphic_through self, a
+ else
+ through_reflection.add_as_through a
+ end
+ end
+
protected
def actual_source_reflection # FIXME: this is a horrible name
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index d48bcea28a..90a6a466fd 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -242,6 +242,38 @@ module ActiveRecord
find_nth! 41
end
+ # Find the third-to-last record.
+ # If no order is defined it will order by primary key.
+ #
+ # Person.third_to_last # returns the third-to-last object fetched by SELECT * FROM people
+ # Person.offset(3).third_to_last # returns the third-to-last object from OFFSET 3
+ # Person.where(["user_name = :u", { u: user_name }]).third_to_last
+ def third_to_last
+ find_nth -3
+ end
+
+ # Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
+ # is found.
+ def third_to_last!
+ find_nth! -3
+ end
+
+ # Find the second-to-last record.
+ # If no order is defined it will order by primary key.
+ #
+ # Person.second_to_last # returns the second-to-last object fetched by SELECT * FROM people
+ # Person.offset(3).second_to_last # returns the second-to-last object from OFFSET 3
+ # Person.where(["user_name = :u", { u: user_name }]).second_to_last
+ def second_to_last
+ find_nth -2
+ end
+
+ # Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
+ # is found.
+ def second_to_last!
+ find_nth! -2
+ end
+
# Returns true if a record exists in the table that matches the +id+ or
# conditions given, or false otherwise. The argument can take six forms:
#
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index ecaa521283..e975f4fbdd 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -408,6 +408,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
assert_no_queries do
+ bulbs.third_to_last()
+ bulbs.third_to_last({})
+ end
+
+ assert_no_queries do
+ bulbs.second_to_last()
+ bulbs.second_to_last({})
+ end
+
+ assert_no_queries do
bulbs.last()
bulbs.last({})
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index bd333da081..3d4cc8fae6 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Add `Array#second_to_last` and `Array#third_to_last` methods.
+
+ *Brian Christian*
+
* Fix regression in `Hash#dig` for HashWithIndifferentAccess.
*Jon Moss*
diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb
index 3177d8498e..37d833887a 100644
--- a/activesupport/lib/active_support/core_ext/array/access.rb
+++ b/activesupport/lib/active_support/core_ext/array/access.rb
@@ -73,4 +73,18 @@ class Array
def forty_two
self[41]
end
+
+ # Equal to <tt>self[-3]</tt>.
+ #
+ # %w( a b c d e ).third_to_last # => "c"
+ def third_to_last
+ self[-3]
+ end
+
+ # Equal to <tt>self[-2]</tt>.
+ #
+ # %w( a b c d e ).second_to_last # => "d"
+ def second_to_last
+ self[-2]
+ end
end
diff --git a/activesupport/test/core_ext/array/access_test.rb b/activesupport/test/core_ext/array/access_test.rb
index 3f1e0c4cb4..1d834667f0 100644
--- a/activesupport/test/core_ext/array/access_test.rb
+++ b/activesupport/test/core_ext/array/access_test.rb
@@ -26,6 +26,8 @@ class AccessTest < ActiveSupport::TestCase
assert_equal array[3], array.fourth
assert_equal array[4], array.fifth
assert_equal array[41], array.forty_two
+ assert_equal array[-3], array.third_to_last
+ assert_equal array[-2], array.second_to_last
end
def test_without
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 0aca6db9b6..10122629b2 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -2240,7 +2240,7 @@ Similarly, `from` returns the tail from the element at the passed index to the e
[].from(0) # => []
```
-The methods `second`, `third`, `fourth`, and `fifth` return the corresponding element (`first` is built-in). Thanks to social wisdom and positive constructiveness all around, `forty_two` is also available.
+The methods `second`, `third`, `fourth`, and `fifth` return the corresponding element, as do `second_to_last` and `third_to_last` (`first` and `last` are built-in). Thanks to social wisdom and positive constructiveness all around, `forty_two` is also available.
```ruby
%w(a b c d).third # => "c"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
index d51b2ec199..bd5c0b10f6 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
@@ -19,7 +19,7 @@ default: &default
encoding: unicode
# For details on connection pooling, see rails configuration guide
# http://guides.rubyonrails.org/configuring.html#database-pooling
- pool: 5
+ pool: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default