mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
[ActiveStorage] Add ability to use pre-defined variants
This commit is contained in:
parent
ea4acf540e
commit
1adea93265
8 changed files with 117 additions and 8 deletions
|
@ -1,3 +1,25 @@
|
||||||
|
* Add ability to use pre-defined variants.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class User < ActiveRecord::Base
|
||||||
|
has_one_attached :avatar, variants: {
|
||||||
|
thumb: { resize: "100x100" },
|
||||||
|
medium: { resize: "300x300", monochrome: true }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
class Gallery < ActiveRecord::Base
|
||||||
|
has_many_attached :photos, variants: {
|
||||||
|
thumb: { resize: "100x100" },
|
||||||
|
medium: { resize: "300x300", monochrome: true }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
<%= image_tag user.avatar.variant(:thumb) %>
|
||||||
|
```
|
||||||
|
|
||||||
|
*fatkodima*
|
||||||
|
|
||||||
* Add `config.active_storage.web_image_content_types` to allow applications
|
* Add `config.active_storage.web_image_content_types` to allow applications
|
||||||
to add content types (like `image/webp`) in which variants can be processed,
|
to add content types (like `image/webp`) in which variants can be processed,
|
||||||
instead of letting those images be converted to the fallback PNG format.
|
instead of letting those images be converted to the fallback PNG format.
|
||||||
|
|
|
@ -30,6 +30,19 @@ class ActiveStorage::Attachment < ActiveRecord::Base
|
||||||
blob&.purge_later
|
blob&.purge_later
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def variant(transformations)
|
||||||
|
case transformations
|
||||||
|
when Symbol
|
||||||
|
variant_name = transformations
|
||||||
|
transformations = variants.fetch(variant_name) do
|
||||||
|
record_model_name = record.to_model.model_name.name
|
||||||
|
raise ArgumentError, "Cannot find variant :#{variant_name} for #{record_model_name}##{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
blob.variant(transformations)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def identify_blob
|
def identify_blob
|
||||||
blob.identify
|
blob.identify
|
||||||
|
@ -51,6 +64,10 @@ class ActiveStorage::Attachment < ActiveRecord::Base
|
||||||
def dependent
|
def dependent
|
||||||
record.attachment_reflections[name]&.options[:dependent]
|
record.attachment_reflections[name]&.options[:dependent]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def variants
|
||||||
|
record.attachment_reflections[name]&.options[:variants]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ActiveSupport.run_load_hooks :active_storage_attachment, ActiveStorage::Attachment
|
ActiveSupport.run_load_hooks :active_storage_attachment, ActiveStorage::Attachment
|
||||||
|
|
|
@ -40,7 +40,7 @@ module ActiveStorage
|
||||||
# has_one_attached :avatar, service: :s3
|
# has_one_attached :avatar, service: :s3
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
def has_one_attached(name, dependent: :purge_later, service: nil)
|
def has_one_attached(name, dependent: :purge_later, service: nil, variants: {})
|
||||||
validate_service_configuration(name, service)
|
validate_service_configuration(name, service)
|
||||||
|
|
||||||
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
||||||
|
@ -72,7 +72,7 @@ module ActiveStorage
|
||||||
:has_one_attached,
|
:has_one_attached,
|
||||||
name,
|
name,
|
||||||
nil,
|
nil,
|
||||||
{ dependent: dependent, service_name: service },
|
{ dependent: dependent, service_name: service, variants: variants },
|
||||||
self
|
self
|
||||||
)
|
)
|
||||||
ActiveRecord::Reflection.add_attachment_reflection(self, name, reflection)
|
ActiveRecord::Reflection.add_attachment_reflection(self, name, reflection)
|
||||||
|
@ -110,7 +110,7 @@ module ActiveStorage
|
||||||
# has_many_attached :photos, service: :s3
|
# has_many_attached :photos, service: :s3
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
def has_many_attached(name, dependent: :purge_later, service: nil)
|
def has_many_attached(name, dependent: :purge_later, service: nil, variants: {})
|
||||||
validate_service_configuration(name, service)
|
validate_service_configuration(name, service)
|
||||||
|
|
||||||
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
||||||
|
@ -159,7 +159,7 @@ module ActiveStorage
|
||||||
:has_many_attached,
|
:has_many_attached,
|
||||||
name,
|
name,
|
||||||
nil,
|
nil,
|
||||||
{ dependent: dependent, service_name: service },
|
{ dependent: dependent, service_name: service, variants: variants },
|
||||||
self
|
self
|
||||||
)
|
)
|
||||||
ActiveRecord::Reflection.add_attachment_reflection(self, name, reflection)
|
ActiveRecord::Reflection.add_attachment_reflection(self, name, reflection)
|
||||||
|
|
|
@ -610,6 +610,26 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase
|
||||||
assert_match(/Cannot configure service :unknown for User#featured_photos/, error.message)
|
assert_match(/Cannot configure service :unknown for User#featured_photos/, error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "creating variation by variation name" do
|
||||||
|
@user.highlights_with_variants.attach fixture_file_upload("racecar.jpg")
|
||||||
|
variant = @user.highlights_with_variants.first.variant(:thumb).processed
|
||||||
|
|
||||||
|
image = read_image(variant)
|
||||||
|
assert_equal "JPEG", image.type
|
||||||
|
assert_equal 100, image.width
|
||||||
|
assert_equal 67, image.height
|
||||||
|
end
|
||||||
|
|
||||||
|
test "raises error when unknown variant name is used" do
|
||||||
|
@user.highlights_with_variants.attach fixture_file_upload("racecar.jpg")
|
||||||
|
|
||||||
|
error = assert_raises ArgumentError do
|
||||||
|
@user.highlights_with_variants.first.variant(:unknown).processed
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_match(/Cannot find variant :unknown for User#highlights_with_variants/, error.message)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def append_on_assign
|
def append_on_assign
|
||||||
ActiveStorage.replace_on_assign_to_many, previous = false, ActiveStorage.replace_on_assign_to_many
|
ActiveStorage.replace_on_assign_to_many, previous = false, ActiveStorage.replace_on_assign_to_many
|
||||||
|
|
|
@ -584,4 +584,24 @@ class ActiveStorage::OneAttachedTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
assert_match(/Cannot configure service :unknown for User#featured_photo/, error.message)
|
assert_match(/Cannot configure service :unknown for User#featured_photo/, error.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "creating variation by variation name" do
|
||||||
|
@user.avatar_with_variants.attach fixture_file_upload("racecar.jpg")
|
||||||
|
variant = @user.avatar_with_variants.variant(:thumb).processed
|
||||||
|
|
||||||
|
image = read_image(variant)
|
||||||
|
assert_equal "JPEG", image.type
|
||||||
|
assert_equal 100, image.width
|
||||||
|
assert_equal 67, image.height
|
||||||
|
end
|
||||||
|
|
||||||
|
test "raises error when unknown variant name is used" do
|
||||||
|
@user.avatar_with_variants.attach fixture_file_upload("racecar.jpg")
|
||||||
|
|
||||||
|
error = assert_raises ArgumentError do
|
||||||
|
@user.avatar_with_variants.variant(:unknown).processed
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_match(/Cannot find variant :unknown for User#avatar_with_variants/, error.message)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,9 @@ class ActiveStorage::ReflectionTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
reflection = User.reflect_on_attachment(:cover_photo)
|
reflection = User.reflect_on_attachment(:cover_photo)
|
||||||
assert_equal :local, reflection.options[:service_name]
|
assert_equal :local, reflection.options[:service_name]
|
||||||
|
|
||||||
|
reflection = User.reflect_on_attachment(:avatar_with_variants)
|
||||||
|
assert_instance_of Hash, reflection.options[:variants]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "reflection on a singular attachment with the same name as an attachment on another model" do
|
test "reflection on a singular attachment with the same name as an attachment on another model" do
|
||||||
|
@ -28,13 +31,16 @@ class ActiveStorage::ReflectionTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
reflection = User.reflect_on_attachment(:vlogs)
|
reflection = User.reflect_on_attachment(:vlogs)
|
||||||
assert_equal :local, reflection.options[:service_name]
|
assert_equal :local, reflection.options[:service_name]
|
||||||
|
|
||||||
|
reflection = User.reflect_on_attachment(:highlights_with_variants)
|
||||||
|
assert_instance_of Hash, reflection.options[:variants]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "reflecting on all attachments" do
|
test "reflecting on all attachments" do
|
||||||
reflections = User.reflect_on_all_attachments.sort_by(&:name)
|
reflections = User.reflect_on_all_attachments.sort_by(&:name)
|
||||||
assert_equal [ User ], reflections.collect(&:active_record).uniq
|
assert_equal [ User ], reflections.collect(&:active_record).uniq
|
||||||
assert_equal %i[ avatar cover_photo highlights vlogs ], reflections.collect(&:name)
|
assert_equal %i[ avatar avatar_with_variants cover_photo highlights highlights_with_variants vlogs ], reflections.collect(&:name)
|
||||||
assert_equal %i[ has_one_attached has_one_attached has_many_attached has_many_attached ], reflections.collect(&:macro)
|
assert_equal %i[ has_one_attached has_one_attached has_one_attached has_many_attached has_many_attached has_many_attached ], reflections.collect(&:macro)
|
||||||
assert_equal [ :purge_later, false, :purge_later, false ], reflections.collect { |reflection| reflection.options[:dependent] }
|
assert_equal [ :purge_later, :purge_later, false, :purge_later, :purge_later, false ], reflections.collect { |reflection| reflection.options[:dependent] }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -119,9 +119,11 @@ class User < ActiveRecord::Base
|
||||||
|
|
||||||
has_one_attached :avatar
|
has_one_attached :avatar
|
||||||
has_one_attached :cover_photo, dependent: false, service: :local
|
has_one_attached :cover_photo, dependent: false, service: :local
|
||||||
|
has_one_attached :avatar_with_variants, variants: { thumb: { resize: "100x100" } }
|
||||||
|
|
||||||
has_many_attached :highlights
|
has_many_attached :highlights
|
||||||
has_many_attached :vlogs, dependent: false, service: :local
|
has_many_attached :vlogs, dependent: false, service: :local
|
||||||
|
has_many_attached :highlights_with_variants, variants: { thumb: { resize: "100x100" } }
|
||||||
end
|
end
|
||||||
|
|
||||||
class Group < ActiveRecord::Base
|
class Group < ActiveRecord::Base
|
||||||
|
|
|
@ -129,7 +129,7 @@ amazon:
|
||||||
secret_access_key: ""
|
secret_access_key: ""
|
||||||
region: ""
|
region: ""
|
||||||
bucket: ""
|
bucket: ""
|
||||||
upload:
|
upload:
|
||||||
server_side_encryption: "" # 'aws:kms' or 'AES256'
|
server_side_encryption: "" # 'aws:kms' or 'AES256'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -332,6 +332,20 @@ class User < ApplicationRecord
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can configure specific variants per attachment using the `variants` option:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class User < ApplicationRecord
|
||||||
|
has_one_attached :avatar, variants: { thumb: { resize: "100x100" } }
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Call `avatar.variant(:thumb)` to get a thumb variant of an avatar:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
<%= image_tag user.avatar.variant(:thumb) %>
|
||||||
|
```
|
||||||
|
|
||||||
### `has_many_attached`
|
### `has_many_attached`
|
||||||
|
|
||||||
The `has_many_attached` macro sets up a one-to-many relationship between records
|
The `has_many_attached` macro sets up a one-to-many relationship between records
|
||||||
|
@ -382,6 +396,14 @@ class Message < ApplicationRecord
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Configuring specific variants is done the same way as `has_one_attached`, by using the `variants` option:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class Message < ApplicationRecord
|
||||||
|
has_many_attached :images, variants: { thumb: { resize: "100x100" } }
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Attaching File/IO Objects
|
### Attaching File/IO Objects
|
||||||
|
|
||||||
Sometimes you need to attach a file that doesn’t arrive via an HTTP request.
|
Sometimes you need to attach a file that doesn’t arrive via an HTTP request.
|
||||||
|
|
Loading…
Reference in a new issue