diff -Nru rails-6.0.3.7+dfsg/actioncable/actioncable.gemspec rails-6.1.4.1+dfsg/actioncable/actioncable.gemspec --- rails-6.0.3.7+dfsg/actioncable/actioncable.gemspec 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/actioncable.gemspec 2021-08-19 16:25:04.000000000 +0000 @@ -31,6 +31,7 @@ # NOTE: Please read our dependency guidelines before updating versions: # https://edgeguides.rubyonrails.org/security.html#dependency-management-and-cves + s.add_dependency "activesupport", version s.add_dependency "actionpack", version s.add_dependency "nio4r", "~> 2.0" diff -Nru rails-6.0.3.7+dfsg/actioncable/app/assets/javascripts/action_cable.js rails-6.1.4.1+dfsg/actioncable/app/assets/javascripts/action_cable.js --- rails-6.0.3.7+dfsg/actioncable/app/assets/javascripts/action_cable.js 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/app/assets/javascripts/action_cable.js 2021-08-19 16:25:04.000000000 +0000 @@ -135,7 +135,7 @@ if (document.visibilityState === "visible") { setTimeout(function() { if (_this2.connectionIsStale() || !_this2.connection.isOpen()) { - logger.log("ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = " + document.visibilityState); + logger.log("ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = " + document.visibilityState); _this2.connection.reopen(); } }, 200); diff -Nru rails-6.0.3.7+dfsg/actioncable/app/javascript/action_cable/connection_monitor.js rails-6.1.4.1+dfsg/actioncable/app/javascript/action_cable/connection_monitor.js --- rails-6.0.3.7+dfsg/actioncable/app/javascript/action_cable/connection_monitor.js 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/app/javascript/action_cable/connection_monitor.js 2021-08-19 16:25:04.000000000 +0000 @@ -105,7 +105,7 @@ if (document.visibilityState === "visible") { setTimeout(() => { if (this.connectionIsStale() || !this.connection.isOpen()) { - logger.log(`ConnectionMonitor reopening stale connection on visibilitychange. visbilityState = ${document.visibilityState}`) + logger.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`) this.connection.reopen() } } diff -Nru rails-6.0.3.7+dfsg/actioncable/app/javascript/action_cable/consumer.js rails-6.1.4.1+dfsg/actioncable/app/javascript/action_cable/consumer.js --- rails-6.0.3.7+dfsg/actioncable/app/javascript/action_cable/consumer.js 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/app/javascript/action_cable/consumer.js 2021-08-19 16:25:04.000000000 +0000 @@ -6,7 +6,7 @@ // The Consumer instance is also the gateway to establishing subscriptions to desired channels through the #createSubscription // method. // -// The following example shows how this can be setup: +// The following example shows how this can be set up: // // App = {} // App.cable = ActionCable.createConsumer("ws://example.com/accounts/1") diff -Nru rails-6.0.3.7+dfsg/actioncable/app/javascript/action_cable/logger.js rails-6.1.4.1+dfsg/actioncable/app/javascript/action_cable/logger.js --- rails-6.0.3.7+dfsg/actioncable/app/javascript/action_cable/logger.js 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/app/javascript/action_cable/logger.js 2021-08-19 16:25:04.000000000 +0000 @@ -1,5 +1,17 @@ import adapters from "./adapters" +// The logger is disabled by default. You can enable it with: +// +// ActionCable.logger.enabled = true +// +// Example: +// +// import * as ActionCable from '@rails/actioncable' +// +// ActionCable.logger.enabled = true +// ActionCable.logger.log('Connection Established.') +// + export default { log(...messages) { if (this.enabled) { diff -Nru rails-6.0.3.7+dfsg/actioncable/app/javascript/action_cable/subscriptions.js rails-6.1.4.1+dfsg/actioncable/app/javascript/action_cable/subscriptions.js --- rails-6.0.3.7+dfsg/actioncable/app/javascript/action_cable/subscriptions.js 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/app/javascript/action_cable/subscriptions.js 2021-08-19 16:25:04.000000000 +0000 @@ -1,7 +1,8 @@ import Subscription from "./subscription" -// Collection class for creating (and internally managing) channel subscriptions. The only method intended to be triggered by the user -// us ActionCable.Subscriptions#create, and it should be called through the consumer like so: +// Collection class for creating (and internally managing) channel subscriptions. +// The only method intended to be triggered by the user is ActionCable.Subscriptions#create, +// and it should be called through the consumer like so: // // App = {} // App.cable = ActionCable.createConsumer("ws://example.com/accounts/1") diff -Nru rails-6.0.3.7+dfsg/actioncable/CHANGELOG.md rails-6.1.4.1+dfsg/actioncable/CHANGELOG.md --- rails-6.0.3.7+dfsg/actioncable/CHANGELOG.md 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/CHANGELOG.md 2021-08-19 16:25:04.000000000 +0000 @@ -1,234 +1,82 @@ -## Rails 6.0.3.7 (May 05, 2021) ## +## Rails 6.1.4.1 (August 19, 2021) ## * No changes. -## Rails 6.0.3.6 (May 04, 2021) ## +## Rails 6.1.4 (June 24, 2021) ## -* No changes. - - -## Rails 6.0.3.6 (March 26, 2021) ## - -* No changes. - - -## Rails 6.0.3.5 (February 10, 2021) ## - -* No changes. - - -## Rails 6.0.3.4 (October 07, 2020) ## - -* No changes. - - -## Rails 6.0.3.3 (September 09, 2020) ## - -* No changes. - - -## Rails 6.0.3.2 (June 17, 2020) ## +* Fix `ArgumentError` with ruby 3.0 on `RemoteConnection#disconnect`. -* No changes. - - -## Rails 6.0.3.1 (May 18, 2020) ## - -* No changes. + *Vladislav* -## Rails 6.0.3 (May 06, 2020) ## +## Rails 6.1.3.2 (May 05, 2021) ## * No changes. -## Rails 6.0.2.2 (March 19, 2020) ## +## Rails 6.1.3.1 (March 26, 2021) ## * No changes. -## Rails 6.0.2.1 (December 18, 2019) ## +## Rails 6.1.3 (February 17, 2021) ## * No changes. -## Rails 6.0.2 (December 13, 2019) ## +## Rails 6.1.2.1 (February 10, 2021) ## * No changes. -## Rails 6.0.1 (November 5, 2019) ## +## Rails 6.1.2 (February 09, 2021) ## * No changes. -## Rails 6.0.0 (August 16, 2019) ## +## Rails 6.1.1 (January 07, 2021) ## * No changes. -## Rails 6.0.0.rc2 (July 22, 2019) ## - -* No changes. - - -## Rails 6.0.0.rc1 (April 24, 2019) ## - -* No changes. - - -## Rails 6.0.0.beta3 (March 11, 2019) ## - -* No changes. - - -## Rails 6.0.0.beta2 (February 25, 2019) ## - -* PostgreSQL subscription adapters now support `channel_prefix` option in cable.yml - - Avoids channel name collisions when multiple apps use the same database for Action Cable. +## Rails 6.1.0 (December 09, 2020) ## - *Vladimir Dementyev* +* `ActionCable::Connection::Base` now allows intercepting unhandled exceptions + with `rescue_from` before they are logged, which is useful for error reporting + tools and other integrations. -* Allow passing custom configuration to `ActionCable::Server::Base`. + *Justin Talbott* - You can now create a standalone Action Cable server with a custom configuration - (e.g. to run it in isolation from the default one): +* Add `ActionCable::Channel#stream_or_reject_for` to stream if record is present, otherwise reject the connection - ```ruby - config = ActionCable::Server::Configuration.new - config.cable = { adapter: "redis", channel_prefix: "custom_" } - - CUSTOM_CABLE = ActionCable::Server::Base.new(config: config) - ``` - - Then you can mount it in the `routes.rb` file: - - ```ruby - Rails.application.routes.draw do - mount CUSTOM_CABLE => "/custom_cable" - # ... - end - ``` - - *Vladimir Dementyev* - -* Add `:action_cable_connection` and `:action_cable_channel` load hooks. - - You can use them to extend `ActionCable::Connection::Base` and `ActionCable::Channel::Base` - functionality: - - ```ruby - ActiveSupport.on_load(:action_cable_channel) do - # do something in the context of ActionCable::Channel::Base - end - ``` - - *Vladimir Dementyev* - -* Add `Channel::Base#broadcast_to`. - - You can now call `broadcast_to` within a channel action, which equals to - the `self.class.broadcast_to`. - - *Vladimir Dementyev* - -* Make `Channel::Base.broadcasting_for` a public API. - - You can use `.broadcasting_for` to generate a unique stream identifier within - a channel for the specified target (e.g. Active Record model): - - ```ruby - ChatChannel.broadcasting_for(model) # => "chat:" - ``` - - *Vladimir Dementyev* + *Atul Bhosale* +* Add `ActionCable::Channel#stop_stream_from` and `#stop_stream_for` to unsubscribe from a specific stream. -## Rails 6.0.0.beta1 (January 18, 2019) ## + *Zhang Kang* -* [Rename npm package](https://github.com/rails/rails/pull/34905) from - [`actioncable`](https://www.npmjs.com/package/actioncable) to - [`@rails/actioncable`](https://www.npmjs.com/package/@rails/actioncable). +* Add PostgreSQL subscription connection identificator. - *Javan Makhmali* + Now you can distinguish Action Cable PostgreSQL subscription connections among others. + Also, you can set custom `id` in `cable.yml` configuration. -* Merge [`action-cable-testing`](https://github.com/palkan/action-cable-testing) to Rails. - - *Vladimir Dementyev* - -* The JavaScript WebSocket client will no longer try to reconnect - when you call `reject_unauthorized_connection` on the connection. - - *Mick Staugaard* - -* `ActionCable.Connection#getState` now references the configurable - `ActionCable.adapters.WebSocket` property rather than the `WebSocket` global - variable, matching the behavior of `ActionCable.Connection#open`. - - *Richard Macklin* - -* The ActionCable javascript package has been converted from CoffeeScript - to ES2015, and we now publish the source code in the npm distribution. - - This allows ActionCable users to depend on the javascript source code - rather than the compiled code, which can produce smaller javascript bundles. - - This change includes some breaking changes to optional parts of the - ActionCable javascript API: - - - Configuration of the WebSocket adapter and logger adapter have been moved - from properties of `ActionCable` to properties of `ActionCable.adapters`. - If you are currently configuring these adapters you will need to make - these changes when upgrading: - - ```diff - - ActionCable.WebSocket = MyWebSocket - + ActionCable.adapters.WebSocket = MyWebSocket - ``` - ```diff - - ActionCable.logger = myLogger - + ActionCable.adapters.logger = myLogger - ``` - - - The `ActionCable.startDebugging()` and `ActionCable.stopDebugging()` - methods have been removed and replaced with the property - `ActionCable.logger.enabled`. If you are currently using these methods you - will need to make these changes when upgrading: - - ```diff - - ActionCable.startDebugging() - + ActionCable.logger.enabled = true - ``` - ```diff - - ActionCable.stopDebugging() - + ActionCable.logger.enabled = false - ``` - - *Richard Macklin* - -* Add `id` option to redis adapter so now you can distinguish - ActionCable's redis connections among others. Also, you can set - custom id in options. - - Before: - ``` - $ redis-cli client list - id=669 addr=127.0.0.1:46442 fd=8 name= age=18 ... - ``` - - After: - ``` - $ redis-cli client list - id=673 addr=127.0.0.1:46516 fd=8 name=ActionCable-PID-19413 age=2 ... + ```sql + SELECT application_name FROM pg_stat_activity; + /* + application_name + ------------------------ + psql + ActionCable-PID-42 + (2 rows) + */ ``` - *Ilia Kasianenko* + *Sergey Ponomarev* -* Rails 6 requires Ruby 2.5.0 or newer. +* Subscription confirmations and rejections are now logged at the `DEBUG` level instead of `INFO`. - *Jeremy Daer*, *Kasper Timm Hansen* + *DHH* -Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/actioncable/CHANGELOG.md) for previous changes. +Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actioncable/CHANGELOG.md) for previous changes. diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/channel/base.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/channel/base.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/channel/base.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/channel/base.rb 2021-08-19 16:25:04.000000000 +0000 @@ -194,7 +194,7 @@ end private - # Called once a consumer has become a subscriber of the channel. Usually the place to setup any streams + # Called once a consumer has become a subscriber of the channel. Usually the place to set up any streams # you want this channel to be sending to the subscriber. def subscribed # :doc: # Override in subclasses @@ -283,7 +283,7 @@ def transmit_subscription_confirmation unless subscription_confirmation_sent? - logger.info "#{self.class.name} is transmitting the subscription confirmation" + logger.debug "#{self.class.name} is transmitting the subscription confirmation" ActiveSupport::Notifications.instrument("transmit_subscription_confirmation.action_cable", channel_class: self.class.name) do connection.transmit identifier: @identifier, type: ActionCable::INTERNAL[:message_types][:confirmation] @@ -298,7 +298,7 @@ end def transmit_subscription_rejection - logger.info "#{self.class.name} is transmitting the subscription rejection" + logger.debug "#{self.class.name} is transmitting the subscription rejection" ActiveSupport::Notifications.instrument("transmit_subscription_rejection.action_cable", channel_class: self.class.name) do connection.transmit identifier: @identifier, type: ActionCable::INTERNAL[:message_types][:rejection] diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/channel/naming.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/channel/naming.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/channel/naming.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/channel/naming.rb 2021-08-19 16:25:04.000000000 +0000 @@ -14,7 +14,7 @@ # Chats::AppearancesChannel.channel_name # => 'chats:appearances' # FooChats::BarAppearancesChannel.channel_name # => 'foo_chats:bar_appearances' def channel_name - @channel_name ||= name.sub(/Channel$/, "").gsub("::", ":").underscore + @channel_name ||= name.delete_suffix("Channel").gsub("::", ":").underscore end end diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/channel/streams.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/channel/streams.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/channel/streams.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/channel/streams.rb 2021-08-19 16:25:04.000000000 +0000 @@ -25,7 +25,7 @@ # # An example broadcasting for this channel looks like so: # - # ActionCable.server.broadcast "comments_for_45", author: 'DHH', content: 'Rails is just swell' + # ActionCable.server.broadcast "comments_for_45", { author: 'DHH', content: 'Rails is just swell' } # # If you have a stream that is related to a model, then the broadcasting used can be generated from the model and channel. # The following example would subscribe to a broadcasting like comments:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE. @@ -82,7 +82,7 @@ # Build a stream handler by wrapping the user-provided callback with # a decoder or defaulting to a JSON-decoding retransmitter. handler = worker_pool_stream_handler(broadcasting, callback || block, coder: coder) - streams << [ broadcasting, handler ] + streams[broadcasting] = handler connection.server.event_loop.post do pubsub.subscribe(broadcasting, handler, lambda do @@ -102,6 +102,20 @@ stream_from(broadcasting_for(model), callback || block, coder: coder) end + # Unsubscribes streams from the named broadcasting. + def stop_stream_from(broadcasting) + callback = streams.delete(broadcasting) + if callback + pubsub.unsubscribe(broadcasting, callback) + logger.info "#{self.class.name} stopped streaming from #{broadcasting}" + end + end + + # Unsubscribes streams for the model. + def stop_stream_for(model) + stop_stream_from(broadcasting_for(model)) + end + # Unsubscribes all streams associated with this channel from the pubsub queue. def stop_all_streams streams.each do |broadcasting, callback| @@ -110,11 +124,23 @@ end.clear end + # Calls stream_for if record is present, otherwise calls reject. + # This method is intended to be called when you're looking + # for a record based on a parameter, if its found it will start + # streaming. If the record is nil then it will reject the connection. + def stream_or_reject_for(record) + if record + stream_for record + else + reject + end + end + private delegate :pubsub, to: :connection def streams - @_streams ||= [] + @_streams ||= {} end # Always wrap the outermost handler to invoke the user handler on the diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/channel/test_case.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/channel/test_case.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/channel/test_case.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/channel/test_case.rb 2021-08-19 16:25:04.000000000 +0000 @@ -15,9 +15,9 @@ end end - # Stub `stream_from` to track streams for the channel. - # Add public aliases for `subscription_confirmation_sent?` and - # `subscription_rejected?`. + # Stub +stream_from+ to track streams for the channel. + # Add public aliases for +subscription_confirmation_sent?+ and + # +subscription_rejected?+. module ChannelStub def confirmed? subscription_confirmation_sent? @@ -123,7 +123,7 @@ # connection:: # An ActionCable::Channel::ConnectionStub, representing the current HTTP connection. # subscription:: - # An instance of the current channel, created when you call `subscribe`. + # An instance of the current channel, created when you call +subscribe+. # transmissions:: # A list of all messages that have been transmitted into the channel. # @@ -159,7 +159,7 @@ # def test_speak # subscribe room_id: rooms(:chat).id # - # assert_broadcasts_on(rooms(:chat), text: "Hello, Rails!") do + # assert_broadcast_on(rooms(:chat), text: "Hello, Rails!") do # perform :speak, message: "Hello, Rails!" # end # end @@ -209,7 +209,7 @@ end end - # Setup test connection with the specified identifiers: + # Set up test connection with the specified identifiers: # # class ApplicationCable < ActionCable::Connection::Base # identified_by :user, :token diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/connection/base.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/connection/base.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/connection/base.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/connection/base.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,6 +1,7 @@ # frozen_string_literal: true require "action_dispatch" +require "active_support/rescuable" module ActionCable module Connection @@ -46,6 +47,7 @@ include Identification include InternalChannel include Authorization + include ActiveSupport::Rescuable attr_reader :server, :env, :subscriptions, :logger, :worker_pool, :protocol delegate :event_loop, :pubsub, to: :server diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/connection/subscriptions.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/connection/subscriptions.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/connection/subscriptions.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/connection/subscriptions.rb 2021-08-19 16:25:04.000000000 +0000 @@ -21,6 +21,7 @@ logger.error "Received unrecognized command in #{data.inspect}" end rescue Exception => e + @connection.rescue_with_handler(e) logger.error "Could not execute command from (#{data.inspect}) [#{e.class} - #{e.message}]: #{e.backtrace.first(5).join(" | ")}" end diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/connection/test_case.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/connection/test_case.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/connection/test_case.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/connection/test_case.rb 2021-08-19 16:25:04.000000000 +0000 @@ -85,7 +85,7 @@ # end # end # - # +connect+ accepts additional information the HTTP request with the + # +connect+ accepts additional information about the HTTP request with the # +params+, +headers+, +session+ and Rack +env+ options. # # def test_connect_with_headers_and_query_string @@ -101,7 +101,7 @@ # assert_equal "1", connection.user.id # end # - # You can also setup the correct cookies before the connection request: + # You can also set up the correct cookies before the connection request: # # def test_connect_with_cookies # # Plain cookies: diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/engine.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/engine.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/engine.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/engine.rb 2021-08-19 16:25:04.000000000 +0000 @@ -30,7 +30,7 @@ ActiveSupport.on_load(:action_cable) do if (config_path = Pathname.new(app.config.paths["config/cable"].first)).exist? - self.cable = Rails.application.config_for(config_path).with_indifferent_access + self.cable = Rails.application.config_for(config_path).to_h.with_indifferent_access end previous_connection_class = connection_class diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/gem_version.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/gem_version.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/gem_version.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/gem_version.rb 2021-08-19 16:25:04.000000000 +0000 @@ -8,9 +8,9 @@ module VERSION MAJOR = 6 - MINOR = 0 - TINY = 3 - PRE = "7" + MINOR = 1 + TINY = 4 + PRE = "1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/helpers/action_cable_helper.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/helpers/action_cable_helper.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/helpers/action_cable_helper.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/helpers/action_cable_helper.rb 2021-08-19 16:25:04.000000000 +0000 @@ -12,11 +12,11 @@ # # # This is then used by Action Cable to determine the URL of your WebSocket server. - # Your CoffeeScript can then connect to the server without needing to specify the + # Your JavaScript can then connect to the server without needing to specify the # URL directly: # - # #= require cable - # @App = {} + # window.Cable = require("@rails/actioncable") + # window.App = {} # App.cable = Cable.createConsumer() # # Make sure to specify the correct server location in each of your environment diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/remote_connections.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/remote_connections.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/remote_connections.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/remote_connections.rb 2021-08-19 16:25:04.000000000 +0000 @@ -45,7 +45,7 @@ # Uses the internal channel to disconnect the connection. def disconnect - server.broadcast internal_channel, type: "disconnect" + server.broadcast internal_channel, { type: "disconnect" } end # Returns all the identifiers that were applied to this connection. diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/server/base.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/server/base.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/server/base.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/server/base.rb 2021-08-19 16:25:04.000000000 +0000 @@ -27,7 +27,7 @@ @remote_connections = @event_loop = @worker_pool = @pubsub = nil end - # Called by Rack to setup the server. + # Called by Rack to set up the server. def call(env) setup_heartbeat_timer config.connection_class.call.new(self, env).process diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/server/broadcasting.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/server/broadcasting.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/server/broadcasting.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/server/broadcasting.rb 2021-08-19 16:25:04.000000000 +0000 @@ -40,7 +40,7 @@ end def broadcast(message) - server.logger.debug "[ActionCable] Broadcasting to #{broadcasting}: #{message.inspect}" + server.logger.debug { "[ActionCable] Broadcasting to #{broadcasting}: #{message.inspect}" } payload = { broadcasting: broadcasting, message: message, coder: coder } ActiveSupport::Notifications.instrument("broadcast.action_cable", payload) do diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/server/worker.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/server/worker.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/server/worker.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/server/worker.rb 2021-08-19 16:25:04.000000000 +0000 @@ -2,6 +2,7 @@ require "active_support/callbacks" require "active_support/core_ext/module/attribute_accessors_per_thread" +require "action_cable/server/worker/active_record_connection_management" require "concurrent" module ActionCable diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/server.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/server.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/server.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/server.rb 2021-08-19 16:25:04.000000000 +0000 @@ -11,7 +11,6 @@ autoload :Configuration autoload :Worker - autoload :ActiveRecordConnectionManagement, "action_cable/server/worker/active_record_connection_management" end end end diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/subscription_adapter/base.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/subscription_adapter/base.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/subscription_adapter/base.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/subscription_adapter/base.rb 2021-08-19 16:25:04.000000000 +0000 @@ -25,6 +25,10 @@ def shutdown raise NotImplementedError end + + def identifier + @server.config.cable[:id] ||= "ActionCable-PID-#{$$}" + end end end end diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/subscription_adapter/postgresql.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/subscription_adapter/postgresql.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/subscription_adapter/postgresql.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/subscription_adapter/postgresql.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,6 +1,6 @@ # frozen_string_literal: true -gem "pg", ">= 0.18", "< 2.0" +gem "pg", "~> 1.1" require "pg" require "thread" require "digest/sha1" @@ -42,6 +42,7 @@ pg_conn = ar_conn.raw_connection verify!(pg_conn) + pg_conn.exec("SET application_name = #{pg_conn.escape_identifier(identifier)}") yield pg_conn ensure ar_conn.disconnect! diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/subscription_adapter/redis.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/subscription_adapter/redis.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/subscription_adapter/redis.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/subscription_adapter/redis.rb 2021-08-19 16:25:04.000000000 +0000 @@ -15,7 +15,6 @@ # Overwrite this factory method for Redis connections if you want to use a different Redis library than the redis gem. # This is needed, for example, when using Makara proxies for distributed Redis. cattr_accessor :redis_connector, default: ->(config) do - config[:id] ||= "ActionCable-PID-#{$$}" ::Redis.new(config.except(:adapter, :channel_prefix)) end @@ -57,7 +56,7 @@ end def redis_connection - self.class.redis_connector.call(@server.config.cable) + self.class.redis_connector.call(@server.config.cable.merge(id: identifier)) end class Listener < SubscriberMap diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable/test_helper.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable/test_helper.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable/test_helper.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable/test_helper.rb 2021-08-19 16:25:04.000000000 +0000 @@ -42,10 +42,10 @@ # end # end # - def assert_broadcasts(stream, number) + def assert_broadcasts(stream, number, &block) if block_given? original_count = broadcasts_size(stream) - yield + assert_nothing_raised(&block) new_count = broadcasts_size(stream) actual_count = new_count - original_count else @@ -94,7 +94,7 @@ # end # end # - def assert_broadcast_on(stream, data) + def assert_broadcast_on(stream, data, &block) # Encode to JSON and back–we want to use this value to compare # with decoded JSON. # Comparing JSON strings doesn't work due to the order if the keys. @@ -106,7 +106,7 @@ old_messages = new_messages clear_messages(stream) - yield + assert_nothing_raised(&block) new_messages = broadcasts(stream) clear_messages(stream) diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/action_cable.rb rails-6.1.4.1+dfsg/actioncable/lib/action_cable.rb --- rails-6.0.3.7+dfsg/actioncable/lib/action_cable.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/action_cable.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,7 +1,7 @@ # frozen_string_literal: true #-- -# Copyright (c) 2015-2019 Basecamp, LLC +# Copyright (c) 2015-2020 Basecamp, LLC # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt rails-6.1.4.1+dfsg/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt --- rails-6.0.3.7+dfsg/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt 2021-08-19 16:25:04.000000000 +0000 @@ -1,5 +1,5 @@ // Action Cable provides the framework to deal with WebSockets in Rails. -// You can generate new channels where WebSocket features live using the `rails generate channel` command. +// You can generate new channels where WebSocket features live using the `bin/rails generate channel` command. import { createConsumer } from "@rails/actioncable" diff -Nru rails-6.0.3.7+dfsg/actioncable/lib/rails/generators/channel/USAGE rails-6.1.4.1+dfsg/actioncable/lib/rails/generators/channel/USAGE --- rails-6.0.3.7+dfsg/actioncable/lib/rails/generators/channel/USAGE 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/lib/rails/generators/channel/USAGE 2021-08-19 16:25:04.000000000 +0000 @@ -1,11 +1,11 @@ Description: ============ - Stubs out a new cable channel for the server (in Ruby) and client (in JavaScript). + Generates a new cable channel for the server (in Ruby) and client (in JavaScript). Pass the channel name, either CamelCased or under_scored, and an optional list of channel actions as arguments. Example: ======== - rails generate channel Chat speak + bin/rails generate channel Chat speak creates a Chat channel class, test and JavaScript asset: Channel: app/channels/chat_channel.rb diff -Nru rails-6.0.3.7+dfsg/actioncable/MIT-LICENSE rails-6.1.4.1+dfsg/actioncable/MIT-LICENSE --- rails-6.0.3.7+dfsg/actioncable/MIT-LICENSE 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/MIT-LICENSE 2021-08-19 16:25:04.000000000 +0000 @@ -1,4 +1,4 @@ -Copyright (c) 2015-2019 Basecamp, LLC +Copyright (c) 2015-2020 Basecamp, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff -Nru rails-6.0.3.7+dfsg/actioncable/package.json rails-6.1.4.1+dfsg/actioncable/package.json --- rails-6.0.3.7+dfsg/actioncable/package.json 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/package.json 2021-08-19 16:25:04.000000000 +0000 @@ -1,6 +1,6 @@ { "name": "@rails/actioncable", - "version": "6.0.3-7", + "version": "6.1.4-1", "description": "WebSocket framework for Ruby on Rails.", "main": "app/assets/javascripts/action_cable.js", "files": [ @@ -21,7 +21,7 @@ "bugs": { "url": "https://github.com/rails/rails/issues" }, - "homepage": "http://rubyonrails.org/", + "homepage": "https://rubyonrails.org/", "devDependencies": { "babel-core": "^6.25.0", "babel-plugin-external-helpers": "^6.22.0", diff -Nru rails-6.0.3.7+dfsg/actioncable/Rakefile rails-6.1.4.1+dfsg/actioncable/Rakefile --- rails-6.0.3.7+dfsg/actioncable/Rakefile 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/Rakefile 2021-08-19 16:25:04.000000000 +0000 @@ -12,7 +12,7 @@ Rake::TestTask.new do |t| t.libs << "test" - t.test_files = Dir.glob("#{__dir__}/test/**/*_test.rb") + t.test_files = FileList["#{__dir__}/test/**/*_test.rb"] t.warning = true t.verbose = true t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION) diff -Nru rails-6.0.3.7+dfsg/actioncable/test/channel/stream_test.rb rails-6.1.4.1+dfsg/actioncable/test/channel/stream_test.rb --- rails-6.0.3.7+dfsg/actioncable/test/channel/stream_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/test/channel/stream_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -110,6 +110,39 @@ end end + test "stream_or_reject_for" do + run_in_eventmachine do + connection = TestConnection.new + + channel = ChatChannel.new connection, "" + channel.subscribe_to_channel + channel.stream_or_reject_for Room.new(1) + wait_for_async + + pubsub_call = channel.pubsub.class.class_variable_get "@@subscribe_called" + + assert_equal "action_cable:stream_tests:chat:Room#1-Campfire", pubsub_call[:channel] + assert_instance_of Proc, pubsub_call[:callback] + assert_instance_of Proc, pubsub_call[:success_callback] + end + end + + test "reject subscription when nil is passed to stream_or_reject_for" do + run_in_eventmachine do + connection = TestConnection.new + channel = ChatChannel.new connection, "{id: 1}", id: 1 + channel.subscribe_to_channel + channel.stream_or_reject_for nil + assert_nil connection.last_transmission + + wait_for_async + + rejection = { "identifier" => "{id: 1}", "type" => "reject_subscription" } + connection.transmit(rejection) + assert_equal rejection, connection.last_transmission + end + end + test "stream_from subscription confirmation" do run_in_eventmachine do connection = TestConnection.new @@ -144,6 +177,115 @@ assert_equal 1, connection.transmissions.size end end + + test "stop_all_streams" do + run_in_eventmachine do + connection = TestConnection.new + + channel = ChatChannel.new connection, "{id: 3}" + channel.subscribe_to_channel + + assert_equal 0, subscribers_of(connection).size + + channel.stream_from "room_one" + channel.stream_from "room_two" + + wait_for_async + assert_equal 2, subscribers_of(connection).size + + channel2 = ChatChannel.new connection, "{id: 3}" + channel2.subscribe_to_channel + + channel2.stream_from "room_one" + wait_for_async + + subscribers = subscribers_of(connection) + + assert_equal 2, subscribers.size + assert_equal 2, subscribers["room_one"].size + assert_equal 1, subscribers["room_two"].size + + channel.stop_all_streams + + subscribers = subscribers_of(connection) + assert_equal 1, subscribers.size + assert_equal 1, subscribers["room_one"].size + end + end + + test "stop_stream_from" do + run_in_eventmachine do + connection = TestConnection.new + + channel = ChatChannel.new connection, "{id: 3}" + channel.subscribe_to_channel + + channel.stream_from "room_one" + channel.stream_from "room_two" + + channel2 = ChatChannel.new connection, "{id: 3}" + channel2.subscribe_to_channel + + channel2.stream_from "room_one" + + subscribers = subscribers_of(connection) + + wait_for_async + + assert_equal 2, subscribers.size + assert_equal 2, subscribers["room_one"].size + assert_equal 1, subscribers["room_two"].size + + channel.stop_stream_from "room_one" + + subscribers = subscribers_of(connection) + + assert_equal 2, subscribers.size + assert_equal 1, subscribers["room_one"].size + assert_equal 1, subscribers["room_two"].size + end + end + + test "stop_stream_for" do + run_in_eventmachine do + connection = TestConnection.new + + channel = ChatChannel.new connection, "{id: 3}" + channel.subscribe_to_channel + + channel.stream_for Room.new(1) + channel.stream_for Room.new(2) + + channel2 = ChatChannel.new connection, "{id: 3}" + channel2.subscribe_to_channel + + channel2.stream_for Room.new(1) + + subscribers = subscribers_of(connection) + + wait_for_async + + assert_equal 2, subscribers.size + + assert_equal 2, subscribers[ChatChannel.broadcasting_for(Room.new(1))].size + assert_equal 1, subscribers[ChatChannel.broadcasting_for(Room.new(2))].size + + channel.stop_stream_for Room.new(1) + + subscribers = subscribers_of(connection) + + assert_equal 2, subscribers.size + assert_equal 1, subscribers[ChatChannel.broadcasting_for(Room.new(1))].size + assert_equal 1, subscribers[ChatChannel.broadcasting_for(Room.new(2))].size + end + end + + private + def subscribers_of(connection) + connection + .pubsub + .subscriber_map + end end require "action_cable/subscription_adapter/async" diff -Nru rails-6.0.3.7+dfsg/actioncable/test/client_test.rb rails-6.1.4.1+dfsg/actioncable/test/client_test.rb --- rails-6.0.3.7+dfsg/actioncable/test/client_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/test/client_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -91,7 +91,7 @@ rescue RuntimeError => ex # Work around https://bugs.ruby-lang.org/issues/13239 - raise unless ex.message =~ /can't modify frozen IOError/ + raise unless ex.message.match?(/can't modify frozen IOError/) # Handle this as if it were the IOError: do the same as above. server.binder.close diff -Nru rails-6.0.3.7+dfsg/actioncable/test/connection/base_test.rb rails-6.1.4.1+dfsg/actioncable/test/connection/base_test.rb --- rails-6.0.3.7+dfsg/actioncable/test/connection/base_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/test/connection/base_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -76,7 +76,7 @@ connection = open_connection connection.process - # Setup the connection + # Set up the connection connection.send :handle_open assert connection.connected diff -Nru rails-6.0.3.7+dfsg/actioncable/test/connection/subscriptions_test.rb rails-6.1.4.1+dfsg/actioncable/test/connection/subscriptions_test.rb --- rails-6.0.3.7+dfsg/actioncable/test/connection/subscriptions_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/test/connection/subscriptions_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -3,12 +3,25 @@ require "test_helper" class ActionCable::Connection::SubscriptionsTest < ActionCable::TestCase + class ChatChannelError < Exception; end + class Connection < ActionCable::Connection::Base - attr_reader :websocket + attr_reader :websocket, :exceptions + + rescue_from ChatChannelError, with: :error_handler + + def initialize(*) + super + @exceptions = [] + end def send_async(method, *args) send method, *args end + + def error_handler(e) + @exceptions << e + end end class ChatChannel < ActionCable::Channel::Base @@ -22,6 +35,10 @@ def speak(data) @lines << data end + + def throw_exception(_data) + raise ChatChannelError.new("Uh Oh") + end end setup do @@ -85,6 +102,19 @@ end end + test "accessing exceptions thrown during command execution" do + run_in_eventmachine do + setup_connection + subscribe_to_chat_channel + + data = { "content" => "Hello World!", "action" => "throw_exception" } + @subscriptions.execute_command "command" => "message", "identifier" => @chat_identifier, "data" => ActiveSupport::JSON.encode(data) + + exception = @connection.exceptions.first + assert_kind_of ChatChannelError, exception + end + end + test "unsubscribe from all" do run_in_eventmachine do setup_connection diff -Nru rails-6.0.3.7+dfsg/actioncable/test/javascript_package_test.rb rails-6.1.4.1+dfsg/actioncable/test/javascript_package_test.rb --- rails-6.0.3.7+dfsg/actioncable/test/javascript_package_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/test/javascript_package_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "test_helper" + +class JavascriptPackageTest < ActiveSupport::TestCase + def test_compiled_code_is_in_sync_with_source_code + compiled_file = File.expand_path("../app/assets/javascripts/action_cable.js", __dir__) + + assert_no_changes -> { File.read(compiled_file) } do + system "yarn build" + end + end +end diff -Nru rails-6.0.3.7+dfsg/actioncable/test/stubs/test_adapter.rb rails-6.1.4.1+dfsg/actioncable/test/stubs/test_adapter.rb --- rails-6.0.3.7+dfsg/actioncable/test/stubs/test_adapter.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/test/stubs/test_adapter.rb 2021-08-19 16:25:04.000000000 +0000 @@ -5,10 +5,17 @@ end def subscribe(channel, callback, success_callback = nil) + subscriber_map[channel] << callback @@subscribe_called = { channel: channel, callback: callback, success_callback: success_callback } end def unsubscribe(channel, callback) + subscriber_map[channel].delete(callback) + subscriber_map.delete(channel) if subscriber_map[channel].empty? @@unsubscribe_called = { channel: channel, callback: callback } end + + def subscriber_map + @subscribers ||= Hash.new { |h, k| h[k] = [] } + end end diff -Nru rails-6.0.3.7+dfsg/actioncable/test/subscription_adapter/channel_prefix.rb rails-6.1.4.1+dfsg/actioncable/test/subscription_adapter/channel_prefix.rb --- rails-6.0.3.7+dfsg/actioncable/test/subscription_adapter/channel_prefix.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/test/subscription_adapter/channel_prefix.rb 2021-08-19 16:25:04.000000000 +0000 @@ -5,7 +5,7 @@ module ChannelPrefixTest def test_channel_prefix server2 = ActionCable::Server::Base.new(config: ActionCable::Server::Configuration.new) - server2.config.cable = alt_cable_config + server2.config.cable = alt_cable_config.with_indifferent_access server2.config.logger = Logger.new(StringIO.new).tap { |l| l.level = Logger::UNKNOWN } adapter_klass = server2.config.pubsub_adapter diff -Nru rails-6.0.3.7+dfsg/actioncable/test/subscription_adapter/postgresql_test.rb rails-6.1.4.1+dfsg/actioncable/test/subscription_adapter/postgresql_test.rb --- rails-6.0.3.7+dfsg/actioncable/test/subscription_adapter/postgresql_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/test/subscription_adapter/postgresql_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -64,4 +64,24 @@ assert adapter.active? end + + def test_default_subscription_connection_identifier + subscribe_as_queue("channel") { } + + identifiers = ActiveRecord::Base.connection.exec_query("SELECT application_name FROM pg_stat_activity").rows + assert_includes identifiers, ["ActionCable-PID-#{$$}"] + end + + def test_custom_subscription_connection_identifier + server = ActionCable::Server::Base.new + server.config.cable = cable_config.merge(id: "hello-world-42").with_indifferent_access + server.config.logger = Logger.new(StringIO.new).tap { |l| l.level = Logger::UNKNOWN } + + adapter = server.config.pubsub_adapter.new(server) + + subscribe_as_queue("channel", adapter) { } + + identifiers = ActiveRecord::Base.connection.exec_query("SELECT application_name FROM pg_stat_activity").rows + assert_includes identifiers, ["hello-world-42"] + end end diff -Nru rails-6.0.3.7+dfsg/actioncable/test/subscription_adapter/redis_test.rb rails-6.1.4.1+dfsg/actioncable/test/subscription_adapter/redis_test.rb --- rails-6.0.3.7+dfsg/actioncable/test/subscription_adapter/redis_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actioncable/test/subscription_adapter/redis_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -33,24 +33,50 @@ end end -class RedisAdapterTest::Connector < ActionCable::TestCase - test "excludes adapter and channel prefix" do - config = { url: 1, host: 2, port: 3, db: 4, password: 5, id: "Some custom ID" } +class RedisAdapterTest::ConnectorDefaultID < ActionCable::TestCase + def setup + server = ActionCable::Server::Base.new + server.config.cable = cable_config.merge(adapter: "redis").with_indifferent_access + server.config.logger = Logger.new(StringIO.new).tap { |l| l.level = Logger::UNKNOWN } - assert_called_with ::Redis, :new, [ config ] do - connect config.merge(adapter: "redis", channel_prefix: "custom") - end + @adapter = server.config.pubsub_adapter.new(server) + end + + def cable_config + { url: 1, host: 2, port: 3, db: 4, password: 5 } end - test "adds default id if it is not specified" do - config = { url: 1, host: 2, port: 3, db: 4, password: 5, id: "ActionCable-PID-#{$$}" } + def connection_id + "ActionCable-PID-#{$$}" + end - assert_called_with ::Redis, :new, [ config ] do - connect config.except(:id) + def expected_connection + cable_config.merge(id: connection_id) + end + + test "sets connection id for connection" do + assert_called_with ::Redis, :new, [ expected_connection.stringify_keys ] do + @adapter.send(:redis_connection) end end +end + +class RedisAdapterTest::ConnectorCustomID < RedisAdapterTest::ConnectorDefaultID + def cable_config + super.merge(id: connection_id) + end + + def connection_id + "Some custom ID" + end +end + +class RedisAdapterTest::ConnectorWithExcluded < RedisAdapterTest::ConnectorDefaultID + def cable_config + super.merge(adapter: "redis", channel_prefix: "custom") + end - def connect(config) - ActionCable::SubscriptionAdapter::Redis.redis_connector.call(config) + def expected_connection + super.except(:adapter, :channel_prefix) end end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/mailgun/inbound_emails_controller.rb rails-6.1.4.1+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/mailgun/inbound_emails_controller.rb --- rails-6.0.3.7+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/mailgun/inbound_emails_controller.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/mailgun/inbound_emails_controller.rb 2021-08-19 16:25:04.000000000 +0000 @@ -6,7 +6,7 @@ # - +body-mime+: The full RFC 822 message # - +timestamp+: The current time according to Mailgun as the number of seconds passed since the UNIX epoch # - +token+: A randomly-generated, 50-character string - # - +signature+: A hexadecimal HMAC-SHA256 of the timestamp concatenated with the token, generated using the Mailgun API key + # - +signature+: A hexadecimal HMAC-SHA256 of the timestamp concatenated with the token, generated using the Mailgun Signing key # # Authenticates requests by validating their signatures. # @@ -16,21 +16,21 @@ # - 401 Unauthorized if the request's signature could not be validated, or if its timestamp is more than 2 minutes old # - 404 Not Found if Action Mailbox is not configured to accept inbound emails from Mailgun # - 422 Unprocessable Entity if the request is missing required parameters - # - 500 Server Error if the Mailgun API key is missing, or one of the Active Record database, + # - 500 Server Error if the Mailgun Signing key is missing, or one of the Active Record database, # the Active Storage service, or the Active Job backend is misconfigured or unavailable # # == Usage # - # 1. Give Action Mailbox your {Mailgun API key}[https://help.mailgun.com/hc/en-us/articles/203380100-Where-can-I-find-my-API-key-and-SMTP-credentials-] + # 1. Give Action Mailbox your Mailgun Signing key (which you can find under Settings -> Security & Users -> API security in Mailgun) # so it can authenticate requests to the Mailgun ingress. # - # Use rails credentials:edit to add your API key to your application's encrypted credentials under - # +action_mailbox.mailgun_api_key+, where Action Mailbox will automatically find it: + # Use bin/rails credentials:edit to add your Signing key to your application's encrypted credentials under + # +action_mailbox.mailgun_signing_key+, where Action Mailbox will automatically find it: # # action_mailbox: - # mailgun_api_key: ... + # mailgun_signing_key: ... # - # Alternatively, provide your API key in the +MAILGUN_INGRESS_API_KEY+ environment variable. + # Alternatively, provide your Signing key in the +MAILGUN_INGRESS_SIGNING_KEY+ environment variable. # # 2. Tell Action Mailbox to accept emails from Mailgun: # @@ -46,10 +46,16 @@ before_action :authenticate def create - ActionMailbox::InboundEmail.create_and_extract_message_id! params.require("body-mime") + ActionMailbox::InboundEmail.create_and_extract_message_id! mail end private + def mail + params.require("body-mime").tap do |raw_email| + raw_email.prepend("X-Original-To: ", params.require(:recipient), "\n") if params.key?(:recipient) + end + end + def authenticate head :unauthorized unless authenticated? end @@ -64,14 +70,28 @@ ).authenticated? else raise ArgumentError, <<~MESSAGE.squish - Missing required Mailgun API key. Set action_mailbox.mailgun_api_key in your application's - encrypted credentials or provide the MAILGUN_INGRESS_API_KEY environment variable. + Missing required Mailgun Signing key. Set action_mailbox.mailgun_signing_key in your application's + encrypted credentials or provide the MAILGUN_INGRESS_SIGNING_KEY environment variable. MESSAGE end end def key - Rails.application.credentials.dig(:action_mailbox, :mailgun_api_key) || ENV["MAILGUN_INGRESS_API_KEY"] + if Rails.application.credentials.dig(:action_mailbox, :mailgun_api_key) + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Rails.application.credentials.action_mailbox.api_key is deprecated and will be ignored in Rails 6.2. + Use Rails.application.credentials.action_mailbox.signing_key instead. + MSG + Rails.application.credentials.dig(:action_mailbox, :mailgun_api_key) + elsif ENV["MAILGUN_INGRESS_API_KEY"] + ActiveSupport::Deprecation.warn(<<-MSG.squish) + The MAILGUN_INGRESS_API_KEY environment variable is deprecated and will be ignored in Rails 6.2. + Use MAILGUN_INGRESS_SIGNING_KEY instead. + MSG + ENV["MAILGUN_INGRESS_API_KEY"] + else + Rails.application.credentials.dig(:action_mailbox, :mailgun_signing_key) || ENV["MAILGUN_INGRESS_SIGNING_KEY"] + end end class Authenticator diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/postmark/inbound_emails_controller.rb rails-6.1.4.1+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/postmark/inbound_emails_controller.rb --- rails-6.0.3.7+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/postmark/inbound_emails_controller.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/postmark/inbound_emails_controller.rb 2021-08-19 16:25:04.000000000 +0000 @@ -27,7 +27,7 @@ # # 2. Generate a strong password that Action Mailbox can use to authenticate requests to the Postmark ingress. # - # Use rails credentials:edit to add the password to your application's encrypted credentials under + # Use bin/rails credentials:edit to add the password to your application's encrypted credentials under # +action_mailbox.ingress_password+, where Action Mailbox will automatically find it: # # action_mailbox: diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/relay/inbound_emails_controller.rb rails-6.1.4.1+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/relay/inbound_emails_controller.rb --- rails-6.0.3.7+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/relay/inbound_emails_controller.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/relay/inbound_emails_controller.rb 2021-08-19 16:25:04.000000000 +0000 @@ -27,7 +27,7 @@ # # 2. Generate a strong password that Action Mailbox can use to authenticate requests to the ingress. # - # Use rails credentials:edit to add the password to your application's encrypted credentials under + # Use bin/rails credentials:edit to add the password to your application's encrypted credentials under # +action_mailbox.ingress_password+, where Action Mailbox will automatically find it: # # action_mailbox: diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/sendgrid/inbound_emails_controller.rb rails-6.1.4.1+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/sendgrid/inbound_emails_controller.rb --- rails-6.0.3.7+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/sendgrid/inbound_emails_controller.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/controllers/action_mailbox/ingresses/sendgrid/inbound_emails_controller.rb 2021-08-19 16:25:04.000000000 +0000 @@ -27,7 +27,7 @@ # # 2. Generate a strong password that Action Mailbox can use to authenticate requests to the SendGrid ingress. # - # Use rails credentials:edit to add the password to your application's encrypted credentials under + # Use bin/rails credentials:edit to add the password to your application's encrypted credentials under # +action_mailbox.ingress_password+, where Action Mailbox will automatically find it: # # action_mailbox: @@ -48,7 +48,21 @@ before_action :authenticate_by_password def create - ActionMailbox::InboundEmail.create_and_extract_message_id! params.require(:email) + ActionMailbox::InboundEmail.create_and_extract_message_id! mail + rescue JSON::ParserError => error + logger.error error.message + head :unprocessable_entity end + + private + def mail + params.require(:email).tap do |raw_email| + envelope["to"].each { |to| raw_email.prepend("X-Original-To: ", to, "\n") } if params.key?(:envelope) + end + end + + def envelope + JSON.parse(params.require(:envelope)) + end end end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails/sources_controller.rb rails-6.1.4.1+dfsg/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails/sources_controller.rb --- rails-6.0.3.7+dfsg/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails/sources_controller.rb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails/sources_controller.rb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Rails + class Conductor::ActionMailbox::InboundEmails::SourcesController < Rails::Conductor::BaseController + def new + end + + def create + inbound_email = ActionMailbox::InboundEmail.create_and_extract_message_id! params[:source] + redirect_to main_app.rails_conductor_inbound_email_url(inbound_email) + end + end +end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb rails-6.1.4.1+dfsg/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb --- rails-6.0.3.7+dfsg/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb 2021-08-19 16:25:04.000000000 +0000 @@ -20,7 +20,7 @@ private def new_mail - Mail.new(params.require(:mail).permit(:from, :to, :cc, :bcc, :in_reply_to, :subject, :body).to_h).tap do |mail| + Mail.new(params.require(:mail).permit(:from, :to, :cc, :bcc, :x_original_to, :in_reply_to, :subject, :body).to_h).tap do |mail| mail[:bcc]&.include_in_headers = true params[:mail][:attachments].to_a.each do |attachment| mail.add_file(filename: attachment.original_filename, content: attachment.read) diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/models/action_mailbox/inbound_email/message_id.rb rails-6.1.4.1+dfsg/actionmailbox/app/models/action_mailbox/inbound_email/message_id.rb --- rails-6.0.3.7+dfsg/actionmailbox/app/models/action_mailbox/inbound_email/message_id.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/models/action_mailbox/inbound_email/message_id.rb 2021-08-19 16:25:04.000000000 +0000 @@ -10,7 +10,7 @@ extend ActiveSupport::Concern class_methods do - # Create a new +InboundEmail+ from the raw +source+ of the email, which be uploaded as a Active Storage + # Create a new +InboundEmail+ from the raw +source+ of the email, which is uploaded as an Active Storage # attachment called +raw_email+. Before the upload, extract the Message-ID from the +source+ and set # it as an attribute on the new +InboundEmail+. def create_and_extract_message_id!(source, **options) diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/models/action_mailbox/inbound_email.rb rails-6.1.4.1+dfsg/actionmailbox/app/models/action_mailbox/inbound_email.rb --- rails-6.0.3.7+dfsg/actionmailbox/app/models/action_mailbox/inbound_email.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/models/action_mailbox/inbound_email.rb 2021-08-19 16:25:04.000000000 +0000 @@ -24,7 +24,7 @@ # # inbound_email.mail.from # => 'david@loudthinking.com' # inbound_email.source # Returns the full rfc822 source of the email as text - class InboundEmail < ActiveRecord::Base + class InboundEmail < Record self.table_name = "action_mailbox_inbound_emails" include Incineratable, MessageId, Routable diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/models/action_mailbox/record.rb rails-6.1.4.1+dfsg/actionmailbox/app/models/action_mailbox/record.rb --- rails-6.0.3.7+dfsg/actionmailbox/app/models/action_mailbox/record.rb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/models/action_mailbox/record.rb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module ActionMailbox + class Record < ActiveRecord::Base #:nodoc: + self.abstract_class = true + end +end + +ActiveSupport.run_load_hooks :action_mailbox_record, ActionMailbox::Record diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/index.html.erb rails-6.1.4.1+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/index.html.erb --- rails-6.0.3.7+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/index.html.erb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/index.html.erb 2021-08-19 16:25:04.000000000 +0000 @@ -12,4 +12,5 @@ <% end %> -<%= link_to "Deliver new inbound email", main_app.new_rails_conductor_inbound_email_path %> \ No newline at end of file +<%= link_to "New inbound email by form", main_app.new_rails_conductor_inbound_email_path %> | +<%= link_to "New inbound email by source", main_app.new_rails_conductor_inbound_email_source_path %> diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/new.html.erb rails-6.1.4.1+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/new.html.erb --- rails-6.0.3.7+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/new.html.erb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/new.html.erb 2021-08-19 16:25:04.000000000 +0000 @@ -5,37 +5,42 @@ <%= form_with(url: main_app.rails_conductor_inbound_emails_path, scope: :mail, local: true) do |form| %>
<%= form.label :from, "From" %>
- <%= form.text_field :from %> + <%= form.text_field :from, value: params[:from] %>
<%= form.label :to, "To" %>
- <%= form.text_field :to %> + <%= form.text_field :to, value: params[:to] %>
<%= form.label :cc, "CC" %>
- <%= form.text_field :cc %> + <%= form.text_field :cc, value: params[:cc] %>
<%= form.label :bcc, "BCC" %>
- <%= form.text_field :bcc %> + <%= form.text_field :bcc, value: params[:bcc] %> +
+ +
+ <%= form.label :x_original_to, "X-Original-To" %>
+ <%= form.text_field :x_original_to, value: params[:x_original_to] %>
<%= form.label :in_reply_to, "In-Reply-To" %>
- <%= form.text_field :in_reply_to %> + <%= form.text_field :in_reply_to, value: params[:in_reply_to] %>
<%= form.label :subject, "Subject" %>
- <%= form.text_field :subject %> + <%= form.text_field :subject, value: params[:subject] %>
<%= form.label :body, "Body" %>
- <%= form.text_area :body, size: "40x20" %> + <%= form.text_area :body, size: "40x20", value: params[:body] %>
diff -Nru rails-6.0.3.7+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/sources/new.html.erb rails-6.1.4.1+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/sources/new.html.erb --- rails-6.0.3.7+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/sources/new.html.erb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/app/views/rails/conductor/action_mailbox/inbound_emails/sources/new.html.erb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,12 @@ +<% provide :title, "Deliver new inbound email by source" %> + +

Deliver new inbound email by source

+ +<%= form_with(url: main_app.rails_conductor_inbound_email_sources_path, local: true) do |form| %> +
+ <%= form.label :source, "Source" %>
+ <%= form.text_area :source, size: "80x60" %> +
+ + <%= form.submit "Deliver inbound email" %> +<% end %> diff -Nru rails-6.0.3.7+dfsg/actionmailbox/CHANGELOG.md rails-6.1.4.1+dfsg/actionmailbox/CHANGELOG.md --- rails-6.0.3.7+dfsg/actionmailbox/CHANGELOG.md 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/CHANGELOG.md 2021-08-19 16:25:04.000000000 +0000 @@ -1,98 +1,81 @@ -## Rails 6.0.3.7 (May 05, 2021) ## +## Rails 6.1.4.1 (August 19, 2021) ## * No changes. -## Rails 6.0.3.6 (March 26, 2021) ## +## Rails 6.1.4 (June 24, 2021) ## * No changes. -## Rails 6.0.3.5 (February 10, 2021) ## +## Rails 6.1.3.2 (May 05, 2021) ## * No changes. -## Rails 6.0.3.4 (October 07, 2020) ## +## Rails 6.1.3.1 (March 26, 2021) ## * No changes. -## Rails 6.0.3.3 (September 09, 2020) ## +## Rails 6.1.3 (February 17, 2021) ## * No changes. -## Rails 6.0.3.2 (June 17, 2020) ## +## Rails 6.1.2.1 (February 10, 2021) ## * No changes. -## Rails 6.0.3.1 (May 18, 2020) ## +## Rails 6.1.2 (February 09, 2021) ## * No changes. -## Rails 6.0.3 (May 06, 2020) ## - -* Update Mandrill inbound email route to respond appropriately to HEAD requests for URL health checks from Mandrill. - - *Bill Cromie* - - -## Rails 6.0.2.2 (March 19, 2020) ## +## Rails 6.1.1 (January 07, 2021) ## * No changes. -## Rails 6.0.2.1 (December 18, 2019) ## +## Rails 6.1.0 (December 09, 2020) ## -* No changes. +* Change default queue name of the incineration (`:action_mailbox_incineration`) and + routing (`:action_mailbox_routing`) jobs to be the job adapter's default (`:default`). + *Rafael Mendonça França* -## Rails 6.0.2 (December 13, 2019) ## +* Sendgrid ingress now passes through the envelope recipient as `X-Original-To`. -* No changes. + *Mark Haussmann* +* Update Mandrill inbound email route to respond appropriately to HEAD requests for URL health checks from Mandrill. -## Rails 6.0.1 (November 5, 2019) ## - -* No changes. - - -## Rails 6.0.0 (August 16, 2019) ## - -* Fix Bcc header not being included with emails from `create_inbound_email_from` test helpers. - - *jduff* - - -## Rails 6.0.0.rc2 (July 22, 2019) ## - -* No changes. - + *Bill Cromie* -## Rails 6.0.0.rc1 (April 24, 2019) ## +* Add way to deliver emails via source instead of filling out a form through the conductor interface. -* No changes. + *DHH* +* Mailgun ingress now passes through the envelope recipient as `X-Original-To`. -## Rails 6.0.0.beta3 (March 11, 2019) ## + *Rikki Pitt* -* No changes. +* Deprecate `Rails.application.credentials.action_mailbox.api_key` and `MAILGUN_INGRESS_API_KEY` in favor of `Rails.application.credentials.action_mailbox.signing_key` and `MAILGUN_INGRESS_SIGNING_KEY`. + *Matthijs Vos* -## Rails 6.0.0.beta2 (February 25, 2019) ## +* Allow easier creation of multi-part emails from the `create_inbound_email_from_mail` and `receive_inbound_email_from_mail` test helpers. -* Allow skipping incineration of processed emails. + *Michael Herold* - This can be done by setting `config.action_mailbox.incinerate` to `false`. +* Fix Bcc header not being included with emails from `create_inbound_email_from` test helpers. - *Pratik Naik* + *jduff* +* Add `ApplicationMailbox.mailbox_for` to expose mailbox routing. -## Rails 6.0.0.beta1 (January 18, 2019) ## + *James Dabbs* -* Added to Rails. - *DHH* +Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actionmailbox/CHANGELOG.md) for previous changes. diff -Nru rails-6.0.3.7+dfsg/actionmailbox/config/routes.rb rails-6.1.4.1+dfsg/actionmailbox/config/routes.rb --- rails-6.0.3.7+dfsg/actionmailbox/config/routes.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/config/routes.rb 2021-08-19 16:25:04.000000000 +0000 @@ -17,6 +17,9 @@ # TODO: Should these be mounted within the engine only? scope "rails/conductor/action_mailbox/", module: "rails/conductor/action_mailbox" do resources :inbound_emails, as: :rails_conductor_inbound_emails + get "inbound_emails/sources/new", to: "inbound_emails/sources#new", as: :new_rails_conductor_inbound_email_source + post "inbound_emails/sources", to: "inbound_emails/sources#create", as: :rails_conductor_inbound_email_sources + post ":inbound_email_id/reroute" => "reroutes#create", as: :rails_conductor_inbound_email_reroute end end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/.gitignore rails-6.1.4.1+dfsg/actionmailbox/.gitignore --- rails-6.0.3.7+dfsg/actionmailbox/.gitignore 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/.gitignore 2021-08-19 16:25:04.000000000 +0000 @@ -1,5 +1,4 @@ /test/dummy/db/*.sqlite3 -/test/dummy/db/*.sqlite3-journal /test/dummy/db/*.sqlite3-* /test/dummy/log/*.log /test/dummy/tmp/ diff -Nru rails-6.0.3.7+dfsg/actionmailbox/lib/action_mailbox/gem_version.rb rails-6.1.4.1+dfsg/actionmailbox/lib/action_mailbox/gem_version.rb --- rails-6.0.3.7+dfsg/actionmailbox/lib/action_mailbox/gem_version.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/lib/action_mailbox/gem_version.rb 2021-08-19 16:25:04.000000000 +0000 @@ -8,9 +8,9 @@ module VERSION MAJOR = 6 - MINOR = 0 - TINY = 3 - PRE = "7" + MINOR = 1 + TINY = 4 + PRE = "1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/lib/action_mailbox/router.rb rails-6.1.4.1+dfsg/actionmailbox/lib/action_mailbox/router.rb --- rails-6.0.3.7+dfsg/actionmailbox/lib/action_mailbox/router.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/lib/action_mailbox/router.rb 2021-08-19 16:25:04.000000000 +0000 @@ -21,7 +21,7 @@ end def route(inbound_email) - if mailbox = match_to_mailbox(inbound_email) + if mailbox = mailbox_for(inbound_email) mailbox.receive(inbound_email) else inbound_email.bounced! @@ -30,12 +30,12 @@ end end + def mailbox_for(inbound_email) + routes.detect { |route| route.match?(inbound_email) }&.mailbox_class + end + private attr_reader :routes - - def match_to_mailbox(inbound_email) - routes.detect { |route| route.match?(inbound_email) }.try(:mailbox_class) - end end end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/lib/action_mailbox/routing.rb rails-6.1.4.1+dfsg/actionmailbox/lib/action_mailbox/routing.rb --- rails-6.0.3.7+dfsg/actionmailbox/lib/action_mailbox/routing.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/lib/action_mailbox/routing.rb 2021-08-19 16:25:04.000000000 +0000 @@ -17,6 +17,10 @@ def route(inbound_email) router.route(inbound_email) end + + def mailbox_for(inbound_email) + router.mailbox_for(inbound_email) + end end end end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/lib/action_mailbox/test_helper.rb rails-6.1.4.1+dfsg/actionmailbox/lib/action_mailbox/test_helper.rb --- rails-6.0.3.7+dfsg/actionmailbox/lib/action_mailbox/test_helper.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/lib/action_mailbox/test_helper.rb 2021-08-19 16:25:04.000000000 +0000 @@ -10,11 +10,58 @@ create_inbound_email_from_source file_fixture(fixture_name).read, status: status end - # Create an +InboundEmail+ by specifying it using +Mail.new+ options. Example: + # Creates an +InboundEmail+ by specifying through options or a block. + # + # ==== Options + # + # * :status - The +status+ to set for the created +InboundEmail+. + # For possible statuses, see {its documentation}[rdoc-ref:ActionMailbox::InboundEmail]. + # + # ==== Creating a simple email + # + # When you only need to set basic fields like +from+, +to+, +subject+, and + # +body+, you can pass them directly as options. # # create_inbound_email_from_mail(from: "david@loudthinking.com", subject: "Hello!") - def create_inbound_email_from_mail(status: :processing, **mail_options) - mail = Mail.new(mail_options) + # + # ==== Creating a multi-part email + # + # When you need to create a more intricate email, like a multi-part email + # that contains both a plaintext version and an HTML version, you can pass a + # block. + # + # create_inbound_email_from_mail do + # to "David Heinemeier Hansson " + # from "Bilbo Baggins " + # subject "Come down to the Shire!" + # + # text_part do + # body "Please join us for a party at Bag End" + # end + # + # html_part do + # body "

Please join us for a party at Bag End

" + # end + # end + # + # As with +Mail.new+, you can also use a block parameter to define the parts + # of the message: + # + # create_inbound_email_from_mail do |mail| + # mail.to "David Heinemeier Hansson " + # mail.from "Bilbo Baggins " + # mail.subject "Come down to the Shire!" + # + # mail.text_part do |part| + # part.body "Please join us for a party at Bag End" + # end + # + # mail.html_part do |part| + # part.body "

Please join us for a party at Bag End

" + # end + # end + def create_inbound_email_from_mail(status: :processing, **mail_options, &block) + mail = Mail.new(mail_options, &block) # Bcc header is not encoded by default mail[:bcc].include_in_headers = true if mail[:bcc] @@ -33,10 +80,11 @@ create_inbound_email_from_fixture(*args).tap(&:route) end - # Create an +InboundEmail+ using the same arguments as +create_inbound_email_from_mail+ and immediately route it to - # processing. - def receive_inbound_email_from_mail(**kwargs) - create_inbound_email_from_mail(**kwargs).tap(&:route) + # Create an +InboundEmail+ using the same options or block as + # {create_inbound_email_from_mail}[rdoc-ref:#create_inbound_email_from_mail], + # then immediately route it for processing. + def receive_inbound_email_from_mail(**kwargs, &block) + create_inbound_email_from_mail(**kwargs, &block).tap(&:route) end # Create an +InboundEmail+ using the same arguments as +create_inbound_email_from_source+ and immediately route it diff -Nru rails-6.0.3.7+dfsg/actionmailbox/lib/generators/action_mailbox/install/install_generator.rb rails-6.1.4.1+dfsg/actionmailbox/lib/generators/action_mailbox/install/install_generator.rb --- rails-6.0.3.7+dfsg/actionmailbox/lib/generators/action_mailbox/install/install_generator.rb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/lib/generators/action_mailbox/install/install_generator.rb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "rails/generators/mailbox/mailbox_generator" + +module ActionMailbox + module Generators + class InstallGenerator < ::Rails::Generators::Base + source_root Rails::Generators::MailboxGenerator.source_root + + def create_action_mailbox_files + say "Copying application_mailbox.rb to app/mailboxes", :green + template "application_mailbox.rb", "app/mailboxes/application_mailbox.rb" + end + + def add_action_mailbox_production_environment_config + environment <<~end_of_config, env: "production" + # Prepare the ingress controller used to receive mail + # config.action_mailbox.ingress = :relay + + end_of_config + end + + def create_migrations + rails_command "railties:install:migrations FROM=active_storage,action_mailbox", inline: true + end + end + end +end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/lib/rails/generators/installer.rb rails-6.1.4.1+dfsg/actionmailbox/lib/rails/generators/installer.rb --- rails-6.0.3.7+dfsg/actionmailbox/lib/rails/generators/installer.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/lib/rails/generators/installer.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -say "Copying application_mailbox.rb to app/mailboxes" -copy_file "#{__dir__}/mailbox/templates/application_mailbox.rb", "app/mailboxes/application_mailbox.rb" - -environment <<~end_of_config, env: "production" - # Prepare the ingress controller used to receive mail - # config.action_mailbox.ingress = :relay - -end_of_config diff -Nru rails-6.0.3.7+dfsg/actionmailbox/lib/rails/generators/mailbox/USAGE rails-6.1.4.1+dfsg/actionmailbox/lib/rails/generators/mailbox/USAGE --- rails-6.0.3.7+dfsg/actionmailbox/lib/rails/generators/mailbox/USAGE 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/lib/rails/generators/mailbox/USAGE 2021-08-19 16:25:04.000000000 +0000 @@ -1,12 +1,12 @@ Description: ============ - Stubs out a new mailbox class in app/mailboxes and invokes your template + Generates a new mailbox class in app/mailboxes and invokes your template engine and test framework generators. Example: ======== - rails generate mailbox inbox + bin/rails generate mailbox inbox - creates a InboxMailbox class and test: + creates an InboxMailbox class and test: Mailbox: app/mailboxes/inbox_mailbox.rb Test: test/mailboxes/inbox_mailbox_test.rb diff -Nru rails-6.0.3.7+dfsg/actionmailbox/lib/tasks/install.rake rails-6.1.4.1+dfsg/actionmailbox/lib/tasks/install.rake --- rails-6.0.3.7+dfsg/actionmailbox/lib/tasks/install.rake 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/lib/tasks/install.rake 2021-08-19 16:25:04.000000000 +0000 @@ -1,20 +1,6 @@ # frozen_string_literal: true -namespace :action_mailbox do - # Prevent migration installation task from showing up twice. - Rake::Task["install:migrations"].clear_comments - - desc "Copy over the migration" - task install: %w[ environment run_installer copy_migrations ] - - task :run_installer do - installer_template = File.expand_path("../rails/generators/installer.rb", __dir__) - system "#{RbConfig.ruby} ./bin/rails app:template LOCATION=#{installer_template}" - end - - task :copy_migrations do - Rake::Task["active_storage:install:migrations"].invoke - Rake::Task["railties:install:migrations"].reenable # Otherwise you can't run 2 migration copy tasks in one invocation - Rake::Task["action_mailbox:install:migrations"].invoke - end +desc "Installs Action Mailbox and its dependencies" +task "action_mailbox:install" do + Rails::Command.invoke :generate, ["action_mailbox:install"] end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/MIT-LICENSE rails-6.1.4.1+dfsg/actionmailbox/MIT-LICENSE --- rails-6.0.3.7+dfsg/actionmailbox/MIT-LICENSE 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/MIT-LICENSE 2021-08-19 16:25:04.000000000 +0000 @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Basecamp, LLC +Copyright (c) 2018-2020 Basecamp, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/controllers/ingresses/mailgun/inbound_emails_controller_test.rb rails-6.1.4.1+dfsg/actionmailbox/test/controllers/ingresses/mailgun/inbound_emails_controller_test.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/controllers/ingresses/mailgun/inbound_emails_controller_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/controllers/ingresses/mailgun/inbound_emails_controller_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -2,7 +2,7 @@ require "test_helper" -ENV["MAILGUN_INGRESS_API_KEY"] = "tbsy84uSV1Kt3ZJZELY2TmShPRs91E3yL4tzf96297vBCkDWgL" +ENV["MAILGUN_INGRESS_SIGNING_KEY"] = "tbsy84uSV1Kt3ZJZELY2TmShPRs91E3yL4tzf96297vBCkDWgL" class ActionMailbox::Ingresses::Mailgun::InboundEmailsControllerTest < ActionDispatch::IntegrationTest setup { ActionMailbox.ingress = :mailgun } @@ -25,6 +25,25 @@ assert_equal "0CB459E0-0336-41DA-BC88-E6E28C697DDB@37signals.com", inbound_email.message_id end + test "add X-Original-To to email from Mailgun" do + assert_difference -> { ActionMailbox::InboundEmail.count }, +1 do + travel_to "2018-10-09 15:15:00 EDT" + post rails_mailgun_inbound_emails_url, params: { + timestamp: 1539112500, + token: "7VwW7k6Ak7zcTwoSoNm7aTtbk1g67MKAnsYLfUB7PdszbgR5Xi", + signature: "ef24c5225322217bb065b80bb54eb4f9206d764e3e16abab07f0a64d1cf477cc", + "body-mime" => file_fixture("../files/welcome.eml").read, + recipient: "replies@example.com" + } + end + + assert_response :no_content + + inbound_email = ActionMailbox::InboundEmail.last + mail = Mail.from_source(inbound_email.raw_email.download) + assert_equal "replies@example.com", mail.header["X-Original-To"].decoded + end + test "rejecting a delayed inbound email from Mailgun" do assert_no_difference -> { ActionMailbox::InboundEmail.count } do travel_to "2018-10-09 15:26:00 EDT" @@ -53,7 +72,7 @@ assert_response :unauthorized end - test "raising when the configured Mailgun API key is nil" do + test "raising when the configured Mailgun Signing key is nil" do switch_key_to nil do assert_raises ArgumentError do travel_to "2018-10-09 15:15:00 EDT" @@ -67,7 +86,7 @@ end end - test "raising when the configured Mailgun API key is blank" do + test "raising when the configured Mailgun Signing key is blank" do switch_key_to "" do assert_raises ArgumentError do travel_to "2018-10-09 15:15:00 EDT" @@ -83,9 +102,9 @@ private def switch_key_to(new_key) - previous_key, ENV["MAILGUN_INGRESS_API_KEY"] = ENV["MAILGUN_INGRESS_API_KEY"], new_key + previous_key, ENV["MAILGUN_INGRESS_SIGNING_KEY"] = ENV["MAILGUN_INGRESS_SIGNING_KEY"], new_key yield ensure - ENV["MAILGUN_INGRESS_API_KEY"] = previous_key + ENV["MAILGUN_INGRESS_SIGNING_KEY"] = previous_key end end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/controllers/ingresses/sendgrid/inbound_emails_controller_test.rb rails-6.1.4.1+dfsg/actionmailbox/test/controllers/ingresses/sendgrid/inbound_emails_controller_test.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/controllers/ingresses/sendgrid/inbound_emails_controller_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/controllers/ingresses/sendgrid/inbound_emails_controller_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -18,6 +18,22 @@ assert_equal "0CB459E0-0336-41DA-BC88-E6E28C697DDB@37signals.com", inbound_email.message_id end + test "add X-Original-To to email from Sendgrid" do + assert_difference -> { ActionMailbox::InboundEmail.count }, +1 do + post rails_sendgrid_inbound_emails_url, + headers: { authorization: credentials }, params: { + email: file_fixture("../files/welcome.eml").read, + envelope: "{\"to\":[\"replies@example.com\"],\"from\":\"jason@37signals.com\"}", + } + end + + assert_response :no_content + + inbound_email = ActionMailbox::InboundEmail.last + mail = Mail.from_source(inbound_email.raw_email.download) + assert_equal "replies@example.com", mail.header["X-Original-To"].decoded + end + test "rejecting an unauthorized inbound email from Sendgrid" do assert_no_difference -> { ActionMailbox::InboundEmail.count } do post rails_sendgrid_inbound_emails_url, params: { email: file_fixture("../files/welcome.eml").read } diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/controllers/rails/action_mailbox/inbound_emails_controller_test.rb rails-6.1.4.1+dfsg/actionmailbox/test/controllers/rails/action_mailbox/inbound_emails_controller_test.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/controllers/rails/action_mailbox/inbound_emails_controller_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/controllers/rails/action_mailbox/inbound_emails_controller_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -10,6 +10,7 @@ mail: { from: "Jason Fried ", to: "Replies ", + cc: "CC ", bcc: "Bcc ", in_reply_to: "<4e6e35f5a38b4_479f13bb90078178@small-app-01.mail>", subject: "Hey there", @@ -21,6 +22,7 @@ mail = ActionMailbox::InboundEmail.last.mail assert_equal %w[ jason@37signals.com ], mail.from assert_equal %w[ replies@example.com ], mail.to + assert_equal %w[ cc@example.com ], mail.cc assert_equal %w[ bcc@example.com ], mail.bcc assert_equal "4e6e35f5a38b4_479f13bb90078178@small-app-01.mail", mail.in_reply_to assert_equal "Hey there", mail.subject @@ -58,7 +60,7 @@ to: "Replies ", subject: "Let's debate some attachments", body: "Let's talk about these images:", - attachments: [ fixture_file_upload("files/avatar1.jpeg"), fixture_file_upload("files/avatar2.jpeg") ] + attachments: [ fixture_file_upload("avatar1.jpeg"), fixture_file_upload("avatar2.jpeg") ] } } end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/bin/rails rails-6.1.4.1+dfsg/actionmailbox/test/dummy/bin/rails --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/bin/rails 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/bin/rails 2021-08-19 16:25:04.000000000 +0000 @@ -1,4 +1,4 @@ #!/usr/bin/env ruby APP_PATH = File.expand_path('../config/application', __dir__) -require_relative '../config/boot' -require 'rails/commands' +require_relative "../config/boot" +require "rails/commands" diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/bin/rake rails-6.1.4.1+dfsg/actionmailbox/test/dummy/bin/rake --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/bin/rake 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/bin/rake 2021-08-19 16:25:04.000000000 +0000 @@ -1,4 +1,4 @@ #!/usr/bin/env ruby -require_relative '../config/boot' -require 'rake' +require_relative "../config/boot" +require "rake" Rake.application.run diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/bin/setup rails-6.1.4.1+dfsg/actionmailbox/test/dummy/bin/setup --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/bin/setup 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/bin/setup 2021-08-19 16:25:04.000000000 +0000 @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -require 'fileutils' +require "fileutils" include FileUtils # path to your application root. @@ -10,7 +10,7 @@ end chdir APP_ROOT do - # This script is a starting point to setup your application. + # This script is a starting point to set up your application. # Add necessary setup steps to this file. puts '== Installing dependencies ==' diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/bin/update rails-6.1.4.1+dfsg/actionmailbox/test/dummy/bin/update --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/bin/update 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/bin/update 2021-08-19 16:25:04.000000000 +0000 @@ -1,5 +1,5 @@ #!/usr/bin/env ruby -require 'fileutils' +require "fileutils" include FileUtils # path to your application root. diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/application.rb rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/application.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/application.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/application.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,6 +1,6 @@ -require_relative 'boot' +require_relative "boot" -require 'rails/all' +require "rails/all" Bundler.require(*Rails.groups) diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/boot.rb rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/boot.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/boot.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/boot.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,5 +1,5 @@ # Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) -require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) +require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/environment.rb rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/environment.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/environment.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/environment.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,5 +1,5 @@ # Load the Rails application. -require_relative 'application' +require_relative "application" # Initialize the Rails application. Rails.application.initialize! diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/environments/development.rb rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/environments/development.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/environments/development.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/environments/development.rb 2021-08-19 16:25:04.000000000 +0000 @@ -3,8 +3,8 @@ # config.webpacker.check_yarn_integrity = true # Settings specified here will take precedence over those in config/application.rb. - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false @@ -15,7 +15,7 @@ config.consider_all_requests_local = true # Enable/disable caching. By default caching is disabled. - # Run rails dev:cache to toggle caching. + # Run bin/rails dev:cache to toggle caching. if Rails.root.join('tmp', 'caching-dev.txt').exist? config.action_controller.perform_caching = true @@ -46,6 +46,10 @@ # Highlight code that triggered database queries in logs. config.active_record.verbose_query_logs = true + # Show a warning when Rails couldn't parse your database.yml + # for multiple databases. + config.active_record.suppress_multiple_database_warning = false + # Debug mode disables concatenation and preprocessing of assets. # This option may cause significant delays in view rendering with a large # number of complex assets. @@ -55,7 +59,10 @@ config.assets.quiet = true # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names + # config.action_view.annotate_rendered_view_with_filenames = true # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/environments/production.rb rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/environments/production.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/environments/production.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/environments/production.rb 2021-08-19 16:25:04.000000000 +0000 @@ -32,7 +32,7 @@ # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' + # config.asset_host = 'http://assets.example.com' # Specifies the header that your server uses for sending files. # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache @@ -80,7 +80,7 @@ config.log_formatter = ::Logger::Formatter.new # Use a different logger for distributed setups. - # require 'syslog/logger' + # require "syslog/logger" # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') if ENV["RAILS_LOG_TO_STDOUT"].present? diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/environments/test.rb rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/environments/test.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/environments/test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/environments/test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -42,5 +42,8 @@ config.active_support.deprecation = :stderr # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names + # config.action_view.annotate_rendered_view_with_filenames = true end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/initializers/backtrace_silencers.rb rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/initializers/backtrace_silencers.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/initializers/backtrace_silencers.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/initializers/backtrace_silencers.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,7 +1,7 @@ # Be sure to restart your server when you modify this file. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. -# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } +# Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) } # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. # Rails.backtrace_cleaner.remove_silencers! diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/puma.rb rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/puma.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/puma.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/puma.rb 2021-08-19 16:25:04.000000000 +0000 @@ -4,16 +4,16 @@ # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +threads_count = Integer(ENV.fetch("RAILS_MAX_THREADS", "5")) threads threads_count, threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # -port ENV.fetch("PORT") { 3000 } +port Integer(ENV.fetch("PORT", "3000")) # Specifies the `environment` that Puma will run in. # -environment ENV.fetch("RAILS_ENV") { "development" } +environment ENV.fetch("RAILS_ENV", "development") # Specifies the number of `workers` to boot in clustered mode. # Workers are forked web server processes. If using threads and workers together @@ -21,7 +21,7 @@ # Workers do not work on JRuby or Windows (both of which do not support # processes). # -# workers ENV.fetch("WEB_CONCURRENCY") { 2 } +# workers Integer(ENV.fetch("WEB_CONCURRENCY", "2")) # Use the `preload_app!` method when specifying a `workers` number. # This directive tells Puma to first boot the application and load code @@ -30,5 +30,5 @@ # # preload_app! -# Allow puma to be restarted by `rails restart` command. +# Allow puma to be restarted by `bin/rails restart` command. plugin :tmp_restart diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/storage.yml rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/storage.yml --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config/storage.yml 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config/storage.yml 2021-08-19 16:25:04.000000000 +0000 @@ -6,7 +6,7 @@ service: Disk root: <%= Rails.root.join("storage") %> -# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) # amazon: # service: S3 # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> @@ -21,7 +21,7 @@ # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> # bucket: your_own_bucket -# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) # microsoft: # service: AzureStorage # path: your_azure_storage_path diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config.ru rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config.ru --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/config.ru 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/config.ru 2021-08-19 16:25:04.000000000 +0000 @@ -1,5 +1,5 @@ # This file is used by Rack-based servers to start the application. -require_relative 'config/environment' +require_relative "config/environment" run Rails.application diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/db/migrate/20180212164506_create_active_storage_tables.active_storage.rb rails-6.1.4.1+dfsg/actionmailbox/test/dummy/db/migrate/20180212164506_create_active_storage_tables.active_storage.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/db/migrate/20180212164506_create_active_storage_tables.active_storage.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/db/migrate/20180212164506_create_active_storage_tables.active_storage.rb 2021-08-19 16:25:04.000000000 +0000 @@ -2,13 +2,14 @@ class CreateActiveStorageTables < ActiveRecord::Migration[5.2] def change create_table :active_storage_blobs do |t| - t.string :key, null: false - t.string :filename, null: false + t.string :key, null: false + t.string :filename, null: false t.string :content_type t.text :metadata - t.bigint :byte_size, null: false - t.string :checksum, null: false - t.datetime :created_at, null: false + t.string :service_name, null: false + t.bigint :byte_size, null: false + t.string :checksum, null: false + t.datetime :created_at, null: false t.index [ :key ], unique: true end @@ -23,5 +24,13 @@ t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true t.foreign_key :active_storage_blobs, column: :blob_id end + + create_table :active_storage_variant_records do |t| + t.belongs_to :blob, null: false, index: false + t.string :variation_digest, null: false + + t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end end end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/db/schema.rb rails-6.1.4.1+dfsg/actionmailbox/test/dummy/db/schema.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/db/schema.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/db/schema.rb 2021-08-19 16:25:04.000000000 +0000 @@ -2,8 +2,8 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# This file is the source Rails uses to define your schema when running `rails -# db:schema:load`. When creating a new database, `rails db:schema:load` tends to +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to # be faster and is potentially less error prone than running all of your # migrations from scratch. Old migrations may fail to apply correctly if those # migrations use external dependencies or application code. @@ -36,6 +36,7 @@ t.string "filename", null: false t.string "content_type" t.text "metadata" + t.string "service_name", null: false t.bigint "byte_size", null: false t.string "checksum", null: false t.datetime "created_at", null: false diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/dummy/Rakefile rails-6.1.4.1+dfsg/actionmailbox/test/dummy/Rakefile --- rails-6.0.3.7+dfsg/actionmailbox/test/dummy/Rakefile 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/dummy/Rakefile 2021-08-19 16:25:04.000000000 +0000 @@ -1,6 +1,6 @@ # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. -require_relative 'config/application' +require_relative "config/application" Rails.application.load_tasks diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/generators/mailbox_generator_test.rb rails-6.1.4.1+dfsg/actionmailbox/test/generators/mailbox_generator_test.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/generators/mailbox_generator_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/generators/mailbox_generator_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -49,7 +49,7 @@ end def test_check_class_collision - Object.send :const_set, :InboxMailbox, Class.new + Object.const_set :InboxMailbox, Class.new content = capture(:stderr) { run_generator } assert_match(/The name 'InboxMailbox' is either already used in your application or reserved/, content) ensure diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/test_helper.rb rails-6.1.4.1+dfsg/actionmailbox/test/test_helper.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/test_helper.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/test_helper.rb 2021-08-19 16:25:04.000000000 +0000 @@ -10,8 +10,6 @@ require "byebug" require "webmock/minitest" -Minitest.backtrace_filter = Minitest::BacktraceFilter.new - require "rails/test_unit/reporter" Rails::TestUnitReporter.executable = "bin/test" diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/unit/mailbox/routing_test.rb rails-6.1.4.1+dfsg/actionmailbox/test/unit/mailbox/routing_test.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/unit/mailbox/routing_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/unit/mailbox/routing_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -28,4 +28,9 @@ assert_equal "Discussion: Let's debate these attachments", $processed end end + + test "mailbox_for" do + inbound_email = create_inbound_email_from_fixture "welcome.eml", status: :pending + assert_equal RepliesMailbox, ApplicationMailbox.mailbox_for(inbound_email) + end end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/unit/mail_ext/addresses_test.rb rails-6.1.4.1+dfsg/actionmailbox/test/unit/mail_ext/addresses_test.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/unit/mail_ext/addresses_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/unit/mail_ext/addresses_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require_relative "../../test_helper" + +module MailExt + class AddressesTest < ActiveSupport::TestCase + setup do + @mail = Mail.new \ + from: "sally@example.com", + to: "david@basecamp.com", + cc: "jason@basecamp.com", + bcc: "andrea@basecamp.com", + x_original_to: "ryan@basecamp.com" + end + + test "from address uses address object" do + assert_equal "example.com", @mail.from_address.domain + end + + test "recipients include everyone from to, cc, bcc, and x-original-to" do + assert_equal %w[ david@basecamp.com jason@basecamp.com andrea@basecamp.com ryan@basecamp.com ], @mail.recipients + end + + test "recipients addresses use address objects" do + assert_equal "basecamp.com", @mail.recipients_addresses.first.domain + end + + test "to addresses use address objects" do + assert_equal "basecamp.com", @mail.to_addresses.first.domain + end + + test "cc addresses use address objects" do + assert_equal "basecamp.com", @mail.cc_addresses.first.domain + end + + test "bcc addresses use address objects" do + assert_equal "basecamp.com", @mail.bcc_addresses.first.domain + end + + test "x_original_to addresses use address objects" do + assert_equal "basecamp.com", @mail.x_original_to_addresses.first.domain + end + end +end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/unit/mail_ext/recipients_test.rb rails-6.1.4.1+dfsg/actionmailbox/test/unit/mail_ext/recipients_test.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/unit/mail_ext/recipients_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/unit/mail_ext/recipients_test.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../test_helper" - -module MailExt - class RecipientsTest < ActiveSupport::TestCase - setup do - @mail = Mail.new \ - to: "david@basecamp.com", - cc: "jason@basecamp.com", - bcc: "andrea@basecamp.com", - x_original_to: "ryan@basecamp.com" - end - - test "recipients include everyone from to, cc, bcc, and x-original-to" do - assert_equal %w[ david@basecamp.com jason@basecamp.com andrea@basecamp.com ryan@basecamp.com ], @mail.recipients - end - - test "recipients addresses use address objects" do - assert_equal "basecamp.com", @mail.recipients_addresses.first.domain - end - - test "to addresses use address objects" do - assert_equal "basecamp.com", @mail.to_addresses.first.domain - end - - test "cc addresses use address objects" do - assert_equal "basecamp.com", @mail.cc_addresses.first.domain - end - - test "bcc addresses use address objects" do - assert_equal "basecamp.com", @mail.bcc_addresses.first.domain - end - end -end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/unit/router_test.rb rails-6.1.4.1+dfsg/actionmailbox/test/unit/router_test.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/unit/router_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/unit/router_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -144,5 +144,19 @@ @router.add_route Array.new, to: :first end end + + test "single string mailbox_for" do + @router.add_routes("first@example.com" => :first) + + inbound_email = create_inbound_email_from_mail(to: "first@example.com", subject: "This is a reply") + assert_equal FirstMailbox, @router.mailbox_for(inbound_email) + end + + test "mailbox_for with no matches" do + @router.add_routes("first@example.com" => :first) + + inbound_email = create_inbound_email_from_mail(to: "second@example.com", subject: "This is a reply") + assert_nil @router.mailbox_for(inbound_email) + end end end diff -Nru rails-6.0.3.7+dfsg/actionmailbox/test/unit/test_helper_test.rb rails-6.1.4.1+dfsg/actionmailbox/test/unit/test_helper_test.rb --- rails-6.0.3.7+dfsg/actionmailbox/test/unit/test_helper_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailbox/test/unit/test_helper_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require_relative "../test_helper" + +module ActionMailbox + class TestHelperTest < ActiveSupport::TestCase + test "multi-part mail can be built in tests using a block" do + inbound_email = create_inbound_email_from_mail do + to "test@example.com" + from "hello@example.com" + + text_part do + body "Hello, world" + end + + html_part do + body "

Hello, world

" + end + end + + mail = inbound_email.mail + + expected_mail_text_part = <<~TEXT.chomp + Content-Type: text/plain;\r + charset=UTF-8\r + Content-Transfer-Encoding: 7bit\r + \r + Hello, world + TEXT + + expected_mail_html_part = <<~HTML.chomp + Content-Type: text/html;\r + charset=UTF-8\r + Content-Transfer-Encoding: 7bit\r + \r +

Hello, world

+ HTML + + assert_equal 2, mail.parts.count + assert_equal expected_mail_text_part, mail.text_part.to_s + assert_equal expected_mail_html_part, mail.html_part.to_s + end + end +end diff -Nru rails-6.0.3.7+dfsg/actionmailer/actionmailer.gemspec rails-6.1.4.1+dfsg/actionmailer/actionmailer.gemspec --- rails-6.0.3.7+dfsg/actionmailer/actionmailer.gemspec 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/actionmailer.gemspec 2021-08-19 16:25:04.000000000 +0000 @@ -32,6 +32,7 @@ # NOTE: Please read our dependency guidelines before updating versions: # https://edgeguides.rubyonrails.org/security.html#dependency-management-and-cves + s.add_dependency "activesupport", version s.add_dependency "actionpack", version s.add_dependency "actionview", version s.add_dependency "activejob", version diff -Nru rails-6.0.3.7+dfsg/actionmailer/CHANGELOG.md rails-6.1.4.1+dfsg/actionmailer/CHANGELOG.md --- rails-6.0.3.7+dfsg/actionmailer/CHANGELOG.md 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/CHANGELOG.md 2021-08-19 16:25:04.000000000 +0000 @@ -1,158 +1,63 @@ -## Rails 6.0.3.7 (May 05, 2021) ## +## Rails 6.1.4.1 (August 19, 2021) ## * No changes. -## Rails 6.0.3.6 (March 26, 2021) ## +## Rails 6.1.4 (June 24, 2021) ## * No changes. -## Rails 6.0.3.5 (February 10, 2021) ## +## Rails 6.1.3.2 (May 05, 2021) ## * No changes. -## Rails 6.0.3.4 (October 07, 2020) ## +## Rails 6.1.3.1 (March 26, 2021) ## * No changes. -## Rails 6.0.3.3 (September 09, 2020) ## +## Rails 6.1.3 (February 17, 2021) ## * No changes. -## Rails 6.0.3.2 (June 17, 2020) ## +## Rails 6.1.2.1 (February 10, 2021) ## * No changes. -## Rails 6.0.3.1 (May 18, 2020) ## +## Rails 6.1.2 (February 09, 2021) ## * No changes. -## Rails 6.0.3 (May 06, 2020) ## - -* No changes. +## Rails 6.1.1 (January 07, 2021) ## +* Sets default mailer queue to `"default"` in the mail assertions. -## Rails 6.0.2.2 (March 19, 2020) ## + *Paul Keen* -* No changes. +## Rails 6.1.0 (December 09, 2020) ## -## Rails 6.0.2.1 (December 18, 2019) ## +* Change default queue name of the deliver (`:mailers`) job to be the job adapter's + default (`:default`). -* No changes. + *Rafael Mendonça França* +* Remove deprecated `ActionMailer::Base.receive` in favor of [Action Mailbox](https://github.com/rails/rails/tree/master/actionmailbox). -## Rails 6.0.2 (December 13, 2019) ## + *Rafael Mendonça França* * Fix ActionMailer assertions don't work for parameterized mail with legacy delivery job. *bogdanvlviv* +* Added `email_address_with_name` to properly escape addresses with names. -## Rails 6.0.1 (November 5, 2019) ## - -* No changes. - - -## Rails 6.0.0 (August 16, 2019) ## - -* No changes. - - -## Rails 6.0.0.rc2 (July 22, 2019) ## - -* No changes. - - -## Rails 6.0.0.rc1 (April 24, 2019) ## - -* No changes. - - -## Rails 6.0.0.beta3 (March 11, 2019) ## - -* No changes. - - -## Rails 6.0.0.beta2 (February 25, 2019) ## - -* No changes. - - -## Rails 6.0.0.beta1 (January 18, 2019) ## - -* Deprecate `ActionMailer::Base.receive` in favor of [Action Mailbox](https://github.com/rails/rails/tree/master/actionmailbox). - - *George Claghorn* - -* Add `MailDeliveryJob` for delivering both regular and parameterized mail. Deprecate using `DeliveryJob` and `Parameterized::DeliveryJob`. - - *Gannon McGibbon* - -* Fix ActionMailer assertions not working when a Mail defines - a custom delivery job class - - *Edouard Chin* - -* Mails with multipart `format` blocks with implicit render now also check for - a template name in options hash instead of only using the action name. - - *Marcus Ilgner* - -* `ActionDispatch::IntegrationTest` includes `ActionMailer::TestHelper` module by default. - - *Ricardo Díaz* - -* Add `perform_deliveries` to a payload of `deliver.action_mailer` notification. - - *Yoshiyuki Kinjo* - -* Change delivery logging message when `perform_deliveries` is false. - - *Yoshiyuki Kinjo* - -* Allow call `assert_enqueued_email_with` with no block. - - Example: - ``` - def test_email - ContactMailer.welcome.deliver_later - assert_enqueued_email_with ContactMailer, :welcome - end - - def test_email_with_arguments - ContactMailer.welcome("Hello", "Goodbye").deliver_later - assert_enqueued_email_with ContactMailer, :welcome, args: ["Hello", "Goodbye"] - end - ``` - - *bogdanvlviv* - -* Ensure mail gem is eager autoloaded when eager load is true to prevent thread deadlocks. - - *Samuel Cochran* - -* Perform email jobs in `assert_emails`. - - *Gannon McGibbon* - -* Add `Base.unregister_observer`, `Base.unregister_observers`, - `Base.unregister_interceptor`, `Base.unregister_interceptors`, - `Base.unregister_preview_interceptor` and `Base.unregister_preview_interceptors`. - This makes it possible to dynamically add and remove email observers and - interceptors at runtime in the same way they're registered. - - *Claudio Ortolina*, *Kota Miyake* - -* Rails 6 requires Ruby 2.5.0 or newer. - - *Jeremy Daer*, *Kasper Timm Hansen* + *Sunny Ripert* -Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/actionmailer/CHANGELOG.md) for previous changes. +Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actionmailer/CHANGELOG.md) for previous changes. diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/base.rb rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/base.rb --- rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/base.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/base.rb 2021-08-19 16:25:04.000000000 +0000 @@ -16,11 +16,11 @@ # # To use Action Mailer, you need to create a mailer model. # - # $ rails generate mailer Notifier + # $ bin/rails generate mailer Notifier # # The generated model inherits from ApplicationMailer which in turn # inherits from ActionMailer::Base. A mailer model defines methods - # used to generate an email message. In these methods, you can setup variables to be used in + # used to generate an email message. In these methods, you can set up variables to be used in # the mailer views, options on the mail itself such as the :from address, and attachments. # # class ApplicationMailer < ActionMailer::Base @@ -455,10 +455,6 @@ PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout] - def _protected_ivars # :nodoc: - PROTECTED_IVARS - end - helper ActionMailer::MailHelper class_attribute :delivery_job, default: ::ActionMailer::DeliveryJob @@ -551,32 +547,6 @@ # config.action_mailer.default_options = { from: "no-reply@example.org" } alias :default_options= :default - # Receives a raw email, parses it into an email object, decodes it, - # instantiates a new mailer, and passes the email object to the mailer - # object's +receive+ method. - # - # If you want your mailer to be able to process incoming messages, you'll - # need to implement a +receive+ method that accepts the raw email string - # as a parameter: - # - # class MyMailer < ActionMailer::Base - # def receive(mail) - # # ... - # end - # end - def receive(raw_mail) - ActiveSupport::Deprecation.warn(<<~MESSAGE.squish) - ActionMailer::Base.receive is deprecated and will be removed in Rails 6.1. - Use Action Mailbox to process inbound email. - MESSAGE - - ActiveSupport::Notifications.instrument("receive.action_mailer") do |payload| - mail = Mail.new(raw_mail) - set_payload_for_mail(payload, mail) - new.receive(mail) - end - end - # Wraps an email delivery inside of ActiveSupport::Notifications instrumentation. # # This method is actually called by the Mail::Message object itself @@ -590,6 +560,14 @@ end end + # Returns an email in the format "Name ". + def email_address_with_name(address, name) + Mail::Address.new.tap do |builder| + builder.address = address + builder.display_name = name + end.to_s + end + private def set_payload_for_mail(payload, mail) payload[:mail] = mail.encoded @@ -657,6 +635,11 @@ self.class.mailer_name end + # Returns an email in the format "Name ". + def email_address_with_name(address, name) + self.class.email_address_with_name(address, name) + end + # Allows you to pass random and unusual headers to the new Mail::Message # object which will add them to itself. # @@ -738,7 +721,7 @@ end class LateAttachmentsProxy < SimpleDelegator - def inline; _raise_error end + def inline; self end def []=(_name, _content); _raise_error end private @@ -859,8 +842,9 @@ @_mail_was_called = true create_parts_from_responses(message, responses) + wrap_inline_attachments(message) - # Setup content type, reapply charset and handle parts order + # Set up content type, reapply charset and handle parts order message.content_type = set_content_type(message, content_type, headers[:content_type]) message.charset = charset @@ -888,7 +872,7 @@ when user_content_type.present? user_content_type when m.has_attachments? - if m.attachments.detect(&:inline?) + if m.attachments.all?(&:inline?) ["multipart", "related", params] else ["multipart", "mixed", params] @@ -915,12 +899,9 @@ end def apply_defaults(headers) - default_values = self.class.default.map do |key, value| - [ - key, - compute_default(value) - ] - end.to_h + default_values = self.class.default.transform_values do |value| + compute_default(value) + end headers_with_defaults = headers.reverse_merge(default_values) headers_with_defaults[:subject] ||= default_i18n_subject @@ -989,6 +970,27 @@ end end + def wrap_inline_attachments(message) + # If we have both types of attachment, wrap all the inline attachments + # in multipart/related, but not the actual attachments + if message.attachments.detect(&:inline?) && message.attachments.detect { |a| !a.inline? } + related = Mail::Part.new + related.content_type = "multipart/related" + mixed = [ related ] + + message.parts.each do |p| + if p.attachment? && !p.inline? + mixed << p + else + related.add_part(p) + end + end + + message.parts.clear + mixed.each { |c| message.add_part(c) } + end + end + def create_parts_from_responses(m, responses) if responses.size == 1 && !m.has_attachments? responses[0].each { |k, v| m[k] = v } @@ -1020,6 +1022,10 @@ "action_mailer" end + def _protected_ivars + PROTECTED_IVARS + end + ActiveSupport.run_load_hooks(:action_mailer, self) end end diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/delivery_job.rb rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/delivery_job.rb --- rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/delivery_job.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/delivery_job.rb 2021-08-19 16:25:04.000000000 +0000 @@ -15,7 +15,7 @@ before_perform do ActiveSupport::Deprecation.warn <<~MSG.squish Sending mail with DeliveryJob and Parameterized::DeliveryJob - is deprecated and will be removed in Rails 6.1. + is deprecated and will be removed in Rails 6.2. Please use MailDeliveryJob instead. MSG end diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/delivery_methods.rb rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/delivery_methods.rb --- rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/delivery_methods.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/delivery_methods.rb 2021-08-19 16:25:04.000000000 +0000 @@ -49,7 +49,7 @@ # arguments: '-i' def add_delivery_method(symbol, klass, default_options = {}) class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings") - send(:"#{symbol}_settings=", default_options) + public_send(:"#{symbol}_settings=", default_options) self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze end diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/gem_version.rb rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/gem_version.rb --- rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/gem_version.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/gem_version.rb 2021-08-19 16:25:04.000000000 +0000 @@ -8,9 +8,9 @@ module VERSION MAJOR = 6 - MINOR = 0 - TINY = 3 - PRE = "7" + MINOR = 1 + TINY = 4 + PRE = "1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/log_subscriber.rb rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/log_subscriber.rb --- rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/log_subscriber.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/log_subscriber.rb 2021-08-19 16:25:04.000000000 +0000 @@ -20,12 +20,6 @@ debug { event.payload[:mail] } end - # An email was received. - def receive(event) - info { "Received mail (#{event.duration.round(1)}ms)" } - debug { event.payload[:mail] } - end - # An email was generated. def process(event) debug do diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/message_delivery.rb rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/message_delivery.rb --- rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/message_delivery.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/message_delivery.rb 2021-08-19 16:25:04.000000000 +0000 @@ -53,12 +53,14 @@ # Notifier.welcome(User.first).deliver_later! # Notifier.welcome(User.first).deliver_later!(wait: 1.hour) # Notifier.welcome(User.first).deliver_later!(wait_until: 10.hours.from_now) + # Notifier.welcome(User.first).deliver_later!(priority: 10) # # Options: # # * :wait - Enqueue the email to be delivered with a delay # * :wait_until - Enqueue the email to be delivered at (after) a specific date / time # * :queue - Enqueue the email on the specified queue + # * :priority - Enqueues the email with the specified priority # # By default, the email will be enqueued using ActionMailer::DeliveryJob. Each # ActionMailer::Base class can specify the job to use by setting the class variable @@ -77,12 +79,14 @@ # Notifier.welcome(User.first).deliver_later # Notifier.welcome(User.first).deliver_later(wait: 1.hour) # Notifier.welcome(User.first).deliver_later(wait_until: 10.hours.from_now) + # Notifier.welcome(User.first).deliver_later(priority: 10) # # Options: # # * :wait - Enqueue the email to be delivered with a delay. # * :wait_until - Enqueue the email to be delivered at (after) a specific date / time. # * :queue - Enqueue the email on the specified queue. + # * :priority - Enqueues the email with the specified priority # # By default, the email will be enqueued using ActionMailer::DeliveryJob. Each # ActionMailer::Base class can specify the job to use by setting the class variable @@ -138,14 +142,37 @@ else job = @mailer_class.delivery_job - if job <= MailDeliveryJob + if use_new_args?(job) job.set(options).perform_later( @mailer_class.name, @action.to_s, delivery_method.to_s, args: @args) + elsif job <= DeliveryJob + job.set(options).perform_later( + @mailer_class.name, @action.to_s, delivery_method.to_s, *@args) else + ActiveSupport::Deprecation.warn(<<~EOM) + In Rails 6.2, Action Mailer will pass the mail arguments inside the `:args` keyword argument. + The `perform` method of the #{job} needs to change and forward the mail arguments + from the `args` keyword argument. + + The `perform` method should now look like: + + `def perform(mailer, mail_method, delivery, args:)` + EOM + job.set(options).perform_later( @mailer_class.name, @action.to_s, delivery_method.to_s, *@args) end end end + + def use_new_args?(job) + parameters = job.public_instance_method(:perform).parameters + + parameters.find do |key, name| + return true if key == :keyreq && name == :args + + key == :keyrest + end + end end end diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/preview.rb rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/preview.rb --- rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/preview.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/preview.rb 2021-08-19 16:25:04.000000000 +0000 @@ -114,7 +114,7 @@ # Returns the underscored name of the mailer preview without the suffix. def preview_name - name.sub(/Preview$/, "").underscore + name.delete_suffix("Preview").underscore end private diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/railtie.rb rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/railtie.rb --- rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/railtie.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/railtie.rb 2021-08-19 16:25:04.000000000 +0000 @@ -18,11 +18,6 @@ paths = app.config.paths options = app.config.action_mailer - if app.config.force_ssl - options.default_url_options ||= {} - options.default_url_options[:protocol] ||= "https" - end - options.assets_dir ||= paths["public"].first options.javascripts_dir ||= paths["public/javascripts"].first options.stylesheets_dir ||= paths["public/stylesheets"].first diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/test_helper.rb rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/test_helper.rb --- rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer/test_helper.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer/test_helper.rb 2021-08-19 16:25:04.000000000 +0000 @@ -123,13 +123,13 @@ # ContactMailer.with(email: 'user@example.com').welcome.deliver_later # end # end - def assert_enqueued_email_with(mailer, method, args: nil, queue: "mailers", &block) + def assert_enqueued_email_with(mailer, method, args: nil, queue: ActionMailer::Base.deliver_later_queue_name || "default", &block) args = if args.is_a?(Hash) [mailer.to_s, method.to_s, "deliver_now", params: args, args: []] else [mailer.to_s, method.to_s, "deliver_now", args: Array(args)] end - assert_enqueued_with(job: mailer.delivery_job, args: args, queue: queue, &block) + assert_enqueued_with(job: mailer.delivery_job, args: args, queue: queue.to_s, &block) end # Asserts that no emails are enqueued for later delivery. diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer.rb rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer.rb --- rails-6.0.3.7+dfsg/actionmailer/lib/action_mailer.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/action_mailer.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,7 +1,7 @@ # frozen_string_literal: true #-- -# Copyright (c) 2004-2019 David Heinemeier Hansson +# Copyright (c) 2004-2020 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -67,4 +67,5 @@ ActiveSupport.on_load(:action_view) do ActionView::Base.default_formats ||= Mime::SET.symbols ActionView::Template::Types.delegate_to Mime + ActionView::LookupContext::DetailsKey.clear end diff -Nru rails-6.0.3.7+dfsg/actionmailer/lib/rails/generators/mailer/USAGE rails-6.1.4.1+dfsg/actionmailer/lib/rails/generators/mailer/USAGE --- rails-6.0.3.7+dfsg/actionmailer/lib/rails/generators/mailer/USAGE 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/lib/rails/generators/mailer/USAGE 2021-08-19 16:25:04.000000000 +0000 @@ -1,6 +1,6 @@ Description: ============ - Stubs out a new mailer and its views. Passes the mailer name, either + Generates a new mailer and its views. Passes the mailer name, either CamelCased or under_scored, and an optional list of emails as arguments. This generates a mailer class in app/mailers and invokes your template @@ -8,10 +8,9 @@ Example: ======== - rails generate mailer Notifications signup forgot_password invoice + bin/rails generate mailer Notifications signup forgot_password invoice creates a Notifications mailer class, views, and test: Mailer: app/mailers/notifications_mailer.rb Views: app/views/notifications_mailer/signup.text.erb [...] Test: test/mailers/notifications_mailer_test.rb - diff -Nru rails-6.0.3.7+dfsg/actionmailer/MIT-LICENSE rails-6.1.4.1+dfsg/actionmailer/MIT-LICENSE --- rails-6.0.3.7+dfsg/actionmailer/MIT-LICENSE 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/MIT-LICENSE 2021-08-19 16:25:04.000000000 +0000 @@ -1,4 +1,4 @@ -Copyright (c) 2004-2019 David Heinemeier Hansson +Copyright (c) 2004-2020 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff -Nru rails-6.0.3.7+dfsg/actionmailer/README.rdoc rails-6.1.4.1+dfsg/actionmailer/README.rdoc --- rails-6.0.3.7+dfsg/actionmailer/README.rdoc 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/README.rdoc 2021-08-19 16:25:04.000000000 +0000 @@ -116,7 +116,7 @@ Source code can be downloaded as part of the Rails project on GitHub: -* https://github.com/rails/rails/tree/master/actionmailer +* https://github.com/rails/rails/tree/main/actionmailer == License diff -Nru rails-6.0.3.7+dfsg/actionmailer/test/asset_host_test.rb rails-6.1.4.1+dfsg/actionmailer/test/asset_host_test.rb --- rails-6.0.3.7+dfsg/actionmailer/test/asset_host_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/test/asset_host_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -29,7 +29,7 @@ def test_asset_host_as_one_argument_proc AssetHostMailer.config.asset_host = Proc.new { |source| - if source.starts_with?("/images") + if source.start_with?("/images") "http://images.example.com" end } diff -Nru rails-6.0.3.7+dfsg/actionmailer/test/base_test.rb rails-6.1.4.1+dfsg/actionmailer/test/base_test.rb --- rails-6.0.3.7+dfsg/actionmailer/test/base_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/test/base_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -36,6 +36,7 @@ email = BaseMailer.welcome assert_equal(["system@test.lindsaar.net"], email.to) assert_equal(["jose@test.plataformatec.com"], email.from) + assert_equal(["mikel@test.lindsaar.net"], email.reply_to) assert_equal("The first email on new API!", email.subject) end @@ -82,6 +83,12 @@ assert_equal("text/plain", mail.mime_type) end + test "mail() using email_address_with_name" do + email = BaseMailer.with_name + assert_equal("Sunny ", email["To"].value) + assert_equal("Mikel ", email["Reply-To"].value) + end + # Custom headers test "custom headers" do email = BaseMailer.welcome @@ -179,6 +186,20 @@ assert_equal("logo.png", email.parts[1].filename) end + test "can embed an inline attachment and other attachments" do + email = BaseMailer.inline_and_other_attachments + # Need to call #encoded to force the JIT sort on parts + email.encoded + assert_equal(2, email.parts.length) + assert_equal("multipart/mixed", email.mime_type) + assert_equal("multipart/related", email.parts[0].mime_type) + assert_equal("multipart/alternative", email.parts[0].parts[0].mime_type) + assert_equal("text/plain", email.parts[0].parts[0].parts[0].mime_type) + assert_equal("text/html", email.parts[0].parts[0].parts[1].mime_type) + assert_equal("logo.png", email.parts[0].parts[1].filename) + assert_equal("certificate.pdf", email.parts[1].filename) + end + # Defaults values test "uses default charset from class" do with_default BaseMailer, charset: "US-ASCII" do @@ -267,6 +288,17 @@ assert_match(/Can't add attachments after `mail` was called./, e.message) end + test "accessing inline attachments after mail was called works" do + class LateInlineAttachmentAccessorMailer < ActionMailer::Base + def welcome + mail body: "yay", from: "welcome@example.com", to: "to@example.com" + attachments.inline["invoice.pdf"] + end + end + + assert_nothing_raised { LateInlineAttachmentAccessorMailer.welcome.message } + end + test "adding inline attachments while rendering mail works" do class LateInlineAttachmentMailer < ActionMailer::Base def on_render @@ -872,6 +904,11 @@ assert_equal "Anonymous mailer body", mailer.welcome.body.encoded.strip end + test "email_address_with_name escapes" do + address = BaseMailer.email_address_with_name("test@example.org", 'I "<3" email') + assert_equal '"I \"<3\" email" ', address + end + test "default_from can be set" do class DefaultFromMailer < ActionMailer::Base default to: "system@test.lindsaar.net" @@ -944,13 +981,13 @@ def swap(klass, new_values) old_values = {} new_values.each do |key, value| - old_values[key] = klass.send key - klass.send :"#{key}=", value + old_values[key] = klass.public_send key + klass.public_send :"#{key}=", value end yield ensure old_values.each do |key, value| - klass.send :"#{key}=", value + klass.public_send :"#{key}=", value end end diff -Nru rails-6.0.3.7+dfsg/actionmailer/test/fixtures/base_mailer/inline_and_other_attachments.html.erb rails-6.1.4.1+dfsg/actionmailer/test/fixtures/base_mailer/inline_and_other_attachments.html.erb --- rails-6.0.3.7+dfsg/actionmailer/test/fixtures/base_mailer/inline_and_other_attachments.html.erb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/test/fixtures/base_mailer/inline_and_other_attachments.html.erb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,5 @@ +

Inline Image

+ +<%= image_tag attachments['logo.png'].url %> + +

This is an image that is inline

\ No newline at end of file diff -Nru rails-6.0.3.7+dfsg/actionmailer/test/fixtures/base_mailer/inline_and_other_attachments.text.erb rails-6.1.4.1+dfsg/actionmailer/test/fixtures/base_mailer/inline_and_other_attachments.text.erb --- rails-6.0.3.7+dfsg/actionmailer/test/fixtures/base_mailer/inline_and_other_attachments.text.erb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/test/fixtures/base_mailer/inline_and_other_attachments.text.erb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,4 @@ +Inline Image + +No image for you + diff -Nru rails-6.0.3.7+dfsg/actionmailer/test/legacy_delivery_job_test.rb rails-6.1.4.1+dfsg/actionmailer/test/legacy_delivery_job_test.rb --- rails-6.0.3.7+dfsg/actionmailer/test/legacy_delivery_job_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/test/legacy_delivery_job_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -11,6 +11,21 @@ class LegacyDeliveryJob < ActionMailer::DeliveryJob end + class LegacyArgumentDeliveryJob < ActiveJob::Base + def perform(mailer, mail_method, delivery_method, *args) + end + end + + class NewArgumentDeliveryJob < ActiveJob::Base + def perform(mailer, mail_method, delivery_method, args:) + end + end + + class KeyRestArgumentJob < ActiveJob::Base + def perform(mailer, mail_method, delivery_method, **kwargs) + end + end + setup do @previous_logger = ActiveJob::Base.logger ActiveJob::Base.logger = Logger.new(nil) @@ -66,6 +81,36 @@ end end end + end + + test "triggers a deprecation warning when a delivery job use legacy arguments" do + with_delivery_job(LegacyArgumentDeliveryJob) do + assert_deprecated("Action Mailer will pass the mail arguments inside the `:args` keyword argument") do + perform_enqueued_jobs do + DelayedMailer.test_message(1, 2, 3).deliver_later + end + end + end + end + + test "does not trigger a deprecation warning when a delivery job use a required `args` kwargs" do + with_delivery_job(NewArgumentDeliveryJob) do + assert_not_deprecated do + perform_enqueued_jobs do + DelayedMailer.test_message(1, 2, 3).deliver_later + end + end + end + end + + test "does not trigger a deprecation warning when a delivery job use a keyrest argument" do + with_delivery_job(KeyRestArgumentJob) do + assert_not_deprecated do + perform_enqueued_jobs do + DelayedMailer.test_message(1, 2, 3).deliver_later + end + end + end end private diff -Nru rails-6.0.3.7+dfsg/actionmailer/test/log_subscriber_test.rb rails-6.1.4.1+dfsg/actionmailer/test/log_subscriber_test.rb --- rails-6.0.3.7+dfsg/actionmailer/test/log_subscriber_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/test/log_subscriber_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -50,16 +50,4 @@ ensure BaseMailer.deliveries.clear end - - def test_receive_is_notified - fixture = File.read(File.expand_path("fixtures/raw_email", __dir__)) - assert_deprecated do - TestMailer.receive(fixture) - end - wait - assert_equal(1, @logger.logged(:info).size) - assert_match(/Received mail/, @logger.logged(:info).first) - assert_equal(1, @logger.logged(:debug).size) - assert_match(/Jamis/, @logger.logged(:debug).first) - end end diff -Nru rails-6.0.3.7+dfsg/actionmailer/test/mailers/base_mailer.rb rails-6.1.4.1+dfsg/actionmailer/test/mailers/base_mailer.rb --- rails-6.0.3.7+dfsg/actionmailer/test/mailers/base_mailer.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/test/mailers/base_mailer.rb 2021-08-19 16:25:04.000000000 +0000 @@ -5,7 +5,7 @@ default to: "system@test.lindsaar.net", from: "jose@test.plataformatec.com", - reply_to: "mikel@test.lindsaar.net" + reply_to: email_address_with_name("mikel@test.lindsaar.net", "Mikel") def welcome(hash = {}) headers["X-SPAM"] = "Not SPAM" @@ -26,6 +26,11 @@ mail.perform_deliveries = false end + def with_name + to = email_address_with_name("sunny@example.com", "Sunny") + mail(template_name: "welcome", to: to) + end + def html_only(hash = {}) mail(hash) end @@ -39,6 +44,12 @@ mail end + def inline_and_other_attachments + attachments.inline["logo.png"] = "\312\213\254\232" + attachments["certificate.pdf"] = "This is test File content" + mail + end + def attachment_with_content(hash = {}) attachments["invoice.pdf"] = "This is test File content" mail(hash) diff -Nru rails-6.0.3.7+dfsg/actionmailer/test/message_delivery_test.rb rails-6.1.4.1+dfsg/actionmailer/test/message_delivery_test.rb --- rails-6.0.3.7+dfsg/actionmailer/test/message_delivery_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/test/message_delivery_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -76,13 +76,18 @@ end test "should enqueue a delivery with a delay" do - travel_to Time.new(2004, 11, 24, 01, 04, 44) do + travel_to Time.new(2004, 11, 24, 1, 4, 44) do assert_performed_with(job: ActionMailer::MailDeliveryJob, at: Time.current + 10.minutes, args: ["DelayedMailer", "test_message", "deliver_now", args: [1, 2, 3]]) do @mail.deliver_later wait: 10.minutes end end end + test "should enqueue a delivery with a priority" do + job = @mail.deliver_later priority: 10 + assert_equal 10, job.priority + end + test "should enqueue a delivery at a specific time" do later_time = Time.current + 1.hour assert_performed_with(job: ActionMailer::MailDeliveryJob, at: later_time, args: ["DelayedMailer", "test_message", "deliver_now", args: [1, 2, 3]]) do diff -Nru rails-6.0.3.7+dfsg/actionmailer/test/test_helper_test.rb rails-6.1.4.1+dfsg/actionmailer/test/test_helper_test.rb --- rails-6.0.3.7+dfsg/actionmailer/test/test_helper_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/test/test_helper_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -34,6 +34,14 @@ class TestHelperMailerTest < ActionMailer::TestCase include ActiveSupport::Testing::Stream + setup do + @previous_deliver_later_queue_name = ActionMailer::Base.deliver_later_queue_name + end + + teardown do + ActionMailer::Base.deliver_later_queue_name = @previous_delivery_method + end + def test_setup_sets_right_action_mailer_options assert_equal :test, ActionMailer::Base.delivery_method assert ActionMailer::Base.perform_deliveries @@ -313,6 +321,54 @@ silence_stream($stdout) do TestHelperMailer.test.deliver_later end + end + end + end + + def test_assert_enqueued_email_with_when_deliver_later_queue_name_is_nil + ActionMailer::Base.deliver_later_queue_name = nil + + assert_nothing_raised do + assert_enqueued_email_with TestHelperMailer, :test do + silence_stream($stdout) do + TestHelperMailer.test.deliver_later + end + end + end + end + + def test_assert_enqueued_email_with_when_deliver_later_queue_name_with_non_default_name + ActionMailer::Base.deliver_later_queue_name = "sample_mailer_queue_name" + + assert_nothing_raised do + assert_enqueued_email_with TestHelperMailer, :test do + silence_stream($stdout) do + TestHelperMailer.test.deliver_later + end + end + end + end + + def test_assert_enqueued_email_with_when_deliver_later_queue_name_is_symbol + ActionMailer::Base.deliver_later_queue_name = :mailers + + assert_nothing_raised do + assert_enqueued_email_with TestHelperMailer, :test do + silence_stream($stdout) do + TestHelperMailer.test.deliver_later + end + end + end + end + + def test_assert_enqueued_email_with_when_queue_arg_is_symbol + ActionMailer::Base.deliver_later_queue_name = "mailers" + + assert_nothing_raised do + assert_enqueued_email_with TestHelperMailer, :test, queue: :mailers do + silence_stream($stdout) do + TestHelperMailer.test.deliver_later + end end end end diff -Nru rails-6.0.3.7+dfsg/actionmailer/test/url_test.rb rails-6.1.4.1+dfsg/actionmailer/test/url_test.rb --- rails-6.0.3.7+dfsg/actionmailer/test/url_test.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionmailer/test/url_test.rb 2021-08-19 16:25:04.000000000 +0000 @@ -57,10 +57,6 @@ end end - def encode(text, charset = "UTF-8") - quoted_printable(text, charset) - end - def new_mail(charset = "UTF-8") mail = Mail.new mail.mime_version = "1.0" diff -Nru rails-6.0.3.7+dfsg/actionpack/actionpack.gemspec rails-6.1.4.1+dfsg/actionpack/actionpack.gemspec --- rails-6.0.3.7+dfsg/actionpack/actionpack.gemspec 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/actionpack.gemspec 2021-08-19 16:25:04.000000000 +0000 @@ -34,7 +34,7 @@ s.add_dependency "activesupport", version - s.add_dependency "rack", "~> 2.0", ">= 2.0.8" + s.add_dependency "rack", "~> 2.0", ">= 2.0.9" s.add_dependency "rack-test", ">= 0.6.3" s.add_dependency "rails-html-sanitizer", "~> 1.0", ">= 1.2.0" s.add_dependency "rails-dom-testing", "~> 2.0" diff -Nru rails-6.0.3.7+dfsg/actionpack/CHANGELOG.md rails-6.1.4.1+dfsg/actionpack/CHANGELOG.md --- rails-6.0.3.7+dfsg/actionpack/CHANGELOG.md 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/CHANGELOG.md 2021-08-19 16:25:04.000000000 +0000 @@ -1,4 +1,53 @@ -## Rails 6.0.3.7 (May 05, 2021) ## +## Rails 6.1.4.1 (August 19, 2021) ## + +* [CVE-2021-22942] Fix possible open redirect in Host Authorization middleware. + + Specially crafted "X-Forwarded-Host" headers in combination with certain + "allowed host" formats can cause the Host Authorization middleware in Action + Pack to redirect users to a malicious website. + +## Rails 6.1.4 (June 24, 2021) ## + +* Ignore file fixtures on `db:fixtures:load` + + *Kevin Sjöberg* + +* Fix ActionController::Live controller test deadlocks by removing the body buffer size limit for tests. + + *Dylan Thacker-Smith* + +* Correctly place optional path parameter booleans. + + Previously, if you specify a url parameter that is part of the path as false it would include that part + of the path as parameter for example: + + ``` + get "(/optional/:optional_id)/things" => "foo#foo", as: :things + things_path(optional_id: false) # => /things?optional_id=false + ``` + + After this change, true and false will be treated the same when used as optional path parameters. Meaning now: + + ``` + get '(this/:my_bool)/that' as: :that + + that_path(my_bool: true) # => `/this/true/that` + that_path(my_bool: false) # => `/this/false/that` + ``` + + *Adam Hess* + +* Add support for 'private, no-store' Cache-Control headers. + + Previously, 'no-store' was exclusive; no other directives could be specified. + + *Alex Smith* + + +## Rails 6.1.3.2 (May 05, 2021) ## + +* Prevent open redirects by correctly escaping the host allow list + CVE-2021-22903 * Prevent catastrophic backtracking during mime parsing CVE-2021-22902 @@ -16,12 +65,19 @@ *Gannon McGibbon* -## Rails 6.0.3.6 (March 26, 2021) ## +## Rails 6.1.3.1 (March 26, 2021) ## * No changes. -## Rails 6.0.3.5 (February 10, 2021) ## +## Rails 6.1.3 (February 17, 2021) ## + +* Re-define routes when not set correctly via inheritance. + + *John Hawthorn* + + +## Rails 6.1.2.1 (February 10, 2021) ## * Prevent open redirect when allowed host starts with a dot @@ -33,389 +89,440 @@ *Aaron Patterson* -## Rails 6.0.3.4 (October 07, 2020) ## +## Rails 6.1.2 (February 09, 2021) ## -* [CVE-2020-8264] Prevent XSS in Actionable Exceptions +* Fix error in `ActionController::LogSubscriber` that would happen when throwing inside a controller action. + *Janko Marohnić* -## Rails 6.0.3.3 (September 09, 2020) ## +* Fix `fixture_file_upload` deprecation when `file_fixture_path` is a relative path. -* No changes. + *Eugene Kenny* -## Rails 6.0.3.2 (June 17, 2020) ## +## Rails 6.1.1 (January 07, 2021) ## -* [CVE-2020-8185] Only allow ActionableErrors if show_detailed_exceptions is enabled +* Fix nil translation key lookup in controllers/ -## Rails 6.0.3.1 (May 18, 2020) ## + *Jan Klimo* -* [CVE-2020-8166] HMAC raw CSRF token before masking it, so it cannot be used to reconstruct a per-form token +* Quietly handle unknown HTTP methods in Action Dispatch SSL middleware. -* [CVE-2020-8164] Return self when calling #each, #each_pair, and #each_value instead of the raw @parameters hash + *Alex Robbin* -## Rails 6.0.3 (May 06, 2020) ## +* Change the request method to a `GET` when passing failed requests down to `config.exceptions_app`. -* Include child session assertion count in ActionDispatch::IntegrationTest + *Alex Robbin* - `IntegrationTest#open_session` uses `dup` to create the new session, which - meant it had its own copy of `@assertions`. This prevented the assertions - from being correctly counted and reported. - Child sessions now have their `attr_accessor` overriden to delegate to the - root session. +## Rails 6.1.0 (December 09, 2020) ## - Fixes #32142 +* Support for the HTTP header `Feature-Policy` has been revised to reflect + its [rename](https://github.com/w3c/webappsec-permissions-policy/pull/379) to [`Permissions-Policy`](https://w3c.github.io/webappsec-permissions-policy/#permissions-policy-http-header-field). - *Sam Bostock* + ```ruby + Rails.application.config.permissions_policy do |p| + p.camera :none + p.gyroscope :none + p.microphone :none + p.usb :none + p.fullscreen :self + p.payment :self, "https://secure-example.com" + end + ``` + *Julien Grillot* -## Rails 6.0.2.2 (March 19, 2020) ## +* Allow `ActionDispatch::HostAuthorization` to exclude specific requests. -* No changes. + Host Authorization checks can be skipped for specific requests. This allows for health check requests to be permitted for requests with missing or non-matching host headers. + *Chris Bisnett* -## Rails 6.0.2.1 (December 18, 2019) ## +* Add `config.action_dispatch.request_id_header` to allow changing the name of + the unique X-Request-Id header -* Fix possible information leak / session hijacking vulnerability. + *Arlston Fernandes* - The `ActionDispatch::Session::MemcacheStore` is still vulnerable given it requires the - gem dalli to be updated as well. +* Deprecate `config.action_dispatch.return_only_media_type_on_content_type`. - CVE-2019-16782. + *Rafael Mendonça França* +* Change `ActionDispatch::Response#content_type` to return the full Content-Type header. -## Rails 6.0.2 (December 13, 2019) ## + *Rafael Mendonça França* -* Allow using mountable engine route helpers in System Tests. +* Remove deprecated `ActionDispatch::Http::ParameterFilter`. - *Chalo Fernandez* + *Rafael Mendonça França* +* Added support for exclusive no-store Cache-Control header. -## Rails 6.0.1 (November 5, 2019) ## + If `no-store` is set on Cache-Control header it is exclusive (all other cache directives are dropped). -* `ActionDispatch::SystemTestCase` now inherits from `ActiveSupport::TestCase` - rather than `ActionDispatch::IntegrationTest`. This permits running jobs in - system tests. + *Chris Kruger* - *George Claghorn*, *Edouard Chin* +* Catch invalid UTF-8 parameters for POST requests and respond with BadRequest. -* Registered MIME types may contain extra flags: + Additionally, perform `#set_binary_encoding` in `ActionDispatch::Http::Request#GET` and + `ActionDispatch::Http::Request#POST` prior to validating encoding. - ```ruby - Mime::Type.register "text/html; fragment", :html_fragment - ``` + *Adrianna Chang* - *Aaron Patterson* +* Allow `assert_recognizes` routing assertions to work on mounted root routes. + *Gannon McGibbon* -## Rails 6.0.0 (August 16, 2019) ## +* Change default redirection status code for non-GET/HEAD requests to 308 Permanent Redirect for `ActionDispatch::SSL`. -* No changes. + *Alan Tan*, *Oz Ben-David* +* Fix `follow_redirect!` to follow redirection with same HTTP verb when following + a 308 redirection. -## Rails 6.0.0.rc2 (July 22, 2019) ## + *Alan Tan* -* Add the ability to set the CSP nonce only to the specified directives. +* When multiple domains are specified for a cookie, a domain will now be + chosen only if it is equal to or is a superdomain of the request host. - Fixes #35137. + *Jonathan Hefner* - *Yuji Yaginuma* +* `ActionDispatch::Static` handles precompiled Brotli (.br) files. -* Keep part when scope option has value. + Adds to existing support for precompiled gzip (.gz) files. + Brotli files are preferred due to much better compression. - When a route was defined within an optional scope, if that route didn't - take parameters the scope was lost when using path helpers. This commit - ensures scope is kept both when the route takes parameters or when it - doesn't. + When the browser requests /some.js with `Accept-Encoding: br`, + we check for public/some.js.br and serve that file, if present, with + `Content-Encoding: br` and `Vary: Accept-Encoding` headers. - Fixes #33219 + *Ryan Edward Hall*, *Jeremy Daer* - *Alberto Almagro* +* Add raise_on_missing_translations support for controllers. -* Change `ActionDispatch::Response#content_type` to return Content-Type header as it is. + This configuration determines whether an error should be raised for missing translations. + It can be enabled through `config.i18n.raise_on_missing_translations`. Note that described + configuration also affects raising error for missing translations in views. - Previously, `ActionDispatch::Response#content_type` returned value does NOT - contain charset part. This behavior changed to returned Content-Type header - containing charset part as it is. - - If you want just MIME type, please use `ActionDispatch::Response#media_type` - instead. - - Enable `action_dispatch.return_only_media_type_on_content_type` to use this change. - If not enabled, `ActionDispatch::Response#content_type` returns the same - value as before version, but its behavior is deprecate. + *fatkodima* - *Yuji Yaginuma* +* Added `compact` and `compact!` to `ActionController::Parameters`. -* Calling `ActionController::Parameters#transform_keys/!` without a block now returns - an enumerator for the parameters instead of the underlying hash. + *Eugene Kenny* + +* Calling `each_pair` or `each_value` on an `ActionController::Parameters` + without passing a block now returns an enumerator. *Eugene Kenny* -* Fix a bug where DebugExceptions throws an error when malformed query parameters are provided +* `fixture_file_upload` now uses path relative to `file_fixture_path` - *Yuki Nishijima*, *Stan Lo* + Previously the path had to be relative to `fixture_path`. + You can change your existing code as follow: + ```ruby + # Before + fixture_file_upload('files/dog.png') -## Rails 6.0.0.rc1 (April 24, 2019) ## + # After + fixture_file_upload('dog.png') + ``` -* Make system tests take a failed screenshot in a `before_teardown` hook - rather than an `after_teardown` hook. + *Edouard Chin* - This helps minimize the time gap between when an assertion fails and when - the screenshot is taken (reducing the time in which the page could have - been dynamically updated after the assertion failed). +* Remove deprecated `force_ssl` at the controller level. - *Richard Macklin* + *Rafael Mendonça França* -* Introduce `ActionDispatch::ActionableExceptions`. +* The +helper+ class method for controllers loads helper modules specified as + strings/symbols with `String#constantize` instead of `require_dependency`. - The `ActionDispatch::ActionableExceptions` middleware dispatches actions - from `ActiveSupport::ActionableError` descendants. + Remember that support for strings/symbols is only a convenient API. You can + always pass a module object: - Actionable errors let's you dispatch actions from Rails' error pages. + ```ruby + helper UtilsHelper + ``` - *Vipul A M*, *Yao Jie*, *Genadi Samokovarov* + which is recommended because it is simple and direct. When a string/symbol + is received, `helper` just manipulates and inflects the argument to obtain + that same module object. -* Raise an `ArgumentError` if a resource custom param contains a colon (`:`). + *Xavier Noria*, *Jean Boussier* - After this change it's not possible anymore to configure routes like this: +* Correctly identify the entire localhost IPv4 range as trusted proxy. - ``` - routes.draw do - resources :users, param: 'name/:sneaky' - end - ``` + *Nick Soracco* - Fixes #30467. +* `url_for` will now use "https://" as the default protocol when + `Rails.application.config.force_ssl` is set to true. - *Josua Schmid* + *Jonathan Hefner* +* Accept and default to base64_urlsafe CSRF tokens. -## Rails 6.0.0.beta3 (March 11, 2019) ## + Base64 strict-encoded CSRF tokens are not inherently websafe, which makes + them difficult to deal with. For example, the common practice of sending + the CSRF token to a browser in a client-readable cookie does not work properly + out of the box: the value has to be url-encoded and decoded to survive transport. -* No changes. + Now, we generate Base64 urlsafe-encoded CSRF tokens, which are inherently safe + to transport. Validation accepts both urlsafe tokens, and strict-encoded tokens + for backwards compatibility. + *Scott Blum* -## Rails 6.0.0.beta2 (February 25, 2019) ## +* Support rolling deploys for cookie serialization/encryption changes. -* Make debug exceptions works in an environment where ActiveStorage is not loaded. + In a distributed configuration like rolling update, users may observe + both old and new instances during deployment. Users may be served by a + new instance and then by an old instance. - *Tomoyuki Kurosawa* + That means when the server changes `cookies_serializer` from `:marshal` + to `:hybrid` or the server changes `use_authenticated_cookie_encryption` + from `false` to `true`, users may lose their sessions if they access the + server during deployment. -* `ActionDispatch::SystemTestCase.driven_by` can now be called with a block - to define specific browser capabilities. + We added fallbacks to downgrade the cookie format when necessary during + deployment, ensuring compatibility on both old and new instances. - *Edouard Chin* + *Masaki Hara* +* `ActionDispatch::Request.remote_ip` has ip address even when all sites are trusted. -## Rails 6.0.0.beta1 (January 18, 2019) ## + Before, if all `X-Forwarded-For` sites were trusted, the `remote_ip` would default to `127.0.0.1`. + Now, the furthest proxy site is used. e.g.: It now gives an ip address when using curl from the load balancer. -* Remove deprecated `fragment_cache_key` helper in favor of `combined_fragment_cache_key`. + *Keenan Brock* - *Rafael Mendonça França* +* Fix possible information leak / session hijacking vulnerability. + + The `ActionDispatch::Session::MemcacheStore` is still vulnerable given it requires the + gem dalli to be updated as well. -* Remove deprecated methods in `ActionDispatch::TestResponse`. + CVE-2019-16782. - `#success?`, `missing?` and `error?` were deprecated in Rails 5.2 in favor of - `#successful?`, `not_found?` and `server_error?`. +* Include child session assertion count in ActionDispatch::IntegrationTest. - *Rafael Mendonça França* + `IntegrationTest#open_session` uses `dup` to create the new session, which + meant it had its own copy of `@assertions`. This prevented the assertions + from being correctly counted and reported. -* Introduce `ActionDispatch::HostAuthorization`. + Child sessions now have their `attr_accessor` overridden to delegate to the + root session. - This is a new middleware that guards against DNS rebinding attacks by - explicitly permitting the hosts a request can be made to. + Fixes #32142. - Each host is checked with the case operator (`#===`) to support `Regexp`, - `Proc`, `IPAddr` and custom objects as host allowances. + *Sam Bostock* - *Genadi Samokovarov* +* Add SameSite protection to every written cookie. -* Allow using `parsed_body` in `ActionController::TestCase`. + Enabling `SameSite` cookie protection is an addition to CSRF protection, + where cookies won't be sent by browsers in cross-site POST requests when set to `:lax`. - In addition to `ActionDispatch::IntegrationTest`, allow using - `parsed_body` in `ActionController::TestCase`: + `:strict` disables cookies being sent in cross-site GET or POST requests. - ``` - class SomeControllerTest < ActionController::TestCase - def test_some_action - post :action, body: { foo: 'bar' } - assert_equal({ "foo" => "bar" }, response.parsed_body) - end - end - ``` + Passing `:none` disables this protection and is the same as previous versions albeit a `; SameSite=None` is appended to the cookie. - Fixes #34676. + See upgrade instructions in config/initializers/new_framework_defaults_6_1.rb. - *Tobias Bühlmann* + More info [here](https://tools.ietf.org/html/draft-west-first-party-cookies-07) -* Raise an error on root route naming conflicts. + _NB: Technically already possible as Rack supports SameSite protection, this is to ensure it's applied to all cookies_ - Raises an `ArgumentError` when multiple root routes are defined in the - same context instead of assigning nil names to subsequent roots. + *Cédric Fabianski* - *Gannon McGibbon* +* Bring back the feature that allows loading external route files from the router. -* Allow rescue from parameter parse errors: + This feature existed back in 2012 but got reverted with the incentive that + https://github.com/rails/routing_concerns was a better approach. Turned out + that this wasn't fully the case and loading external route files from the router + can be helpful for applications with a really large set of routes. + Without this feature, application needs to implement routes reloading + themselves and it's not straightforward. - ``` - rescue_from ActionDispatch::Http::Parameters::ParseError do - head :unauthorized + ```ruby + # config/routes.rb + + Rails.application.routes.draw do + draw(:admin) end + + # config/routes/admin.rb + + get :foo, to: 'foo#bar' ``` - *Gannon McGibbon*, *Josh Cheek* + *Yehuda Katz*, *Edouard Chin* -* Reset Capybara sessions if failed system test screenshot raising an exception. +* Fix system test driver option initialization for non-headless browsers. - Reset Capybara sessions if `take_failed_screenshot` raise exception - in system test `after_teardown`. + *glaszig* - *Maxim Perepelitsa* +* `redirect_to.action_controller` notifications now include the `ActionDispatch::Request` in + their payloads as `:request`. -* Use request object for context if there's no controller + *Austin Story* - There is no controller instance when using a redirect route or a - mounted rack application so pass the request object as the context - when resolving dynamic CSP sources in this scenario. +* `respond_to#any` no longer returns a response's Content-Type based on the + request format but based on the block given. - Fixes #34200. + Example: - *Andrew White* + ```ruby + def my_action + respond_to do |format| + format.any { render(json: { foo: 'bar' }) } + end + end -* Apply mapping to symbols returned from dynamic CSP sources + get('my_action.csv') + ``` - Previously if a dynamic source returned a symbol such as :self it - would be converted to a string implicitly, e.g: + The previous behaviour was to respond with a `text/csv` Content-Type which + is inaccurate since a JSON response is being rendered. - policy.default_src -> { :self } + Now it correctly returns a `application/json` Content-Type. - would generate the header: + *Edouard Chin* - Content-Security-Policy: default-src self +* Replaces (back)slashes in failure screenshot image paths with dashes. - and now it generates: + If a failed test case contained a slash or a backslash, a screenshot would be created in a + nested directory, causing issues with `tmp:clear`. - Content-Security-Policy: default-src 'self' + *Damir Zekic* - *Andrew White* +* Add `params.member?` to mimic Hash behavior. -* Add `ActionController::Parameters#each_value`. + *Younes Serraj* - *Lukáš Zapletal* +* `process_action.action_controller` notifications now include the following in their payloads: -* Deprecate `ActionDispatch::Http::ParameterFilter` in favor of `ActiveSupport::ParameterFilter`. + * `:request` - the `ActionDispatch::Request` + * `:response` - the `ActionDispatch::Response` - *Yoshiyuki Kinjo* + *George Claghorn* -* Encode Content-Disposition filenames on `send_data` and `send_file`. - Previously, `send_data 'data', filename: "\u{3042}.txt"` sends - `"filename=\"\u{3042}.txt\""` as Content-Disposition and it can be - garbled. - Now it follows [RFC 2231](https://tools.ietf.org/html/rfc2231) and - [RFC 5987](https://tools.ietf.org/html/rfc5987) and sends - `"filename=\"%3F.txt\"; filename*=UTF-8''%E3%81%82.txt"`. - Most browsers can find filename correctly and old browsers fallback to ASCII - converted name. +* Updated `ActionDispatch::Request.remote_ip` setter to clear set the instance + `remote_ip` to `nil` before setting the header that the value is derived + from. - *Fumiaki Matsushima* + Fixes #37383. -* Expose `ActionController::Parameters#each_key` which allows iterating over - keys without allocating an array. + *Norm Provost* - *Richard Schneeman* +* `ActionController::Base.log_at` allows setting a different log level per request. -* Purpose metadata for signed/encrypted cookies. + ```ruby + # Use the debug level if a particular cookie is set. + class ApplicationController < ActionController::Base + log_at :debug, if: -> { cookies[:debug] } + end + ``` - Rails can now thwart attacks that attempt to copy signed/encrypted value - of a cookie and use it as the value of another cookie. + *George Claghorn* - It does so by stashing the cookie-name in the purpose field which is - then signed/encrypted along with the cookie value. Then, on a server-side - read, we verify the cookie-names and discard any attacked cookies. +* Allow system test screen shots to be taken more than once in + a test by prefixing the file name with an incrementing counter. - Enable `action_dispatch.use_cookies_with_metadata` to use this feature, which - writes cookies with the new purpose and expiry metadata embedded. + Add an environment variable `RAILS_SYSTEM_TESTING_SCREENSHOT_HTML` to + enable saving of HTML during a screenshot in addition to the image. + This uses the same image name, with the extension replaced with `.html` - *Assain Jaleel* + *Tom Fakes* -* Raises `ActionController::RespondToMismatchError` with conflicting `respond_to` invocations. +* Add `Vary: Accept` header when using `Accept` header for response. - `respond_to` can match multiple types and lead to undefined behavior when - multiple invocations are made and the types do not match: + For some requests like `/users/1`, Rails uses requests' `Accept` + header to determine what to return. And if we don't add `Vary` + in the response header, browsers might accidentally cache different + types of content, which would cause issues: e.g. javascript got displayed + instead of html content. This PR fixes these issues by adding `Vary: Accept` + in these types of requests. For more detailed problem description, please read: - respond_to do |outer_type| - outer_type.js do - respond_to do |inner_type| - inner_type.html { render body: "HTML" } - end - end - end + https://github.com/rails/rails/pull/36213 - *Patrick Toomey* + Fixes #25842. -* `ActionDispatch::Http::UploadedFile` now delegates `to_path` to its tempfile. + *Stan Lo* - This allows uploaded file objects to be passed directly to `File.read` - without raising a `TypeError`: +* Fix IntegrationTest `follow_redirect!` to follow redirection using the same HTTP verb when following + a 307 redirection. - uploaded_file = ActionDispatch::Http::UploadedFile.new(tempfile: tmp_file) - File.read(uploaded_file) + *Edouard Chin* - *Aaron Kromer* +* System tests require Capybara 3.26 or newer. -* Pass along arguments to underlying `get` method in `follow_redirect!` + *George Claghorn* - Now all arguments passed to `follow_redirect!` are passed to the underlying - `get` method. This for example allows to set custom headers for the - redirection request to the server. +* Reduced log noise handling ActionController::RoutingErrors. - follow_redirect!(params: { foo: :bar }) + *Alberto Fernández-Capel* - *Remo Fritzsche* +* Add DSL for configuring HTTP Feature Policy. -* Introduce a new error page to when the implicit render page is accessed in the browser. + This new DSL provides a way to configure an HTTP Feature Policy at a + global or per-controller level. Full details of HTTP Feature Policy + specification and guidelines can be found at MDN: - Now instead of showing an error page that with exception and backtraces we now show only - one informative page. + https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy - *Vinicius Stock* + Example global policy: -* Introduce `ActionDispatch::DebugExceptions.register_interceptor`. + ```ruby + Rails.application.config.feature_policy do |f| + f.camera :none + f.gyroscope :none + f.microphone :none + f.usb :none + f.fullscreen :self + f.payment :self, "https://secure.example.com" + end + ``` - Exception aware plugin authors can use the newly introduced - `.register_interceptor` method to get the processed exception, instead of - monkey patching DebugExceptions. + Example controller level policy: - ActionDispatch::DebugExceptions.register_interceptor do |request, exception| - HypoteticalPlugin.capture_exception(request, exception) - end + ```ruby + class PagesController < ApplicationController + feature_policy do |p| + p.geolocation "https://example.com" + end + end + ``` + + *Jacob Bednarz* - *Genadi Samokovarov* +* Add the ability to set the CSP nonce only to the specified directives. + + Fixes #35137. -* Output only one Content-Security-Policy nonce header value per request. + *Yuji Yaginuma* - Fixes #32597. +* Keep part when scope option has value. - *Andrey Novikov*, *Andrew White* + When a route was defined within an optional scope, if that route didn't + take parameters the scope was lost when using path helpers. This commit + ensures scope is kept both when the route takes parameters or when it + doesn't. -* Move default headers configuration into their own module that can be included in controllers. + Fixes #33219. - *Kevin Deisz* + *Alberto Almagro* -* Add method `dig` to `session`. +* Added `deep_transform_keys` and `deep_transform_keys!` methods to ActionController::Parameters. - *claudiob*, *Takumi Shotoku* + *Gustavo Gutierrez* -* Controller level `force_ssl` has been deprecated in favor of - `config.force_ssl`. +* Calling `ActionController::Parameters#transform_keys`/`!` without a block now returns + an enumerator for the parameters instead of the underlying hash. - *Derek Prior* + *Eugene Kenny* -* Rails 6 requires Ruby 2.5.0 or newer. +* Fix strong parameters blocks all attributes even when only some keys are invalid (non-numerical). + It should only block invalid key's values instead. - *Jeremy Daer*, *Kasper Timm Hansen* + *Stan Lo* -Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/actionpack/CHANGELOG.md) for previous changes. +Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actionpack/CHANGELOG.md) for previous changes. diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/base.rb rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/base.rb --- rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/base.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/base.rb 2021-08-19 16:25:04.000000000 +0000 @@ -9,6 +9,35 @@ module AbstractController # Raised when a non-existing controller action is triggered. class ActionNotFound < StandardError + attr_reader :controller, :action + def initialize(message = nil, controller = nil, action = nil) + @controller = controller + @action = action + super(message) + end + + class Correction + def initialize(error) + @error = error + end + + def corrections + if @error.action + maybe_these = @error.controller.class.action_methods + + maybe_these.sort_by { |n| + DidYouMean::Jaro.distance(@error.action.to_s, n) + }.reverse.first(4) + else + [] + end + end + end + + # We may not have DYM, and DYM might not let us register error handlers + if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error) + DidYouMean.correct_error(self, Correction) + end end # AbstractController::Base is a low-level API. Nobody should be @@ -104,7 +133,7 @@ # ==== Returns # * String def controller_path - @controller_path ||= name.sub(/Controller$/, "").underscore unless anonymous? + @controller_path ||= name.delete_suffix("Controller").underscore unless anonymous? end # Refresh the cached action_methods when a new action_method is added. @@ -128,7 +157,7 @@ @_action_name = action.to_s unless action_name = _find_action_name(@_action_name) - raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}" + raise ActionNotFound.new("The action '#{action}' could not be found for #{self.class.name}", self, action) end @_response_body = nil @@ -175,6 +204,10 @@ true end + def inspect # :nodoc: + "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>" + end + private # Returns true if the name can be considered an action because # it has a method defined in the controller. diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/callbacks.rb rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/callbacks.rb --- rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/callbacks.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/callbacks.rb 2021-08-19 16:25:04.000000000 +0000 @@ -37,7 +37,7 @@ # Override AbstractController::Base#process_action to run the # process_action callbacks around the normal behavior. - def process_action(*args) + def process_action(*) run_callbacks(:process_action) do super end @@ -69,7 +69,7 @@ end def _normalize_callback_option(options, from, to) # :nodoc: - if from = options[from] + if from = options.delete(from) _from = Array(from).map(&:to_s).to_set from = proc { |c| _from.include? c.action_name } options[to] = Array(options[to]).unshift(from) diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/helpers.rb rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/helpers.rb --- rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/helpers.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/helpers.rb 2021-08-19 16:25:04.000000000 +0000 @@ -7,8 +7,19 @@ extend ActiveSupport::Concern included do - class_attribute :_helpers, default: Module.new class_attribute :_helper_methods, default: Array.new + + # This is here so that it is always higher in the inheritance chain than + # the definition in lib/action_view/rendering.rb + redefine_singleton_method(:_helpers) do + if @_helpers ||= nil + @_helpers + else + superclass._helpers + end + end + + self._helpers = define_helpers_module(self) end class MissingHelperError < LoadError @@ -25,17 +36,24 @@ end end + def _helpers + self.class._helpers + end + module ClassMethods # When a class is inherited, wrap its helper module in a new module. # This ensures that the parent class's module can be changed # independently of the child class's. def inherited(klass) - helpers = _helpers - klass._helpers = Module.new { include helpers } + # Inherited from parent by default + klass._helpers = nil + klass.class_eval { default_helper_module! } unless klass.anonymous? super end + attr_writer :_helpers + # Declare a controller method as a helper. For example, the following # makes the +current_user+ and +logged_in?+ controller methods available # to the view: @@ -57,60 +75,81 @@ # ==== Parameters # * method[, method] - A name or names of a method on the controller # to be made available on the view. - def helper_method(*meths) - meths.flatten! - self._helper_methods += meths - - meths.each do |method| - _helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 - def #{method}(*args, &blk) # def current_user(*args, &blk) - controller.send(%(#{method}), *args, &blk) # controller.send(:current_user, *args, &blk) - end # end - ruby2_keywords(%(#{method})) if respond_to?(:ruby2_keywords, true) + def helper_method(*methods) + methods.flatten! + self._helper_methods += methods + + location = caller_locations(1, 1).first + file, line = location.path, location.lineno + + methods.each do |method| + _helpers_for_modification.class_eval <<-ruby_eval, file, line + def #{method}(*args, &block) # def current_user(*args, &block) + controller.send(:'#{method}', *args, &block) # controller.send(:'current_user', *args, &block) + end # end + ruby2_keywords(:'#{method}') if respond_to?(:ruby2_keywords, true) ruby_eval end end - # The +helper+ class method can take a series of helper module names, a block, or both. + # Includes the given modules in the template class. + # + # Modules can be specified in different ways. All of the following calls + # include +FooHelper+: + # + # # Module, recommended. + # helper FooHelper + # + # # String/symbol without the "helper" suffix, camel or snake case. + # helper "Foo" + # helper :Foo + # helper "foo" + # helper :foo + # + # The last two assume that "foo".camelize returns "Foo". + # + # When strings or symbols are passed, the method finds the actual module + # object using +String#constantize+. Therefore, if the module has not been + # yet loaded, it has to be autoloadable, which is normally the case. # - # ==== Options - # * *args - Module, Symbol, String - # * block - A block defining helper methods + # Namespaces are supported. The following calls include +Foo::BarHelper+: # - # When the argument is a module it will be included directly in the template class. - # helper FooHelper # => includes FooHelper + # # Module, recommended. + # helper Foo::BarHelper # - # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file - # and include the module in the template class. The second form illustrates how to include custom helpers - # when working with namespaced controllers, or other cases where the file containing the helper definition is not - # in one of Rails' standard load paths: - # helper :foo # => requires 'foo_helper' and includes FooHelper - # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper + # # String/symbol without the "helper" suffix, camel or snake case. + # helper "Foo::Bar" + # helper :"Foo::Bar" + # helper "foo/bar" + # helper :"foo/bar" # - # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available - # to the template. + # The last two assume that "foo/bar".camelize returns "Foo::Bar". # - # # One line - # helper { def hello() "Hello, world!" end } + # The method accepts a block too. If present, the block is evaluated in + # the context of the controller helper module. This simple call makes the + # +wadus+ method available in templates of the enclosing controller: # - # # Multi-line # helper do - # def foo(bar) - # "#{bar} is the very best" + # def wadus + # "wadus" # end # end # - # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of - # +symbols+, +strings+, +modules+ and blocks. + # Furthermore, all the above styles can be mixed together: # - # helper(:three, BlindHelper) { def mice() 'mice' end } + # helper FooHelper, "woo", "bar/baz" do + # def wadus + # "wadus" + # end + # end # def helper(*args, &block) modules_for_helpers(args).each do |mod| - add_template_helper(mod) + next if _helpers.include?(mod) + _helpers_for_modification.include(mod) end - _helpers.module_eval(&block) if block_given? + _helpers_for_modification.module_eval(&block) if block_given? end # Clears up all existing helpers in this class, only keeping the helper @@ -124,71 +163,47 @@ default_helper_module! unless anonymous? end - # Returns a list of modules, normalized from the acceptable kinds of - # helpers with the following behavior: - # - # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper", - # and "foo_bar_helper.rb" is loaded using require_dependency. - # - # Module:: No further processing - # - # After loading the appropriate files, the corresponding modules - # are returned. - # - # ==== Parameters - # * args - An array of helpers - # - # ==== Returns - # * Array - A normalized list of modules for the list of - # helpers provided. - def modules_for_helpers(args) - args.flatten.map! do |arg| - case arg - when String, Symbol - file_name = "#{arg.to_s.underscore}_helper" - begin - require_dependency(file_name) - rescue LoadError => e - raise AbstractController::Helpers::MissingHelperError.new(e, file_name) - end - - mod_name = file_name.camelize - begin - mod_name.constantize - rescue LoadError - # dependencies.rb gives a similar error message but its wording is - # not as clear because it mentions autoloading. To the user all it - # matters is that a helper module couldn't be loaded, autoloading - # is an internal mechanism that should not leak. - raise NameError, "Couldn't find #{mod_name}, expected it to be defined in helpers/#{file_name}.rb" - end + # Given an array of values like the ones accepted by +helper+, this method + # returns an array with the corresponding modules, in the same order. + def modules_for_helpers(modules_or_helper_prefixes) + modules_or_helper_prefixes.flatten.map! do |module_or_helper_prefix| + case module_or_helper_prefix when Module - arg + module_or_helper_prefix + when String, Symbol + helper_prefix = module_or_helper_prefix.to_s + helper_prefix = helper_prefix.camelize unless helper_prefix.start_with?(/[A-Z]/) + "#{helper_prefix}Helper".constantize else raise ArgumentError, "helper must be a String, Symbol, or Module" end end end + def _helpers_for_modification + unless @_helpers + self._helpers = define_helpers_module(self, superclass._helpers) + end + _helpers + end + private - # Makes all the (instance) methods in the helper module available to templates - # rendered through this controller. - # - # ==== Parameters - # * module - The module to include into the current helper module - # for the class - def add_template_helper(mod) - _helpers.module_eval { include mod } + def define_helpers_module(klass, helpers = nil) + # In some tests inherited is called explicitly. In that case, just + # return the module from the first time it was defined + return klass.const_get(:HelperMethods) if klass.const_defined?(:HelperMethods, false) + + mod = Module.new + klass.const_set(:HelperMethods, mod) + mod.include(helpers) if helpers + mod end def default_helper_module! - module_name = name.sub(/Controller$/, "") - module_path = module_name.underscore - helper module_path - rescue LoadError => e - raise e unless e.is_missing? "helpers/#{module_path}_helper" + helper_prefix = name.delete_suffix("Controller") + helper(helper_prefix) rescue NameError => e - raise e unless e.missing_name? "#{module_name}Helper" + raise unless e.missing_name?("#{helper_prefix}Helper") end end end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/railties/routes_helpers.rb rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/railties/routes_helpers.rb --- rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/railties/routes_helpers.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/railties/routes_helpers.rb 2021-08-19 16:25:04.000000000 +0000 @@ -7,11 +7,27 @@ Module.new do define_method(:inherited) do |klass| super(klass) - if namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) } + + namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) } + actual_routes = namespace ? namespace.railtie_routes_url_helpers._routes : routes + + if namespace klass.include(namespace.railtie_routes_url_helpers(include_path_helpers)) else klass.include(routes.url_helpers(include_path_helpers)) end + + # In the case that we have ex. + # class A::Foo < ApplicationController + # class Bar < A::Foo + # We will need to redefine _routes because it will not be correct + # via inheritance. + unless klass._routes.equal?(actual_routes) + klass.redefine_singleton_method(:_routes) { actual_routes } + klass.include(Module.new do + define_method(:_routes) { @_routes || actual_routes } + end) + end end end end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/rendering.rb rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/rendering.rb --- rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/rendering.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/rendering.rb 2021-08-19 16:25:04.000000000 +0000 @@ -28,6 +28,7 @@ else _set_rendered_content_type rendered_format end + _set_vary_header self.response_body = rendered_body end @@ -55,20 +56,16 @@ Mime[:text] end - DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %i( - @_action_name @_response_body @_formats @_prefixes - ) + DEFAULT_PROTECTED_INSTANCE_VARIABLES = %i(@_action_name @_response_body @_formats @_prefixes) # This method should return a hash with assigns. # You can overwrite this configuration per controller. def view_assigns - protected_vars = _protected_ivars - variables = instance_variables + variables = instance_variables - _protected_ivars - variables.reject! { |s| protected_vars.include? s } - variables.each_with_object({}) { |name, hash| + variables.each_with_object({}) do |name, hash| hash[name.slice(1, name.length)] = instance_variable_get(name) - } + end end private @@ -109,6 +106,9 @@ def _set_html_content_type # :nodoc: end + def _set_vary_header # :nodoc: + end + def _set_rendered_content_type(format) # :nodoc: end @@ -120,7 +120,7 @@ options end - def _protected_ivars # :nodoc: + def _protected_ivars DEFAULT_PROTECTED_INSTANCE_VARIABLES end end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/translation.rb rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/translation.rb --- rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller/translation.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller/translation.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,7 +1,11 @@ # frozen_string_literal: true +require "active_support/core_ext/symbol/starts_ends_with" + module AbstractController module Translation + mattr_accessor :raise_on_missing_translations, default: false + # Delegates to I18n.translate. Also aliased as t. # # When the given key starts with a period, it will be scoped by the current @@ -11,14 +15,16 @@ # to translate many keys within the same controller / action and gives you a # simple framework for scoping them consistently. def translate(key, **options) - if key.to_s.first == "." + if key&.start_with?(".") path = controller_path.tr("/", ".") defaults = [:"#{path}#{key}"] defaults << options[:default] if options[:default] options[:default] = defaults.flatten key = "#{path}.#{action_name}#{key}" end - I18n.translate(key, **options) + + i18n_raise = options.fetch(:raise, self.raise_on_missing_translations) + I18n.translate(key, **options, raise: i18n_raise) end alias :t :translate diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller.rb rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller.rb --- rails-6.0.3.7+dfsg/actionpack/lib/abstract_controller.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/abstract_controller.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,6 +1,7 @@ # frozen_string_literal: true require "action_pack" +require "active_support" require "active_support/rails" require "active_support/i18n" diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/api.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/api.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/api.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/api.rb 2021-08-19 16:25:04.000000000 +0000 @@ -93,7 +93,7 @@ # the ones passed as arguments: # # class MyAPIBaseController < ActionController::Metal - # ActionController::API.without_modules(:ForceSSL, :UrlFor).each do |left| + # ActionController::API.without_modules(:UrlFor).each do |left| # include left # end # end @@ -120,9 +120,9 @@ BasicImplicitRender, StrongParameters, - ForceSSL, DataStreaming, DefaultHeaders, + Logging, # Before callbacks should also be executed as early as possible, so # also include them at the bottom. diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/base.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/base.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/base.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/base.rb 2021-08-19 16:25:04.000000000 +0000 @@ -226,13 +226,14 @@ FormBuilder, RequestForgeryProtection, ContentSecurityPolicy, - ForceSSL, + PermissionsPolicy, Streaming, DataStreaming, HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, HttpAuthentication::Token::ControllerMethods, DefaultHeaders, + Logging, # Before callbacks should also be executed as early as possible, so # also include them at the bottom. @@ -261,9 +262,10 @@ @_view_renderer @_lookup_context @_routes @_view_runtime @_db_runtime @_helper_proxy ) - def _protected_ivars # :nodoc: + def _protected_ivars PROTECTED_IVARS end + private :_protected_ivars ActiveSupport.run_load_hooks(:action_controller_base, self) ActiveSupport.run_load_hooks(:action_controller, self) diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/caching.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/caching.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/caching.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/caching.rb 2021-08-19 16:25:04.000000000 +0000 @@ -22,7 +22,6 @@ # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211') # config.action_controller.cache_store = MyOwnStore.new('parameter') module Caching - extend ActiveSupport::Autoload extend ActiveSupport::Concern included do diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/log_subscriber.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/log_subscriber.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/log_subscriber.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/log_subscriber.rb 2021-08-19 16:25:04.000000000 +0000 @@ -11,6 +11,7 @@ params = payload[:params].except(*INTERNAL_PARAMS) format = payload[:format] format = format.to_s.upcase if format.is_a?(Symbol) + format = "*/*" if format.nil? info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}" info " Parameters: #{params.inspect}" unless params.empty? @@ -22,15 +23,14 @@ additions = ActionController::Base.log_process_action(payload) status = payload[:status] - if status.nil? && payload[:exception].present? - exception_class_name = payload[:exception].first + if status.nil? && (exception_class_name = payload[:exception]&.first) status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name) end additions << "Allocations: #{event.allocations}" message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms" - message << " (#{additions.join(" | ")})" unless additions.empty? + message << " (#{additions.join(" | ")})" message << "\n\n" if defined?(Rails.env) && Rails.env.development? message diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/conditional_get.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/conditional_get.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/conditional_get.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/conditional_get.rb 2021-08-19 16:25:04.000000000 +0000 @@ -1,5 +1,8 @@ # frozen_string_literal: true +require "active_support/core_ext/object/try" +require "active_support/core_ext/integer/time" + module ActionController module ConditionalGet extend ActiveSupport::Concern @@ -17,12 +20,12 @@ # of cached pages. # # class InvoicesController < ApplicationController - # etag { current_user.try :id } + # etag { current_user&.id } # # def show # # Etag will differ even for the same invoice when it's viewed by a different current_user # @invoice = Invoice.find(params[:id]) - # fresh_when(@invoice) + # fresh_when etag: @invoice # end # end def etag(&etagger) @@ -179,7 +182,7 @@ # # You can also pass an object that responds to +maximum+, such as a # collection of active records. In this case +last_modified+ will be set by - # calling +maximum(:updated_at)+ on the collection (the timestamp of the + # calling maximum(:updated_at) on the collection (the timestamp of the # most recently updated record) and the +etag+ by passing the object itself. # # def index @@ -234,6 +237,11 @@ # expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds # expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds, stale_if_error: 5.minutes # + # HTTP Cache-Control Extensions other values: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control + # Any additional key-value pairs are concatenated onto the `Cache-Control` header in the response: + # + # expires_in 3.hours, public: true, "s-maxage": 3.hours, "no-transform": true + # # The method will also ensure an HTTP Date header for client compatibility. def expires_in(seconds, options = {}) response.cache_control.merge!( diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/content_security_policy.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/content_security_policy.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/content_security_policy.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/content_security_policy.rb 2021-08-19 16:25:04.000000000 +0000 @@ -45,7 +45,7 @@ end def current_content_security_policy - request.content_security_policy.try(:clone) || ActionDispatch::ContentSecurityPolicy.new + request.content_security_policy&.clone || ActionDispatch::ContentSecurityPolicy.new end end end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/cookies.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/cookies.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/cookies.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/cookies.rb 2021-08-19 16:25:04.000000000 +0000 @@ -9,7 +9,9 @@ end private - def cookies + # The cookies for the current request. See ActionDispatch::Cookies for + # more information. + def cookies # :doc: request.cookie_jar end end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/data_streaming.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/data_streaming.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/data_streaming.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/data_streaming.rb 2021-08-19 16:25:04.000000000 +0000 @@ -53,7 +53,7 @@ # # Show a 404 page in the browser: # - # send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404 + # send_file '/path/to/404.html', type: 'text/html; charset=utf-8', disposition: 'inline', status: 404 # # Read about the other Content-* HTTP headers if you'd like to # provide the user with more information (such as Content-Description) in diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/etag_with_template_digest.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/etag_with_template_digest.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/etag_with_template_digest.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/etag_with_template_digest.rb 2021-08-19 16:25:04.000000000 +0000 @@ -26,10 +26,8 @@ included do class_attribute :etag_with_template_digest, default: true - ActiveSupport.on_load :action_view, yield: true do - etag do |options| - determine_template_etag(options) if etag_with_template_digest - end + etag do |options| + determine_template_etag(options) if etag_with_template_digest end end @@ -46,7 +44,7 @@ # template digest from the ETag. def pick_template_for_etag(options) unless options[:template] == false - options[:template] || "#{controller_path}/#{action_name}" + options[:template] || lookup_context.find_all(action_name, _prefixes).first&.virtual_path end end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/exceptions.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/exceptions.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/exceptions.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/exceptions.rb 2021-08-19 16:25:04.000000000 +0000 @@ -23,6 +23,39 @@ end class UrlGenerationError < ActionControllerError #:nodoc: + attr_reader :routes, :route_name, :method_name + + def initialize(message, routes = nil, route_name = nil, method_name = nil) + @routes = routes + @route_name = route_name + @method_name = method_name + + super(message) + end + + class Correction + def initialize(error) + @error = error + end + + def corrections + if @error.method_name + maybe_these = @error.routes.named_routes.helper_names.grep(/#{@error.route_name}/) + maybe_these -= [@error.method_name.to_s] # remove exact match + + maybe_these.sort_by { |n| + DidYouMean::Jaro.distance(@error.route_name, n) + }.reverse.first(4) + else + [] + end + end + end + + # We may not have DYM, and DYM might not let us register error handlers + if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error) + DidYouMean.correct_error(self, Correction) + end end class MethodNotAllowed < ActionControllerError #:nodoc: diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/force_ssl.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/force_ssl.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/force_ssl.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/force_ssl.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -require "active_support/core_ext/hash/except" -require "active_support/core_ext/hash/slice" - -module ActionController - # This module is deprecated in favor of +config.force_ssl+ in your environment - # config file. This will ensure all endpoints not explicitly marked otherwise - # will have all communication served over HTTPS. - module ForceSSL # :nodoc: - extend ActiveSupport::Concern - include AbstractController::Callbacks - - ACTION_OPTIONS = [:only, :except, :if, :unless] - URL_OPTIONS = [:protocol, :host, :domain, :subdomain, :port, :path] - REDIRECT_OPTIONS = [:status, :flash, :alert, :notice] - - module ClassMethods # :nodoc: - def force_ssl(options = {}) - ActiveSupport::Deprecation.warn(<<-MESSAGE.squish) - Controller-level `force_ssl` is deprecated and will be removed from - Rails 6.1. Please enable `config.force_ssl` in your environment - configuration to enable the ActionDispatch::SSL middleware to more - fully enforce that your application communicate over HTTPS. If needed, - you can use `config.ssl_options` to exempt matching endpoints from - being redirected to HTTPS. - MESSAGE - - action_options = options.slice(*ACTION_OPTIONS) - redirect_options = options.except(*ACTION_OPTIONS) - before_action(action_options) do - force_ssl_redirect(redirect_options) - end - end - end - - def force_ssl_redirect(host_or_options = nil) - unless request.ssl? - options = { - protocol: "https://", - host: request.host, - path: request.fullpath, - status: :moved_permanently, - } - - if host_or_options.is_a?(Hash) - options.merge!(host_or_options) - elsif host_or_options - options[:host] = host_or_options - end - - secure_url = ActionDispatch::Http::URL.url_for(options.slice(*URL_OPTIONS)) - flash.keep if respond_to?(:flash) && request.respond_to?(:flash) - redirect_to secure_url, options.slice(*REDIRECT_OPTIONS) - end - end - end -end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/head.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/head.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/head.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/head.rb 2021-08-19 16:25:04.000000000 +0000 @@ -29,19 +29,22 @@ content_type = options.delete(:content_type) options.each do |key, value| - headers[key.to_s.dasherize.split("-").each { |v| v[0] = v[0].chr.upcase }.join("-")] = value.to_s + headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s end self.status = status self.location = url_for(location) if location - self.response_body = "" - if include_content?(response_code) - self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html] + unless self.media_type + self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html] + end + response.charset = false end + self.response_body = "" + true end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/helpers.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/helpers.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/helpers.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/helpers.rb 2021-08-19 16:25:04.000000000 +0000 @@ -11,7 +11,12 @@ # # In previous versions of \Rails the controller will include a helper which # matches the name of the controller, e.g., MyController will automatically - # include MyHelper. To return old behavior set +config.action_controller.include_all_helpers+ to +false+. + # include MyHelper. You can revert to the old behavior with the following: + # + # # config/application.rb + # class Application < Rails::Application + # config.action_controller.include_all_helpers = false + # end # # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any # controller which inherits from it. @@ -73,6 +78,11 @@ end # Provides a proxy to access helper methods from outside the view. + # + # Note that the proxy is rendered under a different view context. + # This may cause incorrect behaviour with capture methods. Consider + # using {helper}[rdoc-ref:AbstractController::Helpers::ClassMethods#helper] + # instead when using +capture+. def helpers @helper_proxy ||= begin proxy = ActionView::Base.empty diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/http_authentication.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/http_authentication.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/http_authentication.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/http_authentication.rb 2021-08-19 16:25:04.000000000 +0000 @@ -76,6 +76,8 @@ def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil) authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password| + # This comparison uses & so that it doesn't short circuit and + # uses `secure_compare` so that length information isn't leaked. ActiveSupport::SecurityUtils.secure_compare(given_name, name) & ActiveSupport::SecurityUtils.secure_compare(given_password, password) end @@ -136,7 +138,7 @@ # # === Simple \Digest example # - # require 'digest/md5' + # require "digest/md5" # class PostsController < ApplicationController # REALM = "SuperSecret" # USERS = {"dhh" => "secret", #plain text password @@ -482,7 +484,7 @@ def raw_params(auth) _raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/) - if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}}) + if !_raw_params.first&.start_with?(TOKEN_KEY) _raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}" end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/implicit_render.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/implicit_render.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/implicit_render.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/implicit_render.rb 2021-08-19 16:25:04.000000000 +0000 @@ -22,7 +22,7 @@ # Third, if we DON'T find a template AND the request is a page load in a web # browser (technically, a non-XHR GET request for an HTML response) where # you reasonably expect to have rendered a template, then we raise - # ActionView::UnknownFormat with an explanation. + # ActionController::MissingExactTemplate with an explanation. # # Finally, if we DON'T find a template AND the request isn't a browser page # load, then we implicitly respond with 204 No Content. diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/instrumentation.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/instrumentation.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/instrumentation.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/instrumentation.rb 2021-08-19 16:25:04.000000000 +0000 @@ -16,10 +16,11 @@ attr_internal :view_runtime - def process_action(*args) + def process_action(*) raw_payload = { controller: self.class.name, action: action_name, + request: request, params: request.filtered_parameters, headers: request.headers, format: request.format.ref, @@ -27,18 +28,19 @@ path: request.fullpath } - ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup) + ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload) ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload| - super.tap do - payload[:status] = response.status - end + result = super + payload[:response] = response + payload[:status] = response.status + result ensure append_info_to_payload(payload) end end - def render(*args) + def render(*) render_output = nil self.view_runtime = cleanup_view_runtime do Benchmark.ms { render_output = super } @@ -59,8 +61,8 @@ end end - def redirect_to(*args) - ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload| + def redirect_to(*) + ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: request) do |payload| result = super payload[:status] = response.status payload[:location] = response.filtered_location @@ -70,7 +72,7 @@ private # A hook invoked every time a before callback is halted. - def halted_callback_hook(filter) + def halted_callback_hook(filter, _) ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter) end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/live.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/live.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/live.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/live.rb 2021-08-19 16:25:04.000000000 +0000 @@ -127,6 +127,11 @@ class Buffer < ActionDispatch::Response::Buffer #:nodoc: include MonitorMixin + class << self + attr_accessor :queue_size + end + @queue_size = 10 + # Ignore that the client has disconnected. # # If this value is `true`, calling `write` after the client @@ -136,11 +141,11 @@ attr_accessor :ignore_disconnect def initialize(response) + super(response, build_queue(self.class.queue_size)) @error_callback = lambda { true } @cv = new_cond @aborted = false @ignore_disconnect = false - super(response, SizedQueue.new(10)) end def write(string) @@ -214,6 +219,10 @@ yield str end end + + def build_queue(queue_size) + queue_size ? SizedQueue.new(queue_size) : Queue.new + end end class Response < ActionDispatch::Response #:nodoc: all diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/logging.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/logging.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/logging.rb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/logging.rb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module ActionController + module Logging + extend ActiveSupport::Concern + + module ClassMethods + # Set a different log level per request. + # + # # Use the debug log level if a particular cookie is set. + # class ApplicationController < ActionController::Base + # log_at :debug, if: -> { cookies[:debug] } + # end + # + def log_at(level, **options) + around_action ->(_, action) { logger.log_at(level, &action) }, **options + end + end + end +end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/mime_responds.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/mime_responds.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/mime_responds.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/mime_responds.rb 2021-08-19 16:25:04.000000000 +0000 @@ -142,7 +142,7 @@ # # You can set the variant in a +before_action+: # - # request.variant = :tablet if request.user_agent =~ /iPad/ + # request.variant = :tablet if /iPad/.match?(request.user_agent) # # Respond to variants in the action just like you respond to formats: # @@ -209,7 +209,7 @@ raise ActionController::RespondToMismatchError end _process_format(format) - _set_rendered_content_type format + _set_rendered_content_type(format) unless collector.any_response? response = collector.response response.call if response else @@ -268,6 +268,10 @@ end end + def any_response? + !@responses.fetch(format, false) && @responses[Mime::ALL] + end + def response response = @responses.fetch(format, @responses[Mime::ALL]) if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/parameter_encoding.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/parameter_encoding.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/parameter_encoding.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/parameter_encoding.rb 2021-08-19 16:25:04.000000000 +0000 @@ -12,11 +12,13 @@ end def setup_param_encode # :nodoc: - @_parameter_encodings = {} + @_parameter_encodings = Hash.new { |h, k| h[k] = {} } end - def binary_params_for?(action) # :nodoc: - @_parameter_encodings[action.to_s] + def action_encoding_template(action) # :nodoc: + if @_parameter_encodings.has_key?(action.to_s) + @_parameter_encodings[action.to_s] + end end # Specify that a given action's parameters should all be encoded as @@ -44,7 +46,36 @@ # encoded as ASCII-8BIT. This is useful in the case where an application # must handle data but encoding of the data is unknown, like file system data. def skip_parameter_encoding(action) - @_parameter_encodings[action.to_s] = true + @_parameter_encodings[action.to_s] = Hash.new { Encoding::ASCII_8BIT } + end + + # Specify the encoding for a parameter on an action. + # If not specified the default is UTF-8. + # + # You can specify a binary (ASCII_8BIT) parameter with: + # + # class RepositoryController < ActionController::Base + # # This specifies that file_path is not UTF-8 and is instead ASCII_8BIT + # param_encoding :show, :file_path, Encoding::ASCII_8BIT + # + # def show + # @repo = Repository.find_by_filesystem_path params[:file_path] + # + # # params[:repo_name] remains UTF-8 encoded + # @repo_name = params[:repo_name] + # end + # + # def index + # @repositories = Repository.all + # end + # end + # + # The file_path parameter on the show action would be encoded as ASCII-8BIT, + # but all other arguments will remain UTF-8 encoded. + # This is useful in the case where an application must handle data + # but encoding of the data is unknown, like file system data. + def param_encoding(action, param, encoding) + @_parameter_encodings[action.to_s][param.to_s] = encoding end end end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/params_wrapper.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/params_wrapper.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/params_wrapper.rb 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/params_wrapper.rb 2021-08-19 16:25:04.000000000 +0000 @@ -107,10 +107,14 @@ unless super || exclude if m.respond_to?(:attribute_names) && m.attribute_names.any? + self.include = m.attribute_names + if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty? - self.include = m.attribute_names + m.stored_attributes.values.flatten.map(&:to_s) - else - self.include = m.attribute_names + self.include += m.stored_attributes.values.flatten.map(&:to_s) + end + + if m.respond_to?(:attribute_aliases) && m.attribute_aliases.any? + self.include += m.attribute_aliases.keys end if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any? @@ -151,7 +155,7 @@ # try to find Foo::Bar::User, Foo::User and finally User. def _default_wrap_model return nil if klass.anonymous? - model_name = klass.name.sub(/Controller$/, "").classify + model_name = klass.name.delete_suffix("Controller").classify begin if model_klass = model_name.safe_constantize @@ -189,7 +193,7 @@ # # wrap_parameters Person # # wraps parameters by determining the wrapper key from Person class - # (+person+, in this case) and the list of attribute names + # # (+person+, in this case) and the list of attribute names # # wrap_parameters include: [:username, :title] # # wraps only +:username+ and +:title+ attributes from parameters. @@ -240,7 +244,7 @@ # Performs parameters wrapping upon the request. Called automatically # by the metal call stack. - def process_action(*args) + def process_action(*) _perform_parameter_wrapping if _wrapper_enabled? super end @@ -264,9 +268,11 @@ def _extract_parameters(parameters) if include_only = _wrapper_options.include parameters.slice(*include_only) + elsif _wrapper_options.exclude + exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS + parameters.except(*exclude) else - exclude = _wrapper_options.exclude || [] - parameters.except(*(exclude + EXCLUDE_PARAMETERS)) + parameters.except(*EXCLUDE_PARAMETERS) end end @@ -275,7 +281,10 @@ return false unless request.has_content_type? ref = request.content_mime_type.ref + _wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key) + rescue ActionDispatch::Http::Parameters::ParseError + false end def _perform_parameter_wrapping @@ -289,8 +298,6 @@ # This will display the wrapped hash in the log file. request.filtered_parameters.merge! wrapped_filtered_hash - rescue ActionDispatch::Http::Parameters::ParseError - # swallow parse error exception end end end diff -Nru rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/permissions_policy.rb rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/permissions_policy.rb --- rails-6.0.3.7+dfsg/actionpack/lib/action_controller/metal/permissions_policy.rb 1970-01-01 00:00:00.000000000 +0000 +++ rails-6.1.4.1+dfsg/actionpack/lib/action_controller/metal/permissions_policy.rb 2021-08-19 16:25:04.000000000 +0000 @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module ActionController #:nodoc: + # HTTP Permissions Policy is a web standard for defining a mechanism to + # allow and deny the use of browser permissions in its own context, and + # in content within any ``` -This loads arbitrary HTML and/or JavaScript from an external source and embeds it as part of the site. This iframe is taken from an actual attack on legitimate Italian sites using the [Mpack attack framework](https://isc.sans.edu/diary/MPack+Analysis/3015). Mpack tries to install malicious software through security holes in the web browser - very successfully, 50% of the attacks succeed. +This loads arbitrary HTML and/or JavaScript from an external source and embeds it as part of the site. This `iframe` is taken from an actual attack on legitimate Italian sites using the [Mpack attack framework](https://isc.sans.edu/diary/MPack+Analysis/3015). Mpack tries to install malicious software through security holes in the web browser - very successfully, 50% of the attacks succeed. A more specialized attack could overlap the entire web site or display a login form, which looks the same as the site's original, but transmits the user name and password to the attacker's site. Or it could use CSS and/or JavaScript to hide a legitimate link in the web application, and display another one at its place which redirects to a fake web site. @@ -775,13 +776,13 @@ Especially for XSS, it is important to do _permitted input filtering instead of restricted_. Permitted list filtering states the values allowed as opposed to the values not allowed. Restricted lists are never complete. -Imagine a restricted list deletes "script" from the user input. Now the attacker injects "<scrscriptipt>", and after the filter, "<script>" remains. Earlier versions of Rails used a restricted list approach for the strip_tags(), strip_links() and sanitize() method. So this kind of injection was possible: +Imagine a restricted list deletes `"script"` from the user input. Now the attacker injects `""`, and after the filter, `""`, which makes an attack work. That's why a permitted list approach is better, using the updated Rails 2 method `sanitize()`: ```ruby tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p) @@ -790,18 +791,18 @@ This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags. -As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &, ", <, and > by their uninterpreted representations in HTML (`&`, `"`, `<`, and `>`). +As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters `&`, `"`, `<`, and `>` by their uninterpreted representations in HTML (`&`, `"`, `<`, and `>`). ##### Obfuscation and Encoding Injection Network traffic is mostly based on the limited Western alphabet, so new character encodings, such as Unicode, emerged, to transmit characters in other languages. But, this is also a threat to web applications, as malicious code can be hidden in different encodings that the web browser might be able to process, but the web application might not. Here is an attack vector in UTF-8 encoding: -``` +```html ``` -This example pops up a message box. It will be recognized by the above sanitize() filter, though. A great tool to obfuscate and encode strings, and thus "get to know your enemy", is the [Hackvertor](https://hackvertor.co.uk/public). Rails' sanitize() method does a good job to fend off encoding attacks. +This example pops up a message box. It will be recognized by the above `sanitize()` filter, though. A great tool to obfuscate and encode strings, and thus "get to know your enemy", is the [Hackvertor](https://hackvertor.co.uk/public). Rails' `sanitize()` method does a good job to fend off encoding attacks. #### Examples from the Underground @@ -809,7 +810,7 @@ The following is an excerpt from the [Js.Yamanner@m](http://www.symantec.com/security_response/writeup.jsp?docid=2006-061211-4111-99&tabid=1) Yahoo! Mail [worm](http://groovin.net/stuff/yammer.txt). It appeared on June 11, 2006 and was the first webmail interface worm: -``` +```html ``` -So the payload is in the style attribute. But there are no quotes allowed in the payload, because single and double quotes have already been used. But JavaScript has a handy eval() function which executes any string as code. +So the payload is in the style attribute. But there are no quotes allowed in the payload, because single and double quotes have already been used. But JavaScript has a handy `eval()` function which executes any string as code. ```html
``` -The eval() function is a nightmare for restricted list input filters, as it allows the style attribute to hide the word "innerHTML": +The `eval()` function is a nightmare for restricted list input filters, as it allows the style attribute to hide the word "innerHTML": -``` +```js alert(eval('document.body.inne' + 'rHTML')); ``` -The next problem was MySpace filtering the word "javascript", so the author used "java<NEWLINE>script" to get around this: +The next problem was MySpace filtering the word `"javascript"`, so the author used `"javascript"` to get around this: ```html -
+
``` Another problem for the worm's author was the [CSRF security tokens](#cross-site-request-forgery-csrf). Without them he couldn't send a friend request over POST. He got around it by sending a GET to the page right before adding a user and parsing the result for the CSRF token. @@ -865,21 +866,21 @@ If you want to provide text formatting other than HTML (due to security), use a mark-up language which is converted to HTML on the server-side. [RedCloth](http://redcloth.org/) is such a language for Ruby, but without precautions, it is also vulnerable to XSS. -For example, RedCloth translates `_test_` to <em>test<em>, which makes the text italic. However, up to the current version 3.0.4, it is still vulnerable to XSS. Get the [all-new version 4](http://www.redcloth.org) that removed serious bugs. However, even that version has [some security bugs](http://www.rorsecurity.info/journal/2008/10/13/new-redcloth-security.html), so the countermeasures still apply. Here is an example for version 3.0.4: +For example, RedCloth translates `_test_` to `test`, which makes the text italic. However, up to the current version 3.0.4, it is still vulnerable to XSS. Get the [all-new version 4](http://www.redcloth.org) that removed serious bugs. However, even that version has [some security bugs](http://www.rorsecurity.info/journal/2008/10/13/new-redcloth-security.html), so the countermeasures still apply. Here is an example for version 3.0.4: ```ruby RedCloth.new('').to_html # => "" ``` -Use the :filter_html option to remove HTML which was not created by the Textile processor. +Use the `:filter_html` option to remove HTML which was not created by the Textile processor. ```ruby RedCloth.new('', [:filter_html]).to_html # => "alert(1)" ``` -However, this does not filter all HTML, a few tags will be left (by design), for example <a>: +However, this does not filter all HTML, a few tags will be left (by design), for example ``: ```ruby RedCloth.new("hello", [:filter_html]).to_html @@ -894,13 +895,13 @@ NOTE: _The same security precautions have to be taken for Ajax actions as for "normal" ones. There is at least one exception, however: The output has to be escaped in the controller already, if the action doesn't render a view._ -If you use the [in_place_editor plugin](https://rubygems.org/gems/in_place_editing), or actions that return a string, rather than rendering a view, _you have to escape the return value in the action_. Otherwise, if the return value contains a XSS string, the malicious code will be executed upon return to the browser. Escape any input value using the h() method. +If you use the [in_place_editor plugin](https://rubygems.org/gems/in_place_editing), or actions that return a string, rather than rendering a view, _you have to escape the return value in the action_. Otherwise, if the return value contains a XSS string, the malicious code will be executed upon return to the browser. Escape any input value using the `h()` method. ### Command Line Injection NOTE: _Use user-supplied command line parameters with caution._ -If your application has to execute commands in the underlying operating system, there are several methods in Ruby: exec(command), syscall(command), system(command) and `command`. You will have to be especially careful with these functions if the user may enter the whole command, or a part of it. This is because in most shells, you can execute another command at the end of the first one, concatenating them with a semicolon (;) or a vertical bar (|). +If your application has to execute commands in the underlying operating system, there are several methods in Ruby: `exec(command)`, `syscall(command)`, `system(command)` and `command`. You will have to be especially careful with these functions if the user may enter the whole command, or a part of it. This is because in most shells, you can execute another command at the end of the first one, concatenating them with a semicolon (`;`) or a vertical bar (`|`). A countermeasure is to _use the `system(command, parameters)` method which passes command line parameters safely_. @@ -909,7 +910,6 @@ # prints "hello; rm *" and does not delete files ``` - ### Header Injection WARNING: _HTTP headers are dynamically generated and under certain circumstances user input may be injected. This can lead to false redirection, XSS, or HTTP response splitting._ @@ -922,7 +922,7 @@ redirect_to params[:referer] ``` -What happens is that Rails puts the string into the Location header field and sends a 302 (redirect) status to the browser. The first thing a malicious user would do, is this: +What happens is that Rails puts the string into the `Location` header field and sends a 302 (redirect) status to the browser. The first thing a malicious user would do, is this: ``` http://www.yourapplication.com/controller/action?referer=http://www.malicious.tld @@ -935,9 +935,9 @@ http://www.yourapplication.com/controller/action?referer=path/at/your/app%0d%0aLocation:+http://www.malicious.tld ``` -Note that "%0d%0a" is URL-encoded for "\r\n" which is a carriage-return and line-feed (CRLF) in Ruby. So the resulting HTTP header for the second example will be the following because the second Location header field overwrites the first. +Note that `%0d%0a` is URL-encoded for `\r\n` which is a carriage-return and line-feed (CRLF) in Ruby. So the resulting HTTP header for the second example will be the following because the second Location header field overwrites the first. -``` +```http HTTP/1.1 302 Moved Temporarily (...) Location: http://www.malicious.tld @@ -949,10 +949,10 @@ If Header Injection was possible, Response Splitting might be, too. In HTTP, the header block is followed by two CRLFs and the actual data (usually HTML). The idea of Response Splitting is to inject two CRLFs into a header field, followed by another response with malicious HTML. The response will be: -``` +```http HTTP/1.1 302 Found [First standard 302 response] Date: Tue, 12 Apr 2005 22:09:07 GMT -Location:
Content-Type: text/html +Location:Content-Type: text/html HTTP/1.1 200 OK [Second New response created by attacker begins] @@ -1045,9 +1045,9 @@ Here is a list of common headers: -* **X-Frame-Options:** _'SAMEORIGIN' in Rails by default_ - allow framing on same domain. Set it to 'DENY' to deny framing at all or 'ALLOWALL' if you want to allow framing for all website. -* **X-XSS-Protection:** _'1; mode=block' in Rails by default_ - use XSS Auditor and block page if XSS attack is detected. Set it to '0;' if you want to switch XSS Auditor off(useful if response contents scripts from request parameters) -* **X-Content-Type-Options:** _'nosniff' in Rails by default_ - stops the browser from guessing the MIME type of a file. +* **X-Frame-Options:** _`SAMEORIGIN` in Rails by default_ - allow framing on same domain. Set it to 'DENY' to deny framing at all or remove this header completely if you want to allow framing on all websites. +* **X-XSS-Protection:** _`1; mode=block` in Rails by default_ - use XSS Auditor and block page if XSS attack is detected. Set it to '0;' if you want to switch XSS Auditor off(useful if response contents scripts from request parameters) +* **X-Content-Type-Options:** _`nosniff` in Rails by default_ - stops the browser from guessing the MIME type of a file. * **X-Content-Security-Policy:** [A powerful mechanism for controlling which sites certain content types can be loaded from](http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html) * **Access-Control-Allow-Origin:** Used to control which sites are allowed to bypass same origin policies and send cross-origin requests. * **Strict-Transport-Security:** [Used to control if the browser is allowed to only access a site over a secure connection](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) @@ -1169,32 +1169,34 @@ It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, master key for `credentials.yml`, and other unencrypted secrets. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information. -### Custom credentials +### Custom Credentials -Rails stores secrets in `config/credentials.yml.enc`, which is encrypted and hence cannot be edited directly. Rails uses `config/master.key` or alternatively looks for environment variable `ENV["RAILS_MASTER_KEY"]` to encrypt the credentials file. The credentials file can be stored in version control, as long as master key is kept safe. +Rails stores secrets in `config/credentials.yml.enc`, which is encrypted and hence cannot be edited directly. Rails uses `config/master.key` or alternatively looks for the environment variable `ENV["RAILS_MASTER_KEY"]` to encrypt the credentials file. Because the credentials file is encrypted, it can be stored in version control, as long as the master key is kept safe. -To add new secret to credentials, first run `rails secret` to get a new secret. Then run `rails credentials:edit` to edit credentials, and add the secret. Running `credentials:edit` creates new credentials file and master key, if they did not already exist. +By default, the credentials file contains the application's +`secret_key_base`. It can also be used to store other secrets such as access keys for external APIs. -By default, this file contains the application's -`secret_key_base`, but it could also be used to store other credentials such as access keys for external APIs. +To edit the credentials file, run `bin/rails credentials:edit`. This command will create the credentials file if it does not exist. Additionally, this command will create `config/master.key` if no master key is defined. -The secrets kept in credentials file are accessible via `Rails.application.credentials`. +Secrets kept in the credentials file are accessible via `Rails.application.credentials`. For example, with the following decrypted `config/credentials.yml.enc`: - secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 - some_api_key: SOMEKEY +```yaml +secret_key_base: 3b7cd72... +some_api_key: SOMEKEY +``` -`Rails.application.credentials.some_api_key` returns `SOMEKEY` in any environment. +`Rails.application.credentials.some_api_key` returns `"SOMEKEY"`. -If you want an exception to be raised when some key is blank, use the bang +If you want an exception to be raised when some key is blank, you can use the bang version: ```ruby -Rails.application.credentials.some_api_key! # => raises KeyError: :some_api_key is blank +# When some_api_key is blank... +Rails.application.credentials.some_api_key! # => KeyError: :some_api_key is blank ``` - -TIP: Learn more about credentials with `rails credentials:help`. +TIP: Learn more about credentials with `bin/rails credentials:help`. WARNING: Keep your master key safe. Do not commit your master key. @@ -1210,5 +1212,5 @@ * Subscribe to the Rails security [mailing list](https://groups.google.com/forum/#!forum/rubyonrails-security). * [Brakeman - Rails Security Scanner](https://brakemanscanner.org/) - To perform static security analysis for Rails applications. -* [Keep up to date on the other application layers](http://secunia.com/) (they have a weekly newsletter, too). -* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet). +* [Mozilla's Web Security Guidelines](https://infosec.mozilla.org/guidelines/web_security.html) - Recommendations on topics covering Content Security Policy, HTTP headers, Cookies, TLS configuration, etc. +* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.md). diff -Nru rails-6.0.3.7+dfsg/guides/source/testing.md rails-6.1.4.1+dfsg/guides/source/testing.md --- rails-6.0.3.7+dfsg/guides/source/testing.md 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/guides/source/testing.md 2021-08-19 16:25:04.000000000 +0000 @@ -54,7 +54,6 @@ The `application_system_test_case.rb` holds the default configuration for your system tests. - ### The Test Environment By default, every Rails application has three environments: development, test, and production. @@ -65,12 +64,12 @@ ### Rails meets Minitest -If you remember, we used the `rails generate model` command in the +If you remember, we used the `bin/rails generate model` command in the [Getting Started with Rails](getting_started.html) guide. We created our first model, and among other things it created test stubs in the `test` directory: ```bash -$ rails generate model article title:string body:text +$ bin/rails generate model article title:string body:text ... create app/models/article.rb create test/models/article_test.rb @@ -81,7 +80,7 @@ The default test stub in `test/models/article_test.rb` looks like this: ```ruby -require 'test_helper' +require "test_helper" class ArticleTest < ActiveSupport::TestCase # test "the truth" do @@ -93,7 +92,7 @@ A line by line examination of this file will help get you oriented to Rails testing code and terminology. ```ruby -require 'test_helper' +require "test_helper" ``` By requiring this file, `test_helper.rb` the default configuration to run our tests is loaded. We will include this with all the tests we write, so any methods added to this file are available to all our tests. @@ -125,7 +124,7 @@ Although you can still use regular method definitions, using the `test` macro allows for a more readable test name. -NOTE: The method name is generated by replacing spaces with underscores. The result does not need to be a valid Ruby identifier though, the name may contain punctuation characters etc. That's because in Ruby technically any string may be a method name. This may require use of `define_method` and `send` calls to function properly, but formally there's little restriction on the name. +NOTE: The method name is generated by replacing spaces with underscores. The result does not need to be a valid Ruby identifier though — the name may contain punctuation characters, etc. That's because in Ruby technically any string may be a method name. This may require use of `define_method` and `send` calls to function properly, but formally there's little restriction on the name. Next, let's look at our first assertion: @@ -156,7 +155,7 @@ Let us run this newly added test (where `6` is the number of line where the test is defined). ```bash -$ rails test test/models/article_test.rb:6 +$ bin/rails test test/models/article_test.rb:6 Run options: --seed 44656 # Running: @@ -175,7 +174,6 @@ Finished in 0.023918s, 41.8090 runs/s, 41.8090 assertions/s. 1 runs, 1 assertions, 1 failures, 0 errors, 0 skips - ``` In the output, `F` denotes a failure. You can see the corresponding trace shown under `Failure` along with the name of the failing test. The next few lines contain the stack trace followed by a message that mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable, every assertion provides an optional message parameter, as shown here: @@ -189,7 +187,7 @@ Running this test shows the friendlier assertion message: -```bash +``` Failure: ArticleTest#test_should_not_save_article_without_title [/path/to/blog/test/models/article_test.rb:6]: Saved the article without a title @@ -206,7 +204,7 @@ Now the test should pass. Let us verify by running the test again: ```bash -$ rails test test/models/article_test.rb:6 +$ bin/rails test test/models/article_test.rb:6 Run options: --seed 31252 # Running: @@ -224,7 +222,7 @@ referred to as [_Test-Driven Development_ (TDD)](http://c2.com/cgi/wiki?TestDrivenDevelopment). -#### What an error looks like +#### What an Error Looks Like To see how an error gets reported, here's a test containing an error: @@ -239,7 +237,7 @@ Now you can see even more output in the console from running the tests: ```bash -$ rails test test/models/article_test.rb +$ bin/rails test test/models/article_test.rb Run options: --seed 1808 # Running: @@ -276,7 +274,7 @@ backtrace. Set the `-b` (or `--backtrace`) argument to enable this behavior: ```bash -$ rails test -b test/models/article_test.rb +$ bin/rails test -b test/models/article_test.rb ``` If we want this test to pass we can modify it to use `assert_raises` like so: @@ -320,7 +318,7 @@ | `assert_in_delta( expected, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.| | `assert_not_in_delta( expected, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.| | `assert_in_epsilon ( expected, actual, [epsilon], [msg] )` | Ensures that the numbers `expected` and `actual` have a relative error less than `epsilon`.| -| `assert_not_in_epsilon ( expected, actual, [epsilon], [msg] )` | Ensures that the numbers `expected` and `actual` don't have a relative error less than `epsilon`.| +| `assert_not_in_epsilon ( expected, actual, [epsilon], [msg] )` | Ensures that the numbers `expected` and `actual` have a relative error not less than `epsilon`.| | `assert_throws( symbol, [msg] ) { block }` | Ensures that the given block throws the symbol.| | `assert_raises( exception1, exception2, ... ) { block }` | Ensures that the given block raises one of the given exceptions.| | `assert_instance_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class`.| @@ -358,7 +356,7 @@ | [`assert_recognizes(expected_options, path, extras={}, message=nil)`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes) | Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.| | [`assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_generates) | Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.| | [`assert_response(type, message = nil)`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_response) | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range. You can also pass an explicit status number or its symbolic equivalent. For more information, see [full list of status codes](http://rubydoc.info/github/rack/rack/master/Rack/Utils#HTTP_STATUS_CODES-constant) and how their [mapping](https://rubydoc.info/github/rack/rack/master/Rack/Utils#SYMBOL_TO_STATUS_CODE-constant) works.| -| [`assert_redirected_to(options = {}, message=nil)`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_redirected_to) | Asserts that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on. You can also pass named routes such as `assert_redirected_to root_path` and Active Record objects such as `assert_redirected_to @article`.| +| [`assert_redirected_to(options = {}, message=nil)`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_redirected_to) | Asserts that the response is a redirect to a URL matching the given options. You can also pass named routes such as `assert_redirected_to root_path` and Active Record objects such as `assert_redirected_to @article`.| You'll see the usage of some of these assertions in the next chapter. @@ -381,12 +379,12 @@ ### The Rails Test Runner -We can run all of our tests at once by using the `rails test` command. +We can run all of our tests at once by using the `bin/rails test` command. -Or we can run a single test file by passing the `rails test` command the filename containing the test cases. +Or we can run a single test file by passing the `bin/rails test` command the filename containing the test cases. ```bash -$ rails test test/models/article_test.rb +$ bin/rails test test/models/article_test.rb Run options: --seed 1559 # Running: @@ -404,7 +402,7 @@ `-n` or `--name` flag and the test's method name. ```bash -$ rails test test/models/article_test.rb -n test_the_truth +$ bin/rails test test/models/article_test.rb -n test_the_truth Run options: -n test_the_truth --seed 43583 # Running: @@ -419,29 +417,29 @@ You can also run a test at a specific line by providing the line number. ```bash -$ rails test test/models/article_test.rb:6 # run specific test and line +$ bin/rails test test/models/article_test.rb:6 # run specific test and line ``` You can also run an entire directory of tests by providing the path to the directory. ```bash -$ rails test test/controllers # run all tests from specific directory +$ bin/rails test test/controllers # run all tests from specific directory ``` The test runner also provides a lot of other features like failing fast, deferring test output -at the end of test run and so on. Check the documentation of the test runner as follows: +at the end of the test run and so on. Check the documentation of the test runner as follows: ```bash -$ rails test -h +$ bin/rails test -h Usage: rails test [options] [files or directories] You can run a single test by appending a line number to a filename: - rails test test/models/user_test.rb:27 + bin/rails test test/models/user_test.rb:27 You can run multiple files and directories at the same time: - rails test test/controllers test/integration/login_test.rb + bin/rails test test/controllers test/integration/login_test.rb By default test failures and errors are reported inline during a run. @@ -470,7 +468,7 @@ default method, threading is supported as well. Running tests in parallel reduces the time it takes your entire test suite to run. -### Parallel testing with processes +### Parallel Testing with Processes The default parallelization method is to fork processes using Ruby's DRb system. The processes are forked based on the number of workers provided. The default number is the actual core count @@ -489,7 +487,7 @@ to be able to easily change the number of workers a test run should use: ```bash -PARALLEL_WORKERS=15 rails test +$ PARALLEL_WORKERS=15 bin/rails test ``` When parallelizing tests, Active Record automatically handles creating a database and loading the schema into the database for each @@ -522,7 +520,7 @@ These methods are not needed or available when using parallel testing with threads. -### Parallel testing with threads +### Parallel Testing with Threads If you prefer using threads or are using JRuby, a threaded parallelization option is provided. The threaded parallelizer is backed by Minitest's `Parallel::Executor`. @@ -535,16 +533,42 @@ end ``` -Rails applications generated from JRuby will automatically include the `with: :threads` option. +Rails applications generated from JRuby or TruffleRuby will automatically include the `with: :threads` option. The number of workers passed to `parallelize` determines the number of threads the tests will use. You may want to parallelize your local test suite differently from your CI, so an environment variable is provided to be able to easily change the number of workers a test run should use: ```bash -PARALLEL_WORKERS=15 rails test +$ PARALLEL_WORKERS=15 bin/rails test +``` + +### Testing Parallel Transactions + +Rails automatically wraps any test case in a database transaction that is rolled +back after the test completes. This makes test cases independent of each other +and changes to the database are only visible within a single test. + +When you want to test code that runs parallel transactions in threads, +transactions can block each other because they are already nested under the test +transaction. + +You can disable transactions in a test case class by setting +`self.use_transactional_tests = false`: + +```ruby +class WorkerTest < ActiveSupport::TestCase + self.use_transactional_tests = false + + test "parallel transactions" do + # start some threads that create transactions + end +end ``` +NOTE: With disabled transactional tests, you have to clean up any data tests +create as changes are not automatically rolled back after the test completes. + The Test Database ----------------- @@ -554,7 +578,6 @@ A dedicated test database allows you to set up and interact with test data in isolation. This way your tests can mangle test data with confidence, without worrying about the data in the development or production databases. - ### Maintaining the test database schema In order to run your tests, your test database will need to have the current @@ -562,11 +585,11 @@ migrations. It will try to load your `db/schema.rb` or `db/structure.sql` into the test database. If migrations are still pending, an error will be raised. Usually this indicates that your schema is not fully migrated. Running -the migrations against the development database (`rails db:migrate`) will +the migrations against the development database (`bin/rails db:migrate`) will bring the schema up to date. NOTE: If there were modifications to existing migrations, the test database needs to -be rebuilt. This can be done by executing `rails db:test:prepare`. +be rebuilt. This can be done by executing `bin/rails db:test:prepare`. ### The Low-Down on Fixtures @@ -574,13 +597,13 @@ In Rails, you can handle this by defining and customizing fixtures. You can find comprehensive documentation in the [Fixtures API documentation](https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html). -#### What Are Fixtures? +#### What are Fixtures? _Fixtures_ is a fancy word for sample data. Fixtures allow you to populate your testing database with predefined data before your tests run. Fixtures are database independent and written in YAML. There is one file per model. NOTE: Fixtures are not designed to create every object that your tests need, and are best managed when only used for default data that can be applied to the common case. -You'll find fixtures under your `test/fixtures` directory. When you run `rails generate model` to create a new model, Rails automatically creates fixture stubs in this directory. +You'll find fixtures under your `test/fixtures` directory. When you run `bin/rails generate model` to create a new model, Rails automatically creates fixture stubs in this directory. #### YAML @@ -608,11 +631,13 @@ a `belongs_to`/`has_many` association: ```yaml -# In fixtures/categories.yml +# fixtures/categories.yml about: name: About +``` -# In fixtures/articles.yml +```yaml +# fixtures/articles.yml first: title: Welcome to Rails! body: Hello world! @@ -679,12 +704,12 @@ a generator to create a model test skeleton for you. ```bash -$ rails generate test_unit:model article title:string body:text +$ bin/rails generate test_unit:model article title:string body:text create test/models/article_test.rb create test/fixtures/articles.yml ``` -Model tests don't have their own superclass like `ActionMailer::TestCase` instead they inherit from [`ActiveSupport::TestCase`](https://api.rubyonrails.org/classes/ActiveSupport/TestCase.html). +Model tests don't have their own superclass like `ActionMailer::TestCase`. Instead, they inherit from [`ActiveSupport::TestCase`](https://api.rubyonrails.org/classes/ActiveSupport/TestCase.html). System Testing -------------- @@ -696,7 +721,7 @@ application. Rails provides a generator to create a system test skeleton for you. ```bash -$ rails generate system_test users +$ bin/rails generate system_test users invoke test_unit create test/system/users_test.rb ``` @@ -781,13 +806,13 @@ to view screenshots later for debugging. Two methods are provided: `take_screenshot` and `take_failed_screenshot`. -`take_failed_screenshot` is automatically included in `after_teardown` inside +`take_failed_screenshot` is automatically included in `before_teardown` inside Rails. The `take_screenshot` helper method can be included anywhere in your tests to take a screenshot of the browser. -### Implementing a system test +### Implementing a System Test Now we're going to add a system test to our blog application. We'll demonstrate writing a system test by visiting the index page and creating a new blog article. @@ -797,13 +822,13 @@ system test skeleton. ```bash -$ rails generate system_test articles +$ bin/rails generate system_test articles ``` It should have created a test file placeholder for us. With the output of the previous command you should see: -```bash +``` invoke test_unit create test/system/articles_test.rb ``` @@ -826,13 +851,14 @@ Run the system tests. ```bash -rails test:system +$ bin/rails test:system ``` -NOTE: By default, running `rails test` won't run your system tests. -Make sure to run `rails test:system` to actually run them. +NOTE: By default, running `bin/rails test` won't run your system tests. +Make sure to run `bin/rails test:system` to actually run them. +You can also run `bin/rails test:all` to run all tests, including system tests. -#### Creating articles system test +#### Creating Articles System Test Now let's test the flow for creating a new article in our blog. @@ -877,6 +903,7 @@ driven_by :selenium, using: :chrome, screen_size: [375, 667] end ``` + To use this configuration, create a test inside `test/system` that inherits from `MobileSystemTestCase`. Now you can test your app using multiple different configurations. @@ -904,12 +931,12 @@ Integration Testing ------------------- -Integration tests are used to test how various parts of your application interact. They are generally used to test important workflows within our application. +Integration tests are used to test how various parts of our application interact. They are generally used to test important workflows within our application. For creating Rails integration tests, we use the `test/integration` directory for our application. Rails provides a generator to create an integration test skeleton for us. ```bash -$ rails generate integration_test user_flows +$ bin/rails generate integration_test user_flows exists test/integration/ create test/integration/user_flows_test.rb ``` @@ -917,7 +944,7 @@ Here's what a freshly generated integration test looks like: ```ruby -require 'test_helper' +require "test_helper" class UserFlowsTest < ActionDispatch::IntegrationTest # test "the truth" do @@ -945,13 +972,13 @@ We'll start by generating our integration test skeleton: ```bash -$ rails generate integration_test blog_flow +$ bin/rails generate integration_test blog_flow ``` It should have created a test file placeholder for us. With the output of the previous command we should see: -```bash +``` invoke test_unit create test/integration/blog_flow_test.rb ``` @@ -959,7 +986,7 @@ Now let's open that file and write our first assertion: ```ruby -require 'test_helper' +require "test_helper" class BlogFlowTest < ActionDispatch::IntegrationTest test "can see the welcome page" do @@ -1033,7 +1060,7 @@ The easiest way to see functional tests in action is to generate a controller using the scaffold generator: ```bash -$ rails generate scaffold_controller article title:string body:text +$ bin/rails generate scaffold_controller article title:string body:text ... create app/controllers/articles_controller.rb ... @@ -1049,7 +1076,7 @@ each of the seven default actions, you can use the following command: ```bash -$ rails generate test_unit:scaffold article +$ bin/rails generate test_unit:scaffold article ... invoke test_unit create test/controllers/articles_controller_test.rb @@ -1080,7 +1107,7 @@ * `headers`: for setting the headers that will be passed with the request. * `env`: for customizing the request environment as needed. * `xhr`: whether the request is Ajax request or not. Can be set to true for marking the request as Ajax. -* `as`: for encoding the request with different content type. Supports `:json` by default. +* `as`: for encoding the request with different content type. All of these keyword arguments are optional. @@ -1096,14 +1123,21 @@ patch article_url(Article.last), params: { article: { title: "updated" } }, xhr: true ``` +One more example: Calling the `:create` action to create a new article, passing in +text for the `title` in `params`, as JSON request: + +```ruby +post articles_path, params: { article: { title: "Ahoy!" } }, as: :json +``` + NOTE: If you try running `test_should_create_article` test from `articles_controller_test.rb` it will fail on account of the newly added model level validation and rightly so. Let us modify `test_should_create_article` test in `articles_controller_test.rb` so that all our test pass: ```ruby test "should create article" do - assert_difference('Article.count') do - post articles_url, params: { article: { body: 'Rails is awesome!', title: 'Hello Rails' } } + assert_difference("Article.count") do + post articles_url, params: { article: { body: "Rails is awesome!", title: "Hello Rails" } } end assert_redirected_to article_path(Article.last) @@ -1112,10 +1146,10 @@ Now you can try running all the tests and they should pass. -NOTE: If you followed the steps in the Basic Authentication section, you'll need to add authorization to every request header to get all the tests passing: +NOTE: If you followed the steps in the [Basic Authentication](getting_started.html#basic-authentication) section, you'll need to add authorization to every request header to get all the tests passing: ```ruby -post articles_url, params: { article: { body: 'Rails is awesome!', title: 'Hello Rails' } }, headers: { Authorization: ActionController::HttpAuthentication::Basic.encode_credentials('dhh', 'secret') } +post articles_url, params: { article: { body: "Rails is awesome!", title: "Hello Rails" } }, headers: { Authorization: ActionController::HttpAuthentication::Basic.encode_credentials("dhh", "secret") } ``` ### Available Request Types for Functional Tests @@ -1143,7 +1177,7 @@ article = articles(:one) get article_url(article), xhr: true - assert_equal 'hello world', @response.body + assert_equal "hello world", @response.body assert_equal "text/javascript", @response.media_type end ``` @@ -1211,19 +1245,19 @@ ```ruby test "should create article" do - assert_difference('Article.count') do - post article_url, params: { article: { title: 'Some title' } } + assert_difference("Article.count") do + post articles_url, params: { article: { title: "Some title" } } end assert_redirected_to article_path(Article.last) - assert_equal 'Article was successfully created.', flash[:notice] + assert_equal "Article was successfully created.", flash[:notice] end ``` If we run our test now, we should see a failure: ```bash -$ rails test test/controllers/articles_controller_test.rb -n test_should_create_article +$ bin/rails test test/controllers/articles_controller_test.rb -n test_should_create_article Run options: -n test_should_create_article --seed 32266 # Running: @@ -1250,10 +1284,10 @@ @article = Article.new(article_params) if @article.save - flash[:notice] = 'Article was successfully created.' + flash[:notice] = "Article was successfully created." redirect_to @article else - render 'new' + render "new" end end ``` @@ -1261,7 +1295,7 @@ Now if we run our tests, we should see it pass: ```bash -$ rails test test/controllers/articles_controller_test.rb -n test_should_create_article +$ bin/rails test test/controllers/articles_controller_test.rb -n test_should_create_article Run options: -n test_should_create_article --seed 18981 # Running: @@ -1294,7 +1328,7 @@ ```ruby test "should destroy article" do article = articles(:one) - assert_difference('Article.count', -1) do + assert_difference("Article.count", -1) do delete article_url(article) end @@ -1322,7 +1356,7 @@ Our test should now look something as what follows. Disregard the other tests for now, we're leaving them out for brevity. ```ruby -require 'test_helper' +require "test_helper" class ArticlesControllerTest < ActionDispatch::IntegrationTest # called before every single test @@ -1343,7 +1377,7 @@ end test "should destroy article" do - assert_difference('Article.count', -1) do + assert_difference("Article.count", -1) do delete article_url(@article) end @@ -1383,7 +1417,7 @@ ``` ```ruby -require 'test_helper' +require "test_helper" class ProfileControllerTest < ActionDispatch::IntegrationTest @@ -1414,13 +1448,13 @@ These helpers can then be explicitly required as needed and included as needed ```ruby -require 'test_helper' -require 'test_helpers/multiple_assertions' +require "test_helper" +require "test_helpers/multiple_assertions" class NumberTest < ActiveSupport::TestCase include MultipleAssertions - test '420 is a multiple of forty two' do + test "420 is a multiple of forty two" do assert_multiple_of_forty_two 420 end end @@ -1430,7 +1464,7 @@ ```ruby # test/test_helper.rb -require 'test_helpers/sign_in_helper' +require "test_helpers/sign_in_helper" class ActionDispatch::IntegrationTest include SignInHelper @@ -1443,7 +1477,7 @@ ```ruby # test/test_helper.rb -Dir[Rails.root.join('test', 'test_helpers', '**', '*.rb')].each { |file| require file } +Dir[Rails.root.join("test", "test_helpers", "**", "*.rb")].each { |file| require file } ``` This has the downside of increasing the boot-up time, as opposed to manually requiring only the necessary files in your individual tests. @@ -1471,7 +1505,7 @@ For example, you could verify the contents on the title element in your response with: ```ruby -assert_select 'title', "Welcome to Rails Testing Guide" +assert_select "title", "Welcome to Rails Testing Guide" ``` You can also use nested `assert_select` blocks for deeper investigation. @@ -1480,8 +1514,8 @@ within the collection of elements selected by the outer block: ```ruby -assert_select 'ul.navigation' do - assert_select 'li.menu_item' +assert_select "ul.navigation" do + assert_select "li.menu_item" end ``` @@ -1517,7 +1551,7 @@ ```ruby assert_select_email do - assert_select 'small', 'Please click the "Unsubscribe" link if you want to opt-out.' + assert_select "small", "Please click the 'Unsubscribe' link if you want to opt-out." end ``` @@ -1573,7 +1607,7 @@ #### From All Sides -There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a known value (a fixture.) In the functional tests you don't so much test the minute details produced by the mailer; instead, we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time. +There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a known value (a fixture). In the functional tests you don't so much test the minute details produced by the mailer; instead, we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time. ### Unit Testing @@ -1590,13 +1624,13 @@ Here's a unit test to test a mailer named `UserMailer` whose action `invite` is used to send an invitation to a friend. It is an adapted version of the base test created by the generator for an `invite` action. ```ruby -require 'test_helper' +require "test_helper" class UserMailerTest < ActionMailer::TestCase test "invite" do # Create the email and store it for further assertions - email = UserMailer.create_invite('me@example.com', - 'friend@example.com', Time.now) + email = UserMailer.create_invite("me@example.com", + "friend@example.com", Time.now) # Send the email, then test that it got queued assert_emails 1 do @@ -1604,10 +1638,10 @@ end # Test the body of the sent email contains what we expect it to - assert_equal ['me@example.com'], email.from - assert_equal ['friend@example.com'], email.to - assert_equal 'You have been invited by me@example.com', email.subject - assert_equal read_fixture('invite').join, email.body.to_s + assert_equal ["me@example.com"], email.from + assert_equal ["friend@example.com"], email.to + assert_equal "You have been invited by me@example.com", email.subject + assert_equal read_fixture("invite").join, email.body.to_s end end ``` @@ -1649,13 +1683,13 @@ ```ruby # Integration Test -require 'test_helper' +require "test_helper" class UsersControllerTest < ActionDispatch::IntegrationTest test "invite friend" do # Asserts the difference in the ActionMailer::Base.deliveries assert_emails 1 do - post invite_friend_url, params: { email: 'friend@example.com' } + post invite_friend_url, params: { email: "friend@example.com" } end end end @@ -1663,16 +1697,16 @@ ```ruby # System Test -require 'test_helper' +require "test_helper" class UsersTest < ActionDispatch::SystemTestCase driven_by :selenium, using: :headless_chrome test "inviting a friend" do visit invite_users_url - fill_in 'Email', with: 'friend@example.com' + fill_in "Email", with: "friend@example.com" assert_emails 1 do - click_on 'Invite' + click_on "Invite" end end end @@ -1693,10 +1727,10 @@ under the `test/jobs` directory. Here's an example test with a billing job: ```ruby -require 'test_helper' +require "test_helper" class BillingJobTest < ActiveJob::TestCase - test 'that account is charged' do + test "that account is charged" do BillingJob.perform_now(account, product) assert account.reload.charged_for?(product) end @@ -1711,7 +1745,7 @@ and enqueued jobs are cleared before any test run so you can safely assume that no jobs have already been executed in the scope of each test. -### Custom Assertions And Testing Jobs Inside Other Components +### Custom Assertions and Testing Jobs inside Other Components Active Job ships with a bunch of custom assertions that can be used to lessen the verbosity of tests. For a full list of available assertions, see the API documentation for [`ActiveJob::TestHelper`](https://api.rubyonrails.org/classes/ActiveJob/TestHelper.html). @@ -1721,12 +1755,12 @@ within a model: ```ruby -require 'test_helper' +require "test_helper" class ProductTest < ActiveSupport::TestCase include ActiveJob::TestHelper - test 'billing job scheduling' do + test "billing job scheduling" do assert_enqueued_with(job: BillingJob) do product.charge(account) end @@ -1734,25 +1768,6 @@ end ``` -### Asserting Time Arguments in Jobs - -When serializing job arguments, `Time`, `DateTime`, and `ActiveSupport::TimeWithZone` lose microsecond precision. This means comparing deserialized time with actual time doesn't always work. To compensate for the loss of precision, `assert_enqueued_with` and `assert_performed_with` will remove microseconds from time objects in argument assertions. - -```ruby -require 'test_helper' - -class ProductTest < ActiveSupport::TestCase - include ActiveJob::TestHelper - - test 'that product is reserved at a given time' do - now = Time.now - assert_performed_with(job: ReservationJob, args: [product, now]) do - product.reserve(now) - end - end -end -``` - Testing Action Cable -------------------- @@ -1848,7 +1863,7 @@ within a model: ```ruby -require 'test_helper' +require "test_helper" class ProductTest < ActionCable::TestCase test "broadcast status after charge" do @@ -1859,7 +1874,7 @@ end ``` -If you want to test the broadcasting made with `Channel.broadcast_to`, you shoud use +If you want to test the broadcasting made with `Channel.broadcast_to`, you should use `Channel.broadcasting_for` to generate an underlying stream name: ```ruby @@ -1869,9 +1884,11 @@ ChatChannel.broadcast_to room, text: message end end +``` +```ruby # test/jobs/chat_relay_job_test.rb -require 'test_helper' +require "test_helper" class ChatRelayJobTest < ActiveJob::TestCase include ActionCable::TestHelper @@ -1897,7 +1914,7 @@ ```ruby # Lets say that a user is eligible for gifting a month after they register. -user = User.create(name: 'Gaurish', activation_date: Date.new(2004, 10, 24)) +user = User.create(name: "Gaurish", activation_date: Date.new(2004, 10, 24)) assert_not user.applicable_for_gifting? travel_to Date.new(2004, 11, 24) do assert_equal Date.new(2004, 10, 24), user.activation_date # inside the `travel_to` block `Date.current` is mocked diff -Nru rails-6.0.3.7+dfsg/guides/source/threading_and_code_execution.md rails-6.1.4.1+dfsg/guides/source/threading_and_code_execution.md --- rails-6.0.3.7+dfsg/guides/source/threading_and_code_execution.md 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/guides/source/threading_and_code_execution.md 2021-08-19 16:25:04.000000000 +0000 @@ -176,7 +176,7 @@ needs too. `ActionDispatch::Executor` and `ActionDispatch::Reloader` are Rack middlewares -that wraps the request with a supplied Executor or Reloader, respectively. They +that wrap requests with a supplied Executor or Reloader, respectively. They are automatically included in the default application stack. The Reloader will ensure any arriving HTTP request is served with a freshly-loaded copy of the application if any code changes have occurred. @@ -186,7 +186,7 @@ Action Cable uses the Executor instead: because a Cable connection is linked to a specific instance of a class, it's not possible to reload for every arriving -websocket message. Only the message handler is wrapped, though; a long-running +WebSocket message. Only the message handler is wrapped, though; a long-running Cable connection does not prevent a reload that's triggered by a new incoming request or job. Instead, Action Cable uses the Reloader's `before_class_unload` callback to disconnect all its connections. When the client automatically @@ -301,7 +301,6 @@ end ``` - ### ActionDispatch::DebugLocks If your application is deadlocking and you think the Load Interlock may be diff -Nru rails-6.0.3.7+dfsg/guides/source/upgrading_ruby_on_rails.md rails-6.1.4.1+dfsg/guides/source/upgrading_ruby_on_rails.md --- rails-6.0.3.7+dfsg/guides/source/upgrading_ruby_on_rails.md 2021-05-05 16:01:04.000000000 +0000 +++ rails-6.1.4.1+dfsg/guides/source/upgrading_ruby_on_rails.md 2021-08-19 16:25:04.000000000 +0000 @@ -51,7 +51,7 @@ interactive session. ```bash -$ rails app:update +$ bin/rails app:update identical config/boot.rb exist config conflict config/routes.rb @@ -72,6 +72,134 @@ To allow you to upgrade to new defaults one by one, the update task has created a file `config/initializers/new_framework_defaults.rb`. Once your application is ready to run with new defaults, you can remove this file and flip the `config.load_defaults` value. +Upgrading from Rails 6.0 to Rails 6.1 +------------------------------------- + +For more information on changes made to Rails 6.1 please see the [release notes](6_1_release_notes.html). + +### `Rails.application.config_for` return value no longer supports access with String keys. + +Given a configuration file like this: + +```yaml +# config/example.yml +development: + options: + key: value +``` + +```ruby +Rails.application.config_for(:example).options +``` + +This used to return a hash on which you could access values with String keys. That was deprecated in 6.0, and now doesn't work anymore. + +You can call `with_indifferent_access` on the return value of `config_for` if you still want to access values with String keys, e.g.: + +```ruby +Rails.application.config_for(:example).with_indifferent_access.dig('options', 'key') +``` + +### Response's Content-Type when using `respond_to#any` + +The Content-Type header returned in the response can differ from what Rails 6.0 returned, +more specifically if your application uses `respond_to { |format| format.any }`. +The Content-Type will now be based on the given block rather than the request's format. + +Example: + +```ruby +def my_action + respond_to do |format| + format.any { render(json: { foo: 'bar' }) } + end +end +``` + +```ruby +get('my_action.csv') +``` + +Previous behaviour was returning a `text/csv` response's Content-Type which is inaccurate since a JSON response is being rendered. +Current behaviour correctly returns a `application/json` response's Content-Type. + +If your application relies on the previous incorrect behaviour, you are encouraged to specify +which formats your action accepts, i.e. + +```ruby +format.any(:xml, :json) { render request.format.to_sym => @people } +``` + +### `ActiveSupport::Callbacks#halted_callback_hook` now receive a second argument + +Active Support allows you to override the `halted_callback_hook` whenever a callback +halts the chain. This method now receive a second argument which is the name of the callback being halted. +If you have classes that override this method, make sure it accepts two arguments. Note that this is a breaking +change without a prior deprecation cycle (for performance reasons). + +Example: + +```ruby +class Book < ApplicationRecord + before_save { throw(:abort) } + before_create { throw(:abort) } + + def halted_callback_hook(filter, callback_name) # => This method now accepts 2 arguments instead of 1 + Rails.logger.info("Book couldn't be #{callback_name}d") + end +end +``` + +### The `helper` class method in controllers uses `String#constantize` + +Conceptually, before Rails 6.1 + +```ruby +helper "foo/bar" +``` + +resulted in + +```ruby +require_dependency "foo/bar_helper" +module_name = "foo/bar_helper".camelize +module_name.constantize +``` + +Now it does this instead: + +```ruby +prefix = "foo/bar".camelize +"#{prefix}Helper".constantize +``` + +This change is backwards compatible for the majority of applications, in which case you do not need to do anything. + +Technically, however, controllers could configure `helpers_path` to point to a directory in `$LOAD_PATH` that was not in the autoload paths. That use case is no longer supported out of the box. If the helper module is not autoloadable, the application is responsible for loading it before calling `helper`. + +### Redirection to HTTPS from HTTP will now use the 308 HTTP status code + +The default HTTP status code used in `ActionDispatch::SSL` when redirecting non-GET/HEAD requests from HTTP to HTTPS has been changed to `308` as defined in https://tools.ietf.org/html/rfc7538. + +### Active Storage now requires Image Processing + +When processing variants in Active Storage, it's now required to have the [image_processing gem](https://github.com/janko-m/image_processing) bundled instead of directly using `mini_magick`. Image Processing is configured by default to use `mini_magick` behind the scenes, so the easiest way to upgrade is by replacing the `mini_magick` gem for the `image_processing` gem and making sure to remove the explicit usage of `combine_options` since it's no longer needed. + +For readability, you may wish to change raw `resize` calls to `image_processing` macros. For example, instead of: + +```ruby +video.preview(resize: "100x100") +video.preview(resize: "100x100>") +video.preview(resize: "100x100^") +``` + +you can respectively do: + +```ruby +video.preview(resize_to_fit: [100, 100]) +video.preview(resize_to_limit: [100, 100]) +video.preview(resize_to_fill: [100, 100]) +``` Upgrading from Rails 5.2 to Rails 6.0 ------------------------------------- @@ -88,8 +216,8 @@ gem "webpacker" ``` -```sh -bin/rails webpacker:install +```bash +$ bin/rails webpacker:install ``` ### Force SSL @@ -99,13 +227,14 @@ connections throughout your application. If you need to exempt certain endpoints from redirection, you can use `config.ssl_options` to configure that behavior. -### Purpose in signed or encrypted cookie is now embedded within cookies +### Purpose and expiry metadata is now embedded inside signed and encrypted cookies for increased security + +To improve security, Rails embeds the purpose and expiry metadata inside encrypted or signed cookies value. -To improve security, Rails embeds the purpose information in encrypted or signed cookies value. Rails can then thwart attacks that attempt to copy the signed/encrypted value of a cookie and use it as the value of another cookie. -This new embed information make those cookies incompatible with versions of Rails older than 6.0. +This new embed metadata make those cookies incompatible with versions of Rails older than 6.0. If you require your cookies to be read by Rails 5.2 and older, or you are still validating your 6.0 deploy and want to be able to rollback set @@ -136,35 +265,37 @@ If you are configuring these adapters you will need to make these changes: - ```diff - - ActionCable.WebSocket = MyWebSocket - + ActionCable.adapters.WebSocket = MyWebSocket - ``` - ```diff - - ActionCable.logger = myLogger - + ActionCable.adapters.logger = myLogger - ``` + ```diff + - ActionCable.WebSocket = MyWebSocket + + ActionCable.adapters.WebSocket = MyWebSocket + ``` + + ```diff + - ActionCable.logger = myLogger + + ActionCable.adapters.logger = myLogger + ``` - The `ActionCable.startDebugging()` and `ActionCable.stopDebugging()` methods have been removed and replaced with the property `ActionCable.logger.enabled`. If you are using these methods you will need to make these changes: - ```diff - - ActionCable.startDebugging() - + ActionCable.logger.enabled = true - ``` - ```diff - - ActionCable.stopDebugging() - + ActionCable.logger.enabled = false - ``` + ```diff + - ActionCable.startDebugging() + + ActionCable.logger.enabled = true + ``` + + ```diff + - ActionCable.stopDebugging() + + ActionCable.logger.enabled = false + ``` -### `ActionDispatch::Response#content_type` now returned Content-Type header as it is. +### `ActionDispatch::Response#content_type` now returns the Content-Type header without modification -Previously, `ActionDispatch::Response#content_type` returned value does NOT contain charset part. -This behavior changed to returned Content-Type header containing charset part as it is. +Previously, the return value of `ActionDispatch::Response#content_type` did NOT contain the charset part. +This behavior has changed to include the previously omitted charset part as well. -If you want just MIME type, please use `ActionDispatch::Response#media_type` instead. +If you want just the MIME type, please use `ActionDispatch::Response#media_type` instead. Before: @@ -188,7 +319,7 @@ ```ruby # config/application.rb -config.load_defaults "6.0" +config.load_defaults 6.0 ``` enables `zeitwerk` autoloading mode on CRuby. In that mode, autoloading, reloading, and eager loading are managed by [Zeitwerk](https://github.com/fxn/zeitwerk). @@ -213,7 +344,7 @@ Compatibility can be checked with the `zeitwerk:check` task: -``` +```bash $ bin/rails zeitwerk:check Hold on, I am eager loading the application. All is good! @@ -386,7 +517,7 @@ #### Thread-safety -In classic mode, constant autoloading is not thread-safe, though Rails has locks in place for example to make web requests thread-safe when autoloading is enabled, as it is common in `development` mode. +In classic mode, constant autoloading is not thread-safe, though Rails has locks in place for example to make web requests thread-safe when autoloading is enabled, as it is common in the development environment. Constant autoloading is thread-safe in `zeitwerk` mode. For example, you can now autoload in multi-threaded scripts executed by the `runner` command. @@ -419,7 +550,7 @@ ```ruby # config/application.rb -config.load_defaults "6.0" +config.load_defaults 6.0 config.autoloader = :classic ``` @@ -427,7 +558,7 @@ ### Active Storage assignment behavior change -In Rails 5.2, assigning to a collection of attachments declared with `has_many_attached` appended new files: +With the configuration defaults for Rails 5.2, assigning to a collection of attachments declared with `has_many_attached` appends new files: ```ruby class User < ApplicationRecord @@ -435,7 +566,7 @@ end user.highlights.attach(filename: "funky.jpg", ...) -user.higlights.count # => 1 +user.highlights.count # => 1 blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...) user.update!(highlights: [ blob ]) @@ -445,8 +576,7 @@ user.highlights.second.filename # => "town.jpg" ``` -With the default configuration for Rails 6.0, assigning to a collection of attachments replaces existing files -instead of appending to them. This matches Active Record behavior when assigning to a collection association: +With the configuration defaults for Rails 6.0, assigning to a collection of attachments replaces existing files instead of appending to them. This matches Active Record behavior when assigning to a collection association: ```ruby user.highlights.attach(filename: "funky.jpg", ...) @@ -470,8 +600,7 @@ user.highlights.second.filename # => "town.jpg" ``` -Opt in to the new default behavior by setting `config.active_storage.replace_on_assign_to_many` to `true`. -The old behavior will be deprecated in Rails 6.1 and removed in a subsequent release. +Existing applications can opt in to this new behavior by setting `config.active_storage.replace_on_assign_to_many` to `true`. The old behavior will be deprecated in Rails 6.1 and removed in a subsequent release. Upgrading from Rails 5.1 to Rails 5.2 ------------------------------------- @@ -562,7 +691,7 @@ When upgrading from Rails 4.2 to Rails 5.0, you need to create an `application_record.rb` file in `app/models/` and add the following content: -``` +```ruby class ApplicationRecord < ActiveRecord::Base self.abstract_class = true end @@ -588,7 +717,9 @@ When you are ready, you can opt into the new behavior and remove the deprecation warning by adding the following configuration to your `config/application.rb`: - ActiveSupport.halt_callback_chains_on_return_false = false +```ruby +ActiveSupport.halt_callback_chains_on_return_false = false +``` Note that this option will not affect Active Support callbacks since they never halted the chain when any value was returned. @@ -603,7 +734,7 @@ When upgrading from Rails 4.2 to Rails 5.0, you need to create an `application_job.rb` file in `app/jobs/` and add the following content: -``` +```ruby class ApplicationJob < ActiveJob::Base end ``` @@ -620,7 +751,7 @@ continue using these methods in your controller tests, add `gem 'rails-controller-testing'` to your `Gemfile`. -If you are using Rspec for testing, please see the extra configuration required in the gem's +If you are using RSpec for testing, please see the extra configuration required in the gem's documentation. #### New behavior when uploading files @@ -644,8 +775,7 @@ For the vast majority of applications this change needs no action. But in the very rare event that your application needs autoloading while running in -production mode, set `Rails.application.config.enable_dependency_loading` to -true. +production, set `Rails.application.config.enable_dependency_loading` to true. ### XML Serialization @@ -663,18 +793,16 @@ `debugger` is not supported by Ruby 2.2 which is required by Rails 5. Use `byebug` instead. -### Use `rails` for running tasks and tests +### Use `bin/rails` for running tasks and tests Rails 5 adds the ability to run tasks and tests through `bin/rails` instead of rake. Generally -these changes are in parallel with rake, but some were ported over altogether. As the `rails` -command already looks for and runs `bin/rails`, we recommend you to use the shorter `rails` -over `bin/rails. +these changes are in parallel with rake, but some were ported over altogether. -To use the new test runner simply type `rails test`. +To use the new test runner simply type `bin/rails test`. -`rake dev:cache` is now `rails dev:cache`. +`rake dev:cache` is now `bin/rails dev:cache`. -Run `rails` inside your application's directory to see the list of commands available. +Run `bin/rails` inside your application's root directory to see the list of commands available. ### `ActionController::Parameters` No Longer Inherits from `HashWithIndifferentAccess` @@ -683,7 +811,9 @@ and other methods that depend on being able to read the hash regardless of `permitted?` you will need to upgrade your application to first permit and then convert to a hash. - params.permit([:proceed_to, :return_to]).to_h +```ruby +params.permit([:proceed_to, :return_to]).to_h +``` ### `protect_from_forgery` Now Defaults to `prepend: false` @@ -754,7 +884,7 @@ to include `ActionController::Live` directly to the controller once the `StreamingSupport` is included. This means that if your application used to have its own streaming module, the following code -would break in production mode: +would break in production: ```ruby # This is a work-around for streamed controllers performing authentication with Warden/Devise. @@ -784,10 +914,32 @@ This can be turned off per-association with `optional: true`. -This default will be automatically configured in new applications. If existing application -want to add this feature it will need to be turned on in an initializer. +This default will be automatically configured in new applications. If an existing application +wants to add this feature it will need to be turned on in an initializer: - config.active_record.belongs_to_required_by_default = true +```ruby +config.active_record.belongs_to_required_by_default = true +``` + +The configuration is by default global for all your models, but you can +override it on a per model basis. This should help you migrate all your models to have their +associations required by default. + +```ruby +class Book < ApplicationRecord + # model is not yet ready to have its association required by default + + self.belongs_to_required_by_default = false + belongs_to(:author) +end + +class Car < ApplicationRecord + # model is ready to have its association required by default + + self.belongs_to_required_by_default = true + belongs_to(:pilot) +end +``` #### Per-form CSRF Tokens @@ -795,7 +947,9 @@ created by JavaScript. With this option turned on, forms in your application will each have their own CSRF token that is specific to the action and method for that form. - config.action_controller.per_form_csrf_tokens = true +```ruby +config.action_controller.per_form_csrf_tokens = true +``` #### Forgery Protection with Origin Check @@ -803,40 +957,52 @@ against the site's origin as an additional CSRF defense. Set the following in your config to true: - config.action_controller.forgery_protection_origin_check = true +```ruby +config.action_controller.forgery_protection_origin_check = true +``` #### Allow Configuration of Action Mailer Queue Name The default mailer queue name is `mailers`. This configuration option allows you to globally change the queue name. Set the following in your config: - config.action_mailer.deliver_later_queue_name = :new_queue_name +```ruby +config.action_mailer.deliver_later_queue_name = :new_queue_name +``` #### Support Fragment Caching in Action Mailer Views Set `config.action_mailer.perform_caching` in your config to determine whether your Action Mailer views should support caching. - config.action_mailer.perform_caching = true +```ruby +config.action_mailer.perform_caching = true +``` #### Configure the Output of `db:structure:dump` If you're using `schema_search_path` or other PostgreSQL extensions, you can control how the schema is dumped. Set to `:all` to generate all dumps, or to `:schema_search_path` to generate from schema search path. - config.active_record.dump_schemas = :all +```ruby +config.active_record.dump_schemas = :all +``` #### Configure SSL Options to Enable HSTS with Subdomains Set the following in your config to enable HSTS when using subdomains: - config.ssl_options = { hsts: { subdomains: true } } +```ruby +config.ssl_options = { hsts: { subdomains: true } } +``` #### Preserve Timezone of the Receiver When using Ruby 2.4, you can preserve the timezone of the receiver when calling `to_time`. - ActiveSupport.to_time_preserves_timezone = false +```ruby +ActiveSupport.to_time_preserves_timezone = false +``` ### Changes with JSON/JSONB serialization @@ -903,7 +1069,9 @@ deprecation warning by adding following configuration to your `config/application.rb`: - config.active_record.raise_in_transactional_callbacks = true +```ruby +config.active_record.raise_in_transactional_callbacks = true +``` See [#14488](https://github.com/rails/rails/pull/14488) and [#16537](https://github.com/rails/rails/pull/16537) for more details. @@ -1010,7 +1178,6 @@ The [`TagAssertions` module](https://api.rubyonrails.org/v4.1/classes/ActionDispatch/Assertions/TagAssertions.html) (containing methods such as `assert_tag`), [has been deprecated](https://github.com/rails/rails/blob/6061472b8c310158a2a2e8e9a6b81a1aef6b60fe/actionpack/lib/action_dispatch/testing/assertions/dom.rb) in favor of the `assert_select` methods from the `SelectorAssertions` module, which has been extracted into the [rails-dom-testing gem](https://github.com/rails/rails-dom-testing). - ### Masked Authenticity Tokens In order to mitigate SSL attacks, `form_authenticity_token` is now masked so that it varies with each request. Thus, tokens are validated by unmasking and then decrypting. As a result, any strategies for verifying requests from non-rails forms that relied on a static session CSRF token have to take this into account. @@ -1030,7 +1197,9 @@ mail(to: user.email, ...) end end +``` +```ruby mail = Notifier.notify(user, ...) # Notifier#notify is not yet called at this point mail = mail.deliver_now # Prints "Called" ``` @@ -1099,7 +1268,7 @@ 1. Add `gem 'spring', group: :development` to your `Gemfile`. 2. Install spring using `bundle install`. -3. Springify your binstubs with `bundle exec spring binstub --all`. +3. Generate the Spring binstub with `bundle exec spring binstub`. NOTE: User defined rake tasks will run in the `development` environment by default. If you want them to run in other environments consult the @@ -1124,10 +1293,10 @@ ``` 2. Use your existing `secret_key_base` from the `secret_token.rb` initializer to - set the SECRET_KEY_BASE environment variable for whichever users running the - Rails application in production mode. Alternatively, you can simply copy the existing + set the `SECRET_KEY_BASE` environment variable for whichever users running the + Rails application in production. Alternatively, you can simply copy the existing `secret_key_base` from the `secret_token.rb` initializer to `secrets.yml` - under the `production` section, replacing '<%= ENV["SECRET_KEY_BASE"] %>'. + under the `production` section, replacing `<%= ENV["SECRET_KEY_BASE"] %>`. 3. Remove the `secret_token.rb` initializer. @@ -1139,7 +1308,7 @@ If your test helper contains a call to `ActiveRecord::Migration.check_pending!` this can be removed. The check -is now done automatically when you `require 'rails/test_help'`, although +is now done automatically when you `require "rails/test_help"`, although leaving this line in your helper is not harmful in any way. ### Cookies serializer @@ -1216,7 +1385,7 @@ WARNING: Do not simply replace `MultiJson.dump` and `MultiJson.load` with `JSON.dump` and `JSON.load`. These JSON gem APIs are meant for serializing and -deserializing arbitrary Ruby objects and are generally [unsafe](http://www.ruby-doc.org/stdlib-2.2.2/libdoc/json/rdoc/JSON.html#method-i-load). +deserializing arbitrary Ruby objects and are generally [unsafe](https://ruby-doc.org/stdlib-2.2.2/libdoc/json/rdoc/JSON.html#method-i-load). #### JSON gem compatibility @@ -1234,9 +1403,13 @@ { foo: 'bar' } end end +``` ->> FooBar.new.to_json # => "{\"foo\":\"bar\"}" ->> JSON.generate(FooBar.new, quirks_mode: true) # => "\"#\"" +```irb +irb> FooBar.new.to_json +=> "{\"foo\":\"bar\"}" +irb> JSON.generate(FooBar.new, quirks_mode: true) +=> "\"#\"" ``` #### New JSON encoder @@ -1259,7 +1432,7 @@ now returns millisecond precision by default. If you need to keep old behavior with no millisecond precision, set the following in an initializer: -``` +```ruby ActiveSupport::JSON::Encoding.time_precision = 0 ``` @@ -1323,6 +1496,7 @@ Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path))) end end + ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers ``` @@ -1503,7 +1677,7 @@ a resource in conjunction with a custom route using the `PUT` HTTP method: ```ruby -resources :users, do +resources :users do put :update_name, on: :member end ``` @@ -1523,16 +1697,16 @@ If the action is not being used in a public API and you are free to change the HTTP method, you can update your route to use `patch` instead of `put`: -`PUT` requests to `/users/:id` in Rails 4 get routed to `update` as they are -today. So, if you have an API that gets real PUT requests it is going to work. -The router also routes `PATCH` requests to `/users/:id` to the `update` action. - ```ruby resources :users do patch :update_name, on: :member end ``` +`PUT` requests to `/users/:id` in Rails 4 get routed to `update` as they are +today. So, if you have an API that gets real PUT requests it is going to work. +The router also routes `PATCH` requests to `/users/:id` to the `update` action. + If the action is being used in a public API and you can't change to HTTP method being used, you can update your form to use the `PUT` method instead: @@ -1550,8 +1724,8 @@ such format is [JSON Patch](https://tools.ietf.org/html/rfc6902). While Rails does not support JSON Patch natively, it's easy enough to add support: -``` -# in your controller +```ruby +# in your controller: def update respond_to do |format| format.json do @@ -1564,8 +1738,10 @@ end end end +``` -# In config/initializers/json_patch.rb: +```ruby +# config/initializers/json_patch.rb Mime::Type.register 'application/json-patch+json', :json_patch ``` @@ -1610,12 +1786,12 @@ * Rails 4.0 requires that scopes use a callable object such as a Proc or lambda: -```ruby - scope :active, where(active: true) + ```ruby + scope :active, where(active: true) - # becomes - scope :active, -> { where active: true } -``` + # becomes + scope :active, -> { where active: true } + ``` * Rails 4.0 has deprecated `ActiveRecord::Fixtures` in favor of `ActiveRecord::FixtureSet`. @@ -1641,15 +1817,15 @@ * Rails 4.0 has changed to default join table for `has_and_belongs_to_many` relations to strip the common prefix off the second table name. Any existing `has_and_belongs_to_many` relationship between models with a common prefix must be specified with the `join_table` option. For example: -```ruby -CatalogCategory < ActiveRecord::Base - has_and_belongs_to_many :catalog_products, join_table: 'catalog_categories_catalog_products' -end + ```ruby + CatalogCategory < ActiveRecord::Base + has_and_belongs_to_many :catalog_products, join_table: 'catalog_categories_catalog_products' + end -CatalogProduct < ActiveRecord::Base - has_and_belongs_to_many :catalog_categories, join_table: 'catalog_categories_catalog_products' -end -``` + CatalogProduct < ActiveRecord::Base + has_and_belongs_to_many :catalog_categories, join_table: 'catalog_categories_catalog_products' + end + ``` * Note that the prefix takes scopes into account as well, so relations between `Catalog::Category` and `Catalog::Product` or `Catalog::Category` and `CatalogProduct` need to be updated similarly. @@ -1663,30 +1839,30 @@ * Rails 4.0 has changed `ActiveModel::Serializers::JSON.include_root_in_json` default value to `false`. Now, Active Model Serializers and Active Record objects have the same default behavior. This means that you can comment or remove the following option in the `config/initializers/wrap_parameters.rb` file: -```ruby -# Disable root element in JSON by default. -# ActiveSupport.on_load(:active_record) do -# self.include_root_in_json = false -# end -``` + ```ruby + # Disable root element in JSON by default. + # ActiveSupport.on_load(:active_record) do + # self.include_root_in_json = false + # end + ``` ### Action Pack -* Rails 4.0 introduces `ActiveSupport::KeyGenerator` and uses this as a base from which to generate and verify signed cookies (among other things). Existing signed cookies generated with Rails 3.x will be transparently upgraded if you leave your existing `secret_token` in place and add the new `secret_key_base`. +* Rails 4.0 introduces `ActiveSupport::KeyGenerator` and uses this as a base from which to generate and verify signed cookies (among other things). Existing signed cookies generated with Rails 3.x will be transparently upgraded if you leave your existing `secret_token` in place and add the new `secret_key_base`. -```ruby - # config/initializers/secret_token.rb - Myapp::Application.config.secret_token = 'existing secret token' - Myapp::Application.config.secret_key_base = 'new secret key base' -``` + ```ruby + # config/initializers/secret_token.rb + Myapp::Application.config.secret_token = 'existing secret token' + Myapp::Application.config.secret_key_base = 'new secret key base' + ``` -Please note that you should wait to set `secret_key_base` until you have 100% of your userbase on Rails 4.x and are reasonably sure you will not need to rollback to Rails 3.x. This is because cookies signed based on the new `secret_key_base` in Rails 4.x are not backwards compatible with Rails 3.x. You are free to leave your existing `secret_token` in place, not set the new `secret_key_base`, and ignore the deprecation warnings until you are reasonably sure that your upgrade is otherwise complete. + Please note that you should wait to set `secret_key_base` until you have 100% of your userbase on Rails 4.x and are reasonably sure you will not need to rollback to Rails 3.x. This is because cookies signed based on the new `secret_key_base` in Rails 4.x are not backwards compatible with Rails 3.x. You are free to leave your existing `secret_token` in place, not set the new `secret_key_base`, and ignore the deprecation warnings until you are reasonably sure that your upgrade is otherwise complete. -If you are relying on the ability for external applications or JavaScript to be able to read your Rails app's signed session cookies (or signed cookies in general) you should not set `secret_key_base` until you have decoupled these concerns. + If you are relying on the ability for external applications or JavaScript to be able to read your Rails app's signed session cookies (or signed cookies in general) you should not set `secret_key_base` until you have decoupled these concerns. -* Rails 4.0 encrypts the contents of cookie-based sessions if `secret_key_base` has been set. Rails 3.x signed, but did not encrypt, the contents of cookie-based session. Signed cookies are "secure" in that they are verified to have been generated by your app and are tamper-proof. However, the contents can be viewed by end users, and encrypting the contents eliminates this caveat/concern without a significant performance penalty. +* Rails 4.0 encrypts the contents of cookie-based sessions if `secret_key_base` has been set. Rails 3.x signed, but did not encrypt, the contents of cookie-based session. Signed cookies are "secure" in that they are verified to have been generated by your app and are tamper-proof. However, the contents can be viewed by end users, and encrypting the contents eliminates this caveat/concern without a significant performance penalty. -Please read [Pull Request #9978](https://github.com/rails/rails/pull/9978) for details on the move to encrypted session cookies. + Please read [Pull Request #9978](https://github.com/rails/rails/pull/9978) for details on the move to encrypted session cookies. * Rails 4.0 removed the `ActionController::Base.asset_path` option. Use the assets pipeline feature. @@ -1709,69 +1885,69 @@ * Rails 4.0 changed how `assert_generates`, `assert_recognizes`, and `assert_routing` work. Now all these assertions raise `Assertion` instead of `ActionController::RoutingError`. -* Rails 4.0 raises an `ArgumentError` if clashing named routes are defined. This can be triggered by explicitly defined named routes or by the `resources` method. Here are two examples that clash with routes named `example_path`: +* Rails 4.0 raises an `ArgumentError` if clashing named routes are defined. This can be triggered by explicitly defined named routes or by the `resources` method. Here are two examples that clash with routes named `example_path`: -```ruby - get 'one' => 'test#example', as: :example - get 'two' => 'test#example', as: :example -``` + ```ruby + get 'one' => 'test#example', as: :example + get 'two' => 'test#example', as: :example + ``` -```ruby - resources :examples - get 'clashing/:id' => 'test#example', as: :example -``` + ```ruby + resources :examples + get 'clashing/:id' => 'test#example', as: :example + ``` -In the first case, you can simply avoid using the same name for multiple -routes. In the second, you can use the `only` or `except` options provided by -the `resources` method to restrict the routes created as detailed in the -[Routing Guide](routing.html#restricting-the-routes-created). + In the first case, you can simply avoid using the same name for multiple + routes. In the second, you can use the `only` or `except` options provided by + the `resources` method to restrict the routes created as detailed in the + [Routing Guide](routing.html#restricting-the-routes-created). -* Rails 4.0 also changed the way unicode character routes are drawn. Now you can draw unicode character routes directly. If you already draw such routes, you must change them, for example: +* Rails 4.0 also changed the way unicode character routes are drawn. Now you can draw unicode character routes directly. If you already draw such routes, you must change them, for example: -```ruby -get Rack::Utils.escape('こんにちは'), controller: 'welcome', action: 'index' -``` + ```ruby + get Rack::Utils.escape('こんにちは'), controller: 'welcome', action: 'index' + ``` -becomes + becomes -```ruby -get 'こんにちは', controller: 'welcome', action: 'index' -``` + ```ruby + get 'こんにちは', controller: 'welcome', action: 'index' + ``` -* Rails 4.0 requires that routes using `match` must specify the request method. For example: +* Rails 4.0 requires that routes using `match` must specify the request method. For example: -```ruby - # Rails 3.x - match '/' => 'root#index' + ```ruby + # Rails 3.x + match '/' => 'root#index' - # becomes - match '/' => 'root#index', via: :get + # becomes + match '/' => 'root#index', via: :get - # or - get '/' => 'root#index' -``` + # or + get '/' => 'root#index' + ``` -* Rails 4.0 has removed `ActionDispatch::BestStandardsSupport` middleware, `` already triggers standards mode per https://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx and ChromeFrame header has been moved to `config.action_dispatch.default_headers`. +* Rails 4.0 has removed `ActionDispatch::BestStandardsSupport` middleware, `` already triggers standards mode per https://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx and ChromeFrame header has been moved to `config.action_dispatch.default_headers`. -Remember you must also remove any references to the middleware from your application code, for example: + Remember you must also remove any references to the middleware from your application code, for example: -```ruby -# Raise exception -config.middleware.insert_before(Rack::Lock, ActionDispatch::BestStandardsSupport) -``` + ```ruby + # Raise exception + config.middleware.insert_before(Rack::Lock, ActionDispatch::BestStandardsSupport) + ``` -Also check your environment settings for `config.action_dispatch.best_standards_support` and remove it if present. + Also check your environment settings for `config.action_dispatch.best_standards_support` and remove it if present. -* Rails 4.0 allows configuration of HTTP headers by setting `config.action_dispatch.default_headers`. The defaults are as follows: +* Rails 4.0 allows configuration of HTTP headers by setting `config.action_dispatch.default_headers`. The defaults are as follows: -```ruby - config.action_dispatch.default_headers = { - 'X-Frame-Options' => 'SAMEORIGIN', - 'X-XSS-Protection' => '1; mode=block' - } -``` + ```ruby + config.action_dispatch.default_headers = { + 'X-Frame-Options' => 'SAMEORIGIN', + 'X-XSS-Protection' => '1; mode=block' + } + ``` -Please note that if your application is dependent on loading certain pages in a `` or `