aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock1
-rw-r--r--actionmailer/test/test_helper_test.rb2
-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--activerecord/test/cases/arel/attributes/math_test.rb20
-rw-r--r--activerecord/test/cases/arel/select_manager_test.rb2
-rw-r--r--activerecord/test/cases/arel/visitors/postgres_test.rb6
-rw-r--r--activerecord/test/cases/arel/visitors/to_sql_test.rb2
-rw-r--r--activerecord/test/cases/relation/select_test.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--activesupport/test/cache/behaviors/cache_store_behavior.rb2
-rw-r--r--guides/source/active_record_basics.md2
-rw-r--r--guides/source/development_dependencies_install.md28
-rw-r--r--railties/lib/rails/commands/server/server_command.rb2
23 files changed, 245 insertions, 128 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/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb
index dcea23aabb..d31170706b 100644
--- a/actionmailer/test/test_helper_test.rb
+++ b/actionmailer/test/test_helper_test.rb
@@ -290,7 +290,7 @@ class TestHelperMailerTest < ActionMailer::TestCase
end
end
- def test_assert_enqueued_email_with_with_no_block_wiht_parameterized_args
+ def test_assert_enqueued_email_with_with_no_block_with_parameterized_args
assert_nothing_raised do
silence_stream($stdout) do
TestHelperMailer.with(all: "good").test_parameter_args.deliver_later
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/activerecord/test/cases/arel/attributes/math_test.rb b/activerecord/test/cases/arel/attributes/math_test.rb
index f3aabe4f60..41eea217c0 100644
--- a/activerecord/test/cases/arel/attributes/math_test.rb
+++ b/activerecord/test/cases/arel/attributes/math_test.rb
@@ -6,35 +6,35 @@ module Arel
module Attributes
class MathTest < Arel::Spec
%i[* /].each do |math_operator|
- it "average should be compatiable with #{math_operator}" do
+ it "average should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].average.public_send(math_operator, 2)).to_sql.must_be_like %{
AVG("users"."id") #{math_operator} 2
}
end
- it "count should be compatiable with #{math_operator}" do
+ it "count should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].count.public_send(math_operator, 2)).to_sql.must_be_like %{
COUNT("users"."id") #{math_operator} 2
}
end
- it "maximum should be compatiable with #{math_operator}" do
+ it "maximum should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].maximum.public_send(math_operator, 2)).to_sql.must_be_like %{
MAX("users"."id") #{math_operator} 2
}
end
- it "minimum should be compatiable with #{math_operator}" do
+ it "minimum should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].minimum.public_send(math_operator, 2)).to_sql.must_be_like %{
MIN("users"."id") #{math_operator} 2
}
end
- it "attribute node should be compatiable with #{math_operator}" do
+ it "attribute node should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].public_send(math_operator, 2)).to_sql.must_be_like %{
"users"."id" #{math_operator} 2
@@ -43,35 +43,35 @@ module Arel
end
%i[+ - & | ^ << >>].each do |math_operator|
- it "average should be compatiable with #{math_operator}" do
+ it "average should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].average.public_send(math_operator, 2)).to_sql.must_be_like %{
(AVG("users"."id") #{math_operator} 2)
}
end
- it "count should be compatiable with #{math_operator}" do
+ it "count should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].count.public_send(math_operator, 2)).to_sql.must_be_like %{
(COUNT("users"."id") #{math_operator} 2)
}
end
- it "maximum should be compatiable with #{math_operator}" do
+ it "maximum should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].maximum.public_send(math_operator, 2)).to_sql.must_be_like %{
(MAX("users"."id") #{math_operator} 2)
}
end
- it "minimum should be compatiable with #{math_operator}" do
+ it "minimum should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].minimum.public_send(math_operator, 2)).to_sql.must_be_like %{
(MIN("users"."id") #{math_operator} 2)
}
end
- it "attribute node should be compatiable with #{math_operator}" do
+ it "attribute node should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].public_send(math_operator, 2)).to_sql.must_be_like %{
("users"."id" #{math_operator} 2)
diff --git a/activerecord/test/cases/arel/select_manager_test.rb b/activerecord/test/cases/arel/select_manager_test.rb
index 6b10d0b612..c17487ae88 100644
--- a/activerecord/test/cases/arel/select_manager_test.rb
+++ b/activerecord/test/cases/arel/select_manager_test.rb
@@ -278,7 +278,7 @@ module Arel
@m2.where(table[:age].lt(99))
end
- it "should interect two managers" do
+ it "should intersect two managers" do
# FIXME should this intersect "managers" or "statements" ?
# FIXME this probably shouldn't return a node
node = @m1.intersect @m2
diff --git a/activerecord/test/cases/arel/visitors/postgres_test.rb b/activerecord/test/cases/arel/visitors/postgres_test.rb
index ba9cfcfc64..f7f2c76b6f 100644
--- a/activerecord/test/cases/arel/visitors/postgres_test.rb
+++ b/activerecord/test/cases/arel/visitors/postgres_test.rb
@@ -215,7 +215,7 @@ module Arel
}
end
- it "should know how to generate paranthesis when supplied with many Dimensions" do
+ it "should know how to generate parenthesis when supplied with many Dimensions" do
dim1 = Arel::Nodes::GroupingElement.new(@table[:name])
dim2 = Arel::Nodes::GroupingElement.new([@table[:bool], @table[:created_at]])
node = Arel::Nodes::Cube.new([dim1, dim2])
@@ -241,7 +241,7 @@ module Arel
}
end
- it "should know how to generate paranthesis when supplied with many Dimensions" do
+ it "should know how to generate parenthesis when supplied with many Dimensions" do
group1 = Arel::Nodes::GroupingElement.new(@table[:name])
group2 = Arel::Nodes::GroupingElement.new([@table[:bool], @table[:created_at]])
node = Arel::Nodes::GroupingSet.new([group1, group2])
@@ -267,7 +267,7 @@ module Arel
}
end
- it "should know how to generate paranthesis when supplied with many Dimensions" do
+ it "should know how to generate parenthesis when supplied with many Dimensions" do
group1 = Arel::Nodes::GroupingElement.new(@table[:name])
group2 = Arel::Nodes::GroupingElement.new([@table[:bool], @table[:created_at]])
node = Arel::Nodes::RollUp.new([group1, group2])
diff --git a/activerecord/test/cases/arel/visitors/to_sql_test.rb b/activerecord/test/cases/arel/visitors/to_sql_test.rb
index ce836eded7..e8ac50bfa3 100644
--- a/activerecord/test/cases/arel/visitors/to_sql_test.rb
+++ b/activerecord/test/cases/arel/visitors/to_sql_test.rb
@@ -427,7 +427,7 @@ module Arel
compile(node).must_equal %(("products"."price" - 7))
end
- it "should handle Concatination" do
+ it "should handle Concatenation" do
table = Table.new(:users)
node = table[:name].concat(table[:name])
compile(node).must_equal %("users"."name" || "users"."name")
diff --git a/activerecord/test/cases/relation/select_test.rb b/activerecord/test/cases/relation/select_test.rb
index 0577e6bfdb..dec8a6925d 100644
--- a/activerecord/test/cases/relation/select_test.rb
+++ b/activerecord/test/cases/relation/select_test.rb
@@ -7,7 +7,7 @@ module ActiveRecord
class SelectTest < ActiveRecord::TestCase
fixtures :posts
- def test_select_with_nil_agrument
+ def test_select_with_nil_argument
expected = Post.select(:title).to_sql
assert_equal expected, Post.select(nil).select(:title).to_sql
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/activesupport/test/cache/behaviors/cache_store_behavior.rb b/activesupport/test/cache/behaviors/cache_store_behavior.rb
index ae272e87d7..e17c09ba39 100644
--- a/activesupport/test/cache/behaviors/cache_store_behavior.rb
+++ b/activesupport/test/cache/behaviors/cache_store_behavior.rb
@@ -148,7 +148,7 @@ module CacheStoreBehavior
end
end
- # Use strings that are guarenteed to compress well, so we can easily tell if
+ # Use strings that are guaranteed to compress well, so we can easily tell if
# the compression kicked in or not.
SMALL_STRING = "0" * 100
LARGE_STRING = "0" * 2.kilobytes
diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md
index d90ea2e26a..fad4c19827 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -115,7 +115,7 @@ to Active Record instances:
* `created_at` - Automatically gets set to the current date and time when the
record is first created.
* `updated_at` - Automatically gets set to the current date and time whenever
- the record is updated.
+ the record is created or updated.
* `lock_version` - Adds [optimistic
locking](http://api.rubyonrails.org/classes/ActiveRecord/Locking.html) to
a model.
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/commands/server/server_command.rb b/railties/lib/rails/commands/server/server_command.rb
index 2c5440d9ec..9d517f3239 100644
--- a/railties/lib/rails/commands/server/server_command.rb
+++ b/railties/lib/rails/commands/server/server_command.rb
@@ -286,7 +286,7 @@ module Rails
original_options.concat [ "-u", using ]
else
# Use positional internally to get around Thor's immutable options.
- # TODO: Replace `using` occurences with `options[:using]` after deprecation removal.
+ # TODO: Replace `using` occurrences with `options[:using]` after deprecation removal.
@using = options[:using]
end
end