aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_dispatch/routing.rb28
-rw-r--r--actionview/lib/action_view.rb1
-rw-r--r--actionview/lib/action_view/file_template.rb33
-rw-r--r--actionview/lib/action_view/template.rb20
-rw-r--r--actionview/lib/action_view/template/handlers.rb28
-rw-r--r--actionview/lib/action_view/template/handlers/builder.rb4
-rw-r--r--actionview/lib/action_view/template/handlers/erb.rb10
-rw-r--r--actionview/lib/action_view/template/handlers/html.rb2
-rw-r--r--actionview/lib/action_view/template/handlers/raw.rb4
-rw-r--r--actionview/lib/action_view/template/resolver.rb3
-rw-r--r--actionview/test/abstract_unit.rb2
-rw-r--r--actionview/test/actionpack/controller/layout_test.rb4
-rw-r--r--actionview/test/template/dependency_tracker_test.rb4
-rw-r--r--actionview/test/template/render_test.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb7
-rw-r--r--activerecord/lib/active_record/middleware/database_selector.rb7
-rw-r--r--activerecord/lib/active_record/middleware/database_selector/resolver.rb13
-rw-r--r--activerecord/lib/active_record/railtie.rb4
-rw-r--r--activerecord/lib/active_record/test_fixtures.rb4
-rw-r--r--activerecord/test/cases/database_selector_test.rb48
-rw-r--r--activerecord/test/cases/fixtures_test.rb2
-rw-r--r--activestorage/lib/active_storage/service/s3_service.rb4
-rw-r--r--activestorage/test/service/s3_service_test.rb18
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb31
-rw-r--r--activesupport/test/dependencies_test.rb2
-rw-r--r--activesupport/test/hash_with_indifferent_access_test.rb2
-rw-r--r--activesupport/test/notifications_test.rb2
-rw-r--r--activesupport/test/safe_buffer_test.rb2
-rw-r--r--activesupport/test/share_lock_test.rb2
-rw-r--r--activesupport/test/time_travel_test.rb2
-rw-r--r--activesupport/test/time_zone_test.rb2
-rw-r--r--guides/assets/images/getting_started/template_is_missing_articles_new.pngbin472167 -> 26796 bytes
-rw-r--r--guides/source/routing.md2
34 files changed, 241 insertions, 74 deletions
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 5cde677051..f832719f19 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -74,8 +74,8 @@ module ActionDispatch
# For routes that don't fit the <tt>resources</tt> mold, you can use the HTTP helper
# methods <tt>get</tt>, <tt>post</tt>, <tt>patch</tt>, <tt>put</tt> and <tt>delete</tt>.
#
- # get 'post/:id' => 'posts#show'
- # post 'post/:id' => 'posts#create_comment'
+ # get 'post/:id', to: 'posts#show'
+ # post 'post/:id', to: 'posts#create_comment'
#
# Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
# URL will route to the <tt>show</tt> action.
@@ -83,7 +83,7 @@ module ActionDispatch
# If your route needs to respond to more than one HTTP method (or all methods) then using the
# <tt>:via</tt> option on <tt>match</tt> is preferable.
#
- # match 'post/:id' => 'posts#show', via: [:get, :post]
+ # match 'post/:id', to: 'posts#show', via: [:get, :post]
#
# == Named routes
#
@@ -94,7 +94,7 @@ module ActionDispatch
# Example:
#
# # In config/routes.rb
- # get '/login' => 'accounts#login', as: 'login'
+ # get '/login', to: 'accounts#login', as: 'login'
#
# # With render, redirect_to, tests, etc.
# redirect_to login_url
@@ -120,9 +120,9 @@ module ActionDispatch
#
# # In config/routes.rb
# controller :blog do
- # get 'blog/show' => :list
- # get 'blog/delete' => :delete
- # get 'blog/edit' => :edit
+ # get 'blog/show', to: :list
+ # get 'blog/delete', to: :delete
+ # get 'blog/edit', to: :edit
# end
#
# # provides named routes for show, delete, and edit
@@ -132,7 +132,7 @@ module ActionDispatch
#
# Routes can generate pretty URLs. For example:
#
- # get '/articles/:year/:month/:day' => 'articles#find_by_id', constraints: {
+ # get '/articles/:year/:month/:day', to: 'articles#find_by_id', constraints: {
# year: /\d{4}/,
# month: /\d{1,2}/,
# day: /\d{1,2}/
@@ -147,7 +147,7 @@ module ActionDispatch
# You can specify a regular expression to define a format for a parameter.
#
# controller 'geocode' do
- # get 'geocode/:postalcode' => :show, constraints: {
+ # get 'geocode/:postalcode', to: :show, constraints: {
# postalcode: /\d{5}(-\d{4})?/
# }
# end
@@ -156,13 +156,13 @@ module ActionDispatch
# expression modifiers:
#
# controller 'geocode' do
- # get 'geocode/:postalcode' => :show, constraints: {
+ # get 'geocode/:postalcode', to: :show, constraints: {
# postalcode: /hx\d\d\s\d[a-z]{2}/i
# }
# end
#
# controller 'geocode' do
- # get 'geocode/:postalcode' => :show, constraints: {
+ # get 'geocode/:postalcode', to: :show, constraints: {
# postalcode: /# Postalcode format
# \d{5} #Prefix
# (-\d{4})? #Suffix
@@ -178,13 +178,13 @@ module ActionDispatch
#
# You can redirect any path to another path using the redirect helper in your router:
#
- # get "/stories" => redirect("/posts")
+ # get "/stories", to: redirect("/posts")
#
# == Unicode character routes
#
# You can specify unicode character routes in your router:
#
- # get "こんにちは" => "welcome#index"
+ # get "こんにちは", to: "welcome#index"
#
# == Routing to Rack Applications
#
@@ -192,7 +192,7 @@ module ActionDispatch
# index action in the PostsController, you can specify any Rack application
# as the endpoint for a matcher:
#
- # get "/application.js" => Sprockets
+ # get "/application.js", to: Sprockets
#
# == Reloading routes
#
diff --git a/actionview/lib/action_view.rb b/actionview/lib/action_view.rb
index c4a614fa6d..6ff2d70e35 100644
--- a/actionview/lib/action_view.rb
+++ b/actionview/lib/action_view.rb
@@ -45,6 +45,7 @@ module ActionView
autoload :Rendering
autoload :RoutingUrlFor
autoload :Template
+ autoload :FileTemplate
autoload :ViewPaths
autoload_under "renderer" do
diff --git a/actionview/lib/action_view/file_template.rb b/actionview/lib/action_view/file_template.rb
new file mode 100644
index 0000000000..4921074383
--- /dev/null
+++ b/actionview/lib/action_view/file_template.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require "action_view/template"
+
+module ActionView
+ class FileTemplate < Template
+ def initialize(filename, handler, details)
+ @filename = filename
+
+ super(nil, filename, handler, details)
+ end
+
+ def source
+ File.binread @filename
+ end
+
+ def refresh(_)
+ self
+ end
+
+ # Exceptions are marshalled when using the parallel test runner with DRb, so we need
+ # to ensure that references to the template object can be marshalled as well. This means forgoing
+ # the marshalling of the compiler mutex and instantiating that again on unmarshalling.
+ def marshal_dump # :nodoc:
+ [ @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants ]
+ end
+
+ def marshal_load(array) # :nodoc:
+ @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants = *array
+ @compile_mutex = Mutex.new
+ end
+ end
+end
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb
index 8ce8053011..3b2c264ed4 100644
--- a/actionview/lib/action_view/template.rb
+++ b/actionview/lib/action_view/template.rb
@@ -3,6 +3,7 @@
require "active_support/core_ext/object/try"
require "active_support/core_ext/kernel/singleton_class"
require "thread"
+require "delegate"
module ActionView
# = Action View Template
@@ -202,7 +203,9 @@ module ActionView
# before passing the source on to the template engine, leaving a
# blank line in its stead.
def encode!
- return unless source.encoding == Encoding::BINARY
+ source = self.source
+
+ return source unless source.encoding == Encoding::BINARY
# Look for # encoding: *. If we find one, we'll encode the
# String in that encoding, otherwise, we'll use the
@@ -277,6 +280,15 @@ module ActionView
end
end
+ class LegacyTemplate < DelegateClass(Template) # :nodoc:
+ attr_reader :source
+
+ def initialize(template, source)
+ super(template)
+ @source = source
+ end
+ end
+
# Among other things, this method is responsible for properly setting
# the encoding of the compiled template.
#
@@ -290,8 +302,8 @@ module ActionView
# In general, this means that templates will be UTF-8 inside of Rails,
# regardless of the original source encoding.
def compile(mod)
- encode!
- code = @handler.call(self)
+ source = encode!
+ code = @handler.call(self, source)
# Make sure that the resulting String to be eval'd is in the
# encoding of the code
@@ -312,7 +324,7 @@ module ActionView
# handler is valid in the default_internal. This is for handlers
# that handle encoding but screw up
unless source.valid_encoding?
- raise WrongEncodingError.new(@source, Encoding.default_internal)
+ raise WrongEncodingError.new(source, Encoding.default_internal)
end
mod.module_eval(source, identifier, 0)
diff --git a/actionview/lib/action_view/template/handlers.rb b/actionview/lib/action_view/template/handlers.rb
index 7ec76dcc3f..f2a2341b8e 100644
--- a/actionview/lib/action_view/template/handlers.rb
+++ b/actionview/lib/action_view/template/handlers.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/deprecation"
+
module ActionView #:nodoc:
# = Action View Template Handlers
class Template #:nodoc:
@@ -14,7 +16,7 @@ module ActionView #:nodoc:
base.register_template_handler :erb, ERB.new
base.register_template_handler :html, Html.new
base.register_template_handler :builder, Builder.new
- base.register_template_handler :ruby, :source.to_proc
+ base.register_template_handler :ruby, lambda { |_, source| source }
end
@@template_handlers = {}
@@ -24,11 +26,35 @@ module ActionView #:nodoc:
@@template_extensions ||= @@template_handlers.keys
end
+ class LegacyHandlerWrapper < SimpleDelegator # :nodoc:
+ def call(view, source)
+ __getobj__.call(ActionView::Template::LegacyTemplate.new(view, source))
+ end
+ end
+
# Register an object that knows how to handle template files with the given
# extensions. This can be used to implement new template types.
# The handler must respond to +:call+, which will be passed the template
# and should return the rendered template as a String.
def register_template_handler(*extensions, handler)
+ params = if handler.is_a?(Proc)
+ handler.parameters
+ else
+ handler.method(:call).parameters
+ end
+
+ unless params.find_all { |type, _| type == :req }.length >= 2
+ ActiveSupport::Deprecation.warn <<~eowarn
+ Single arity template handlers are deprecated. Template handlers must
+ now accept two parameters, the view object and the source for the view object.
+ Change:
+ >> #{handler.class}#call(#{params.map(&:last).join(", ")})
+ To:
+ >> #{handler.class}#call(#{params.map(&:last).join(", ")}, source)
+ eowarn
+ handler = LegacyHandlerWrapper.new(handler)
+ end
+
raise(ArgumentError, "Extension is required") if extensions.empty?
extensions.each do |extension|
@@template_handlers[extension.to_sym] = handler
diff --git a/actionview/lib/action_view/template/handlers/builder.rb b/actionview/lib/action_view/template/handlers/builder.rb
index 61492ce448..e5413cd2a0 100644
--- a/actionview/lib/action_view/template/handlers/builder.rb
+++ b/actionview/lib/action_view/template/handlers/builder.rb
@@ -5,11 +5,11 @@ module ActionView
class Builder
class_attribute :default_format, default: :xml
- def call(template)
+ def call(template, source)
require_engine
"xml = ::Builder::XmlMarkup.new(:indent => 2);" \
"self.output_buffer = xml.target!;" +
- template.source +
+ source +
";xml.target!;"
end
diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb
index 7d1a6767d7..b6314a5bc3 100644
--- a/actionview/lib/action_view/template/handlers/erb.rb
+++ b/actionview/lib/action_view/template/handlers/erb.rb
@@ -28,8 +28,8 @@ module ActionView
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
- def self.call(template)
- new.call(template)
+ def self.call(template, source)
+ new.call(template, source)
end
def supports_streaming?
@@ -40,17 +40,17 @@ module ActionView
true
end
- def call(template)
+ def call(template, source)
# First, convert to BINARY, so in case the encoding is
# wrong, we can still find an encoding tag
# (<%# encoding %>) inside the String using a regular
# expression
- template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT)
+ template_source = source.dup.force_encoding(Encoding::ASCII_8BIT)
erb = template_source.gsub(ENCODING_TAG, "")
encoding = $2
- erb.force_encoding valid_encoding(template.source.dup, encoding)
+ erb.force_encoding valid_encoding(source.dup, encoding)
# Always make sure we return a String in the default_internal
erb.encode!
diff --git a/actionview/lib/action_view/template/handlers/html.rb b/actionview/lib/action_view/template/handlers/html.rb
index 27004a318c..65857d8587 100644
--- a/actionview/lib/action_view/template/handlers/html.rb
+++ b/actionview/lib/action_view/template/handlers/html.rb
@@ -3,7 +3,7 @@
module ActionView
module Template::Handlers
class Html < Raw
- def call(template)
+ def call(template, source)
"ActionView::OutputBuffer.new #{super}"
end
end
diff --git a/actionview/lib/action_view/template/handlers/raw.rb b/actionview/lib/action_view/template/handlers/raw.rb
index 5cd23a0060..57ebb169fc 100644
--- a/actionview/lib/action_view/template/handlers/raw.rb
+++ b/actionview/lib/action_view/template/handlers/raw.rb
@@ -3,8 +3,8 @@
module ActionView
module Template::Handlers
class Raw
- def call(template)
- "#{template.source.inspect}.html_safe;"
+ def call(template, source)
+ "#{source.inspect}.html_safe;"
end
end
end
diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb
index 12ae82f8c5..3b4594942b 100644
--- a/actionview/lib/action_view/template/resolver.rb
+++ b/actionview/lib/action_view/template/resolver.rb
@@ -226,9 +226,8 @@ module ActionView
template_paths.map do |template|
handler, format, variant = extract_handler_and_format_and_variant(template)
- contents = File.binread(template)
- Template.new(contents, File.expand_path(template), handler,
+ FileTemplate.new(File.expand_path(template), handler,
virtual_path: path.virtual,
format: format,
variant: variant,
diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb
index 56d617309c..acc2ed029b 100644
--- a/actionview/test/abstract_unit.rb
+++ b/actionview/test/abstract_unit.rb
@@ -58,7 +58,7 @@ module RenderERBUtils
template = ActionView::Template.new(
string.strip,
"test template",
- ActionView::Template::Handlers::ERB,
+ ActionView::Template.handler_for_extension(:erb),
{})
template.render(ActionView::Base.empty, {}).strip
diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb
index 6d5c97b7fd..838b564c5d 100644
--- a/actionview/test/actionpack/controller/layout_test.rb
+++ b/actionview/test/actionpack/controller/layout_test.rb
@@ -70,7 +70,7 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
end
def test_third_party_template_library_auto_discovers_layout
- with_template_handler :mab, lambda { |template| template.source.inspect } do
+ with_template_handler :mab, lambda { |template, source| source.inspect } do
@controller = ThirdPartyTemplateLibraryController.new
get :hello
assert_response :success
@@ -212,7 +212,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
end
def test_layout_set_when_using_render
- with_template_handler :mab, lambda { |template| template.source.inspect } do
+ with_template_handler :mab, lambda { |template, source| source.inspect } do
@controller = SetsLayoutInRenderController.new
get :hello
assert_includes @response.body, "layouts/third_party_template_library.mab"
diff --git a/actionview/test/template/dependency_tracker_test.rb b/actionview/test/template/dependency_tracker_test.rb
index ef7aeac039..42cb14a18a 100644
--- a/actionview/test/template/dependency_tracker_test.rb
+++ b/actionview/test/template/dependency_tracker_test.rb
@@ -17,8 +17,8 @@ class FakeTemplate
end
end
-Neckbeard = lambda { |template| template.source }
-Bowtie = lambda { |template| template.source }
+Neckbeard = lambda { |template, source| source }
+Bowtie = lambda { |template, source| source }
class DependencyTrackerTest < ActionView::TestCase
def tracker
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 76ffe3415d..f5d251da20 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -450,13 +450,22 @@ module RenderTestCases
assert_equal "Hello, World!", @view.render(inline: "Hello, World!", type: :bar)
end
- CustomHandler = lambda do |template|
+ CustomHandler = lambda do |template, source|
"@output_buffer = ''.dup\n" \
- "@output_buffer << 'source: #{template.source.inspect}'\n"
+ "@output_buffer << 'source: #{source.inspect}'\n"
end
def test_render_inline_with_render_from_to_proc
- ActionView::Template.register_template_handler :ruby_handler, :source.to_proc
+ ActionView::Template.register_template_handler :ruby_handler, lambda { |_, source| source }
+ assert_equal "3", @view.render(inline: "(1 + 2).to_s", type: :ruby_handler)
+ ensure
+ ActionView::Template.unregister_template_handler :ruby_handler
+ end
+
+ def test_render_inline_with_render_from_to_proc_deprecated
+ assert_deprecated do
+ ActionView::Template.register_template_handler :ruby_handler, :source.to_proc
+ end
assert_equal "3", @view.render(inline: "(1 + 2).to_s", type: :ruby_handler)
ensure
ActionView::Template.unregister_template_handler :ruby_handler
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 16344b160d..b2a6109548 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -682,9 +682,10 @@ module ActiveRecord
end
alias :remove_belongs_to :remove_references
- # Adds a foreign key.
+ # Adds a foreign key to the table using a supplied table name.
#
# t.foreign_key(:authors)
+ # t.foreign_key(:authors, column: :author_id, primary_key: "id")
#
# See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
def foreign_key(*args)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index 112f376d0a..c9e84e48cc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -205,9 +205,12 @@ module ActiveRecord
run_commit_callbacks: run_commit_callbacks)
end
- transaction.materialize! unless @connection.supports_lazy_transactions? && lazy_transactions_enabled?
+ if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
+ @has_unmaterialized_transactions = true
+ else
+ transaction.materialize!
+ end
@stack.push(transaction)
- @has_unmaterialized_transactions = true if @connection.supports_lazy_transactions?
transaction
end
end
diff --git a/activerecord/lib/active_record/middleware/database_selector.rb b/activerecord/lib/active_record/middleware/database_selector.rb
index adcfca4f8d..3ab50f5f6b 100644
--- a/activerecord/lib/active_record/middleware/database_selector.rb
+++ b/activerecord/lib/active_record/middleware/database_selector.rb
@@ -35,13 +35,14 @@ module ActiveRecord
# config.active_record.database_resolver = MyResolver
# config.active_record.database_operations = MyResolver::MySession
class DatabaseSelector
- def initialize(app, resolver_klass = Resolver, operations_klass = Resolver::Session)
+ def initialize(app, resolver_klass = Resolver, operations_klass = Resolver::Session, options = {})
@app = app
@resolver_klass = resolver_klass
@operations_klass = operations_klass
+ @options = options
end
- attr_reader :resolver_klass, :operations_klass
+ attr_reader :resolver_klass, :operations_klass, :options
# Middleware that determines which database connection to use in a multiple
# database application.
@@ -57,7 +58,7 @@ module ActiveRecord
def select_database(request, &blk)
operations = operations_klass.build(request)
- database_resolver = resolver_klass.call(operations)
+ database_resolver = resolver_klass.call(operations, options)
if reading_request?(request)
database_resolver.read(&blk)
diff --git a/activerecord/lib/active_record/middleware/database_selector/resolver.rb b/activerecord/lib/active_record/middleware/database_selector/resolver.rb
index fc71835cea..a84c292714 100644
--- a/activerecord/lib/active_record/middleware/database_selector/resolver.rb
+++ b/activerecord/lib/active_record/middleware/database_selector/resolver.rb
@@ -18,16 +18,18 @@ module ActiveRecord
class Resolver # :nodoc:
SEND_TO_REPLICA_DELAY = 2.seconds
- def self.call(resolver)
- new(resolver)
+ def self.call(resolver, options = {})
+ new(resolver, options)
end
- def initialize(resolver)
+ def initialize(resolver, options = {})
@resolver = resolver
+ @options = options
+ @delay = @options && @options[:delay] ? @options[:delay] : SEND_TO_REPLICA_DELAY
@instrumenter = ActiveSupport::Notifications.instrumenter
end
- attr_reader :resolver, :instrumenter
+ attr_reader :resolver, :delay, :instrumenter
def read(&blk)
if read_from_primary?
@@ -76,8 +78,7 @@ module ActiveRecord
end
def send_to_replica_delay
- (ActiveRecord::Base.database_selector && ActiveRecord::Base.database_selector[:delay]) ||
- SEND_TO_REPLICA_DELAY
+ delay
end
def time_since_last_write_ok?
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index f0b5ab2f08..aac49a92b4 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -89,10 +89,10 @@ module ActiveRecord
end
initializer "active_record.database_selector" do
- if config.active_record.database_selector
+ if options = config.active_record.delete(:database_selector)
resolver = config.active_record.delete(:database_resolver)
operations = config.active_record.delete(:database_operations)
- config.app_middleware.use ActiveRecord::Middleware::DatabaseSelector, resolver, operations
+ config.app_middleware.use ActiveRecord::Middleware::DatabaseSelector, resolver, operations, options
end
end
diff --git a/activerecord/lib/active_record/test_fixtures.rb b/activerecord/lib/active_record/test_fixtures.rb
index d29fc9f84b..8c60d71669 100644
--- a/activerecord/lib/active_record/test_fixtures.rb
+++ b/activerecord/lib/active_record/test_fixtures.rb
@@ -122,7 +122,7 @@ module ActiveRecord
# Begin transactions for connections already established
@fixture_connections = enlist_fixture_connections
@fixture_connections.each do |connection|
- connection.begin_transaction joinable: false
+ connection.begin_transaction joinable: false, _lazy: false
connection.pool.lock_thread = true if lock_threads
end
@@ -138,7 +138,7 @@ module ActiveRecord
end
if connection && !@fixture_connections.include?(connection)
- connection.begin_transaction joinable: false
+ connection.begin_transaction joinable: false, _lazy: false
connection.pool.lock_thread = true if lock_threads
@fixture_connections << connection
end
diff --git a/activerecord/test/cases/database_selector_test.rb b/activerecord/test/cases/database_selector_test.rb
index 6142e223ce..4106a6ec46 100644
--- a/activerecord/test/cases/database_selector_test.rb
+++ b/activerecord/test/cases/database_selector_test.rb
@@ -95,6 +95,54 @@ module ActiveRecord
assert @session_store[:last_write]
end
+ def test_read_from_primary_with_options
+ resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver.new(@session, delay: 5.seconds)
+
+ # Session should start empty
+ assert_nil @session_store[:last_write]
+
+ called = false
+ resolver.write do
+ assert ActiveRecord::Base.connected_to?(role: :writing)
+ called = true
+ end
+ assert called
+
+ # and be populated by the last write time
+ assert @session_store[:last_write]
+
+ read = false
+ resolver.read do
+ assert ActiveRecord::Base.connected_to?(role: :writing)
+ read = true
+ end
+ assert read
+ end
+
+ def test_read_from_replica_with_no_delay
+ resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver.new(@session, delay: 0.seconds)
+
+ # Session should start empty
+ assert_nil @session_store[:last_write]
+
+ called = false
+ resolver.write do
+ assert ActiveRecord::Base.connected_to?(role: :writing)
+ called = true
+ end
+ assert called
+
+ # and be populated by the last write time
+ assert @session_store[:last_write]
+
+ read = false
+ resolver.read do
+ assert ActiveRecord::Base.connected_to?(role: :reading)
+ read = true
+ end
+ assert read
+ end
+
def test_the_middleware_chooses_writing_role_with_POST_request
middleware = ActiveRecord::Middleware::DatabaseSelector.new(lambda { |env|
assert ActiveRecord::Base.connected_to?(role: :writing)
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 2fe4879fe6..b4f28fbfd6 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -924,7 +924,7 @@ class TransactionalFixturesOnConnectionNotification < ActiveRecord::TestCase
def lock_thread=(lock_thread); end
end.new
- assert_called_with(connection, :begin_transaction, [joinable: false]) do
+ assert_called_with(connection, :begin_transaction, [joinable: false, _lazy: false]) do
fire_connection_notification(connection)
end
end
diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb
index 382920ef61..bf94f3f49e 100644
--- a/activestorage/lib/active_storage/service/s3_service.rb
+++ b/activestorage/lib/active_storage/service/s3_service.rb
@@ -16,9 +16,9 @@ module ActiveStorage
@upload_options = upload
end
- def upload(key, io, checksum: nil, **)
+ def upload(key, io, checksum: nil, content_type: nil, **)
instrument :upload, key: key, checksum: checksum do
- object_for(key).put(upload_options.merge(body: io, content_md5: checksum))
+ object_for(key).put(upload_options.merge(body: io, content_md5: checksum, content_type: content_type))
rescue Aws::S3::Errors::BadDigest
raise ActiveStorage::IntegrityError
end
diff --git a/activestorage/test/service/s3_service_test.rb b/activestorage/test/service/s3_service_test.rb
index 0a6004267f..74c0aa0405 100644
--- a/activestorage/test/service/s3_service_test.rb
+++ b/activestorage/test/service/s3_service_test.rb
@@ -59,6 +59,24 @@ if SERVICE_CONFIGURATIONS[:s3]
service.delete key
end
end
+
+ test "upload with content type" do
+ key = SecureRandom.base58(24)
+ data = "Something else entirely!"
+ content_type = "text/plain"
+
+ @service.upload(
+ key,
+ StringIO.new(data),
+ checksum: Digest::MD5.base64digest(data),
+ filename: "cool_data.txt",
+ content_type: content_type
+ )
+
+ assert_equal content_type, @service.bucket.object(key).content_type
+ ensure
+ @service.delete key
+ end
end
else
puts "Skipping S3 Service tests because no S3 configuration was supplied"
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 4e4ca70942..11721db103 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -13,7 +13,8 @@ module ActiveSupport
include Mutex_m
def initialize
- @subscribers = []
+ @string_subscribers = Hash.new { |h, k| h[k] = [] }
+ @other_subscribers = []
@listeners_for = Concurrent::Map.new
super
end
@@ -21,8 +22,13 @@ module ActiveSupport
def subscribe(pattern = nil, block = Proc.new)
subscriber = Subscribers.new pattern, block
synchronize do
- @subscribers << subscriber
- @listeners_for.clear
+ if String === pattern
+ @string_subscribers[pattern] << subscriber
+ @listeners_for.delete(pattern)
+ else
+ @other_subscribers << subscriber
+ @listeners_for.clear
+ end
end
subscriber
end
@@ -31,12 +37,18 @@ module ActiveSupport
synchronize do
case subscriber_or_name
when String
- @subscribers.reject! { |s| s.matches?(subscriber_or_name) }
+ @string_subscribers[subscriber_or_name].clear
+ @listeners_for.delete(subscriber_or_name)
else
- @subscribers.delete(subscriber_or_name)
+ pattern = subscriber_or_name.try(:pattern)
+ if String === pattern
+ @string_subscribers[pattern].delete(subscriber_or_name)
+ @listeners_for.delete(pattern)
+ else
+ @other_subscribers.delete(subscriber_or_name)
+ @listeners_for.clear
+ end
end
-
- @listeners_for.clear
end
end
@@ -56,7 +68,8 @@ module ActiveSupport
# this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
@listeners_for[name] || synchronize do
# use synchronisation when accessing @subscribers
- @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
+ @listeners_for[name] ||=
+ @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) }
end
end
@@ -101,6 +114,8 @@ module ActiveSupport
end
class Evented #:nodoc:
+ attr_reader :pattern
+
def initialize(pattern, delegate)
@pattern = pattern
@delegate = delegate
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index b1b3070891..d4e709137e 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -841,7 +841,7 @@ class DependenciesTest < ActiveSupport::TestCase
remove_constants(:C)
end
- def test_new_contants_in_without_constants
+ def test_new_constants_in_without_constants
assert_equal [], (ActiveSupport::Dependencies.new_constants_in(Object) { })
assert ActiveSupport::Dependencies.constant_watch_stack.all? { |k, v| v.empty? }
end
diff --git a/activesupport/test/hash_with_indifferent_access_test.rb b/activesupport/test/hash_with_indifferent_access_test.rb
index 8a39672609..1d6a07ec5f 100644
--- a/activesupport/test/hash_with_indifferent_access_test.rb
+++ b/activesupport/test/hash_with_indifferent_access_test.rb
@@ -486,7 +486,7 @@ class HashWithIndifferentAccessTest < ActiveSupport::TestCase
assert_equal @strings, roundtrip
assert_equal "1234", roundtrip.default
- # Ensure nested hashes are not HashWithIndiffereneAccess
+ # Ensure nested hashes are not HashWithIndifferentAccess
new_to_hash = @nested_mixed.with_indifferent_access.to_hash
assert_not new_to_hash.instance_of?(HashWithIndifferentAccess)
assert_not new_to_hash["a"].instance_of?(HashWithIndifferentAccess)
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 4e0aef2cc7..b5d72d1a42 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -81,7 +81,7 @@ module Notifications
assert_equal expected, events
end
- def test_subsribing_to_instrumentation_while_inside_it
+ def test_subscribing_to_instrumentation_while_inside_it
# the repro requires that there are no evented subscribers for the "foo" event,
# so we have to duplicate some of the setup code
old_notifier = ActiveSupport::Notifications.notifier
diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb
index 49a3951623..08d04e3223 100644
--- a/activesupport/test/safe_buffer_test.rb
+++ b/activesupport/test/safe_buffer_test.rb
@@ -151,7 +151,7 @@ class SafeBufferTest < ActiveSupport::TestCase
assert_equal "", ActiveSupport::SafeBuffer.new("foo").clone_empty
end
- test "clone_empty keeps the original dirtyness" do
+ test "clone_empty keeps the original dirtiness" do
assert_predicate @buffer.clone_empty, :html_safe?
assert_not_predicate @buffer.gsub!("", "").clone_empty, :html_safe?
end
diff --git a/activesupport/test/share_lock_test.rb b/activesupport/test/share_lock_test.rb
index 30a1ddad3f..a40c813fe3 100644
--- a/activesupport/test/share_lock_test.rb
+++ b/activesupport/test/share_lock_test.rb
@@ -38,7 +38,7 @@ class ShareLockTest < ActiveSupport::TestCase
end
end
- def test_multiple_exlusives_are_able_to_progress
+ def test_multiple_exclusives_are_able_to_progress
with_thread_waiting_in_lock_section(:sharing) do |sharing_thread_release_latch|
exclusive_threads = (1..2).map do
Thread.new do
diff --git a/activesupport/test/time_travel_test.rb b/activesupport/test/time_travel_test.rb
index 9c61ab0ab5..a1f84bf69e 100644
--- a/activesupport/test/time_travel_test.rb
+++ b/activesupport/test/time_travel_test.rb
@@ -148,7 +148,7 @@ class TimeTravelTest < ActiveSupport::TestCase
end
end
- def test_travel_to_will_reset_the_usec_to_avoid_mysql_rouding
+ def test_travel_to_will_reset_the_usec_to_avoid_mysql_rounding
Time.stub(:now, Time.now) do
travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do
assert_equal 50, Time.now.sec
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 6d45a6726d..f948c363df 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -32,7 +32,7 @@ class TimeZoneTest < ActiveSupport::TestCase
end
end
- def test_period_for_local_with_ambigiuous_time
+ def test_period_for_local_with_ambiguous_time
zone = ActiveSupport::TimeZone["Moscow"]
period = zone.period_for_local(Time.utc(2015, 1, 1))
assert_equal period, zone.period_for_local(Time.utc(2014, 10, 26, 1, 0, 0))
diff --git a/guides/assets/images/getting_started/template_is_missing_articles_new.png b/guides/assets/images/getting_started/template_is_missing_articles_new.png
index a1603f5d28..830b19bd9d 100644
--- a/guides/assets/images/getting_started/template_is_missing_articles_new.png
+++ b/guides/assets/images/getting_started/template_is_missing_articles_new.png
Binary files differ
diff --git a/guides/source/routing.md b/guides/source/routing.md
index a33ac6a589..92d5b45e7d 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -260,7 +260,7 @@ In each of these cases, the named routes remain the same as if you did not use `
| PATCH/PUT | /admin/articles/:id | articles#update | article_path(:id) |
| DELETE | /admin/articles/:id | articles#destroy | article_path(:id) |
-TIP: _If you need to use a different controller namespace inside a `namespace` block you can specify an absolute controller path, e.g: `get '/foo' => '/foo#index'`._
+TIP: _If you need to use a different controller namespace inside a `namespace` block you can specify an absolute controller path, e.g: `get '/foo', to: '/foo#index'`._
### Nested Resources