aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock1
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb15
-rw-r--r--activejob/lib/active_job/queue_adapters/test_adapter.rb6
-rw-r--r--activemodel/lib/active_model/serializers/json.rb19
-rw-r--r--activemodel/test/cases/serializers/json_serialization_test.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb2
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb2
-rw-r--r--activestorage/CHANGELOG.md7
-rw-r--r--activestorage/app/models/active_storage/variant.rb48
-rw-r--r--activestorage/app/models/active_storage/variation.rb79
-rw-r--r--activestorage/lib/active_storage.rb8
-rw-r--r--activestorage/lib/active_storage/errors.rb19
-rw-r--r--activestorage/lib/active_storage/transformers/image_processing_transformer.rb39
-rw-r--r--activestorage/lib/active_storage/transformers/mini_magick_transformer.rb38
-rw-r--r--activestorage/lib/active_storage/transformers/transformer.rb42
-rw-r--r--activesupport/CHANGELOG.md2
-rw-r--r--guides/source/development_dependencies_install.md28
-rw-r--r--railties/lib/rails/generators/app_base.rb6
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/bundle.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt2
-rw-r--r--railties/test/application/test_runner_test.rb18
-rw-r--r--railties/test/generators/api_app_generator_test.rb1
-rw-r--r--railties/test/generators/app_generator_test.rb50
25 files changed, 290 insertions, 154 deletions
diff --git a/Gemfile b/Gemfile
index f0c489f642..62f70a1da6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -47,6 +47,7 @@ gem "dalli"
gem "listen", ">= 3.0.5", "< 3.2", require: false
gem "libxml-ruby", platforms: :ruby
gem "connection_pool", require: false
+gem "i18n", "~> 1.0.1"
# for railties app_generator_test
gem "bootsnap", ">= 1.1.0", require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 259221979e..87d017a8a9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -534,6 +534,7 @@ DEPENDENCIES
ffi (<= 1.9.21)
google-cloud-storage (~> 1.11)
hiredis
+ i18n (~> 1.0.1)
image_processing (~> 1.2)
json (>= 2.0.0)
kindlerb (~> 1.2.0)
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index edfef39771..ea637c8150 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -17,7 +17,7 @@ module ActionController #:nodoc:
# access. When a request reaches your application, \Rails verifies the received
# token with the token in the session. All requests are checked except GET requests
# as these should be idempotent. Keep in mind that all session-oriented requests
- # should be CSRF protected, including JavaScript and HTML requests.
+ # are CSRF protected by default, including JavaScript and HTML requests.
#
# Since HTML and JavaScript requests are typically made from the browser, we
# need to ensure to verify request authenticity for the web browser. We can
@@ -30,16 +30,23 @@ module ActionController #:nodoc:
# URL on your site. When your JavaScript response loads on their site, it executes.
# With carefully crafted JavaScript on their end, sensitive data in your JavaScript
# response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
- # Ajax) requests are allowed to make GET requests for JavaScript responses.
+ # Ajax) requests are allowed to make requests for JavaScript responses.
#
- # It's important to remember that XML or JSON requests are also affected and if
- # you're building an API you should change forgery protection method in
+ # It's important to remember that XML or JSON requests are also checked by default. If
+ # you're building an API or an SPA you could change forgery protection method in
# <tt>ApplicationController</tt> (by default: <tt>:exception</tt>):
#
# class ApplicationController < ActionController::Base
# protect_from_forgery unless: -> { request.format.json? }
# end
#
+ # It is generally safe to exclude XHR requests from CSRF protection
+ # (like the code snippet above does), because XHR requests can only be made from
+ # the same origin. Note however that any cross-origin third party domain
+ # allowed via {CORS}[https://en.wikipedia.org/wiki/Cross-origin_resource_sharing]
+ # will also be able to create XHR requests. Be sure to check your
+ # CORS whitelist before disabling forgery protection for XHR.
+ #
# CSRF protection is turned on with the <tt>protect_from_forgery</tt> method.
# By default <tt>protect_from_forgery</tt> protects your session with
# <tt>:null_session</tt> method, which provides an empty session
diff --git a/activejob/lib/active_job/queue_adapters/test_adapter.rb b/activejob/lib/active_job/queue_adapters/test_adapter.rb
index 885f9ff01c..3aa25425eb 100644
--- a/activejob/lib/active_job/queue_adapters/test_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/test_adapter.rb
@@ -29,14 +29,14 @@ module ActiveJob
return if filtered?(job)
job_data = job_to_hash(job)
- enqueue_or_perform(perform_enqueued_jobs, job, job_data)
+ perform_or_enqueue(perform_enqueued_jobs, job, job_data)
end
def enqueue_at(job, timestamp) #:nodoc:
return if filtered?(job)
job_data = job_to_hash(job, at: timestamp)
- enqueue_or_perform(perform_enqueued_at_jobs, job, job_data)
+ perform_or_enqueue(perform_enqueued_at_jobs, job, job_data)
end
private
@@ -44,7 +44,7 @@ module ActiveJob
{ job: job.class, args: job.serialize.fetch("arguments"), queue: job.queue_name }.merge!(extras)
end
- def enqueue_or_perform(perform, job, job_data)
+ def perform_or_enqueue(perform, job, job_data)
if perform
performed_jobs << job_data
Base.execute job.serialize
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index 25e1541d66..f77fb98c32 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -26,13 +26,13 @@ module ActiveModel
# user = User.find(1)
# user.as_json
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
- # # "created_at" => "2006/08/01", "awesome" => true}
+ # # "created_at" => "2006-08-01T17:27:133.000Z", "awesome" => true}
#
# ActiveRecord::Base.include_root_in_json = true
#
# user.as_json
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
- # # "created_at" => "2006/08/01", "awesome" => true } }
+ # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
#
# This behavior can also be achieved by setting the <tt>:root</tt> option
# to +true+ as in:
@@ -40,7 +40,7 @@ module ActiveModel
# user = User.find(1)
# user.as_json(root: true)
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
- # # "created_at" => "2006/08/01", "awesome" => true } }
+ # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }
#
# Without any +options+, the returned Hash will include all the model's
# attributes.
@@ -48,7 +48,7 @@ module ActiveModel
# user = User.find(1)
# user.as_json
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
- # # "created_at" => "2006/08/01", "awesome" => true}
+ # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true}
#
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit
# the attributes included, and work similar to the +attributes+ method.
@@ -63,14 +63,14 @@ module ActiveModel
#
# user.as_json(methods: :permalink)
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
- # # "created_at" => "2006/08/01", "awesome" => true,
+ # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
# # "permalink" => "1-konata-izumi" }
#
# To include associations use <tt>:include</tt>:
#
# user.as_json(include: :posts)
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
- # # "created_at" => "2006/08/01", "awesome" => true,
+ # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
# # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
# # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
#
@@ -81,7 +81,7 @@ module ActiveModel
# only: :body } },
# only: :title } })
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
- # # "created_at" => "2006/08/01", "awesome" => true,
+ # # "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
# # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
# # "title" => "Welcome to the weblog" },
# # { "comments" => [ { "body" => "Don't think too hard" } ],
@@ -93,11 +93,12 @@ module ActiveModel
include_root_in_json
end
+ hash = serializable_hash(options).as_json
if root
root = model_name.element if root == true
- { root => serializable_hash(options) }
+ { root => hash }
else
- serializable_hash(options)
+ hash
end
end
diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb
index aae98c9fe4..625e0a427a 100644
--- a/activemodel/test/cases/serializers/json_serialization_test.rb
+++ b/activemodel/test/cases/serializers/json_serialization_test.rb
@@ -129,6 +129,10 @@ class JsonSerializationTest < ActiveModel::TestCase
assert_equal :name, options[:except]
end
+ test "as_json should serialize timestamps" do
+ assert_equal "2006-08-01T00:00:00.000Z", @contact.as_json["created_at"]
+ end
+
test "as_json should return a hash if include_root_in_json is true" do
begin
original_include_root_in_json = Contact.include_root_in_json
@@ -138,7 +142,7 @@ class JsonSerializationTest < ActiveModel::TestCase
assert_kind_of Hash, json
assert_kind_of Hash, json["contact"]
%w(name age created_at awesome preferences).each do |field|
- assert_equal @contact.send(field), json["contact"][field]
+ assert_equal @contact.send(field).as_json, json["contact"][field]
end
ensure
Contact.include_root_in_json = original_include_root_in_json
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index d2b7817b45..294a3dc32c 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -73,7 +73,7 @@ module ActiveRecord
# `skip_time_zone_conversion_for_attributes` would not be picked up.
subclass.class_eval do
matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
- decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
+ decorate_matching_attribute_types(matcher, "_time_zone_conversion") do |type|
TimeZoneConverter.new(type)
end
end
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 7f096bb532..1030b2368b 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -165,7 +165,7 @@ module ActiveRecord
def inherited(subclass)
subclass.class_eval do
is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
- decorate_matching_attribute_types(is_lock_column, :_optimistic_locking) do |type|
+ decorate_matching_attribute_types(is_lock_column, "_optimistic_locking") do |type|
LockingType.new(type)
end
end
diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md
index 0e9c2b5619..f6f195770c 100644
--- a/activestorage/CHANGELOG.md
+++ b/activestorage/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Active Storage error classes like `ActiveStorage::IntegrityError` and
+ `ActiveStorage::UnrepresentableError` now inherit from `ActiveStorage::Error`
+ instead of `StandardError`. This permits rescuing `ActiveStorage::Error` to
+ handle all Active Storage errors.
+
+ *Andrei Makarov*, *George Claghorn*
+
* Uploaded files assigned to a record are persisted to storage when the record
is saved instead of immediately.
diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb
index 056fd2be99..ea57fa5f78 100644
--- a/activestorage/app/models/active_storage/variant.rb
+++ b/activestorage/app/models/active_storage/variant.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "ostruct"
+
# Image blobs can have variants that are the result of a set of transformations applied to the original.
# These variants are used to create thumbnails, fixed-size avatars, or any other derivative image from the
# original.
@@ -51,7 +53,7 @@
# * {ImageProcessing::Vips}[https://github.com/janko-m/image_processing/blob/master/doc/vips.md#methods]
# * {ruby-vips reference}[http://www.rubydoc.info/gems/ruby-vips/Vips/Image]
class ActiveStorage::Variant
- WEB_IMAGE_CONTENT_TYPES = %w( image/png image/jpeg image/jpg image/gif )
+ WEB_IMAGE_CONTENT_TYPES = %w[ image/png image/jpeg image/jpg image/gif ]
attr_reader :blob, :variation
delegate :service, to: :blob
@@ -95,37 +97,35 @@ class ActiveStorage::Variant
def process
blob.open do |image|
- transform image do |output|
- upload output
- end
+ transform(image) { |output| upload(output) }
end
end
-
- def filename
- if WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type)
- blob.filename
- else
- ActiveStorage::Filename.new("#{blob.filename.base}.png")
- end
+ def transform(image, &block)
+ variation.transform(image, format: format, &block)
end
- def content_type
- blob.content_type.presence_in(WEB_IMAGE_CONTENT_TYPES) || "image/png"
+ def upload(file)
+ service.upload(key, file)
end
- def transform(image)
- format = "png" unless WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type)
- result = variation.transform(image, format: format)
- begin
- yield result
- ensure
- result.close!
- end
+ def specification
+ @specification ||=
+ if WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type)
+ Specification.new \
+ filename: blob.filename,
+ content_type: blob.content_type,
+ format: nil
+ else
+ Specification.new \
+ filename: ActiveStorage::Filename.new("#{blob.filename.base}.png"),
+ content_type: "image/png",
+ format: "png"
+ end
end
- def upload(file)
- service.upload(key, file)
- end
+ delegate :filename, :content_type, :format, to: :specification
+
+ class Specification < OpenStruct; end
end
diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb
index ae1376a6cf..3adc2407e5 100644
--- a/activestorage/app/models/active_storage/variation.rb
+++ b/activestorage/app/models/active_storage/variation.rb
@@ -47,13 +47,9 @@ class ActiveStorage::Variation
# saves the transformed image into a temporary file. If +format+ is specified
# it will be the format of the result image, otherwise the result image
# retains the source format.
- def transform(file, format: nil)
+ def transform(file, format: nil, &block)
ActiveSupport::Notifications.instrument("transform.active_storage") do
- if processor
- image_processing_transform(file, format)
- else
- mini_magick_transform(file, format)
- end
+ transformer.transform(file, format: format, &block)
end
end
@@ -63,63 +59,22 @@ class ActiveStorage::Variation
end
private
- # Applies image transformations using the ImageProcessing gem.
- def image_processing_transform(file, format)
- operations = transformations.each_with_object([]) do |(name, argument), list|
- if name.to_s == "combine_options"
- ActiveSupport::Deprecation.warn("The ImageProcessing ActiveStorage variant backend doesn't need :combine_options, as it already generates a single MiniMagick command. In Rails 6.1 :combine_options will not be supported anymore.")
- list.concat argument.keep_if { |key, value| value.present? }.to_a
- elsif argument.present?
- list << [name, argument]
+ def transformer
+ if ActiveStorage.variant_processor
+ begin
+ require "image_processing"
+ rescue LoadError
+ ActiveSupport::Deprecation.warn <<~WARNING
+ Generating image variants will require the image_processing gem in Rails 6.1.
+ Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.
+ WARNING
+
+ ActiveStorage::Transformers::MiniMagickTransformer.new(transformations)
+ else
+ ActiveStorage::Transformers::ImageProcessingTransformer.new(transformations)
end
- end
-
- processor
- .source(file)
- .loader(page: 0)
- .convert(format)
- .apply(operations)
- .call
- end
-
- # Applies image transformations using the MiniMagick gem.
- def mini_magick_transform(file, format)
- image = MiniMagick::Image.new(file.path, file)
-
- transformations.each do |name, argument_or_subtransformations|
- image.mogrify do |command|
- if name.to_s == "combine_options"
- argument_or_subtransformations.each do |subtransformation_name, subtransformation_argument|
- pass_transform_argument(command, subtransformation_name, subtransformation_argument)
- end
- else
- pass_transform_argument(command, name, argument_or_subtransformations)
- end
- end
- end
-
- image.format(format) if format
-
- image.tempfile.tap(&:open)
- end
-
- # Returns the ImageProcessing processor class specified by `ActiveStorage.variant_processor`.
- def processor
- begin
- require "image_processing"
- rescue LoadError
- ActiveSupport::Deprecation.warn("Using mini_magick gem directly is deprecated and will be removed in Rails 6.1. Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.")
- return nil
- end
-
- ImageProcessing.const_get(ActiveStorage.variant_processor.to_s.camelize) if ActiveStorage.variant_processor
- end
-
- def pass_transform_argument(command, method, argument)
- if argument == true
- command.public_send(method)
- elsif argument.present?
- command.public_send(method, argument)
+ else
+ ActiveStorage::Transformers::MiniMagickTransformer.new(transformations)
end
end
end
diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb
index 928db32691..d3e3a2f49b 100644
--- a/activestorage/lib/active_storage.rb
+++ b/activestorage/lib/active_storage.rb
@@ -50,4 +50,12 @@ module ActiveStorage
mattr_accessor :variable_content_types, default: []
mattr_accessor :content_types_to_serve_as_binary, default: []
mattr_accessor :service_urls_expire_in, default: 5.minutes
+
+ module Transformers
+ extend ActiveSupport::Autoload
+
+ autoload :Transformer
+ autoload :ImageProcessingTransformer
+ autoload :MiniMagickTransformer
+ end
end
diff --git a/activestorage/lib/active_storage/errors.rb b/activestorage/lib/active_storage/errors.rb
index bedcd080c4..f4bf66a615 100644
--- a/activestorage/lib/active_storage/errors.rb
+++ b/activestorage/lib/active_storage/errors.rb
@@ -1,11 +1,22 @@
# frozen_string_literal: true
module ActiveStorage
- class InvariableError < StandardError; end
- class UnpreviewableError < StandardError; end
- class UnrepresentableError < StandardError; end
+ # Generic base class for all Active Storage exceptions.
+ class Error < StandardError; end
+
+ # Raised when ActiveStorage::Blob#variant is called on a blob that isn't variable.
+ # Use ActiveStorage::Blob#variable? to determine whether a blob is variable.
+ class InvariableError < Error; end
+
+ # Raised when ActiveStorage::Blob#preview is called on a blob that isn't previewable.
+ # Use ActiveStorage::Blob#previewable? to determine whether a blob is previewable.
+ class UnpreviewableError < Error; end
+
+ # Raised when ActiveStorage::Blob#representation is called on a blob that isn't representable.
+ # Use ActiveStorage::Blob#representable? to determine whether a blob is representable.
+ class UnrepresentableError < Error; end
# Raised when uploaded or downloaded data does not match a precomputed checksum.
# Indicates that a network error or a software bug caused data corruption.
- class IntegrityError < StandardError; end
+ class IntegrityError < Error; end
end
diff --git a/activestorage/lib/active_storage/transformers/image_processing_transformer.rb b/activestorage/lib/active_storage/transformers/image_processing_transformer.rb
new file mode 100644
index 0000000000..7f8685b72d
--- /dev/null
+++ b/activestorage/lib/active_storage/transformers/image_processing_transformer.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "image_processing"
+
+module ActiveStorage
+ module Transformers
+ class ImageProcessingTransformer < Transformer
+ private
+ def process(file, format:)
+ processor.
+ source(file).
+ loader(page: 0).
+ convert(format).
+ apply(operations).
+ call
+ end
+
+ def processor
+ ImageProcessing.const_get(ActiveStorage.variant_processor.to_s.camelize)
+ end
+
+ def operations
+ transformations.each_with_object([]) do |(name, argument), list|
+ if name.to_s == "combine_options"
+ ActiveSupport::Deprecation.warn <<~WARNING
+ Active Storage's ImageProcessing transformer doesn't support :combine_options,
+ as it always generates a single ImageMagick command. Passing :combine_options will
+ not be supported in Rails 6.1.
+ WARNING
+
+ list.concat argument.keep_if { |key, value| value.present? }.to_a
+ elsif argument.present?
+ list << [ name, argument ]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/transformers/mini_magick_transformer.rb b/activestorage/lib/active_storage/transformers/mini_magick_transformer.rb
new file mode 100644
index 0000000000..e8e99cea9e
--- /dev/null
+++ b/activestorage/lib/active_storage/transformers/mini_magick_transformer.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require "mini_magick"
+
+module ActiveStorage
+ module Transformers
+ class MiniMagickTransformer < Transformer
+ private
+ def process(file, format:)
+ image = MiniMagick::Image.new(file.path, file)
+
+ transformations.each do |name, argument_or_subtransformations|
+ image.mogrify do |command|
+ if name.to_s == "combine_options"
+ argument_or_subtransformations.each do |subtransformation_name, subtransformation_argument|
+ pass_transform_argument(command, subtransformation_name, subtransformation_argument)
+ end
+ else
+ pass_transform_argument(command, name, argument_or_subtransformations)
+ end
+ end
+ end
+
+ image.format(format) if format
+
+ image.tempfile.tap(&:open)
+ end
+
+ def pass_transform_argument(command, method, argument)
+ if argument == true
+ command.public_send(method)
+ elsif argument.present?
+ command.public_send(method, argument)
+ end
+ end
+ end
+ end
+end
diff --git a/activestorage/lib/active_storage/transformers/transformer.rb b/activestorage/lib/active_storage/transformers/transformer.rb
new file mode 100644
index 0000000000..2e21201004
--- /dev/null
+++ b/activestorage/lib/active_storage/transformers/transformer.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module ActiveStorage
+ module Transformers
+ # A Transformer applies a set of transformations to an image.
+ #
+ # The following concrete subclasses are included in Active Storage:
+ #
+ # * ActiveStorage::Transformers::ImageProcessingTransformer:
+ # backed by ImageProcessing, a common interface for MiniMagick and ruby-vips
+ #
+ # * ActiveStorage::Transformers::MiniMagickTransformer:
+ # backed by MiniMagick, a wrapper around the ImageMagick CLI
+ class Transformer
+ attr_reader :transformations
+
+ def initialize(transformations)
+ @transformations = transformations
+ end
+
+ # Applies the transformations to the source image in +file+, producing a target image in the
+ # specified +format+. Yields an open Tempfile containing the target image. Closes and unlinks
+ # the output tempfile after yielding to the given block. Returns the result of the block.
+ def transform(file, format:)
+ output = process(file, format: format)
+
+ begin
+ yield output
+ ensure
+ output.close!
+ end
+ end
+
+ private
+ # Returns an open Tempfile containing a transformed image in the given +format+.
+ # All subclasses implement this method.
+ def process(file, format:) #:doc:
+ raise NotImplementedError
+ end
+ end
+ end
+end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 5423d59984..25e2ee04f9 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -96,7 +96,7 @@
*Godfrey Chan*
-* Fix bug where `URI.unscape` would fail with mixed Unicode/escaped character input:
+* Fix bug where `URI.unescape` would fail with mixed Unicode/escaped character input:
URI.unescape("\xe3\x83\x90") # => "バ"
URI.unescape("%E3%83%90") # => "バ"
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index 057bcf2c1b..7a414f21fe 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -377,30 +377,38 @@ command inside of the `activestorage` directory to install the dependencies:
yarn install
```
-Extracting previews, tested in ActiveStorage's test suite requires third-party
+Extracting previews, tested in Active Storage's test suite requires third-party
applications, FFmpeg for video and muPDF for PDFs, and on macOS also XQuartz
-and Poppler. Without these applications installed, ActiveStorage tests will
+and Poppler. Without these applications installed, Active Storage tests will
raise errors.
On macOS you can run:
```bash
-brew install ffmpeg
-brew cask install xquartz
-brew install mupdf-tools
-brew install poppler
+$ brew install ffmpeg
+$ brew cask install xquartz
+$ brew install mupdf-tools
+$ brew install poppler
```
On Ubuntu, you can run:
```bash
-sudo apt-get update && install ffmpeg
-sudo apt-get update && install mupdf mupdf-tools
+$ sudo apt-get update
+$ sudo apt-get install ffmpeg
+$ sudo apt-get install mupdf mupdf-tools
```
On Fedora or CentOS, just run:
```bash
-sudo yum install ffmpeg
-sudo yum install mupdf
+$ sudo yum install ffmpeg
+$ sudo yum install mupdf
+```
+
+FreeBSD users can just run:
+
+```bash
+# pkg install ffmpeg
+# pkg install mupdf
```
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 985e9ab263..a6dc60342c 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -458,6 +458,12 @@ module Rails
end
end
+ def generate_bundler_binstub
+ if bundle_install?
+ bundle_command("binstubs bundler")
+ end
+ end
+
def generate_spring_binstubs
if bundle_install? && spring_install?
bundle_command("exec spring binstub --all")
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 83c5c9f297..a6d160f1eb 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -469,7 +469,8 @@ module Rails
end
public_task :apply_rails_template, :run_bundle
- public_task :run_webpack, :generate_spring_binstubs
+ public_task :generate_bundler_binstub, :generate_spring_binstubs
+ public_task :run_webpack
def run_after_bundle_callbacks
@after_bundle_callbacks.each(&:call)
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt b/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt
deleted file mode 100644
index a84f0afe47..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/bin/bundle.tt
+++ /dev/null
@@ -1,2 +0,0 @@
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
-load Gem.bin_path('bundler', 'bundle')
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index d646694477..888336af92 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -69,7 +69,7 @@ Rails.application.configure do
# Use a real queuing backend for Active Job (and separate queues per environment).
# config.active_job.queue_adapter = :resque
- # config.active_job.queue_name_prefix = "<%= app_name %>_#{Rails.env}"
+ # config.active_job.queue_name_prefix = "<%= app_name %>_production"
<%- unless options.skip_action_mailer? -%>
config.action_mailer.perform_caching = false
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
index 455dc60efd..5c34b205c9 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -525,9 +525,18 @@ module ApplicationTests
def test_run_in_parallel_with_processes
file_name = create_parallel_processes_test_file
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
output = run_test_command(file_name)
assert_match %r{Finished in.*\n2 runs, 2 assertions}, output
+ assert_no_match "create_table(:users)", output
end
def test_run_in_parallel_with_threads
@@ -539,9 +548,18 @@ module ApplicationTests
file_name = create_parallel_threads_test_file
+ app_file "db/schema.rb", <<-RUBY
+ ActiveRecord::Schema.define(version: 1) do
+ create_table :users do |t|
+ t.string :name
+ end
+ end
+ RUBY
+
output = run_test_command(file_name)
assert_match %r{Finished in.*\n2 runs, 2 assertions}, output
+ assert_no_match "create_table(:users)", output
end
def test_raise_error_when_specified_file_does_not_exist
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
index 9c523ad372..c2540f4091 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -118,7 +118,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
app/views/layouts
app/views/layouts/mailer.html.erb
app/views/layouts/mailer.text.erb
- bin/bundle
bin/rails
bin/rake
bin/setup
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index b0f958091c..f33a7bd99d 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -37,7 +37,6 @@ DEFAULT_APP_FILES = %w(
app/views/layouts/application.html.erb
app/views/layouts/mailer.html.erb
app/views/layouts/mailer.text.erb
- bin/bundle
bin/rails
bin/rake
bin/setup
@@ -763,17 +762,23 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_generation_runs_bundle_install
- assert_generates_with_bundler
+ generator([destination_root], {})
+
+ assert_bundler_command_called("install")
end
def test_dev_option
- assert_generates_with_bundler dev: true
+ generator([destination_root], dev: true)
+
+ assert_bundler_command_called("install")
rails_path = File.expand_path("../../..", Rails.root)
assert_file "Gemfile", /^gem\s+["']rails["'],\s+path:\s+["']#{Regexp.escape(rails_path)}["']$/
end
def test_edge_option
- assert_generates_with_bundler edge: true
+ generator([destination_root], edge: true)
+
+ assert_bundler_command_called("install")
assert_file "Gemfile", %r{^gem\s+["']rails["'],\s+github:\s+["']#{Regexp.escape("rails/rails")}["']$}
end
@@ -782,23 +787,14 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_gem "spring"
end
+ def test_bundler_binstub
+ assert_bundler_command_called("binstubs bundler")
+ end
+
def test_spring_binstubs
jruby_skip "spring doesn't run on JRuby"
- command_check = -> command do
- @binstub_called ||= 0
-
- case command
- when "install"
- # Called when running bundle, we just want to stub it so nothing to do here.
- when "exec spring binstub --all"
- @binstub_called += 1
- assert_equal 1, @binstub_called, "exec spring binstub --all expected to be called once, but was called #{@install_called} times."
- end
- end
- generator.stub :bundle_command, command_check do
- quietly { generator.invoke_all }
- end
+ assert_bundler_command_called("exec spring binstub --all")
end
def test_spring_no_fork
@@ -976,7 +972,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
template
end
- sequence = ["git init", "install", "exec spring binstub --all", "echo ran after_bundle"]
+ sequence = ["git init", "install", "binstubs bundler", "exec spring binstub --all", "echo ran after_bundle"]
@sequence_step ||= 0
ensure_bundler_first = -> command, options = nil do
assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}"
@@ -993,7 +989,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- assert_equal 4, @sequence_step
+ assert_equal 5, @sequence_step
end
def test_gitignore
@@ -1063,18 +1059,14 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def assert_generates_with_bundler(options = {})
- generator([destination_root], options)
-
+ def assert_bundler_command_called(target_command)
command_check = -> command do
- @install_called ||= 0
+ @command_called ||= 0
case command
- when "install"
- @install_called += 1
- assert_equal 1, @install_called, "install expected to be called once, but was called #{@install_called} times"
- when "exec spring binstub --all"
- # Called when running tests with spring, let through unscathed.
+ when target_command
+ @command_called += 1
+ assert_equal 1, @command_called, "#{command} expected to be called once, but was called #{@command_called} times."
end
end