- This is a fix needed to unblock
https://github.com/rails/rails/pull/50284,
because Active Storage relies on a Active Record bug.
The problem is best understood with a small snippet:
```
blob = ActiveStorage::Blob.new
ActiveStorage::Attachment.new(blob: blob)
ActiveStorage::Attachment.new(blob: blob)
# Currently:
p blob.attachments #=> #<ActiveRecord::Associations::CollectionProxy []>
# Once the Active Record bug is fixed:
p blob.attachments #=> #<ActiveRecord::Associations::CollectionProxy [#<ActiveStorage::Attachment id: nil, name: nil, record_type: nil, record_id: nil, blob_id: nil, created_at: nil>, #<ActiveStorage::Attachment id: nil, name: nil, record_type: nil, record_id: nil, blob_id: nil, created_at: nil>]>
# Trying to save the blob would result in trying to create 2 attachments which
# fails because of unique constrainsts.
```
### Code path
The real code path that does what the snippet above does is located here:
9c3ffab47c/activestorage/lib/active_storage/attached/many.rb (L52)
It's basically doing this:
```
user.images.attach "photo1.png"
# Initialize a Blob record and an Attachment
user.images.attach "photo2.png"
# Get the Blob from above, create another Attachment
# Initialize a new Blob record and an new Attachment
# rinse and repeat every time `attach` is called
```
Basically each time we call `attach`, we grab the previous blobs that were attached
(and that already have an Attachment record), and
### Solution
- Explicitly set the `inverse_of`, so that it behaves as if #50284 is shipped
- Don't build a new attachment for blob already having one.
### Tests
I didn't add tests, the test suite is already well covered, adding the `inverse_of`
without any changes breaks the test suite already. You can try by running
for instance the `activestorage/test/models/attached/many_test.rb`.
All of ActiveStorage database modeltable nameshave been hard coded.
Therefore, ActiveRecord::Base.(prefix|suffix) were not taken into
consideration. To fix this we remove the hard coded lines. But then we
need to also override an internal method for specifying the prefix
because of a mystical ActiveRecord/ActiveStorage sync issue
(Suffix does not appear to have the issue)
Some tests were refactored to remove hard coded table name references,
making ActiveStorage test suite compatible with ActiveRecord config.
Follow-up to #48290.
The `:is(button, input)[type='submit']` selector does not match `button`
elements that omit the `type` attribute in favor relying on its default
value (which is `"submit"`).
Co-authored-by: Javan Makhmali <javan@javan.us>
When a blob is a representable of kind `previewable`, the preview
image that's being proxied is always the original preview image,
discarding completely the `variation_key` param passed in the request.
This commit fixes this by editing `Preview` and `VariantWithRecord` to
have full synchronized API with `Variant`. this will then allow the
ProxyController to not call `representable#image` but `representable`
instead.
As all 3 classes are now 100% interchangeable, we can deprecate the use
of `Representable#image`. Users of this class won't need to call this
method as it's become obsolete. and in case of `Preview#image`
erroneous.
`ActiveStorage::Variant`, the class used to handle untracked variants,
is lacking some methods to make it compliant with
`ActiveStorage::Representations::ProxyController#send_blob_stream`.
This commit fixes the proxying of untracked variant by adding the
missing methods.
Preprocessing "unpresentable" blobs (neither variabe, nor previewable)
will result in a `ActiveStorage::UnrepresentableError`, which will retry
following `ActiveJob::Base` default retry logic.
This commit discards any blob that's not representable to cleanup the
job adapter queue.
if an empty hash is passed to a preview call (`blob.preview({})`)
We go through the original preview instead of regenerating a variation
based on the original preview image which would result in a performance
penalty
This change is necessary to address a potential issue that could arise when a button or an input of type submit contains child elements, such as spans, icons, or other HTML elements.
Currently, ActiveStorage's `didClick` event listener checks the target of the click event to determine if a submit button was clicked. The target property of the event refers to the specific HTML element that was clicked.
In cases where a submit button contains child elements, and one of these child elements is the element that actually gets clicked, the target would refer to this child element, not the button itself.
Since the `didClick` function checks if the target is a button or an input of type submit, this check would fail, and the button wouldn't be stored in `submitButtonsByForm`.
As a result, if the form is then submitted after a direct upload, the first submit button in the form could be incorrectly used to submit the form, even if a different button was originally clicked. This could cause unexpected behavior, as different submit buttons might be intended to trigger different actions on form submission.
By using the `event.currentTarget` instead, we'll get back the button or an input of type submit. This way we ensure that the correct button is stored in `submitButtonsByForm`, even if the click event was triggered by a child element of the button. This addresses the issue and ensures that the correct button is used to submit the form after a direct upload.
https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
Prior to this commit, `ActiveStorage::Preview#processed` would only
process the preview image, not the specified variant of the preview
image. For example, `thumb = attached_pdf.preview(:thumb).processed`
would only generate the full-sized preview image, not the `:thumb`
variant of it, until e.g. `thumb.url` was called.
This commit updates `ActiveStorage::Preview#processed` to generate both
the full-sized preview image and the requested variant.
Co-authored-by: chaadow <chedli@hoggo.com>
`ActiveStorage::Preview#url` delegates to the preview image's variant,
which in turn delegates to the variant's blob. Thus when the variant
has already been processed and strict loading is enabled, the
association chain of `preview_image_attachment` => `blob` =>
`variant_records` => `image_attachment` => `blob` must be fully
pre-loaded; otherwise, `ActiveStorage::Preview#url` will raise an
`ActiveRecord::StrictLoadingViolationError`.
The transform_job crahes if you want to preprocess a previewable files.
This commit fixes that by using the blob's `representation` method to
process variants or previews.
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
This adds `linguist-generated` and `linguist-vendored` attributes where
appropriate to suppress the files in diffs and exclude the files from
the project's language stats on GitHub.
See https://github.com/github/linguist for more information.
`service` kwarg in `has_one_attached` and `has_many_attached` methods accepts
only symbols as values. It allows to define a `service` at a class-level context. But in
one of our requirements, we wanted to upload files based on user's region due to
some regulations.
So in order to allow defining a `service` at instance-level context,
this PR makes the changes to accept `service` as a Proc as well.
Co-authored-by: Jonathan Hefner <jonathan@hefner.pro>
ActiveStorage::Attachment records are not directly maintained by
ActiveStorage and it may not be feasible or desired to update the record
when its attachment is saved.
These methods were added in b221a4dc43
But they don't seem to be used by Rails internally or have any tests, so
I assume they were added by accident?
As they both seem to be marked as :nodoc: on ActiveStorage::Blob, we can
remove them without a deprecation warning.
If we decide to keep these methods, they should be added to
ActiveStorage::VariantWithRecord as well. No one complaining about there
methods missing on ActiveStorage::VariantWithRecord is another reason
these methods aren't used.
Pluses cannot be used to create code blocks when the content includes a
space.
Found using a regular expression:
```bash
$ rg '#\s[^+]*\+[^+]*\s[^+]*\S\+'
```
Before this commit, when a file was uploaded to a mirror service,
the file upload to each service (primary and mirrors) was happening
synchronously:
```rb
def upload(key, io, checksum: nil, **options)
each_service.collect do |service|
io.rewind
service.upload key, io, checksum: checksum, **options
end
end
```
In this commit, we change this behavior to only upload the file to the
primary service synchronously and then enqueue a job to upload the
file to the mirrors asynchronously.
ActiveStorage variants are processed on the fly when they are needed but
sometimes we're sure that they are accessed and want to processed them
upfront.
`preprocessed` option is added when declaring variants.
```
class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumb, resize_to_limit: [100, 100], preprocessed: true
end
end
```
We are using two libraries to do the same job. This commit removes the usage of mini_mime in favour of Marcel instead.
Changes are as follows:
- Replace MiniMime lookup by extension with Marcel Mimetype for lookup with extension
- Replaces usage of MiniMime lookup by content type to fetch extension with usage of Marcel Magic lookup. Marcel has multiple extentions being returned, we pick the first one. MiniMime always returns just one
- Removes specs which we specifically checking failing identification issue of MiniMine on jpeg images
- Removes mini_mime from gemspec
Background:
When creating active storage variants, `ActiveStorage::VariantRecord` is
inserted, then a file is uploaded. Because upload can be failed, the
file can be missing even though `ActiveStorage::VariantRecord` exists.
When a file is missing, we need to delete the corresponding
`ActiveStorage::VariantRecord` but there's no API to delete just one
variant e.g., `blob.variant(resize_to_limit: [100, 100]).destroy`.
Co-authored-by: Yuichiro NAKAGAWA <ii.hsif.drows@gmail.com>
Co-authored-by: Ryohei UEDA <ueda@anipos.co.jp>
ActiveStorage::Blob.create_and_upload!(io: file_fixture("test.jpg"), filename: "dummy")
The code looks reasonable, but actually results in an infinite loop
before this change. Now an error is propagated to the user instead.
Closes#46507