diff options
133 files changed, 987 insertions, 1497 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index 4d2bacde32..dce1a30d9f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -135,6 +135,7 @@ Style/FrozenStringLiteralComment: - 'actionpack/test/**/*.builder' - 'actionpack/test/**/*.ruby' - 'activestorage/db/migrate/**/*.rb' + - 'activestorage/db/update_migrate/**/*.rb' - 'actionmailbox/db/migrate/**/*.rb' - 'actiontext/db/migrate/**/*.rb' @@ -14,7 +14,7 @@ gem "capybara", ">= 2.15" gem "rack-cache", "~> 1.2" gem "sass-rails" gem "turbolinks", "~> 5" -gem "webpacker", github: "rails/webpacker", require: ENV["SKIP_REQUIRE_WEBPACKER"] != "true" +gem "webpacker", ">= 4.0.0.rc.3", require: ENV["SKIP_REQUIRE_WEBPACKER"] != "true" # require: false so bcrypt is loaded only when has_secure_password is used. # This is to avoid Active Model (and by extension the entire framework) # being dependent on a binary library. diff --git a/Gemfile.lock b/Gemfile.lock index 23942b11fb..95ea05881e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,15 +15,6 @@ GIT queue_classic (3.2.0.RC1) pg (>= 0.17, < 2.0) -GIT - remote: https://github.com/rails/webpacker.git - revision: bb132d591da35095e3246082cba3d693f847e0b5 - specs: - webpacker (4.0.0.pre.3) - activesupport (>= 4.2) - rack-proxy (>= 0.6.1) - railties (>= 4.2) - PATH remote: . specs: @@ -513,6 +504,10 @@ GEM addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff + webpacker (4.0.0.rc.3) + activesupport (>= 4.2) + rack-proxy (>= 0.6.1) + railties (>= 4.2) websocket (1.2.8) websocket-driver (0.7.0) websocket-extensions (>= 0.1.0) @@ -590,7 +585,7 @@ DEPENDENCIES w3c_validators wdm (>= 0.1.0) webmock - webpacker! + webpacker (>= 4.0.0.rc.3) websocket-client-simple! BUNDLED WITH diff --git a/actioncable/app/assets/javascripts/action_cable.js b/actioncable/app/assets/javascripts/action_cable.js index a68c76f299..280adbfa83 100644 --- a/actioncable/app/assets/javascripts/action_cable.js +++ b/actioncable/app/assets/javascripts/action_cable.js @@ -3,8 +3,8 @@ })(this, function(exports) { "use strict"; var adapters = { - logger: window.console, - WebSocket: window.WebSocket + logger: self.console, + WebSocket: self.WebSocket }; var logger = { log: function log() { @@ -49,7 +49,7 @@ this.startedAt = now(); delete this.stoppedAt; this.startPolling(); - document.addEventListener("visibilitychange", this.visibilityDidChange); + addEventListener("visibilitychange", this.visibilityDidChange); logger.log("ConnectionMonitor started. pollInterval = " + this.getPollInterval() + " ms"); } }; @@ -57,7 +57,7 @@ if (this.isRunning()) { this.stoppedAt = now(); this.stopPolling(); - document.removeEventListener("visibilitychange", this.visibilityDidChange); + removeEventListener("visibilitychange", this.visibilityDidChange); logger.log("ConnectionMonitor stopped"); } }; diff --git a/actioncable/app/javascript/action_cable/adapters.js b/actioncable/app/javascript/action_cable/adapters.js index 9ba6d338ee..4de8131438 100644 --- a/actioncable/app/javascript/action_cable/adapters.js +++ b/actioncable/app/javascript/action_cable/adapters.js @@ -1,4 +1,4 @@ export default { - logger: window.console, - WebSocket: window.WebSocket + logger: self.console, + WebSocket: self.WebSocket } diff --git a/actioncable/app/javascript/action_cable/connection_monitor.js b/actioncable/app/javascript/action_cable/connection_monitor.js index f0e75ae137..312a71d154 100644 --- a/actioncable/app/javascript/action_cable/connection_monitor.js +++ b/actioncable/app/javascript/action_cable/connection_monitor.js @@ -21,7 +21,7 @@ class ConnectionMonitor { this.startedAt = now() delete this.stoppedAt this.startPolling() - document.addEventListener("visibilitychange", this.visibilityDidChange) + addEventListener("visibilitychange", this.visibilityDidChange) logger.log(`ConnectionMonitor started. pollInterval = ${this.getPollInterval()} ms`) } } @@ -30,7 +30,7 @@ class ConnectionMonitor { if (this.isRunning()) { this.stoppedAt = now() this.stopPolling() - document.removeEventListener("visibilitychange", this.visibilityDidChange) + removeEventListener("visibilitychange", this.visibilityDidChange) logger.log("ConnectionMonitor stopped") } } diff --git a/actioncable/lib/rails/generators/test_unit/templates/channel_test.rb.tt b/actioncable/lib/rails/generators/test_unit/templates/channel_test.rb.tt index 301dc0b6fe..7307654611 100644 --- a/actioncable/lib/rails/generators/test_unit/templates/channel_test.rb.tt +++ b/actioncable/lib/rails/generators/test_unit/templates/channel_test.rb.tt @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require "test_helper" class <%= class_name %>ChannelTest < ActionCable::Channel::TestCase diff --git a/actioncable/test/javascript/src/unit/action_cable_test.js b/actioncable/test/javascript/src/unit/action_cable_test.js index daad900aca..83426fa32e 100644 --- a/actioncable/test/javascript/src/unit/action_cable_test.js +++ b/actioncable/test/javascript/src/unit/action_cable_test.js @@ -6,14 +6,14 @@ const {module, test} = QUnit module("ActionCable", () => { module("Adapters", () => { module("WebSocket", () => { - test("default is window.WebSocket", assert => { - assert.equal(ActionCable.adapters.WebSocket, window.WebSocket) + test("default is self.WebSocket", assert => { + assert.equal(ActionCable.adapters.WebSocket, self.WebSocket) }) }) module("logger", () => { - test("default is window.console", assert => { - assert.equal(ActionCable.adapters.logger, window.console) + test("default is self.console", assert => { + assert.equal(ActionCable.adapters.logger, self.console) }) }) }) diff --git a/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb b/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb index 3537a983ef..2cde3db8a0 100644 --- a/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb +++ b/actionmailbox/app/controllers/rails/conductor/action_mailbox/inbound_emails_controller.rb @@ -28,8 +28,7 @@ module Rails end def create_inbound_email(mail) - ActionMailbox::InboundEmail.create! raw_email: \ - { io: StringIO.new(mail.to_s), filename: "inbound.eml", content_type: "message/rfc822" } + ActionMailbox::InboundEmail.create_and_extract_message_id!(mail.to_s) end end end diff --git a/actionmailbox/app/models/action_mailbox/inbound_email/message_id.rb b/actionmailbox/app/models/action_mailbox/inbound_email/message_id.rb index 57b4a2445d..470b93ca20 100644 --- a/actionmailbox/app/models/action_mailbox/inbound_email/message_id.rb +++ b/actionmailbox/app/models/action_mailbox/inbound_email/message_id.rb @@ -9,30 +9,30 @@ module ActionMailbox::InboundEmail::MessageId extend ActiveSupport::Concern - included do - before_save :generate_missing_message_id - end - class_methods do # Create a new +InboundEmail+ from the raw +source+ of the email, which be uploaded as a 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) - create! options.merge(message_id: extract_message_id(source)) do |inbound_email| + message_checksum = Digest::SHA1.hexdigest(source) + message_id = extract_message_id(source) || generate_missing_message_id(message_checksum) + + create! options.merge(message_id: message_id, message_checksum: message_checksum) do |inbound_email| inbound_email.raw_email.attach io: StringIO.new(source), filename: "message.eml", content_type: "message/rfc822" end + rescue ActiveRecord::RecordNotUnique + nil end private def extract_message_id(source) Mail.from_source(source).message_id rescue nil end - end - private - def generate_missing_message_id - self.message_id ||= Mail::MessageIdField.new.message_id.tap do |message_id| - logger.warn "Message-ID couldn't be parsed or is missing. Generated a new Message-ID: #{message_id}" + def generate_missing_message_id(message_checksum) + Mail::MessageIdField.new("<#{message_checksum}@#{::Socket.gethostname}.mail>").message_id.tap do |message_id| + logger.warn "Message-ID couldn't be parsed or is missing. Generated a new Message-ID: #{message_id}" + end end - end + end end diff --git a/actionmailbox/db/migrate/20180917164000_create_action_mailbox_tables.rb b/actionmailbox/db/migrate/20180917164000_create_action_mailbox_tables.rb index 8cf621d7e3..89ab66c1a9 100644 --- a/actionmailbox/db/migrate/20180917164000_create_action_mailbox_tables.rb +++ b/actionmailbox/db/migrate/20180917164000_create_action_mailbox_tables.rb @@ -2,10 +2,16 @@ class CreateActionMailboxTables < ActiveRecord::Migration[6.0] def change create_table :action_mailbox_inbound_emails do |t| t.integer :status, default: 0, null: false - t.string :message_id + t.string :message_id, null: false + t.string :message_checksum, null: false - t.datetime :created_at, precision: 6, null: false - t.datetime :updated_at, precision: 6, null: false + if supports_datetime_with_precision? + t.timestamps precision: 6 + else + t.timestamps + end + + t.index [ :message_id, :message_checksum ], name: "index_action_mailbox_inbound_emails_uniqueness", unique: true end end end diff --git a/actionmailbox/test/dummy/db/migrate/20180208205311_create_action_mailbox_tables.rb b/actionmailbox/test/dummy/db/migrate/20180208205311_create_action_mailbox_tables.rb new file mode 100644 index 0000000000..89ab66c1a9 --- /dev/null +++ b/actionmailbox/test/dummy/db/migrate/20180208205311_create_action_mailbox_tables.rb @@ -0,0 +1,17 @@ +class CreateActionMailboxTables < ActiveRecord::Migration[6.0] + def change + create_table :action_mailbox_inbound_emails do |t| + t.integer :status, default: 0, null: false + t.string :message_id, null: false + t.string :message_checksum, null: false + + if supports_datetime_with_precision? + t.timestamps precision: 6 + else + t.timestamps + end + + t.index [ :message_id, :message_checksum ], name: "index_action_mailbox_inbound_emails_uniqueness", unique: true + end + end +end diff --git a/actionmailbox/test/dummy/db/migrate/20180208205311_create_action_mailroom_tables.rb b/actionmailbox/test/dummy/db/migrate/20180208205311_create_action_mailroom_tables.rb deleted file mode 100644 index 0124b4b98f..0000000000 --- a/actionmailbox/test/dummy/db/migrate/20180208205311_create_action_mailroom_tables.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateActionMailboxTables < ActiveRecord::Migration[6.0] - def change - create_table :action_mailbox_inbound_emails do |t| - t.integer :status, default: 0, null: false - t.string :message_id - - t.datetime :created_at, precision: 6 - t.datetime :updated_at, precision: 6 - end - end -end diff --git a/actionmailbox/test/dummy/db/schema.rb b/actionmailbox/test/dummy/db/schema.rb index 6cfe7de765..10d4111a89 100644 --- a/actionmailbox/test/dummy/db/schema.rb +++ b/actionmailbox/test/dummy/db/schema.rb @@ -2,11 +2,11 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). +# 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 +# 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. # # It's strongly recommended that you check this file into your version control system. @@ -14,9 +14,11 @@ ActiveRecord::Schema.define(version: 2018_02_12_164506) do create_table "action_mailbox_inbound_emails", force: :cascade do |t| t.integer "status", default: 0, null: false - t.string "message_id" - t.datetime "created_at", precision: 6 - t.datetime "updated_at", precision: 6 + t.string "message_id", null: false + t.string "message_checksum", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["message_id", "message_checksum"], name: "index_action_mailbox_inbound_emails_uniqueness", unique: true end create_table "active_storage_attachments", force: :cascade do |t| diff --git a/actionmailbox/test/unit/inbound_email_test.rb b/actionmailbox/test/unit/inbound_email_test.rb index 993423406f..76f047eb61 100644 --- a/actionmailbox/test/unit/inbound_email_test.rb +++ b/actionmailbox/test/unit/inbound_email_test.rb @@ -11,5 +11,27 @@ module ActionMailbox test "source returns the contents of the raw email" do assert_equal file_fixture("welcome.eml").read, create_inbound_email_from_fixture("welcome.eml").source end + + test "email with message id is processed only once when received multiple times" do + mail = Mail.from_source(file_fixture("welcome.eml").read) + assert mail.message_id + + inbound_email_1 = create_inbound_email_from_source(mail.to_s) + assert inbound_email_1 + + inbound_email_2 = create_inbound_email_from_source(mail.to_s) + assert_nil inbound_email_2 + end + + test "email with missing message id is processed only once when received multiple times" do + mail = Mail.from_source("Date: Fri, 28 Sep 2018 11:08:55 -0700\r\nTo: a@example.com\r\nMime-Version: 1.0\r\nContent-Type: text/plain\r\nContent-Transfer-Encoding: 7bit\r\n\r\nHello!") + assert_nil mail.message_id + + inbound_email_1 = create_inbound_email_from_source(mail.to_s) + assert inbound_email_1 + + inbound_email_2 = create_inbound_email_from_source(mail.to_s) + assert_nil inbound_email_2 + end end end diff --git a/actionmailbox/test/unit/mailbox/routing_test.rb b/actionmailbox/test/unit/mailbox/routing_test.rb index d4dad7eafb..d4ba702ac5 100644 --- a/actionmailbox/test/unit/mailbox/routing_test.rb +++ b/actionmailbox/test/unit/mailbox/routing_test.rb @@ -15,11 +15,10 @@ end class ActionMailbox::Base::RoutingTest < ActiveSupport::TestCase setup do $processed = false - @inbound_email = create_inbound_email_from_fixture("welcome.eml") end test "string routing" do - ApplicationMailbox.route @inbound_email + ApplicationMailbox.route create_inbound_email_from_fixture("welcome.eml") assert_equal "Discussion: Let's debate these attachments", $processed end diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 1457794354..94cd719ce9 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,20 @@ +* Remove deprecated `fragment_cache_key` helper in favor of `combined_fragment_cache_key`. + + *Rafael Mendonça França* + +* Remove deprecated methods in `ActionDispatch::TestResponse`. + + `#success?`, `missing?` and `error?` were deprecated in Rails 5.2 in favor of + `#successful?`, `not_found?` and `server_error?`. + + *Rafael Mendonça França* + +* Ensure external redirects are explicitly allowed + + Add `fallback_location` and `allow_other_host` options to `redirect_to`. + + *Gannon McGibbon* + * Introduce ActionDispatch::HostAuthorization This is a new middleware that guards against DNS rebinding attacks by diff --git a/actionpack/lib/abstract_controller/caching/fragments.rb b/actionpack/lib/abstract_controller/caching/fragments.rb index 95078a2a28..4e454adc5f 100644 --- a/actionpack/lib/abstract_controller/caching/fragments.rb +++ b/actionpack/lib/abstract_controller/caching/fragments.rb @@ -61,25 +61,6 @@ module AbstractController end # Given a key (as described in +expire_fragment+), returns - # a key suitable for use in reading, writing, or expiring a - # cached fragment. All keys begin with <tt>views/</tt>, - # followed by any controller-wide key prefix values, ending - # with the specified +key+ value. The key is expanded using - # ActiveSupport::Cache.expand_cache_key. - def fragment_cache_key(key) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Calling fragment_cache_key directly is deprecated and will be removed in Rails 6.0. - All fragment accessors now use the combined_fragment_cache_key method that retains the key as an array, - such that the caching stores can interrogate the parts for cache versions used in - recyclable cache keys. - MSG - - head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) } - tail = key.is_a?(Hash) ? url_for(key).split("://").last : key - ActiveSupport::Cache.expand_cache_key([*head, *tail], :views) - end - - # Given a key (as described in +expire_fragment+), returns # a key array suitable for use in reading, writing, or expiring a # cached fragment. All keys begin with <tt>:views</tt>, # followed by <tt>ENV["RAILS_CACHE_ID"]</tt> or <tt>ENV["RAILS_APP_VERSION"]</tt> if set, diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index 380f2e9591..a4861dc2c0 100644 --- a/actionpack/lib/action_controller/metal/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb @@ -44,18 +44,18 @@ module ActionController #:nodoc: end private - def redirect_to(options = {}, response_status_and_flash = {}) #:doc: + def redirect_to(options = {}, response_options_and_flash = {}) #:doc: self.class._flash_types.each do |flash_type| - if type = response_status_and_flash.delete(flash_type) + if type = response_options_and_flash.delete(flash_type) flash[flash_type] = type end end - if other_flashes = response_status_and_flash.delete(:flash) + if other_flashes = response_options_and_flash.delete(:flash) flash.update(other_flashes) end - super(options, response_status_and_flash) + super(options, response_options_and_flash) end end end diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index 26e6f72b66..205f84ae36 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -13,7 +13,7 @@ module ActionController ACTION_OPTIONS = [:only, :except, :if, :unless] URL_OPTIONS = [:protocol, :host, :domain, :subdomain, :port, :path] - REDIRECT_OPTIONS = [:status, :flash, :alert, :notice] + REDIRECT_OPTIONS = [:status, :flash, :alert, :notice, :allow_other_host] module ClassMethods # :nodoc: def force_ssl(options = {}) @@ -40,7 +40,8 @@ module ActionController protocol: "https://", host: request.host, path: request.fullpath, - status: :moved_permanently + status: :moved_permanently, + allow_other_host: true, } if host_or_options.is_a?(Hash) diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 2804a06a58..8bd003f5ed 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -55,12 +55,12 @@ module ActionController # Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function. # To terminate the execution of the function immediately after the +redirect_to+, use return. # redirect_to post_url(@post) and return - def redirect_to(options = {}, response_status = {}) + def redirect_to(options = {}, response_options = {}) raise ActionControllerError.new("Cannot redirect to nil!") unless options raise AbstractController::DoubleRenderError if response_body - self.status = _extract_redirect_to_status(options, response_status) - self.location = _compute_redirect_to_location(request, options) + self.status = _extract_redirect_to_status(options, response_options) + self.location = _compute_safe_redirect_to_location(request, options, response_options) self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>" end @@ -88,9 +88,13 @@ module ActionController # All other options that can be passed to <tt>redirect_to</tt> are accepted as # options and the behavior is identical. def redirect_back(fallback_location:, allow_other_host: true, **args) - referer = request.headers["Referer"] - redirect_to_referer = referer && (allow_other_host || _url_host_allowed?(referer)) - redirect_to redirect_to_referer ? referer : fallback_location, **args + referer = request.headers.fetch("Referer", fallback_location) + response_options = { + fallback_location: fallback_location, + allow_other_host: allow_other_host, + **args, + } + redirect_to referer, response_options end def _compute_redirect_to_location(request, options) #:nodoc: @@ -114,18 +118,35 @@ module ActionController public :_compute_redirect_to_location private - def _extract_redirect_to_status(options, response_status) + def _compute_safe_redirect_to_location(request, options, response_options) + location = _compute_redirect_to_location(request, options) + location_options = options.is_a?(Hash) ? options : {} + if response_options[:allow_other_host] || _url_host_allowed?(location, location_options) + location + else + fallback_location = response_options.fetch(:fallback_location) do + raise ArgumentError, <<~MSG.squish + Unsafe redirect #{location.inspect}, + use :fallback_location to specify a fallback + or :allow_other_host to redirect anyway. + MSG + end + _compute_redirect_to_location(request, fallback_location) + end + end + + def _extract_redirect_to_status(options, response_options) if options.is_a?(Hash) && options.key?(:status) Rack::Utils.status_code(options.delete(:status)) - elsif response_status.key?(:status) - Rack::Utils.status_code(response_status[:status]) + elsif response_options.key?(:status) + Rack::Utils.status_code(response_options[:status]) else 302 end end - def _url_host_allowed?(url) - URI(url.to_s).host == request.host + def _url_host_allowed?(url, options = {}) + URI(url.to_s).host.in?([request.host, options[:host]]) rescue ArgumentError, URI::Error false end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 94ccd48203..57921f32b7 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -26,7 +26,7 @@ module ActionController end end - # ActionController::TestCase will be deprecated and moved to a gem in Rails 5.1. + # ActionController::TestCase will be deprecated and moved to a gem in the future. # Please use ActionDispatch::IntegrationTest going forward. class TestRequest < ActionDispatch::TestRequest #:nodoc: DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 26d3fd936f..cb28baa229 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -61,10 +61,6 @@ module ActionDispatch get_header Cookies::SIGNED_COOKIE_DIGEST end - def secret_token - get_header Cookies::SECRET_TOKEN - end - def secret_key_base get_header Cookies::SECRET_KEY_BASE end @@ -181,7 +177,6 @@ module ActionDispatch USE_AUTHENTICATED_COOKIE_ENCRYPTION = "action_dispatch.use_authenticated_cookie_encryption" ENCRYPTED_COOKIE_CIPHER = "action_dispatch.encrypted_cookie_cipher" SIGNED_COOKIE_DIGEST = "action_dispatch.signed_cookie_digest" - SECRET_TOKEN = "action_dispatch.secret_token" SECRET_KEY_BASE = "action_dispatch.secret_key_base" COOKIES_SERIALIZER = "action_dispatch.cookies_serializer" COOKIES_DIGEST = "action_dispatch.cookies_digest" @@ -215,9 +210,6 @@ module ActionDispatch # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed # cookie was tampered with by the user (or a 3rd party), +nil+ will be returned. # - # If +secret_key_base+ and +secrets.secret_token+ (deprecated) are both set, - # legacy cookies signed with the old key generator will be transparently upgraded. - # # This jar requires that you set a suitable secret for the verification on your app's +secret_key_base+. # # Example: @@ -233,9 +225,6 @@ module ActionDispatch # Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read. # If the cookie was tampered with by the user (or a 3rd party), +nil+ will be returned. # - # If +secret_key_base+ and +secrets.secret_token+ (deprecated) are both set, - # legacy cookies signed with the old key generator will be transparently upgraded. - # # If +config.action_dispatch.encrypted_cookie_salt+ and +config.action_dispatch.encrypted_signed_cookie_salt+ # are both set, legacy cookies encrypted with HMAC AES-256-CBC will be transparently upgraded. # @@ -264,10 +253,6 @@ module ActionDispatch private - def upgrade_legacy_signed_cookies? - request.secret_token.present? && request.secret_key_base.present? - end - def upgrade_legacy_hmac_aes_cbc_cookies? request.secret_key_base.present? && request.encrypted_signed_cookie_salt.present? && @@ -592,10 +577,6 @@ module ActionDispatch request.cookies_rotations.signed.each do |*secrets, **options| @verifier.rotate(*secrets, serializer: SERIALIZER, **options) end - - if upgrade_legacy_signed_cookies? - @verifier.rotate request.secret_token, serializer: SERIALIZER - end end private @@ -640,10 +621,6 @@ module ActionDispatch @encryptor.rotate(secret, sign_secret, cipher: legacy_cipher, digest: digest, serializer: SERIALIZER) end - - if upgrade_legacy_signed_cookies? - @legacy_verifier = ActiveSupport::MessageVerifier.new(request.secret_token, digest: digest, serializer: SERIALIZER) - end end private @@ -652,7 +629,7 @@ module ActionDispatch @encryptor.decrypt_and_verify(encrypted_message, on_rotation: rotate, purpose: purpose) end rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature - parse_legacy_signed_message(name, encrypted_message) + nil end def commit(name, options) @@ -660,16 +637,6 @@ module ActionDispatch raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE end - - def parse_legacy_signed_message(name, legacy_signed_message) - if defined?(@legacy_verifier) - deserialize(name) do |rotate| - rotate.call - - @legacy_verifier.verified(legacy_signed_message) - end - end - end end def initialize(app) diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index df680c1c5f..02ccfbc81a 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -16,11 +16,6 @@ module ActionDispatch # The cookie jar used for storage is automatically configured to be the # best possible option given your application's configuration. # - # If you only have secret_token set, your cookies will be signed, but - # not encrypted. This means a user cannot alter their +user_id+ without - # knowing your app's secret key, but can easily read their +user_id+. This - # was the default for Rails 3 apps. - # # Your cookies will be encrypted using your apps secret_key_base. This # goes a step further than signed cookies in that encrypted cookies cannot # be altered or read by users. This is the default starting in Rails 4. diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb index 7c1202dc0e..6f7c86fdcf 100644 --- a/actionpack/lib/action_dispatch/testing/test_response.rb +++ b/actionpack/lib/action_dispatch/testing/test_response.rb @@ -14,33 +14,6 @@ module ActionDispatch new response.status, response.headers, response.body end - # Was the response successful? - def success? - ActiveSupport::Deprecation.warn(<<-MSG.squish) - The success? predicate is deprecated and will be removed in Rails 6.0. - Please use successful? as provided by Rack::Response::Helpers. - MSG - successful? - end - - # Was the URL not found? - def missing? - ActiveSupport::Deprecation.warn(<<-MSG.squish) - The missing? predicate is deprecated and will be removed in Rails 6.0. - Please use not_found? as provided by Rack::Response::Helpers. - MSG - not_found? - end - - # Was there a server-side error? - def error? - ActiveSupport::Deprecation.warn(<<-MSG.squish) - The error? predicate is deprecated and will be removed in Rails 6.0. - Please use server_error? as provided by Rack::Response::Helpers. - MSG - server_error? - end - def parsed_body @parsed_body ||= response_parser.call(body) end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index ecb8c37e6b..c7aae034dd 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -28,13 +28,13 @@ class ActionPackAssertionsController < ActionController::Base def redirect_to_path() redirect_to "/some/path" end - def redirect_invalid_external_route() redirect_to "ht_tp://www.rubyonrails.org" end + def redirect_invalid_external_route() redirect_to "ht_tp://www.rubyonrails.org", allow_other_host: true end def redirect_to_named_route() redirect_to route_one_url end - def redirect_external() redirect_to "http://www.rubyonrails.org"; end + def redirect_external() redirect_to "http://www.rubyonrails.org", allow_other_host: true; end - def redirect_external_protocol_relative() redirect_to "//www.rubyonrails.org"; end + def redirect_external_protocol_relative() redirect_to "//www.rubyonrails.org", allow_other_host: true; end def response404() head "404 AWOL" end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 6fe036dd15..5543f9120f 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -60,14 +60,6 @@ class FragmentCachingTest < ActionController::TestCase @m2v2 = ModelWithKeyAndVersion.new("model/2", "2") end - def test_fragment_cache_key - assert_deprecated do - assert_equal "views/what a key", @controller.fragment_cache_key("what a key") - assert_equal "views/test.host/fragment_caching_test/some_action", - @controller.fragment_cache_key(controller: "fragment_caching_test", action: "some_action") - end - end - def test_combined_fragment_cache_key assert_equal [ :views, "what a key" ], @controller.combined_fragment_cache_key("what a key") assert_equal [ :views, "test.host/fragment_caching_test/some_action" ], diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 409a4ec2e6..bf95c633e5 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -242,8 +242,11 @@ end class FlashIntegrationTest < ActionDispatch::IntegrationTest SessionKey = "_myapp_session" - Generator = ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") - Rotations = ActiveSupport::Messages::RotationConfiguration.new + Generator = ActiveSupport::CachingKeyGenerator.new( + ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33", iterations: 1000) + ) + Rotations = ActiveSupport::Messages::RotationConfiguration.new + SIGNED_COOKIE_SALT = "signed cookie" class TestController < ActionController::Base add_flash_types :bar @@ -365,6 +368,7 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest args[0][:env] ||= {} args[0][:env]["action_dispatch.key_generator"] ||= Generator args[0][:env]["action_dispatch.cookies_rotations"] = Rotations + args[0][:env]["action_dispatch.signed_cookie_salt"] = SIGNED_COOKIE_SALT super(path, *args) end diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index b133afb343..dd4ff85d11 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -44,7 +44,10 @@ class HttpDigestAuthenticationTest < ActionController::TestCase setup do # Used as secret in generating nonce to prevent tampering of timestamp @secret = "4fb45da9e4ab4ddeb7580d6a35503d99" - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new(@secret) + @request.env["action_dispatch.key_generator"] = ActiveSupport::CachingKeyGenerator.new( + ActiveSupport::KeyGenerator.new(@secret) + ) + @request.env["action_dispatch.http_auth_salt"] = "http authentication" end teardown do diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 0562c16284..cbebc6b59c 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -25,11 +25,11 @@ module Another end def redirector - redirect_to "http://foo.bar/" + redirect_to "http://foo.bar/", allow_other_host: true end def filterable_redirector - redirect_to "http://secret.foo.bar/" + redirect_to "http://secret.foo.bar/", allow_other_host: true end def data_sender diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 998498e1b2..945d2275c0 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -49,11 +49,11 @@ class RedirectController < ActionController::Base end def url_redirect_with_status - redirect_to("http://www.example.com", status: :moved_permanently) + redirect_to("http://www.example.com", status: :moved_permanently, allow_other_host: true) end def url_redirect_with_status_hash - redirect_to("http://www.example.com", status: 301) + redirect_to("http://www.example.com", status: 301, allow_other_host: true) end def relative_url_redirect_with_status @@ -81,19 +81,27 @@ class RedirectController < ActionController::Base end def redirect_to_url + redirect_to "http://www.rubyonrails.org/", allow_other_host: true + end + + def redirect_to_unsafe_url redirect_to "http://www.rubyonrails.org/" end + def redirect_to_relative_unsafe_url + redirect_to ".br" + end + def redirect_to_url_with_unescaped_query_string - redirect_to "http://example.com/query?status=new" + redirect_to "http://example.com/query?status=new", allow_other_host: true end def redirect_to_url_with_complex_scheme - redirect_to "x-test+scheme.complex:redirect" + redirect_to "x-test+scheme.complex:redirect", allow_other_host: true end def redirect_to_url_with_network_path_reference - redirect_to "//www.rubyonrails.org/" + redirect_to "//www.rubyonrails.org/", allow_other_host: true end def redirect_to_existing_record @@ -113,12 +121,12 @@ class RedirectController < ActionController::Base end def redirect_to_with_block - redirect_to proc { "http://www.rubyonrails.org/" } + redirect_to proc { "http://www.rubyonrails.org/" }, allow_other_host: true end def redirect_to_with_block_and_assigns @url = "http://www.rubyonrails.org/" - redirect_to proc { @url } + redirect_to proc { @url }, allow_other_host: true end def redirect_to_with_block_and_options @@ -245,6 +253,28 @@ class RedirectTest < ActionController::TestCase assert_redirected_to "http://www.rubyonrails.org/" end + def test_redirect_to_unsafe_url + error = assert_raises(ArgumentError) do + get :redirect_to_unsafe_url + end + assert_equal <<~MSG.squish, error.message + Unsafe redirect \"http://www.rubyonrails.org/\", + use :fallback_location to specify a fallback or + :allow_other_host to redirect anyway. + MSG + end + + def test_redirect_to_relative_unsafe_url + error = assert_raises(ArgumentError) do + get :redirect_to_relative_unsafe_url + end + assert_equal <<~MSG.squish, error.message + Unsafe redirect \"http://test.host.br\", + use :fallback_location to specify a fallback or + :allow_other_host to redirect anyway. + MSG + end + def test_redirect_to_url_with_unescaped_query_string get :redirect_to_url_with_unescaped_query_string assert_response :redirect diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 6637c2cae9..8f9dbaf4b3 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -525,21 +525,6 @@ class CookiesTest < ActionController::TestCase assert_equal 45, verifier.verify(@response.cookies["user_id"]) end - def test_signed_cookie_with_legacy_secret_scheme - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - old_message = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", digest: "SHA1", serializer: Marshal).generate(45) - - @request.headers["Cookie"] = "user_id=#{old_message}" - get :get_signed_cookie - assert_equal 45, @controller.send(:cookies).signed[:user_id] - - key_generator = @request.env["action_dispatch.key_generator"] - secret = key_generator.generate_key("signed cookie") - verifier = ActiveSupport::MessageVerifier.new(secret, digest: "SHA1", serializer: Marshal) - assert_equal 45, verifier.verify(@response.cookies["user_id"]) - end - def test_tampered_with_signed_cookie key_generator = @request.env["action_dispatch.key_generator"] secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) @@ -759,175 +744,7 @@ class CookiesTest < ActionController::TestCase assert_equal ["user_name", "user_id"], @request.cookie_jar.instance_variable_get(:@cookies).keys end - def test_raises_argument_error_if_missing_secret - assert_raise(ArgumentError, nil.inspect) { - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new(nil) - get :set_signed_cookie - } - - assert_raise(ArgumentError, "".inspect) { - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new("") - get :set_signed_cookie - } - end - - def test_raises_argument_error_if_secret_is_probably_insecure - assert_raise(ArgumentError, "password".inspect) { - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new("password") - get :set_signed_cookie - } - - assert_raise(ArgumentError, "secret".inspect) { - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new("secret") - get :set_signed_cookie - } - - assert_raise(ArgumentError, "12345678901234567890123456789".inspect) { - @request.env["action_dispatch.key_generator"] = ActiveSupport::LegacyKeyGenerator.new("12345678901234567890123456789") - get :set_signed_cookie - } - end - - def test_legacy_signed_cookie_is_read_and_transparently_upgraded_by_signed_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate(45) - - @request.headers["Cookie"] = "user_id=#{legacy_value}" - get :get_signed_cookie - - assert_equal 45, @controller.send(:cookies).signed[:user_id] - - key_generator = @request.env["action_dispatch.key_generator"] - secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) - verifier = ActiveSupport::MessageVerifier.new(secret) - assert_equal 45, verifier.verify(@response.cookies["user_id"]) - end - - def test_legacy_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate("bar") - - @request.headers["Cookie"] = "foo=#{legacy_value}" - get :get_encrypted_cookie - - assert_equal "bar", @controller.send(:cookies).encrypted[:foo] - - secret = @request.env["action_dispatch.key_generator"].generate_key(@request.env["action_dispatch.authenticated_encrypted_cookie_salt"], 32) - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: Marshal) - assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) - end - - def test_legacy_json_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.cookies_serializer"] = :json - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate(45) - - @request.headers["Cookie"] = "user_id=#{legacy_value}" - get :get_signed_cookie - - assert_equal 45, @controller.send(:cookies).signed[:user_id] - - key_generator = @request.env["action_dispatch.key_generator"] - secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) - verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) - assert_equal 45, verifier.verify(@response.cookies["user_id"]) - end - - def test_legacy_json_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_json_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.cookies_serializer"] = :json - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate("bar") - - @request.headers["Cookie"] = "foo=#{legacy_value}" - get :get_encrypted_cookie - - assert_equal "bar", @controller.send(:cookies).encrypted[:foo] - - cipher = "aes-256-gcm" - salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len(cipher)] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: cipher, serializer: JSON) - assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) - end - - def test_legacy_json_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_hybrid_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.cookies_serializer"] = :hybrid - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate(45) - - @request.headers["Cookie"] = "user_id=#{legacy_value}" - get :get_signed_cookie - - assert_equal 45, @controller.send(:cookies).signed[:user_id] - - key_generator = @request.env["action_dispatch.key_generator"] - secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) - verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) - assert_equal 45, verifier.verify(@response.cookies["user_id"]) - end - - def test_legacy_json_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_hybrid_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.cookies_serializer"] = :hybrid - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33", serializer: JSON).generate("bar") - - @request.headers["Cookie"] = "foo=#{legacy_value}" - get :get_encrypted_cookie - - assert_equal "bar", @controller.send(:cookies).encrypted[:foo] - - salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: JSON) - assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) - end - - def test_legacy_marshal_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_hybrid_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.cookies_serializer"] = :hybrid - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - - legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate(45) - - @request.headers["Cookie"] = "user_id=#{legacy_value}" - get :get_signed_cookie - - assert_equal 45, @controller.send(:cookies).signed[:user_id] - - key_generator = @request.env["action_dispatch.key_generator"] - secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) - verifier = ActiveSupport::MessageVerifier.new(secret, serializer: JSON) - assert_equal 45, verifier.verify(@response.cookies["user_id"]) - end - - def test_legacy_marshal_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_hybrid_cookie_jar_if_both_secret_token_and_secret_key_base_are_set - @request.env["action_dispatch.cookies_serializer"] = :hybrid - - @request.env["action_dispatch.use_authenticated_cookie_encryption"] = true - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" - - legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate("bar") - - @request.headers["Cookie"] = "foo=#{legacy_value}" - get :get_encrypted_cookie - - assert_equal "bar", @controller.send(:cookies).encrypted[:foo] - - salt = @request.env["action_dispatch.authenticated_encrypted_cookie_salt"] - secret = @request.env["action_dispatch.key_generator"].generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len("aes-256-gcm")] - encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm", serializer: JSON) - assert_equal "bar", encryptor.decrypt_and_verify(@response.cookies["foo"]) - end - def test_legacy_signed_cookie_is_treated_as_nil_by_signed_cookie_jar_if_tampered - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.headers["Cookie"] = "user_id=45" get :get_signed_cookie @@ -936,8 +753,6 @@ class CookiesTest < ActionController::TestCase end def test_legacy_signed_cookie_is_treated_as_nil_by_encrypted_cookie_jar_if_tampered - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" - @request.headers["Cookie"] = "foo=baz" get :get_encrypted_cookie diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 9d1246b3a4..2a4d59affe 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -763,7 +763,6 @@ class RequestMethod < BaseRequestTest test "post uneffected by local inflections" do existing_acronyms = ActiveSupport::Inflector.inflections.acronyms.dup - assert_deprecated { ActiveSupport::Inflector.inflections.acronym_regex.dup } begin ActiveSupport::Inflector.inflections do |inflect| inflect.acronym "POS" diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 4dffbd0db1..897d17885e 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -4991,8 +4991,12 @@ end class FlashRedirectTest < ActionDispatch::IntegrationTest SessionKey = "_myapp_session" - Generator = ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") - Rotations = ActiveSupport::Messages::RotationConfiguration.new + Generator = ActiveSupport::CachingKeyGenerator.new( + ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33", iterations: 1000) + ) + Rotations = ActiveSupport::Messages::RotationConfiguration.new + SIGNED_COOKIE_SALT = "signed cookie" + ENCRYPTED_SIGNED_COOKIE_SALT = "sigend encrypted cookie" class KeyGeneratorMiddleware def initialize(app) @@ -5002,6 +5006,8 @@ class FlashRedirectTest < ActionDispatch::IntegrationTest def call(env) env["action_dispatch.key_generator"] ||= Generator env["action_dispatch.cookies_rotations"] ||= Rotations + env["action_dispatch.signed_cookie_salt"] = SIGNED_COOKIE_SALT + env["action_dispatch.encrypted_signed_cookie_salt"] = ENCRYPTED_SIGNED_COOKIE_SALT @app.call(env) end diff --git a/actionpack/test/dispatch/test_response_test.rb b/actionpack/test/dispatch/test_response_test.rb index f0b8f7785d..2629a61057 100644 --- a/actionpack/test/dispatch/test_response_test.rb +++ b/actionpack/test/dispatch/test_response_test.rb @@ -27,11 +27,4 @@ class TestResponseTest < ActiveSupport::TestCase response = ActionDispatch::TestResponse.create(200, { "Content-Type" => "application/json" }, '{ "foo": "fighters" }') assert_equal({ "foo" => "fighters" }, response.parsed_body) end - - test "response status aliases deprecated" do - response = ActionDispatch::TestResponse.create - assert_deprecated { response.success? } - assert_deprecated { response.missing? } - assert_deprecated { response.error? } - end end diff --git a/actiontext/actiontext.gemspec b/actiontext/actiontext.gemspec index e9381d1ec5..5583178e12 100644 --- a/actiontext/actiontext.gemspec +++ b/actiontext/actiontext.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.email = ["javan@javan.us", "sstephenson@gmail.com", "david@loudthinking.com"] s.homepage = "https://rubyonrails.org" - s.files = Dir["CHANGELOG.md", "MIT-LICENSE", "README.md", "lib/**/*", "app/**/*", "config/**/*", "db/**/*"] + s.files = Dir["CHANGELOG.md", "MIT-LICENSE", "README.md", "lib/**/*", "app/**/*", "config/**/*", "db/**/*", "package.json"] s.require_path = "lib" s.metadata = { diff --git a/actiontext/app/javascript/actiontext/index.js b/actiontext/app/javascript/actiontext/index.js index c149eda952..0e9251018a 100644 --- a/actiontext/app/javascript/actiontext/index.js +++ b/actiontext/app/javascript/actiontext/index.js @@ -1,4 +1,3 @@ -import * as Trix from "trix" import { AttachmentUpload } from "./attachment_upload" addEventListener("trix-attachment-add", event => { diff --git a/actiontext/lib/templates/installer.rb b/actiontext/lib/templates/installer.rb index e7c6c2623e..990e41ca00 100644 --- a/actiontext/lib/templates/installer.rb +++ b/actiontext/lib/templates/installer.rb @@ -1,3 +1,13 @@ +require "pathname" +require "json" + +APPLICATION_PACK_PATH = Pathname.new("app/javascript/packs/application.js") +JS_PACKAGE_PATH = Pathname.new("#{__dir__}/../../package.json") + +JS_PACKAGE = JSON.load(JS_PACKAGE_PATH) +JS_DEPENDENCIES = JS_PACKAGE["peerDependencies"].dup.merge \ + JS_PACKAGE["name"] => "^#{JS_PACKAGE["version"]}" + say "Copying actiontext.scss to app/assets/stylesheets" copy_file "#{__dir__}/actiontext.scss", "app/assets/stylesheets/actiontext.scss" @@ -8,14 +18,15 @@ say "Copying blob rendering partial to app/views/active_storage/blobs/_blob.html copy_file "#{__dir__}/../../app/views/active_storage/blobs/_blob.html.erb", "app/views/active_storage/blobs/_blob.html.erb" -say "Installing JavaScript dependency" -run "yarn add @rails/actiontext" - -APPLICATION_PACK_PATH = "app/javascript/packs/application.js" +say "Installing JavaScript dependencies" +run "yarn add #{JS_DEPENDENCIES.map { |name, version| "#{name}@#{version}" }.join(" ")}" -if File.exist?(APPLICATION_PACK_PATH) && File.read(APPLICATION_PACK_PATH) !~ /import "@rails\/actiontext"/ - say "Adding import to default JavaScript pack" - append_to_file APPLICATION_PACK_PATH, <<-EOS -import "@rails/actiontext" -EOS +if APPLICATION_PACK_PATH.exist? + JS_DEPENDENCIES.keys.each do |name| + line = %[require("#{name}")] + unless APPLICATION_PACK_PATH.read.include? line + say "Adding #{name} to #{APPLICATION_PACK_PATH}" + append_to_file APPLICATION_PACK_PATH, "#{line}\n" + end + end end diff --git a/actiontext/package.json b/actiontext/package.json index ec8f35fd3c..ee4666b85c 100644 --- a/actiontext/package.json +++ b/actiontext/package.json @@ -21,7 +21,9 @@ ], "license": "MIT", "dependencies": { - "trix": "^1.0.0", "@rails/activestorage": "^6.0.0-alpha" + }, + "peerDependencies": { + "trix": "^1.0.0" } } diff --git a/actiontext/test/dummy/db/schema.rb b/actiontext/test/dummy/db/schema.rb index 39216ebd23..7f8f4dff4e 100644 --- a/actiontext/test/dummy/db/schema.rb +++ b/actiontext/test/dummy/db/schema.rb @@ -2,11 +2,11 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). +# 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 +# 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. # # It's strongly recommended that you check this file into your version control system. diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 36f10958b6..5b2ea6c556 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,7 @@ +* Remove deprecated `image_alt` helper. + + *Rafael Mendonça França* + * Fix the need of `#protect_against_forgery?` method defined in `ActionView::Base` subclasses. This prevents the use of forms and buttons. diff --git a/actionview/app/assets/javascripts/README.md b/actionview/app/assets/javascripts/README.md index 2b110e604f..b9682b61e2 100644 --- a/actionview/app/assets/javascripts/README.md +++ b/actionview/app/assets/javascripts/README.md @@ -40,8 +40,7 @@ In a conventional Rails application that uses the asset pipeline, require `rails If you're using the Webpacker gem or some other JavaScript bundler, add the following to your main JS file: ```javascript -import Rails from "@rails/ujs" -Rails.start() +require("@rails/ujs").start() ``` ## How to run tests diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index 3d7c8dae75..c186cb8422 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -355,29 +355,6 @@ module ActionView tag("img", options) end - # Returns a string suitable for an HTML image tag alt attribute. - # The +src+ argument is meant to be an image file path. - # The method removes the basename of the file path and the digest, - # if any. It also removes hyphens and underscores from file names and - # replaces them with spaces, returning a space-separated, titleized - # string. - # - # ==== Examples - # - # image_alt('rails.png') - # # => Rails - # - # image_alt('hyphenated-file-name.png') - # # => Hyphenated file name - # - # image_alt('underscored_file_name.png') - # # => Underscored file name - def image_alt(src) - ActiveSupport::Deprecation.warn("image_alt is deprecated and will be removed from Rails 6.0. You must explicitly set alt text on images.") - - File.basename(src, ".*").sub(/-[[:xdigit:]]{32,64}\z/, "").tr("-_", " ").capitalize - end - # Returns an HTML video tag for the +sources+. If +sources+ is a string, # a single video tag will be returned. If +sources+ is an array, a video # tag with nested source tags for each source will be returned. The diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index e68f03d1f4..e371a87614 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -512,26 +512,6 @@ class AssetTagHelperTest < ActionView::TestCase UrlToImageToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end - def test_image_alt - [nil, "/", "/foo/bar/", "foo/bar/"].each do |prefix| - assert_deprecated do - assert_equal "Rails", image_alt("#{prefix}rails.png") - end - assert_deprecated do - assert_equal "Rails", image_alt("#{prefix}rails-9c0a079bdd7701d7e729bd956823d153.png") - end - assert_deprecated do - assert_equal "Rails", image_alt("#{prefix}rails-f56ef62bc41b040664e801a38f068082a75d506d9048307e8096737463503d0b.png") - end - assert_deprecated do - assert_equal "Long file name with hyphens", image_alt("#{prefix}long-file-name-with-hyphens.png") - end - assert_deprecated do - assert_equal "Long file name with underscores", image_alt("#{prefix}long_file_name_with_underscores.png") - end - end - end - def test_image_tag ImageLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb index fde3381df2..ea2ed7dff7 100644 --- a/activemodel/lib/active_model/callbacks.rb +++ b/activemodel/lib/active_model/callbacks.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "active_support/core_ext/array/extract_options" +require "active_support/core_ext/hash/keys" module ActiveModel # == Active \Model \Callbacks diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index e987a0e279..b543a76639 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,10 +1,62 @@ -* Set polymorphic type column to NULL on `dependent: :nullify` strategy. - +* Remove deprecated `#set_state` from the transaction object. + + *Rafael Mendonça França* + +* Remove deprecated `#supports_statement_cache?` from the database adapters. + + *Rafael Mendonça França* + +* Remove deprecated `#insert_fixtures` from the database adapters. + + *Rafael Mendonça França* + +* Remove deprecated `ActiveRecord::ConnectionAdapters::SQLite3Adapter#valid_alter_table_type?`. + + *Rafael Mendonça França* + +* Do not allow passing the column name to `sum` when a block is passed. + + *Rafael Mendonça França* + +* Do not allow passing the column name to `count` when a block is passed. + + *Rafael Mendonça França* + +* Remove delegation of missing methods in a relation to arel. + + *Rafael Mendonça França* + +* Remove delegation of missing methods in a relation to private methods of the class. + + *Rafael Mendonça França* + +* Deprecate `config.activerecord.sqlite3.represent_boolean_as_integer`. + + *Rafael Mendonça França* + +* Change `SQLite3Adapter` to always represent boolean values as integers. + + *Rafael Mendonça França* + +* Remove ability to specify a timestamp name for `#cache_key`. + + *Rafael Mendonça França* + +* Remove deprecated `ActiveRecord::Migrator.migrations_path=`. + + *Rafael Mendonça França* + +* Remove deprecated `expand_hash_conditions_for_aggregates`. + + *Rafael Mendonça França* + +* Set polymorphic type column to NULL on `dependent: :nullify` strategy. + On polymorphic associations both the foreign key and the foreign type columns will be set to NULL. - + *Laerti Papa* -* Allow `ActionController::Params` as argument of `ActiveRecord::Base#exists?`. +* Allow permitted instance of `ActionController::Parameters` as argument of `ActiveRecord::Relation#exists?`. *Gannon McGibbon* diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 79d71bfb5d..aa2ecee74a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -173,13 +173,6 @@ module ActiveRecord exec_delete(sql, name, binds) end - # Returns +true+ when the connection adapter supports prepared statement - # caching, otherwise returns +false+ - def supports_statement_cache? # :nodoc: - true - end - deprecate :supports_statement_cache? - # Runs the given block in a database transaction, and returns the result # of the block. # @@ -336,7 +329,7 @@ module ActiveRecord # Inserts the given fixture into the table. Overridden in adapters that require # something beyond a simple insert (eg. Oracle). - # Most of adapters should implement `insert_fixtures` that leverages bulk SQL insert. + # Most of adapters should implement `insert_fixtures_set` that leverages bulk SQL insert. # We keep this method to provide fallback # for databases like sqlite that do not support bulk inserts. def insert_fixture(fixture, table_name) @@ -365,18 +358,6 @@ module ActiveRecord execute manager.to_sql, "Fixture Insert" end - # Inserts a set of fixtures into the table. Overridden in adapters that require - # something beyond a simple insert (eg. Oracle). - def insert_fixtures(fixtures, table_name) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - `insert_fixtures` is deprecated and will be removed in the next version of Rails. - Consider using `insert_fixtures_set` for performance improvement. - MSG - return if fixtures.empty? - - execute(build_fixture_sql(fixtures, table_name), "Fixtures Insert") - end - def insert_fixtures_set(fixture_set, tables_to_delete = []) fixture_inserts = fixture_set.map do |table_name, fixtures| next if fixtures.empty? diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 718910b090..112f376d0a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -40,24 +40,6 @@ module ActiveRecord committed? || rolledback? end - def set_state(state) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - The set_state method is deprecated and will be removed in - Rails 6.0. Please use rollback! or commit! to set transaction - state directly. - MSG - case state - when :rolledback - rollback! - when :committed - commit! - when nil - nullify! - else - raise ArgumentError, "Invalid transaction state: #{state}" - end - end - def rollback! @children.each { |c| c.rollback! } @state = :rolledback diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index f60d8469cc..9eaf9d9a89 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -248,10 +248,29 @@ module ActiveRecord if db_config resolve_connection(db_config.config).merge("name" => pool_name.to_s) else - raise(AdapterNotSpecified, "'#{env_name}' database is not configured. Available: #{configurations.configurations.map(&:env_name).join(", ")}") + raise AdapterNotSpecified, <<~MSG + The `#{env_name}` database is not configured for the `#{ActiveRecord::ConnectionHandling::DEFAULT_ENV.call}` environment. + + Available databases configurations are: + + #{build_configuration_sentence} + MSG end end + def build_configuration_sentence # :nodoc: + configs = configurations.configs_for(include_replicas: true) + + configs.group_by(&:env_name).map do |env, config| + namespaces = config.map(&:spec_name) + if namespaces.size > 1 + "#{env}: #{namespaces.join(", ")}" + else + env + end + end.join("\n") + end + # Accepts a hash. Expands the "url" key that contains a # URL database connection to a full connection # hash and merges with the rest of the hash. diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb index 29f0e19a98..cb9d32a577 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/quoting.rb @@ -30,19 +30,19 @@ module ActiveRecord end def quoted_true - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "1" : "'t'" + "1" end def unquoted_true - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 1 : "t" + 1 end def quoted_false - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "0" : "'f'" + "0" end def unquoted_false - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 0 : "f" + 0 end private diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 44c6e99112..14dbd20bcd 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -76,22 +76,15 @@ module ActiveRecord json: { name: "json" }, } - ## - # :singleton-method: - # Indicates whether boolean values are stored in sqlite3 databases as 1 - # and 0 or 't' and 'f'. Leaving <tt>ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer</tt> - # set to false is deprecated. SQLite databases have used 't' and 'f' to - # serialize boolean values and must have old data converted to 1 and 0 - # (its native boolean serialization) before setting this flag to true. - # Conversion can be accomplished by setting up a rake task which runs - # - # ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1) - # ExampleModel.where("boolean_column = 'f'").update_all(boolean_column: 0) - # for all models and all boolean columns, after which the flag must be set - # to true by adding the following to your <tt>application.rb</tt> file: - # - # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true - class_attribute :represent_boolean_as_integer, default: false + def self.represent_boolean_as_integer=(value) # :nodoc: + if value == false + raise "`.represent_boolean_as_integer=` is now always true, so make sure your application can work with it and remove this settings." + end + + ActiveSupport::Deprecation.warn( + "`.represent_boolean_as_integer=` is now always true, so setting this is deprecated and will be removed in Rails 6.1." + ) + end class StatementPool < ConnectionAdapters::StatementPool # :nodoc: private @@ -314,11 +307,6 @@ module ActiveRecord rename_table_indexes(table_name, new_name) end - def valid_alter_table_type?(type, options = {}) - !invalid_alter_table_type?(type, options) - end - deprecate :valid_alter_table_type? - def add_column(table_name, column_name, type, options = {}) #:nodoc: if invalid_alter_table_type?(type, options) alter_table(table_name) do |definition| @@ -390,14 +378,6 @@ module ActiveRecord end end - def insert_fixtures(rows, table_name) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - `insert_fixtures` is deprecated and will be removed in the next version of Rails. - Consider using `insert_fixtures_set` for performance improvement. - MSG - insert_fixtures_set(table_name => rows) - end - def insert_fixtures_set(fixture_set, tables_to_delete = []) disable_referential_integrity do transaction(requires_new: true) do diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb index 90fb10a1f1..fa6f0d36ec 100644 --- a/activerecord/lib/active_record/integration.rb +++ b/activerecord/lib/active_record/integration.rb @@ -61,23 +61,14 @@ module ActiveRecord # # Product.cache_versioning = false # Product.find(5).cache_key # => "products/5-20071224150000" (updated_at available) - def cache_key(*timestamp_names) + def cache_key if new_record? "#{model_name.cache_key}/new" else - if cache_version && timestamp_names.none? + if cache_version "#{model_name.cache_key}/#{id}" else - timestamp = if timestamp_names.any? - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Specifying a timestamp name for #cache_key has been deprecated in favor of - the explicit #cache_version method that can be overwritten. - MSG - - max_updated_column_timestamp(timestamp_names) - else - max_updated_column_timestamp - end + timestamp = max_updated_column_timestamp if timestamp timestamp = timestamp.utc.to_s(cache_timestamp_format) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index eca64eb380..4b2e9ed81c 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1169,13 +1169,6 @@ module ActiveRecord class << self attr_accessor :migrations_paths - def migrations_path=(path) - ActiveSupport::Deprecation.warn \ - "`ActiveRecord::Migrator.migrations_path=` is now deprecated and will be removed in Rails 6.0. " \ - "You can set the `migrations_paths` on the `connection` instead through the `database.yml`." - self.migrations_paths = [path] - end - # For cases where a table doesn't exist like loading from schema cache def current_version MigrationContext.new(migrations_paths).current_version diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 2ee6119158..6346a95d57 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -167,8 +167,18 @@ end_error initializer "active_record.set_configs" do |app| ActiveSupport.on_load(:active_record) do - configs = app.config.active_record.dup + configs = app.config.active_record + + represent_boolean_as_integer = configs.sqlite3.delete(:represent_boolean_as_integer) + + unless represent_boolean_as_integer.nil? + ActiveSupport.on_load(:active_record_sqlite3adapter) do + ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = represent_boolean_as_integer + end + end + configs.delete(:sqlite3) + configs.each do |k, v| send "#{k}=", v end @@ -236,35 +246,6 @@ end_error end end - initializer "active_record.check_represent_sqlite3_boolean_as_integer" do - config.after_initialize do - ActiveSupport.on_load(:active_record_sqlite3adapter) do - represent_boolean_as_integer = Rails.application.config.active_record.sqlite3.delete(:represent_boolean_as_integer) - unless represent_boolean_as_integer.nil? - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = represent_boolean_as_integer - end - - unless ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer - ActiveSupport::Deprecation.warn <<-MSG -Leaving `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer` -set to false is deprecated. SQLite databases have used 't' and 'f' to serialize -boolean values and must have old data converted to 1 and 0 (its native boolean -serialization) before setting this flag to true. Conversion can be accomplished -by setting up a rake task which runs - - ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1) - ExampleModel.where("boolean_column = 'f'").update_all(boolean_column: 0) - -for all models and all boolean columns, after which the flag must be set to -true by adding the following to your application.rb file: - - Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true -MSG - end - end - end - end - initializer "active_record.set_filter_attributes" do ActiveSupport.on_load(:active_record) do self.filter_attributes += Rails.application.config.filter_parameters diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index d24324ecce..8de06e8466 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -407,6 +407,10 @@ namespace :railties do if railtie.respond_to?(:paths) && (path = railtie.paths["db/migrate"].first) railties[railtie.railtie_name] = path end + + unless ENV["MIGRATIONS_PATH"].blank? + railties[railtie.railtie_name] = railtie.root + ENV["MIGRATIONS_PATH"] + end end on_skip = Proc.new do |name, migration| diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index c2c4a5a882..cef31bea94 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -41,15 +41,13 @@ module ActiveRecord def count(column_name = nil) if block_given? unless column_name.nil? - ActiveSupport::Deprecation.warn \ - "When `count' is called with a block, it ignores other arguments. " \ - "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0." + raise ArgumentError, "Column name argument is not supported when a block is passed." end - return super() + super() + else + calculate(:count, column_name) end - - calculate(:count, column_name) end # Calculates the average value on a given column. Returns +nil+ if there's @@ -86,15 +84,13 @@ module ActiveRecord def sum(column_name = nil) if block_given? unless column_name.nil? - ActiveSupport::Deprecation.warn \ - "When `sum' is called with a block, it ignores other arguments. " \ - "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0." + raise ArgumentError, "Column name argument is not supported when a block is passed." end - return super() + super() + else + calculate(:sum, column_name) end - - calculate(:sum, column_name) end # This calculates aggregate values in the given column. Methods for #count, #sum, #average, diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 6f67dd3784..6e8a1fcad4 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -112,15 +112,6 @@ module ActiveRecord if @klass.respond_to?(method) self.class.delegate_to_scoped_klass(method) scoping { @klass.public_send(method, *args, &block) } - elsif @delegate_to_klass && @klass.respond_to?(method, true) - ActiveSupport::Deprecation.warn \ - "Delegating missing #{method} method to #{@klass}. " \ - "Accessibility of private/protected class methods in :scope is deprecated and will be removed in Rails 6.0." - @klass.send(method, *args, &block) - elsif arel.respond_to?(method) - ActiveSupport::Deprecation.warn \ - "Delegating #{method} to arel is deprecated and will be removed in Rails 6.0." - arel.public_send(method, *args, &block) else super end diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index b59ff912fe..a0f6ada3ff 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -90,16 +90,20 @@ module ActiveRecord queries.reduce(&:or) elsif table.aggregated_with?(key) mapping = table.reflect_on_aggregation(key).mapping - queries = Array.wrap(value).map do |object| - mapping.map do |field_attr, aggregate_attr| - if mapping.size == 1 && !object.respond_to?(aggregate_attr) - build(table.arel_attribute(field_attr), object) - else + if mapping.length == 1 + column_name, aggr_attr = mapping.first + values = Array.wrap(value).map do |object| + object.respond_to?(aggr_attr) ? object.public_send(aggr_attr) : object + end + build(table.arel_attribute(column_name), values) + else + queries = Array.wrap(value).map do |object| + mapping.map do |field_attr, aggregate_attr| build(table.arel_attribute(field_attr), object.send(aggregate_attr)) - end - end.reduce(&:and) + end.reduce(&:and) + end + queries.reduce(&:or) end - queries.reduce(&:or) else build(table.arel_attribute(key), value) end diff --git a/activerecord/lib/active_record/relation/query_attribute.rb b/activerecord/lib/active_record/relation/query_attribute.rb index b45326bdda..5e0b4ac160 100644 --- a/activerecord/lib/active_record/relation/query_attribute.rb +++ b/activerecord/lib/active_record/relation/query_attribute.rb @@ -20,18 +20,23 @@ module ActiveRecord def nil? !value_before_type_cast.is_a?(StatementCache::Substitute) && (value_before_type_cast.nil? || value_for_database.nil?) + rescue ::RangeError end - def boundable? - return @_boundable if defined?(@_boundable) - nil? - @_boundable = true + def infinite? + infinity?(value_before_type_cast) || infinity?(value_for_database) rescue ::RangeError - @_boundable = false end - def infinite? - infinity?(value_before_type_cast) || boundable? && infinity?(value_for_database) + def unboundable? + if defined?(@_unboundable) + @_unboundable + else + value_for_database + @_unboundable = nil + end + rescue ::RangeError + @_unboundable = type.cast(value_before_type_cast) <=> 0 end private diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 3485d9e557..e6197752bc 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -134,43 +134,6 @@ module ActiveRecord end private - # Accepts a hash of SQL conditions and replaces those attributes - # that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of] - # relationship with their expanded aggregate attribute values. - # - # Given: - # - # class Person < ActiveRecord::Base - # composed_of :address, class_name: "Address", - # mapping: [%w(address_street street), %w(address_city city)] - # end - # - # Then: - # - # { address: Address.new("813 abc st.", "chicago") } - # # => { address_street: "813 abc st.", address_city: "chicago" } - def expand_hash_conditions_for_aggregates(attrs) # :doc: - expanded_attrs = {} - attrs.each do |attr, value| - if aggregation = reflect_on_aggregation(attr.to_sym) - mapping = aggregation.mapping - mapping.each do |field_attr, aggregate_attr| - expanded_attrs[field_attr] = if value.is_a?(Array) - value.map { |it| it.send(aggregate_attr) } - elsif mapping.size == 1 && !value.respond_to?(aggregate_attr) - value - else - value.send(aggregate_attr) - end - end - else - expanded_attrs[attr] = value - end - end - expanded_attrs - end - deprecate :expand_hash_conditions_for_aggregates - def replace_bind_variables(statement, values) raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size) bound = values.dup diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index e19077eb88..2345db7138 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -133,11 +133,10 @@ module ActiveRecord self.class.send(:current_time_from_proper_timezone) end - def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update_in_model) - timestamp_names - .map { |attr| self[attr] } + def max_updated_column_timestamp + timestamp_attributes_for_update_in_model + .map { |attr| self[attr]&.to_time } .compact - .map(&:to_time) .max end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 111b6c9a64..fb745af125 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -58,7 +58,7 @@ module ActiveRecord def build_relation(klass, attribute, value) relation = klass.unscoped comparison = relation.bind_attribute(attribute, value) do |attr, bind| - return relation.none! unless bind.boundable? + return relation.none! if bind.unboundable? if bind.nil? attr.eq(bind) diff --git a/activerecord/lib/arel/nodes/bind_param.rb b/activerecord/lib/arel/nodes/bind_param.rb index f145e44ae3..344e46479f 100644 --- a/activerecord/lib/arel/nodes/bind_param.rb +++ b/activerecord/lib/arel/nodes/bind_param.rb @@ -28,8 +28,8 @@ module Arel # :nodoc: all value.respond_to?(:infinite?) && value.infinite? end - def boundable? - !value.respond_to?(:boundable?) || value.boundable? + def unboundable? + value.respond_to?(:unboundable?) && value.unboundable? end end end diff --git a/activerecord/lib/arel/visitors/to_sql.rb b/activerecord/lib/arel/visitors/to_sql.rb index b5a960ce68..c08403eea9 100644 --- a/activerecord/lib/arel/visitors/to_sql.rb +++ b/activerecord/lib/arel/visitors/to_sql.rb @@ -576,7 +576,7 @@ module Arel # :nodoc: all def visit_Arel_Nodes_In(o, collector) if Array === o.right && !o.right.empty? - o.right.keep_if { |value| boundable?(value) } + o.right.delete_if { |value| unboundable?(value) } end if Array === o.right && o.right.empty? @@ -590,7 +590,7 @@ module Arel # :nodoc: all def visit_Arel_Nodes_NotIn(o, collector) if Array === o.right && !o.right.empty? - o.right.keep_if { |value| boundable?(value) } + o.right.delete_if { |value| unboundable?(value) } end if Array === o.right && o.right.empty? @@ -812,8 +812,8 @@ module Arel # :nodoc: all } end - def boundable?(value) - !value.respond_to?(:boundable?) || value.boundable? + def unboundable?(value) + value.respond_to?(:unboundable?) && value.unboundable? end def has_join_sources?(o) diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb index 42618c2ec3..b12055a40a 100644 --- a/activerecord/test/cases/adapters/postgresql/array_test.rb +++ b/activerecord/test/cases/adapters/postgresql/array_test.rb @@ -226,14 +226,6 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase assert_equal(PgArray.last.tags, tag_values) end - def test_insert_fixtures - tag_values = ["val1", "val2", "val3_with_'_multiple_quote_'_chars"] - assert_deprecated do - @connection.insert_fixtures([{ "tags" => tag_values }], "pg_arrays") - end - assert_equal(PgArray.last.tags, tag_values) - end - def test_attribute_for_inspect_for_array_field record = PgArray.new { |a| a.ratings = (1..10).to_a } assert_equal("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", record.attribute_for_inspect(:ratings)) diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb index 40b58e86bf..9d26f32102 100644 --- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb @@ -6,12 +6,8 @@ require "securerandom" class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase def setup + super @conn = ActiveRecord::Base.connection - @initial_represent_boolean_as_integer = ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer - end - - def teardown - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = @initial_represent_boolean_as_integer end def test_type_cast_binary_encoding_without_logger @@ -22,18 +18,10 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase end def test_type_cast_true - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = false - assert_equal "t", @conn.type_cast(true) - - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = true assert_equal 1, @conn.type_cast(true) end def test_type_cast_false - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = false - assert_equal "f", @conn.type_cast(false) - - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = true assert_equal 0, @conn.type_cast(false) end diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 56ceb45040..5c41c14171 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -536,10 +536,6 @@ module ActiveRecord end end - def test_deprecate_valid_alter_table_type - assert_deprecated { @conn.valid_alter_table_type?(:string) } - end - def test_db_is_not_readonly_when_readonly_option_is_false conn = Base.sqlite3_connection database: ":memory:", adapter: "sqlite3", diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb index bd5f157ca1..22a98036f3 100644 --- a/activerecord/test/cases/bind_parameter_test.rb +++ b/activerecord/test/cases/bind_parameter_test.rb @@ -77,10 +77,6 @@ if ActiveRecord::Base.connection.prepared_statements assert_logs_binds(binds) end - def test_deprecate_supports_statement_cache - assert_deprecated { ActiveRecord::Base.connection.supports_statement_cache? } - end - private def assert_logs_binds(binds) payload = { diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index ade6b9e832..7af26d8ff5 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -917,15 +917,15 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal({ "proposed" => 2, "published" => 2 }, Book.group(:status).count) end - def test_deprecate_count_with_block_and_column_name - assert_deprecated do - assert_equal 6, Account.count(:firm_id) { true } + def test_count_with_block_and_column_name_raises_an_error + assert_raises(ArgumentError) do + Account.count(:firm_id) { true } end end - def test_deprecate_sum_with_block_and_column_name - assert_deprecated do - assert_equal 6, Account.sum(:firm_id) { 1 } + def test_sum_with_block_and_column_name_raises_an_error + assert_raises(ArgumentError) do + Account.sum(:firm_id) { 1 } end end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 1c53362bac..4e8f779951 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -226,11 +226,15 @@ class FinderTest < ActiveRecord::TestCase end def test_exists_with_strong_parameters - assert_equal false, Subscriber.exists?(Parameters.new(nick: "foo")) + assert_equal false, Subscriber.exists?(Parameters.new(nick: "foo").permit!) Subscriber.create!(nick: "foo") - assert_equal true, Subscriber.exists?(Parameters.new(nick: "foo")) + assert_equal true, Subscriber.exists?(Parameters.new(nick: "foo").permit!) + + assert_raises(ActiveModel::ForbiddenAttributesError) do + Subscriber.exists?(Parameters.new(nick: "foo")) + end end def test_exists_passing_active_record_object_is_not_permitted @@ -947,6 +951,7 @@ class FinderTest < ActiveRecord::TestCase assert_kind_of Money, zaphod_balance found_customers = Customer.where(balance: [david_balance, zaphod_balance]) assert_equal [customers(:david), customers(:zaphod)], found_customers.sort_by(&:id) + assert_equal Customer.where(balance: [david_balance.amount, zaphod_balance.amount]).to_sql, found_customers.to_sql end def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_aggregate diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 32021c5ebd..2fe4879fe6 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -301,20 +301,6 @@ class FixturesTest < ActiveRecord::TestCase assert_equal fixtures, result.to_a end - def test_deprecated_insert_fixtures - fixtures = [ - { "name" => "first", "wheels_count" => 2 }, - { "name" => "second", "wheels_count" => 3 } - ] - conn = ActiveRecord::Base.connection - conn.delete("DELETE FROM aircraft") - assert_deprecated do - conn.insert_fixtures(fixtures, "aircraft") - end - result = conn.select_all("SELECT name, wheels_count FROM aircraft ORDER BY id") - assert_equal fixtures, result.to_a - end - def test_broken_yaml_exception badyaml = Tempfile.new ["foo", ".yml"] badyaml.write "a: : " diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb index 5687afbc71..4185e8d682 100644 --- a/activerecord/test/cases/integration_test.rb +++ b/activerecord/test/cases/integration_test.rb @@ -191,21 +191,6 @@ class IntegrationTest < ActiveRecord::TestCase end end - def test_named_timestamps_for_cache_key - assert_deprecated do - owner = owners(:blackbeard) - assert_equal "owners/#{owner.id}-#{owner.happy_at.utc.to_s(:usec)}", owner.cache_key(:updated_at, :happy_at) - end - end - - def test_cache_key_when_named_timestamp_is_nil - assert_deprecated do - owner = owners(:blackbeard) - owner.happy_at = nil - assert_equal "owners/#{owner.id}", owner.cache_key(:happy_at) - end - end - def test_cache_key_is_stable_with_versioning_on with_cache_versioning do developer = Developer.first diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index a38a853d4f..46e2ff79d9 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -78,16 +78,6 @@ class MigrationTest < ActiveRecord::TestCase end end - def test_migrator_migrations_path_is_deprecated - assert_deprecated do - ActiveRecord::Migrator.migrations_path = "/whatever" - end - ensure - assert_deprecated do - ActiveRecord::Migrator.migrations_path = "db/migrate" - end - end - def test_migration_version_matches_component_version assert_equal ActiveRecord::VERSION::STRING.to_f, ActiveRecord::Migration.current_version end diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb index a8030c2d64..b600c999a6 100644 --- a/activerecord/test/cases/relation/delegation_test.rb +++ b/activerecord/test/cases/relation/delegation_test.rb @@ -23,23 +23,8 @@ module ActiveRecord end end - module DeprecatedArelDelegationTests - AREL_METHODS = [ - :with, :orders, :froms, :project, :projections, :taken, :constraints, :exists, :locked, :where_sql, - :ast, :source, :join_sources, :to_dot, :create_insert, :create_true, :create_false - ] - - def test_deprecate_arel_delegation - AREL_METHODS.each do |method| - assert_deprecated { target.public_send(method) } - assert_deprecated { target.public_send(method) } - end - end - end - class DelegationAssociationTest < ActiveRecord::TestCase include ArrayDelegationTests - include DeprecatedArelDelegationTests def target Post.new.comments @@ -48,7 +33,6 @@ module ActiveRecord class DelegationRelationTest < ActiveRecord::TestCase include ArrayDelegationTests - include DeprecatedArelDelegationTests def target Comment.all diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb index 778cf86ac3..18b27bd6d1 100644 --- a/activerecord/test/cases/sanitize_test.rb +++ b/activerecord/test/cases/sanitize_test.rb @@ -168,12 +168,6 @@ class SanitizeTest < ActiveRecord::TestCase assert_equal "#{ActiveRecord::Base.connection.quote('10')}::integer '2009-01-01'::date", l.call end - def test_deprecated_expand_hash_conditions_for_aggregates - assert_deprecated do - assert_equal({ "balance" => 50 }, Customer.send(:expand_hash_conditions_for_aggregates, balance: Money.new(50))) - end - end - private def bind(statement, *vars) if vars.first.is_a?(Hash) diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index f707951a16..418a2ae04e 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -303,13 +303,6 @@ class NamedScopingTest < ActiveRecord::TestCase assert_equal "lifo", topic.author_name end - def test_deprecated_delegating_private_method - assert_deprecated do - scope = Topic.all.by_private_lifo - assert_not scope.instance_variable_get(:@delegate_to_klass) - end - end - def test_reserved_scope_names klass = Class.new(ActiveRecord::Base) do self.table_name = "topics" diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 45c93ca949..1009dd0f99 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -884,17 +884,6 @@ class TransactionTest < ActiveRecord::TestCase assert_predicate transaction.state, :committed? end - def test_set_state_method_is_deprecated - connection = Topic.connection - transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction - - transaction.commit - - assert_deprecated do - transaction.state.set_state(:rolledback) - end - end - def test_mark_transaction_state_as_committed connection = Topic.connection transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction diff --git a/activerecord/test/support/stubs/strong_parameters.rb b/activerecord/test/support/stubs/strong_parameters.rb index acba3a4504..84f93a28b9 100644 --- a/activerecord/test/support/stubs/strong_parameters.rb +++ b/activerecord/test/support/stubs/strong_parameters.rb @@ -3,10 +3,16 @@ class Parameters def initialize(parameters = {}) @parameters = parameters.with_indifferent_access + @permitted = false end def permitted? - true + @permitted + end + + def permit! + @permitted = true + self end def to_h diff --git a/activestorage/README.md b/activestorage/README.md index 4a683dd8cd..f658b8d542 100644 --- a/activestorage/README.md +++ b/activestorage/README.md @@ -118,8 +118,7 @@ Active Storage, with its included JavaScript library, supports uploading directl ``` Using the npm package: ```js - import * as ActiveStorage from "@rails/activestorage" - ActiveStorage.start() + require("@rails/activestorage").start() ``` 2. Annotate file inputs with the direct upload URL. diff --git a/activestorage/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb b/activestorage/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb new file mode 100644 index 0000000000..6830203cd6 --- /dev/null +++ b/activestorage/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb @@ -0,0 +1,7 @@ +class AddForeignKeyConstraintToActiveStorageAttachmentsForBlobId < ActiveRecord::Migration[6.0] + def up + unless foreign_key_exists?(:active_storage_attachments, column: :blob_id) + add_foreign_key :active_storage_attachments, :active_storage_blobs, column: :blob_id + end + end +end diff --git a/activestorage/lib/tasks/activestorage.rake b/activestorage/lib/tasks/activestorage.rake index ac254d717f..6b0469636c 100644 --- a/activestorage/lib/tasks/activestorage.rake +++ b/activestorage/lib/tasks/activestorage.rake @@ -12,4 +12,11 @@ namespace :active_storage do Rake::Task["app:active_storage:install:migrations"].invoke end end + + # desc "Copy over the migrations needed to the application upgrading" + task update: :environment do + ENV["MIGRATIONS_PATH"] = "db/update_migrate" + + Rake::Task["active_storage:install"].invoke + end end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index d4eaee9f6d..68f5e86552 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,3 +1,11 @@ +* Remove deprecated `Module#reachable?` method. + + *Rafael Mendonça França* + +* Remove deprecated `#acronym_regex` method from `Inflections`. + + *Rafael Mendonça França* + * Fix `String#safe_constantize` throwing a `LoadError` for incorrectly cased constant references. *Keenan Brock* diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 1cd7acb05d..d03a8d3997 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -110,12 +110,13 @@ class Date # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with # any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>. def advance(options) - options = options.dup d = self - d = d >> options.delete(:years) * 12 if options[:years] - d = d >> options.delete(:months) if options[:months] - d = d + options.delete(:weeks) * 7 if options[:weeks] - d = d + options.delete(:days) if options[:days] + + d = d >> options[:years] * 12 if options[:years] + d = d >> options[:months] if options[:months] + d = d + options[:weeks] * 7 if options[:weeks] + d = d + options[:days] if options[:days] + d end diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb index d91e3fba6a..542af98c04 100644 --- a/activesupport/lib/active_support/core_ext/module.rb +++ b/activesupport/lib/active_support/core_ext/module.rb @@ -3,7 +3,6 @@ require "active_support/core_ext/module/aliasing" require "active_support/core_ext/module/introspection" require "active_support/core_ext/module/anonymous" -require "active_support/core_ext/module/reachable" require "active_support/core_ext/module/attribute_accessors" require "active_support/core_ext/module/attribute_accessors_per_thread" require "active_support/core_ext/module/attr_internal" diff --git a/activesupport/lib/active_support/core_ext/module/reachable.rb b/activesupport/lib/active_support/core_ext/module/reachable.rb index e9cbda5245..2020f5204c 100644 --- a/activesupport/lib/active_support/core_ext/module/reachable.rb +++ b/activesupport/lib/active_support/core_ext/module/reachable.rb @@ -3,9 +3,4 @@ require "active_support/core_ext/module/anonymous" require "active_support/core_ext/string/inflections" -class Module - def reachable? #:nodoc: - !anonymous? && name.safe_constantize.equal?(self) - end - deprecate :reachable? -end +ActiveSupport::Deprecation.warn("reachable is deprecated and will be removed from the framework.") diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index fa087c4dd6..88cdd99dbd 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -65,8 +65,7 @@ module ActiveSupport @__instance__[locale] ||= new end - attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex - deprecate :acronym_regex + attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc: diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 00edcdd05a..8b61982883 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -38,36 +38,4 @@ module ActiveSupport @cache_keys[args.join] ||= @key_generator.generate_key(*args) end end - - class LegacyKeyGenerator # :nodoc: - SECRET_MIN_LENGTH = 30 # Characters - - def initialize(secret) - ensure_secret_secure(secret) - @secret = secret - end - - def generate_key(salt) - @secret - end - - private - - # To prevent users from using something insecure like "Password" we make sure that the - # secret they've provided is at least 30 characters in length. - def ensure_secret_secure(secret) - if secret.blank? - raise ArgumentError, "A secret is required to generate an integrity hash " \ - "for cookie session data. Set a secret_key_base of at least " \ - "#{SECRET_MIN_LENGTH} characters by running `rails credentials:edit`." - end - - if secret.length < SECRET_MIN_LENGTH - raise ArgumentError, "Secret should be something secure, " \ - "like \"#{SecureRandom.hex(16)}\". The value you " \ - "provided, \"#{secret}\", is shorter than the minimum length " \ - "of #{SECRET_MIN_LENGTH} characters." - end - end - end end diff --git a/activesupport/test/core_ext/module/reachable_test.rb b/activesupport/test/core_ext/module/reachable_test.rb deleted file mode 100644 index f356d46957..0000000000 --- a/activesupport/test/core_ext/module/reachable_test.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -require "abstract_unit" -require "active_support/core_ext/module/reachable" - -class AnonymousTest < ActiveSupport::TestCase - test "an anonymous class or module is not reachable" do - assert_deprecated do - assert_not_predicate Module.new, :reachable? - assert_not_predicate Class.new, :reachable? - end - end - - test "ordinary named classes or modules are reachable" do - assert_deprecated do - assert_predicate Kernel, :reachable? - assert_predicate Object, :reachable? - end - end - - test "a named class or module whose constant has gone is not reachable" do - c = eval "class C; end; C" - m = eval "module M; end; M" - - self.class.send(:remove_const, :C) - self.class.send(:remove_const, :M) - - assert_deprecated do - assert_not_predicate c, :reachable? - assert_not_predicate m, :reachable? - end - end - - test "a named class or module whose constants store different objects are not reachable" do - c = eval "class C; end; C" - m = eval "module M; end; M" - - self.class.send(:remove_const, :C) - self.class.send(:remove_const, :M) - - eval "class C; end" - eval "module M; end" - - assert_deprecated do - assert_predicate C, :reachable? - assert_predicate M, :reachable? - assert_not_predicate c, :reachable? - assert_not_predicate m, :reachable? - end - end -end diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 5e50acf5db..c3e1faff5d 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -224,12 +224,6 @@ class InflectorTest < ActiveSupport::TestCase assert_equal("json_html_api", ActiveSupport::Inflector.underscore("JSONHTMLAPI")) end - def test_acronym_regexp_is_deprecated - assert_deprecated do - ActiveSupport::Inflector.inflections.acronym_regex - end - end - def test_underscore CamelToUnderscore.each do |camel, underscore| assert_equal(underscore, ActiveSupport::Inflector.underscore(camel)) diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md index df02d5bd91..8f5c44849a 100644 --- a/guides/source/action_cable_overview.md +++ b/guides/source/action_cable_overview.md @@ -181,9 +181,9 @@ established using the following JavaScript, which is generated by default by Rai // 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. -import ActionCable from "@rails/actioncable" +import { createConsumer } from "@rails/actioncable" -export default ActionCable.createConsumer() +export default createConsumer() ``` This will ready a consumer that'll connect against `/cable` on your server by default. diff --git a/guides/source/active_storage_overview.md b/guides/source/active_storage_overview.md index 6d07d34dd7..474a93c83e 100644 --- a/guides/source/active_storage_overview.md +++ b/guides/source/active_storage_overview.md @@ -489,8 +489,7 @@ directly from the client to the cloud. Using the npm package: ```js - import * as ActiveStorage from "@rails/activestorage" - ActiveStorage.start() + require("@rails/activestorage").start() ``` 2. Annotate file inputs with the direct upload URL. diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 32682fb91f..a727dcd010 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -387,28 +387,6 @@ The PostgreSQL adapter adds one additional configuration option: highly recommended that you do not enable this in a production environment. Defaults to `false` in all environments. -The SQLite3Adapter adapter adds one additional configuration option: - -* `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer` -indicates whether boolean values are stored in sqlite3 databases as 1 and 0 or -'t' and 'f'. Leaving `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer` -set to false is deprecated. SQLite databases have used 't' and 'f' to serialize -boolean values and must have old data converted to 1 and 0 (its native boolean -serialization) before setting this flag to true. Conversion can be accomplished -by setting up a Rake task which runs - - ```ruby - ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1) - ExampleModel.where("boolean_column = 'f'").update_all(boolean_column: 0) - ``` - - for all models and all boolean columns, after which the flag must be set to true -by adding the following to your `application.rb` file: - - ```ruby - Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true - ``` - The schema dumper adds two additional configuration options: * `ActiveRecord::SchemaDumper.ignore_tables` accepts an array of tables that should _not_ be included in any generated schema file. @@ -896,7 +874,6 @@ text/javascript image/svg+xml application/postscript application/x-shockwave-fla #### With '5.2': - `config.active_record.cache_versioning`: `true` -- `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer`: `true` - `action_dispatch.use_authenticated_cookie_encryption`: `true` - `config.active_support.use_authenticated_message_encryption`: `true` - `config.active_support.use_sha1_digests`: `true` diff --git a/guides/source/security.md b/guides/source/security.md index dbec3cdd2d..a2fb4663cf 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -32,27 +32,17 @@ In order to develop secure web applications you have to keep up to date on all l Sessions -------- -A good place to start looking at security is with sessions, which can be vulnerable to particular attacks. +This chapter describes some particular attacks related to sessions, and security measures to protect your session data. ### What are Sessions? -NOTE: _HTTP is a stateless protocol. Sessions make it stateful._ +INFO: Sessions enable the application to maintain user-specific state, while users interact with the application. For example, sessions allow users to authenticate once and remain signed in for future requests. -Most applications need to keep track of certain state of a particular user. This could be the contents of a shopping basket or the user id of the currently logged in user. Without the idea of sessions, the user would have to identify, and probably authenticate, on every request. -Rails will create a new session automatically if a new user accesses the application. It will load an existing session if the user has already used the application. +Most applications need to keep track of state for users that interact with the application. This could be the contents of a shopping basket, or the user id of the currently logged in user. This kind of user-specific state can be stored in the session. -A session usually consists of a hash of values and a session ID, usually a 32-character string, to identify the hash. Every cookie sent to the client's browser includes the session ID. And the other way round: the browser will send it to the server on every request from the client. In Rails you can save and retrieve values using the session method: +Rails provides a session object for each user that accesses the application. If the user already has an active session, Rails uses the existing session. Otherwise a new session is created. -```ruby -session[:user_id] = @current_user.id -User.find(session[:user_id]) -``` - -### Session ID - -NOTE: _The session ID is a 32-character random hex string._ - -The session ID is generated using `SecureRandom.hex` which generates a random hex string using platform specific methods (such as OpenSSL, /dev/urandom or Win32 CryptoAPI) for generating cryptographically secure random numbers. Currently it is not feasible to brute-force Rails' session IDs. +NOTE: Read more about sessions and how to use them in [Action Controller Overview Guide](action_controller_overview.html#session). ### Session Hijacking @@ -76,35 +66,31 @@ Hence, the cookie serves as temporary authentication for the web application. An The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from 0.5%-10% of account balance, $0.5-$30 for credit card numbers ($20-$60 with full details), $0.1-$1.5 for identities (Name, SSN & DOB), $20-$50 for retailer accounts, and $6-$10 for cloud service provider accounts, according to the [Symantec Internet Security Threat Report (2017)](https://www.symantec.com/content/dam/symantec/docs/reports/istr-22-2017-en.pdf). -### Session Guidelines - -Here are some general guidelines on sessions. - -* _Do not store large objects in a session_. Instead you should store them in the database and save their id in the session. This will eliminate synchronization headaches and it won't fill up your session storage space (depending on what session storage you chose, see below). -This will also be a good idea, if you modify the structure of an object and old versions of it are still in some user's cookies. With server-side session storages you can clear out the sessions, but with client-side storages, this is hard to mitigate. - -* _Critical data should not be stored in session_. If the user clears their cookies or closes the browser, they will be lost. And with a client-side session storage, the user can read the data. +### Session Storage -### Encrypted Session Storage +NOTE: Rails uses `ActionDispatch::Session::CookieStore` as the default session storage. -NOTE: _Rails provides several storage mechanisms for the session hashes. The most important is `ActionDispatch::Session::CookieStore`._ +TIP: Learn more about other session storages in [Action Controller Overview Guide](action_controller_overview.html#session). -The `CookieStore` saves the session hash directly in a cookie on the -client-side. The server retrieves the session hash from the cookie and +Rails `CookieStore` saves the session hash in a cookie on the client-side. +The server retrieves the session hash from the cookie and eliminates the need for a session ID. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications and storage limitations of it: -* Cookies imply a strict size limit of 4kB. This is fine as you should - not store large amounts of data in a session anyway, as described - before. Storing the current user's database id in a session is common - practice. +* Cookies have a size limit of 4kB. Use cookies only for data which is relevant for the session. + +* Cookies are stored on the client-side. The client may preserve cookie contents even for expired cookies. The client may copy cookies to other machines. Avoid storing sensitive data in cookies. + +* Cookies are temporary by nature. The server can set expiration time for the cookie, but the client may delete the cookie and its contents before that. Persist all data that is of more permanent nature on the server side. * Session cookies do not invalidate themselves and can be maliciously reused. It may be a good idea to have your application invalidate old session cookies using a stored timestamp. +* Rails encrypts cookies by default. The client cannot read or edit the contents of the cookie, without breaking encryption. If you take appropriate care of your secrets, you can consider your cookies to be generally secured. + The `CookieStore` uses the [encrypted](http://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-encrypted) cookie jar to provide a secure, encrypted location to store session @@ -114,32 +100,9 @@ verification key used for [signed](http://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-signed) cookies, is derived from the `secret_key_base` configuration value. -As of Rails 5.2 encrypted cookies and sessions are protected using AES -GCM encryption. This form of encryption is a type of Authenticated -Encryption and couples authentication and encryption in single step -while also producing shorter ciphertexts as compared to other -algorithms previously used. The key for cookies encrypted with AES GCM -are derived using a salt value defined by the -`config.action_dispatch.authenticated_encrypted_cookie_salt` -configuration value. - -Prior to this version, encrypted cookies were secured using AES in CBC -mode with HMAC using SHA1 for authentication. The keys for this type of -encryption and for HMAC verification were derived via the salts defined -by `config.action_dispatch.encrypted_cookie_salt` and -`config.action_dispatch.encrypted_signed_cookie_salt` respectively. - -Prior to Rails version 4 in both versions 2 and 3, session cookies were -protected using only HMAC verification. As such, these session cookies -only provided integrity to their content because the actual session data -was stored in plaintext encoded as base64. This is how `signed` cookies -work in the current version of Rails. These kinds of cookies are still -useful for protecting the integrity of certain client-stored data and -information. - -__Do not use a trivial secret for the `secret_key_base`, i.e. a word -from a dictionary, or one which is shorter than 30 characters! Instead -use `rails secret` to generate secret keys!__ +TIP: Secrets must be long and random. Use `rails secret` to get new unique secrets. + +INFO: Learn more about [managing credentials later in this guide](security.html#custom-credentials) It is also important to use different salt values for encrypted and signed cookies. Using the same value for different salt configuration @@ -150,7 +113,7 @@ In test and development applications get a `secret_key_base` derived from the ap secret_key_base: 492f... -If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret. +WARNING: If your application's secrets may have been exposed, strongly consider changing them. Changing `secret_key_base` will expire currently active sessions. ### Rotating Encrypted and Signed Cookies Configurations @@ -1204,23 +1167,18 @@ loaded inline `<script>` elements. Environmental Security ---------------------- -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`, and your server-side secret, e.g. stored in `config/secrets.yml`. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information. +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 -Rails generates a `config/credentials.yml.enc` to store third-party credentials -within the repo. This is only viable because Rails encrypts the file with a master -key that's generated into a version control ignored `config/master.key` — Rails -will also look for that key in `ENV["RAILS_MASTER_KEY"]`. Rails also requires the -key to boot in production, so the credentials can be read. +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. -To edit stored credentials use `rails credentials:edit`. +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, 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. +`secret_key_base`, but it could also be used to store other credentials such as access keys for external APIs. -The credentials added to this file are accessible via `Rails.application.credentials`. +The secrets kept in credentials file are accessible via `Rails.application.credentials`. For example, with the following decrypted `config/credentials.yml.enc`: secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 @@ -1235,6 +1193,11 @@ version: Rails.application.credentials.some_api_key! # => raises KeyError: :some_api_key is blank ``` + +TIP: Learn more about credentials with `rails credentials:help`. + +WARNING: Keep your master key safe. Do not commit your master key. + Dependency Management and CVEs ------------------------------ diff --git a/guides/source/testing.md b/guides/source/testing.md index 576c4d768c..1a2f480407 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -1735,14 +1735,14 @@ Testing Action Cable -------------------- Since Action Cable is used at different levels inside your application, -you'll need to test both the channels and connection classes themsleves and that other +you'll need to test both the channels, connection classes themselves, and that other entities broadcast correct messages. ### Connection Test Case By default, when you generate new Rails application with Action Cable, a test for the base connection class (`ApplicationCable::Connection`) is generated as well under `test/channels/application_cable` directory. -Connection tests aim to check whether a connection's identifiers gets assigned properly +Connection tests aim to check whether a connection's identifiers get assigned properly or that any improper connection requests are rejected. Here is an example: ```ruby @@ -1765,9 +1765,8 @@ end You can also specify request cookies the same way you do in integration tests: - ```ruby -test "connects with_cookies" do +test "connects with cookies" do cookies.signed[:user_id] = "42" connect @@ -1778,7 +1777,6 @@ end See the API documentation for [`AcionCable::Connection::TestCase`](http://api.rubyonrails.org/classes/ActionCable/Connection/TestCase.html) for more information. - ### Channel Test Case By default, when you generate a channel, an associated test will be generated as well @@ -1823,7 +1821,7 @@ See the API documentation for [`AcionCable::Channel::TestCase`](http://api.rubyo Action Cable 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 [`ActionCable::TestHelper`](http://api.rubyonrails.org/classes/ActionCable/TestHelper.html). -It's a good practice to ensure that the correct message has been broadcasted inside another components (e.g. inside your controllers). This is precisely where +It's a good practice to ensure that the correct message has been broadcasted inside other components (e.g. inside your controllers). This is precisely where the custom assertions provided by Action Cable are pretty useful. For instance, within a model: diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index aca55fae80..bd76dc4bc8 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,43 @@ +* Remove deprecated `after_bundle` helper inside plugins templates. + + *Rafael Mendonça França* + +* Remove deprecated support to old `config.ru` that use the application class as argument of `run`. + + *Rafael Mendonça França* + +* Remove deprecated `environment` argument from the rails commands. + + *Rafael Mendonça França* + +* Remove deprecated `capify!`. + + *Rafael Mendonça França* + +* Remove deprecated `config.secret_token`. + + *Rafael Mendonça França* + +* Seed database with inline ActiveJob job adapter. + + *Gannon McGibbon* + +* Add `rails db:system:change` command for changing databases. + + ``` + bin/rails db:system:change --to=postgresql + force config/database.yml + gsub Gemfile + ``` + + The change command copies a template `config/database.yml` with the target database adapter into your app, and replaces your database gem with the target database gem. + + *Gannon McGibbon* + +* Add `rails test:channels`. + + *bogdanvlviv* + * Use original `bundler` environment variables during the process of generating a new rails project. *Marco Costa* diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index acd97b64bf..5a924ab8e6 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -172,14 +172,9 @@ module Rails def key_generator # number of iterations selected based on consultation with the google security # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220 - @caching_key_generator ||= - if secret_key_base - ActiveSupport::CachingKeyGenerator.new( - ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000) - ) - else - ActiveSupport::LegacyKeyGenerator.new(secrets.secret_token) - end + @caching_key_generator ||= ActiveSupport::CachingKeyGenerator.new( + ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000) + ) end # Returns a message verifier object. @@ -254,7 +249,6 @@ module Rails super.merge( "action_dispatch.parameter_filter" => config.filter_parameters, "action_dispatch.redirect_filter" => config.filter_redirect, - "action_dispatch.secret_token" => secrets.secret_token, "action_dispatch.secret_key_base" => secret_key_base, "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions, "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local, @@ -404,14 +398,6 @@ module Rails # Fallback to config.secret_key_base if secrets.secret_key_base isn't set secrets.secret_key_base ||= config.secret_key_base - # Fallback to config.secret_token if secrets.secret_token isn't set - secrets.secret_token ||= config.secret_token - - if secrets.secret_token.present? - ActiveSupport::Deprecation.warn( - "`secrets.secret_token` is deprecated in favor of `secret_key_base` and will be removed in Rails 6.0." - ) - end secrets end @@ -587,7 +573,7 @@ module Rails secret_key_base elsif secret_key_base raise ArgumentError, "`secret_key_base` for #{Rails.env} environment must be a type of String`" - elsif secrets.secret_token.blank? + else raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `rails credentials:edit`" end end diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index c2403c57a7..d5a66b6ec1 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -13,7 +13,7 @@ module Rails :cache_classes, :cache_store, :consider_all_requests_local, :console, :eager_load, :exceptions_app, :file_watcher, :filter_parameters, :force_ssl, :helpers_paths, :hosts, :logger, :log_formatter, :log_tags, - :railties_order, :relative_url_root, :secret_key_base, :secret_token, + :railties_order, :relative_url_root, :secret_key_base, :ssl_options, :public_file_server, :session_options, :time_zone, :reload_classes_only_on_change, :beginning_of_week, :filter_redirect, :x, :enable_dependency_loading, @@ -50,7 +50,6 @@ module Rails @autoflush_log = true @log_formatter = ActiveSupport::Logger::SimpleFormatter.new @eager_load = nil - @secret_token = nil @secret_key_base = nil @api_only = false @debug_exception_response_format = nil @@ -97,10 +96,6 @@ module Rails if respond_to?(:active_record) active_record.cache_versioning = true - # Remove the temporary load hook from SQLite3Adapter when this is removed - ActiveSupport.on_load(:active_record_sqlite3adapter) do - ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = true - end end if respond_to?(:action_dispatch) diff --git a/railties/lib/rails/command/environment_argument.rb b/railties/lib/rails/command/environment_argument.rb index 5dc98b113d..fdc5ee92d9 100644 --- a/railties/lib/rails/command/environment_argument.rb +++ b/railties/lib/rails/command/environment_argument.rb @@ -8,23 +8,13 @@ module Rails extend ActiveSupport::Concern included do - argument :environment, optional: true, banner: "environment" - class_option :environment, aliases: "-e", type: :string, desc: "Specifies the environment to run this console under (test/development/production)." end private def extract_environment_option_from_argument - if environment - self.options = options.merge(environment: acceptable_environment(environment)) - - ActiveSupport::Deprecation.warn "Passing the environment's name as a " \ - "regular argument is deprecated and " \ - "will be removed in the next Rails " \ - "version. Please, use the -e option " \ - "instead." - elsif options[:environment] + if options[:environment] self.options = options.merge(environment: acceptable_environment(options[:environment])) else self.options = options.merge(environment: Rails::Command.environment) diff --git a/railties/lib/rails/commands/db/system/change/change_command.rb b/railties/lib/rails/commands/db/system/change/change_command.rb new file mode 100644 index 0000000000..760c229c07 --- /dev/null +++ b/railties/lib/rails/commands/db/system/change/change_command.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require "rails/generators" +require "rails/generators/rails/db/system/change/change_generator" + +module Rails + module Command + module Db + module System + class ChangeCommand < Base # :nodoc: + class_option :to, desc: "The database system to switch to." + + def perform + Rails::Generators::Db::System::ChangeGenerator.start + end + end + end + end + end +end diff --git a/railties/lib/rails/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb index 6c4cc3cb86..47c3f05bb3 100644 --- a/railties/lib/rails/commands/server/server_command.rb +++ b/railties/lib/rails/commands/server/server_command.rb @@ -21,19 +21,6 @@ module Rails set_environment end - def app - @app ||= begin - app = super - if app.is_a?(Class) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Using `Rails::Application` subclass to start the server is deprecated and will be removed in Rails 6.0. - Please change `run #{app}` to `run Rails.application` in config.ru. - MSG - end - app.respond_to?(:to_app) ? app.to_app : app - end - end - def opt_parser Options.new end diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 6a13a84108..f768c30db0 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -548,7 +548,7 @@ module Rails # Blog::Engine.load_seed def load_seed seed_file = paths["db/seeds.rb"].existent.first - load(seed_file) if seed_file + with_inline_jobs { load(seed_file) } if seed_file end # Add configured load paths to Ruby's load path, and remove duplicate entries. @@ -658,6 +658,18 @@ module Rails end end + def with_inline_jobs + queue_adapter = config.active_job.queue_adapter + ActiveSupport.on_load(:active_job) do + self.queue_adapter = :inline + end + yield + ensure + ActiveSupport.on_load(:active_job) do + self.queue_adapter = queue_adapter + end + end + def has_migrations? paths["db/migrate"].existent.any? end diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index caf8a33c3c..b835b3f3fd 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -23,6 +23,8 @@ module Rails autoload :ActiveModel, "rails/generators/active_model" autoload :Base, "rails/generators/base" autoload :Migration, "rails/generators/migration" + autoload :Database, "rails/generators/database" + autoload :AppName, "rails/generators/app_name" autoload :NamedBase, "rails/generators/named_base" autoload :ResourceHelpers, "rails/generators/resource_helpers" autoload :TestCase, "rails/generators/test_case" @@ -218,6 +220,7 @@ module Rails rails.delete("encryption_key_file") rails.delete("master_key") rails.delete("credentials") + rails.delete("db:system:change") hidden_namespaces.each { |n| groups.delete(n.to_s) } diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 4646a55316..3856a74a39 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -8,7 +8,6 @@ module Rails def initialize(*) # :nodoc: super @indentation = 0 - @after_bundle_callbacks = [] end # Adds an entry into +Gemfile+ for the supplied gem. @@ -248,15 +247,6 @@ module Rails execute_command :rails, command, options end - # Just run the capify command in root - # - # capify! - def capify! - ActiveSupport::Deprecation.warn("`capify!` is deprecated and will be removed in the next version of Rails.") - log :capify, "" - in_root { run("#{extify(:capify)} .", verbose: false) } - end - # Make an entry in Rails routing file <tt>config/routes.rb</tt> # # route "root 'welcome#index'" @@ -276,16 +266,6 @@ module Rails log File.read(find_in_source_paths(path)) end - # Registers a callback to be executed after bundle and spring binstubs - # have run. - # - # after_bundle do - # git add: '.' - # end - def after_bundle(&block) - @after_bundle_callbacks << block - end - private # Define log for backwards compatibility. If just one argument is sent, diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 8df2b32dd2..0023a9a6a6 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -11,9 +11,8 @@ require "active_support/core_ext/array/extract_options" module Rails module Generators class AppBase < Base # :nodoc: - DATABASES = %w( mysql postgresql sqlite3 oracle frontbase ibm_db sqlserver ) - JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc ) - DATABASES.concat(JDBC_DATABASES) + include Database + include AppName attr_accessor :rails_template add_shebang_option! @@ -106,7 +105,6 @@ module Rails @gem_filter = lambda { |gem| true } @extra_entries = [] super - convert_database_option_for_jruby end private @@ -306,34 +304,6 @@ module Rails end end - def gem_for_database - # %w( mysql postgresql sqlite3 oracle frontbase ibm_db sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql ) - case options[:database] - when "mysql" then ["mysql2", [">= 0.4.4"]] - when "postgresql" then ["pg", [">= 0.18", "< 2.0"]] - when "oracle" then ["activerecord-oracle_enhanced-adapter", nil] - when "frontbase" then ["ruby-frontbase", nil] - when "sqlserver" then ["activerecord-sqlserver-adapter", nil] - when "jdbcmysql" then ["activerecord-jdbcmysql-adapter", nil] - when "jdbcsqlite3" then ["activerecord-jdbcsqlite3-adapter", nil] - when "jdbcpostgresql" then ["activerecord-jdbcpostgresql-adapter", nil] - when "jdbc" then ["activerecord-jdbc-adapter", nil] - else [options[:database], nil] - end - end - - def convert_database_option_for_jruby - if defined?(JRUBY_VERSION) - opt = options.dup - case opt[:database] - when "postgresql" then opt[:database] = "jdbcpostgresql" - when "mysql" then opt[:database] = "jdbcmysql" - when "sqlite3" then opt[:database] = "jdbcsqlite3" - end - self.options = opt.freeze - end - end - def assets_gemfile_entry return [] if options[:skip_sprockets] @@ -346,7 +316,7 @@ module Rails if options.dev? || options.edge? GemfileEntry.github "webpacker", "rails/webpacker", nil, "Use development version of Webpacker" else - GemfileEntry.new "webpacker", nil, "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker" + GemfileEntry.version "webpacker", ">= 4.0.0.rc.3", "Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker" end end diff --git a/railties/lib/rails/generators/app_name.rb b/railties/lib/rails/generators/app_name.rb new file mode 100644 index 0000000000..c4f71694d8 --- /dev/null +++ b/railties/lib/rails/generators/app_name.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Rails + module Generators + module AppName # :nodoc: + RESERVED_NAMES = %w(application destroy plugin runner test) + + private + def app_name + @app_name ||= original_app_name.tr("-", "_") + end + + def original_app_name + @original_app_name ||= (defined_app_const_base? ? defined_app_name : File.basename(destination_root)).tr('\\', "").tr(". ", "_") + end + + def defined_app_name + defined_app_const_base.underscore + end + + def defined_app_const_base + Rails.respond_to?(:application) && defined?(Rails::Application) && + Rails.application.is_a?(Rails::Application) && Rails.application.class.name.chomp("::Application") + end + + alias :defined_app_const_base? :defined_app_const_base + + def app_const_base + @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, "_").squeeze("_").camelize + end + alias :camelized :app_const_base + + def app_const + @app_const ||= "#{app_const_base}::Application" + end + + def valid_const? + if /^\d/.match?(app_const) + raise Error, "Invalid application name #{original_app_name}. Please give a name which does not start with numbers." + elsif RESERVED_NAMES.include?(original_app_name) + raise Error, "Invalid application name #{original_app_name}. Please give a " \ + "name which does not match one of the reserved rails " \ + "words: #{RESERVED_NAMES.join(", ")}" + elsif Object.const_defined?(app_const_base) + raise Error, "Invalid application name #{original_app_name}, constant #{app_const_base} is already in use. Please choose another application name." + end + end + end + end +end diff --git a/railties/lib/rails/generators/database.rb b/railties/lib/rails/generators/database.rb new file mode 100644 index 0000000000..be3e61bde8 --- /dev/null +++ b/railties/lib/rails/generators/database.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module Rails + module Generators + module Database # :nodoc: + JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc ) + DATABASES = %w( mysql postgresql sqlite3 oracle frontbase ibm_db sqlserver ) + JDBC_DATABASES + + def initialize(*) + super + convert_database_option_for_jruby + end + + def gem_for_database(database = options[:database]) + case database + when "mysql" then ["mysql2", [">= 0.4.4"]] + when "postgresql" then ["pg", [">= 0.18", "< 2.0"]] + when "oracle" then ["activerecord-oracle_enhanced-adapter", nil] + when "frontbase" then ["ruby-frontbase", nil] + when "sqlserver" then ["activerecord-sqlserver-adapter", nil] + when "jdbcmysql" then ["activerecord-jdbcmysql-adapter", nil] + when "jdbcsqlite3" then ["activerecord-jdbcsqlite3-adapter", nil] + when "jdbcpostgresql" then ["activerecord-jdbcpostgresql-adapter", nil] + when "jdbc" then ["activerecord-jdbc-adapter", nil] + else [database, nil] + end + end + + def convert_database_option_for_jruby + if defined?(JRUBY_VERSION) + opt = options.dup + case opt[:database] + when "postgresql" then opt[:database] = "jdbcpostgresql" + when "mysql" then opt[:database] = "jdbcmysql" + when "sqlite3" then opt[:database] = "jdbcsqlite3" + end + self.options = opt.freeze + end + end + + private + def mysql_socket + @mysql_socket ||= [ + "/tmp/mysql.sock", # default + "/var/run/mysqld/mysqld.sock", # debian/gentoo + "/var/tmp/mysql.sock", # freebsd + "/var/lib/mysql/mysql.sock", # fedora + "/opt/local/lib/mysql/mysql.sock", # fedora + "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql + "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 + "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 + "/opt/lampp/var/mysql/mysql.sock" # xampp for linux + ].find { |f| File.exist?(f) } unless Gem.win_platform? + end + end + end +end diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index e777590be8..f2f46d6e25 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -242,7 +242,6 @@ module Rails # We need to store the RAILS_DEV_PATH in a constant, otherwise the path # can change in Ruby 1.8.7 when we FileUtils.cd. RAILS_DEV_PATH = File.expand_path("../../../../../..", __dir__) - RESERVED_NAMES = %w[application destroy plugin runner test] class AppGenerator < AppBase # :nodoc: WEBPACKS = %w( react vue angular elm stimulus ) @@ -269,7 +268,7 @@ module Rails super if !options[:skip_active_record] && !DATABASES.include?(options[:database]) - raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}." + raise Error, "Invalid value for --database option. Supported preconfigurations are: #{DATABASES.join(", ")}." end # Force sprockets and yarn to be skipped when generating API only apps. @@ -277,6 +276,8 @@ module Rails if options[:api] self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze end + + @after_bundle_callbacks = [] end public_task :set_default_accessors! @@ -306,6 +307,13 @@ module Rails end remove_task :update_bin_files + def update_active_storage + unless skip_active_storage? + rails_command "active_storage:update" + end + end + remove_task :update_active_storage + def create_config_files build(:config) end @@ -491,58 +499,14 @@ module Rails create_file(*args, &block) end - def app_name - @app_name ||= original_app_name.tr("-", "_") - end - - def original_app_name - @original_app_name ||= (defined_app_const_base? ? defined_app_name : File.basename(destination_root)).tr('\\', "").tr(". ", "_") - end - - def defined_app_name - defined_app_const_base.underscore - end - - def defined_app_const_base - Rails.respond_to?(:application) && defined?(Rails::Application) && - Rails.application.is_a?(Rails::Application) && Rails.application.class.name.sub(/::Application$/, "") - end - - alias :defined_app_const_base? :defined_app_const_base - - def app_const_base - @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, "_").squeeze("_").camelize - end - alias :camelized :app_const_base - - def app_const - @app_const ||= "#{app_const_base}::Application" - end - - def valid_const? - if /^\d/.match?(app_const) - raise Error, "Invalid application name #{original_app_name}. Please give a name which does not start with numbers." - elsif RESERVED_NAMES.include?(original_app_name) - raise Error, "Invalid application name #{original_app_name}. Please give a " \ - "name which does not match one of the reserved rails " \ - "words: #{RESERVED_NAMES.join(", ")}" - elsif Object.const_defined?(app_const_base) - raise Error, "Invalid application name #{original_app_name}, constant #{app_const_base} is already in use. Please choose another application name." - end - end - - def mysql_socket - @mysql_socket ||= [ - "/tmp/mysql.sock", # default - "/var/run/mysqld/mysqld.sock", # debian/gentoo - "/var/tmp/mysql.sock", # freebsd - "/var/lib/mysql/mysql.sock", # fedora - "/opt/local/lib/mysql/mysql.sock", # fedora - "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql - "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 - "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 - "/opt/lampp/var/mysql/mysql.sock" # xampp for linux - ].find { |f| File.exist?(f) } unless Gem.win_platform? + # Registers a callback to be executed after bundle and spring binstubs + # have run. + # + # after_bundle do + # git add: '.' + # end + def after_bundle(&block) # :doc: + @after_bundle_callbacks << block end def get_builder_class diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt index 1ad3a4b1f7..d39b5d311f 100644 --- a/railties/lib/rails/generators/rails/app/templates/Gemfile.tt +++ b/railties/lib/rails/generators/rails/app/templates/Gemfile.tt @@ -18,11 +18,11 @@ ruby <%= "'#{RUBY_VERSION}'" -%> <% end -%> <% end -%> -# Use ActiveModel has_secure_password +# Use Active Model has_secure_password # gem 'bcrypt', '~> 3.1.7' <% unless skip_active_storage? -%> -# Use ActiveStorage variant +# Use Active Storage variant # gem 'image_processing', '~> 1.2' <% end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js b/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js index eec7e54b8a..0eceb59b18 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js +++ b/railties/lib/rails/generators/rails/app/templates/app/javascript/channels/consumer.js @@ -1,6 +1,6 @@ // 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. -import ActionCable from "@rails/actioncable" +import { createConsumer } from "@rails/actioncable" -export default ActionCable.createConsumer() +export default createConsumer() diff --git a/railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt index de91713546..908487d500 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/javascript/packs/application.js.tt @@ -3,19 +3,13 @@ // a relevant structure within app/javascript and only use these pack files to reference // that code so it'll be compiled. -import Rails from "@rails/ujs" -Rails.start() +require("@rails/ujs").start() <%- unless options[:skip_turbolinks] -%> - -import Turbolinks from "turbolinks" -Turbolinks.start() +require("turbolinks").start() <%- end -%> <%- unless skip_active_storage? -%> - -import * as ActiveStorage from "@rails/activestorage" -ActiveStorage.start() +require("@rails/activestorage").start() <%- end -%> <%- unless options[:skip_action_cable] -%> - -import "channels" +require("channels") <%- end -%> diff --git a/railties/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt b/railties/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt index cc8337fc6d..800405f15e 100644 --- a/railties/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/test/channels/application_cable/connection_test.rb.tt @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require "test_helper" class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase diff --git a/railties/lib/rails/generators/rails/db/system/change/change_generator.rb b/railties/lib/rails/generators/rails/db/system/change/change_generator.rb new file mode 100644 index 0000000000..63849eb18d --- /dev/null +++ b/railties/lib/rails/generators/rails/db/system/change/change_generator.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "rails/generators/base" + +module Rails + module Generators + module Db + module System + class ChangeGenerator < Base # :nodoc: + include Database + include AppName + + class_option :to, required: true, + desc: "The database system to switch to." + + def self.default_generator_root + path = File.expand_path(File.join(base_name, "app"), base_root) + path if File.exist?(path) + end + + def initialize(*) + super + + unless DATABASES.include?(options[:to]) + raise Error, "Invalid value for --to option. Supported preconfigurations are: #{DATABASES.join(", ")}." + end + + opt = options.dup + opt[:database] ||= opt[:to] + self.options = opt.freeze + end + + def edit_database_config + template("config/databases/#{options[:database]}.yml", "config/database.yml") + end + + def edit_gemfile + database_gem_name, _ = gem_for_database + gsub_file("Gemfile", all_database_gems_regex, database_gem_name) + end + + private + def all_database_gems + DATABASES.map { |database| gem_for_database(database) } + end + + def all_database_gems_regex + all_database_gem_names = all_database_gems.map(&:first) + /(\b#{all_database_gem_names.join('\b|\b')}\b)/ + end + end + end + end + end +end diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index 294c8a2609..79a06648b5 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -263,16 +263,6 @@ task default: :test public_task :apply_rails_template - def run_after_bundle_callbacks - unless @after_bundle_callbacks.empty? - ActiveSupport::Deprecation.warn("`after_bundle` is deprecated and will be removed in the next version of Rails. ") - end - - @after_bundle_callbacks.each do |callback| - callback.call - end - end - def name @name ||= begin # same as ActiveSupport::Inflector#underscore except not replacing '-' diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index 1a3711c446..2886986865 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -2,7 +2,7 @@ namespace :app do desc "Update configs and some other initially generated files (or use just update:configs or update:bin)" - task update: [ "update:configs", "update:bin", "update:upgrade_guide_info" ] + task update: [ "update:configs", "update:bin", "update:active_storage", "update:upgrade_guide_info" ] desc "Applies the template supplied by LOCATION=(/path/to/template) or URL" task template: :environment do @@ -51,6 +51,10 @@ namespace :app do Rails::AppUpdater.invoke_from_app_generator :update_bin_files end + task :active_storage do + Rails::AppUpdater.invoke_from_app_generator :update_active_storage + end + task :upgrade_guide_info do Rails::AppUpdater.invoke_from_app_generator :display_upgrade_guide_info end diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake index ecc458b21e..3a1b62d9d1 100644 --- a/railties/lib/rails/test_unit/testing.rake +++ b/railties/lib/rails/test_unit/testing.rake @@ -28,7 +28,7 @@ namespace :test do desc "Run tests quickly, but also reset db" task db: %w[db:test:prepare test] - ["models", "helpers", "controllers", "mailers", "integration", "jobs", "mailboxes"].each do |name| + ["models", "helpers", "channels", "controllers", "mailers", "integration", "jobs", "mailboxes"].each do |name| task name => "test:prepare" do $: << "test" Rails::TestUnit::Runner.rake_run(["test/#{name}"]) diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index f2d64ce80b..3e979ea20d 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -596,45 +596,6 @@ module ApplicationTests assert_equal "some_value", verifier.verify(message) end - test "application message verifier can be used when the key_generator is ActiveSupport::LegacyKeyGenerator" do - app_file "config/initializers/secret_token.rb", <<-RUBY - Rails.application.credentials.secret_key_base = nil - Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33" - RUBY - - app "production" - - assert_kind_of ActiveSupport::LegacyKeyGenerator, Rails.application.key_generator - message = app.message_verifier(:sensitive_value).generate("some_value") - assert_equal "some_value", Rails.application.message_verifier(:sensitive_value).verify(message) - end - - test "config.secret_token is deprecated" do - app_file "config/initializers/secret_token.rb", <<-RUBY - Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33" - RUBY - - app "production" - - assert_deprecated(/secret_token/) do - app.secrets - end - end - - test "secrets.secret_token is deprecated" do - app_file "config/secrets.yml", <<-YAML - production: - secret_token: "b3c631c314c0bbca50c1b2843150fe33" - YAML - - app "production" - - assert_deprecated(/secret_token/) do - app.secrets - end - end - - test "raises when secret_key_base is blank" do app_file "config/initializers/secret_token.rb", <<-RUBY Rails.application.credentials.secret_key_base = nil @@ -656,20 +617,6 @@ module ApplicationTests end end - test "prefer secrets.secret_token over config.secret_token" do - app_file "config/initializers/secret_token.rb", <<-RUBY - Rails.application.config.secret_token = "" - RUBY - app_file "config/secrets.yml", <<-YAML - development: - secret_token: 3b7cd727ee24e8444053437c36cc66c3 - YAML - - app "development" - - assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secrets.secret_token - end - test "application verifier can build different verifiers" do make_basic_app do |application| application.credentials.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" @@ -711,22 +658,6 @@ module ApplicationTests assert_equal "3b7cd727ee24e8444053437c36cc66c3", app.secrets.secret_key_base end - test "config.secret_token over-writes a blank secrets.secret_token" do - app_file "config/initializers/secret_token.rb", <<-RUBY - Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33" - RUBY - app_file "config/secrets.yml", <<-YAML - development: - secret_key_base: - secret_token: - YAML - - app "development" - - assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.secrets.secret_token - assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.config.secret_token - end - test "custom secrets saved in config/secrets.yml are loaded in app secrets" do app_file "config/secrets.yml", <<-YAML development: @@ -789,19 +720,6 @@ module ApplicationTests assert_equal "iaminallyoursecretkeybase", app.secrets.secret_key_base end - test "uses ActiveSupport::LegacyKeyGenerator as app.key_generator when secrets.secret_key_base is blank" do - app_file "config/initializers/secret_token.rb", <<-RUBY - Rails.application.credentials.secret_key_base = nil - Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33" - RUBY - - app "production" - - assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.config.secret_token - assert_nil app.credentials.secret_key_base - assert_kind_of ActiveSupport::LegacyKeyGenerator, app.key_generator - end - test "that nested keys are symbolized the same as parents for hashes more than one level deep" do app_file "config/secrets.yml", <<-YAML development: @@ -1941,37 +1859,29 @@ module ApplicationTests assert_equal({}, Rails.application.config.my_custom_config) end - test "default SQLite3Adapter.represent_boolean_as_integer for 5.1 is false" do + test "represent_boolean_as_integer is deprecated" do remove_from_config '.*config\.load_defaults.*\n' - app_file "app/models/post.rb", <<-RUBY - class Post < ActiveRecord::Base - end + app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY + Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true RUBY - app "development" - force_lazy_load_hooks { Post } - - assert_not ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer - end - - test "default SQLite3Adapter.represent_boolean_as_integer for new installs is true" do app_file "app/models/post.rb", <<-RUBY class Post < ActiveRecord::Base end RUBY app "development" - force_lazy_load_hooks { Post } - - assert ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer + assert_deprecated do + force_lazy_load_hooks { Post } + end end - test "represent_boolean_as_integer should be able to set via config.active_record.sqlite3.represent_boolean_as_integer" do + test "represent_boolean_as_integer raises when the value is false" do remove_from_config '.*config\.load_defaults.*\n' app_file "config/initializers/new_framework_defaults_6_0.rb", <<-RUBY - Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true + Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = false RUBY app_file "app/models/post.rb", <<-RUBY @@ -1980,9 +1890,9 @@ module ApplicationTests RUBY app "development" - force_lazy_load_hooks { Post } - - assert ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer + assert_raises(RuntimeError) do + force_lazy_load_hooks { Post } + end end test "config_for containing ERB tags should evaluate" do diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb index e74daccbe7..b6270525f0 100644 --- a/railties/test/application/console_test.rb +++ b/railties/test/application/console_test.rb @@ -149,7 +149,7 @@ class FullStackConsoleTest < ActiveSupport::TestCase end def test_environment_option_and_irb_option - spawn_console("test -- --verbose") + spawn_console("-e test -- --verbose") write_prompt "a = 1", "a = 1" write_prompt "puts Rails.env", "puts Rails.env\r\ntest" diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb index 83cf8a27f7..515b32080e 100644 --- a/railties/test/application/middleware/remote_ip_test.rb +++ b/railties/test/application/middleware/remote_ip_test.rb @@ -12,7 +12,9 @@ module ApplicationTests remote_ip = nil env = Rack::MockRequest.env_for("/").merge(env).merge!( "action_dispatch.show_exceptions" => false, - "action_dispatch.key_generator" => ActiveSupport::LegacyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") + "action_dispatch.key_generator" => ActiveSupport::CachingKeyGenerator.new( + ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33", iterations: 1000) + ) ) endpoint = Proc.new do |e| diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb index b25e56b625..479615c133 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -215,8 +215,6 @@ module ApplicationTests RUBY add_to_config <<-RUBY - secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4" - # Enable AEAD cookies config.action_dispatch.use_authenticated_cookie_encryption = true RUBY @@ -238,68 +236,6 @@ module ApplicationTests assert_equal 1, encryptor.decrypt_and_verify(last_response.body, purpose: "cookie._myapp_session")["foo"] end - test "session upgrading signature to encryption cookie store upgrades session to encrypted mode" do - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - get ':controller(/:action)' - end - RUBY - - controller :foo, <<-RUBY - class FooController < ActionController::Base - def write_raw_session - # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1} - cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749" - head :ok - end - - def write_session - session[:foo] = session[:foo] + 1 - head :ok - end - - def read_session - render plain: session[:foo] - end - - def read_encrypted_cookie - render plain: cookies.encrypted[:_myapp_session]['foo'] - end - - def read_raw_cookie - render plain: cookies[:_myapp_session] - end - end - RUBY - - add_to_config <<-RUBY - secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4" - - # Enable AEAD cookies - config.action_dispatch.use_authenticated_cookie_encryption = true - RUBY - - require "#{app_path}/config/environment" - - get "/foo/write_raw_session" - get "/foo/read_session" - assert_equal "1", last_response.body - - get "/foo/write_session" - get "/foo/read_session" - assert_equal "2", last_response.body - - get "/foo/read_encrypted_cookie" - assert_equal "2", last_response.body - - cipher = "aes-256-gcm" - secret = app.key_generator.generate_key("authenticated encrypted cookie") - encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher) - - get "/foo/read_raw_cookie" - assert_equal 2, encryptor.decrypt_and_verify(last_response.body, purpose: "cookie._myapp_session")["foo"] - end - test "session upgrading from AES-CBC-HMAC encryption to AES-GCM encryption" do app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do @@ -370,70 +306,6 @@ module ApplicationTests end end - test "session upgrading legacy signed cookies to new signed cookies" do - app_file "config/routes.rb", <<-RUBY - Rails.application.routes.draw do - get ':controller(/:action)' - end - RUBY - - controller :foo, <<-RUBY - class FooController < ActionController::Base - def write_raw_session - # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1} - cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749" - head :ok - end - - def write_session - session[:foo] = session[:foo] + 1 - head :ok - end - - def read_session - render plain: session[:foo] - end - - def read_signed_cookie - render plain: cookies.signed[:_myapp_session]['foo'] - end - - def read_raw_cookie - render plain: cookies[:_myapp_session] - end - end - RUBY - - add_to_config <<-RUBY - secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4" - Rails.application.credentials.secret_key_base = nil - RUBY - - begin - old_rails_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "production" - - require "#{app_path}/config/environment" - - get "/foo/write_raw_session" - get "/foo/read_session" - assert_equal "1", last_response.body - - get "/foo/write_session" - get "/foo/read_session" - assert_equal "2", last_response.body - - get "/foo/read_signed_cookie" - assert_equal "2", last_response.body - - verifier = ActiveSupport::MessageVerifier.new(app.secrets.secret_token) - - get "/foo/read_raw_cookie" - assert_equal 2, verifier.verify(last_response.body, purpose: "cookie._myapp_session")["foo"] - ensure - ENV["RAILS_ENV"] = old_rails_env - end - end - test "calling reset_session on request does not trigger an error for API apps" do add_to_config "config.api_only = true" diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index 44e3b0f66b..830c36671c 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -118,7 +118,7 @@ module ApplicationTests end def test_code_statistics_sanity - assert_match "Code LOC: 32 Test LOC: 0 Code to Test Ratio: 1:0.0", + assert_match "Code LOC: 29 Test LOC: 0 Code to Test Ratio: 1:0.0", rails("stats") end diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb index ab9e910aed..f4bd09903a 100644 --- a/railties/test/application/server_test.rb +++ b/railties/test/application/server_test.rb @@ -18,20 +18,6 @@ module ApplicationTests teardown_app end - test "deprecate support of older `config.ru`" do - remove_file "config.ru" - app_file "config.ru", <<-RUBY - require_relative 'config/environment' - run AppTemplate::Application - RUBY - - server = Rails::Server.new(config: "#{app_path}/config.ru") - server.app - - log = File.read(Rails.application.config.paths["log"].first) - assert_match(/DEPRECATION WARNING: Using `Rails::Application` subclass to start the server is deprecated/, log) - end - test "restart rails server with custom pid file path" do skip "PTY unavailable" unless available_pty? diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index 6765eef9d0..fda6df500d 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -98,6 +98,17 @@ module ApplicationTests end end + def test_run_channels + create_test_file :channels, "foo_channel" + create_test_file :channels, "bar_channel" + + rails("test:channels").tap do |output| + assert_match "FooChannelTest", output + assert_match "BarChannelTest", output + assert_match "2 runs, 2 assertions, 0 failures", output + end + end + def test_run_controllers create_test_file :controllers, "foo_controller" create_test_file :controllers, "bar_controller" @@ -167,11 +178,11 @@ module ApplicationTests end def test_run_all_suites - suites = [:models, :helpers, :unit, :controllers, :mailers, :functional, :integration, :jobs, :mailboxes] + suites = [:models, :helpers, :unit, :channels, :controllers, :mailers, :functional, :integration, :jobs, :mailboxes] suites.each { |suite| create_test_file suite, "foo_#{suite}" } run_test_command("") .tap do |output| suites.each { |suite| assert_match "Foo#{suite.to_s.camelize}Test", output } - assert_match "9 runs, 9 assertions, 0 failures", output + assert_match "10 runs, 10 assertions, 0 failures", output end end diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb index 0b2fe204f8..1941c83d6d 100644 --- a/railties/test/commands/console_test.rb +++ b/railties/test/commands/console_test.rb @@ -94,28 +94,7 @@ class Rails::ConsoleTest < ActiveSupport::TestCase assert_match(/\sspecial-production\s/, output) end - def test_rails_env_is_production_when_first_argument_is_p - assert_deprecated do - start ["p"] - assert_match(/\sproduction\s/, output) - end - end - - def test_rails_env_is_test_when_first_argument_is_t - assert_deprecated do - start ["t"] - assert_match(/\stest\s/, output) - end - end - - def test_rails_env_is_development_when_argument_is_d - assert_deprecated do - start ["d"] - assert_match(/\sdevelopment\s/, output) - end - end - - def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present + def test_rails_env_is_dev_when_environment_option_is_dev_and_dev_env_is_present Rails::Command::ConsoleCommand.class_eval do alias_method :old_environments, :available_environments @@ -124,9 +103,7 @@ class Rails::ConsoleTest < ActiveSupport::TestCase end end - assert_deprecated do - assert_match("dev", parse_arguments(["dev"])[:environment]) - end + assert_match("dev", parse_arguments(["-e", "dev"])[:environment]) ensure Rails::Command::ConsoleCommand.class_eval do undef_method :available_environments diff --git a/railties/test/commands/db_system_change_test.rb b/railties/test/commands/db_system_change_test.rb new file mode 100644 index 0000000000..2ff45a7878 --- /dev/null +++ b/railties/test/commands/db_system_change_test.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "rails/command" +require "rails/commands/db/system/change/change_command" + +class Rails::Command::Db::System::ChangeCommandTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + setup { build_app } + + teardown { teardown_app } + + test "change to existing database" do + change_database(to: "sqlite3") + + output = change_database(to: "sqlite3") + + assert_match "identical config/database.yml", output + assert_match "gsub Gemfile", output + end + + test "change to invalid database" do + output = change_database(to: "invalid-db") + + assert_match <<~MSG.squish, output + Invalid value for --to option. + Supported preconfigurations are: + mysql, postgresql, sqlite3, oracle, frontbase, + ibm_db, sqlserver, jdbcmysql, jdbcsqlite3, + jdbcpostgresql, jdbc. + MSG + end + + test "change to postgresql" do + output = change_database(to: "postgresql") + + assert_match "force config/database.yml", output + assert_match "gsub Gemfile", output + end + + test "change to mysql" do + output = change_database(to: "mysql") + + assert_match "force config/database.yml", output + assert_match "gsub Gemfile", output + end + + test "change to sqlite3" do + change_database(to: "postgresql") + output = change_database(to: "sqlite3") + + assert_match "force config/database.yml", output + assert_match "gsub Gemfile", output + end + + private + def change_database(to:, **options) + args = ["--to", to] + rails "db:system:change", args, **options + end +end diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb index ce048ac527..adb168f7a3 100644 --- a/railties/test/commands/dbconsole_test.rb +++ b/railties/test/commands/dbconsole_test.rb @@ -99,28 +99,12 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase ENV["RACK_ENV"] = nil end - def test_rails_env_is_development_when_argument_is_dev - assert_deprecated do - stub_available_environments([ "development", "test" ]) do - assert_match("development", parse_arguments([ "dev" ])[:environment]) - end - end - end - def test_rails_env_is_development_when_environment_option_is_dev stub_available_environments([ "development", "test" ]) do assert_match("development", parse_arguments([ "-e", "dev" ])[:environment]) end end - def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present - assert_deprecated do - stub_available_environments([ "dev" ]) do - assert_match("dev", parse_arguments([ "dev" ])[:environment]) - end - end - end - def test_mysql start(adapter: "mysql2", database: "db") assert_not aborted @@ -265,14 +249,14 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase stdout = capture(:stdout) do Rails::Command.invoke(:dbconsole, ["-h"]) end - assert_match(/rails dbconsole \[environment\]/, stdout) + assert_match(/rails dbconsole \[options\]/, stdout) end def test_print_help_long stdout = capture(:stdout) do Rails::Command.invoke(:dbconsole, ["--help"]) end - assert_match(/rails dbconsole \[environment\]/, stdout) + assert_match(/rails dbconsole \[options\]/, stdout) end attr_reader :aborted, :output diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index af475400a1..4932100ea2 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -412,15 +412,6 @@ class ActionsTest < Rails::Generators::TestCase end end - def test_capify_should_run_the_capify_command - content = capture(:stderr) do - assert_called_with(generator, :run, ["capify .", verbose: false]) do - action :capify! - end - end - assert_match(/DEPRECATION WARNING: `capify!` is deprecated/, content) - end - def test_route_should_add_data_to_the_routes_block_in_config_routes run_generator route_command = "route '/login', controller: 'sessions', action: 'new'" diff --git a/railties/test/generators/db_system_change_generator_test.rb b/railties/test/generators/db_system_change_generator_test.rb new file mode 100644 index 0000000000..d476bfd2dc --- /dev/null +++ b/railties/test/generators/db_system_change_generator_test.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require "generators/generators_test_helper" +require "rails/generators/rails/db/system/change/change_generator" + +module Rails + module Generators + module Db + module System + class ChangeGeneratorTest < Rails::Generators::TestCase + include GeneratorsTestHelper + + setup do + copy_gemfile( + GemfileEntry.new("sqlite3", nil, "Use sqlite3 as the database for Active Record") + ) + end + + test "change to invalid database" do + output = capture(:stderr) do + run_generator ["--to", "invalid-db"] + end + + assert_match <<~MSG.squish, output + Invalid value for --to option. + Supported preconfigurations are: + mysql, postgresql, sqlite3, oracle, frontbase, + ibm_db, sqlserver, jdbcmysql, jdbcsqlite3, + jdbcpostgresql, jdbc. + MSG + end + + test "change to postgresql" do + run_generator ["--to", "postgresql"] + + assert_file("config/database.yml") do |content| + assert_match "adapter: postgresql", content + assert_match "database: test_app", content + end + + assert_file("Gemfile") do |content| + assert_match "# Use pg as the database for Active Record", content + assert_match "gem 'pg'", content + end + end + + test "change to mysql" do + run_generator ["--to", "mysql"] + + assert_file("config/database.yml") do |content| + assert_match "adapter: mysql2", content + assert_match "database: test_app", content + end + + assert_file("Gemfile") do |content| + assert_match "# Use mysql2 as the database for Active Record", content + assert_match "gem 'mysql2'", content + end + end + + test "change to sqlite3" do + run_generator ["--to", "sqlite3"] + + assert_file("config/database.yml") do |content| + assert_match "adapter: sqlite3", content + assert_match "db/development.sqlite3", content + end + + assert_file("Gemfile") do |content| + assert_match "# Use sqlite3 as the database for Active Record", content + assert_match "gem 'sqlite3'", content + end + end + end + end + end + end +end diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb index 25d5dba1d8..975a204af4 100644 --- a/railties/test/generators/generators_test_helper.rb +++ b/railties/test/generators/generators_test_helper.rb @@ -30,6 +30,12 @@ module GeneratorsTestHelper include ActiveSupport::Testing::Stream include ActiveSupport::Testing::MethodCallAssertions + GemfileEntry = Struct.new(:name, :version, :comment, :options, :commented_out) do + def initialize(name, version, comment, options = {}, commented_out = false) + super + end + end + def self.included(base) base.class_eval do destination File.join(Rails.root, "tmp") @@ -63,4 +69,34 @@ module GeneratorsTestHelper FileUtils.mkdir_p(destination) FileUtils.cp routes, File.join(destination, "routes.rb") end + + def copy_gemfile(*gemfile_entries) + locals = gemfile_locals.merge(gemfile_entries: gemfile_entries) + gemfile = File.expand_path("../../lib/rails/generators/rails/app/templates/Gemfile.tt", __dir__) + gemfile = evaluate_template(gemfile, locals) + destination = File.join(destination_root) + File.write File.join(destination, "Gemfile"), gemfile + end + + def evaluate_template(file, locals = {}) + erb = ERB.new(File.read(file), nil, "-", "@output_buffer") + context = Class.new do + locals.each do |local, value| + class_attribute local, default: value + end + end + erb.result(context.new.instance_eval("binding")) + end + + private + def gemfile_locals + { + skip_active_storage: true, + depend_on_bootsnap: false, + depend_on_listen: false, + spring_install: false, + depends_on_system_test: false, + options: ActiveSupport::OrderedOptions.new, + } + end end diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 66286fc554..4ec656d18b 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -712,38 +712,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase Object.send(:remove_const, "ENGINE_ROOT") end - def test_after_bundle_callback - path = "http://example.org/rails_template" - template = +%{ after_bundle { run "echo ran after_bundle" } } - template.instance_eval "def read; self; end" # Make the string respond to read - - check_open = -> *args do - assert_equal [ path, "Accept" => "application/x-thor-template" ], args - template - end - - sequence = ["echo ran after_bundle"] - @sequence_step ||= 0 - ensure_bundler_first = -> command do - assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}" - @sequence_step += 1 - end - - content = nil - generator([destination_root], template: path).stub(:open, check_open, template) do - generator.stub(:bundle_command, ensure_bundler_first) do - generator.stub(:run, ensure_bundler_first) do - silence_stream($stdout) do - content = capture(:stderr) { generator.invoke_all } - end - end - end - end - - assert_equal 1, @sequence_step - assert_match(/DEPRECATION WARNING: `after_bundle` is deprecated/, content) - end - private def action(*args, &block) diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index f673832caa..26ce487c5f 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -206,7 +206,7 @@ module SharedGeneratorTests unless generator_class.name == "Rails::Generators::PluginGenerator" assert_file "#{application_path}/app/javascript/packs/application.js" do |content| - assert_match(/^import \* as ActiveStorage from "@rails\/activestorage"\nActiveStorage.start\(\)/, content) + assert_match(/^require\("@rails\/activestorage"\)\.start\(\)/, content) end end @@ -267,7 +267,7 @@ module SharedGeneratorTests assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/ assert_file "#{application_path}/app/javascript/packs/application.js" do |content| - assert_no_match(/^import * as ActiveStorage from "@rails\/activestorage"\nActiveStorage.start\(\)/, content) + assert_no_match(/^require\("@rails\/activestorage"\)\.start\(\)/, content) end assert_file "#{application_path}/config/environments/development.rb" do |content| diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 7d4a26ff9d..01b6cb0a43 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -476,7 +476,7 @@ Module.new do `yarn build` end - `#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-gemfile --skip-listen --no-rc` + `#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/exe/rails new #{app_template_path} --skip-bundle --skip-listen --no-rc` File.open("#{app_template_path}/config/boot.rb", "w") do |f| f.puts "require 'rails/all'" end diff --git a/railties/test/path_generation_test.rb b/railties/test/path_generation_test.rb index 849b183b37..0c1ee259b0 100644 --- a/railties/test/path_generation_test.rb +++ b/railties/test/path_generation_test.rb @@ -66,7 +66,7 @@ class PathGenerationTest < ActiveSupport::TestCase super app = self @routes = TestSet.new ->(c) { app.controller = c } - secrets.secret_token = "foo" + secrets.secret_key_base = "foo" end def app; routes; end } diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index e62e8c8b44..508367212b 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -879,6 +879,18 @@ YAML assert Bukkits::Engine.config.bukkits_seeds_loaded end + test "jobs are ran inline while loading seeds" do + app_file "db/seeds.rb", <<-RUBY + Rails.application.config.seed_queue_adapter = ActiveJob::Base.queue_adapter + RUBY + + boot_rails + Rails.application.load_seed + + assert_instance_of ActiveJob::QueueAdapters::InlineAdapter, Rails.application.config.seed_queue_adapter + assert_instance_of ActiveJob::QueueAdapters::AsyncAdapter, ActiveJob::Base.queue_adapter + end + test "skips nonexistent seed data" do FileUtils.rm "#{app_path}/db/seeds.rb" boot_rails |