Skip to content

Conversation

frantisekrokusekpa
Copy link
Owner

@frantisekrokusekpa frantisekrokusekpa commented Dec 31, 2019

Summary

Hello Rails Team!

First of all I want to thank you for all job that you make here!!! Rails is a wonderfull framework to work with.

Here is my issue: ActiveStorage custom storage path configuration

Here is ou business issue: We use the apartment gem to split our datas on a client basis and we want to make the same with uploaded files and store them in separate folders.

Rails versions: >5.2

thoughtbot has made a wonderfull paperclip gem, but since it is depracated we are in the process of switching to ActiveStorage all our attachments management.

In Paperclip there is a fine feature where you can choose a specific storage path for each of the model attachable attribute and interpolate values in it as follows:

# app/models/user.rb

# in Paperclip
has_attached_file :avatar,
                  path: ':tenant/users/:user_id/:hash/:filename'

The idea is to make this possible in ActiveStorage too and have something like that:

# app/models/user.rb

# in futur ActiveStorage
has_one_attached :avatar,
                 path: ':tenant/users/:id/:unique_secure_token'

For the moment we have made a monkey_patching to implement those storage path by changing the ActiveStorage::Blob#key.

# app/models/user.rb

def document=(attachable)
  document.attach(create_blob(attachable))
end

def attachable_storage_path
  [
    Apartment::Tenant.current.parameterize,
    'users',
    id,
    ActiveStorage::Blob.generate_unique_secure_token
  ].compact.join('/')
end
# app/models/application_record.rb

def attachable_storage_path
  raise NotImplementedError
end

def create_blob(attachable)
  return if attachable.nil?

  ActiveStorage::Blob.new.tap do |blob|
    blob.filename = attachable.original_filename
    blob.key = attachable_storage_path
    blob.upload(attachable)
    blob.save!
  end
end

I have began to make a PR on ActiveStorage but want to have your feedback on this feature before digging deeper in this problematic and validate the right direction! Some questions to guide:

  • Is the ActiveStorage::Blob#keythe right attribute to integrate this feature?
  • Do you think it is worth to make it interpolable as in Paperclip, to avoid being requested to create an #attachable_storage_path method?

Here it is: ActiveStorage custom storage path configuration

Thank you for your feedback and advices!

Other Information

Copy link

@jgigault jgigault left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's too early to submit a review, but it may help you going further!

@@ -77,5 +79,9 @@ def find_or_build_blob
def attachment_service_name
record.attachment_reflections[name].options[:service_name]
end

def attachment_key(key: key)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of the argument? Are you willing to use it somewhere?

@sedubois
Copy link

@frantisekrokusekpa do you still have any plans to submit this PR to Rails? It would be quite helpful to better organize files.

@@ -116,6 +116,14 @@ def create_before_direct_upload!(key: nil, filename:, byte_size:, checksum:, con
def generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THis is great work did you finish building this feature?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I will try, now I have more time.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ispirett the PR have been pushed to rails. Do not hesitate to upvote!

@frantisekrokusekpa frantisekrokusekpa force-pushed the enable_custom_blob_key_configuration branch 2 times, most recently from 393ebf6 to 10a5a08 Compare December 18, 2020 22:21
@frantisekrokusekpa
Copy link
Owner Author

@jgigault and @guillaumebriday would you mind to have a quick review please?

@sedubois
Copy link

The Rails 6.1 changelog contains this info :

You can optionally provide a custom blob key when attaching a new file:

user.avatar.attach key: "avatars/#{user.id}.jpg",
  io: io, content_type: "image/jpeg", filename: "avatar.jpg"
Active Storage will store the blob's data on the configured service at the provided key.

George Claghorn

So is this PR still needed?

@frantisekrokusekpa
Copy link
Owner Author

The Rails 6.1 changelog contains this info :

You can optionally provide a custom blob key when attaching a new file:

user.avatar.attach key: "avatars/#{user.id}.jpg",
  io: io, content_type: "image/jpeg", filename: "avatar.jpg"
Active Storage will store the blob's data on the configured service at the provided key.

George Claghorn

So is this PR still needed?

Thanks Sébastien, I am aware of that change. This PR is tackling an other part of that feature.

@frantisekrokusekpa frantisekrokusekpa force-pushed the enable_custom_blob_key_configuration branch from 60e2f59 to c9c1bac Compare January 3, 2021 17:01
@frantisekrokusekpa
Copy link
Owner Author

Please upvote/comment the final PR on Rails 🎉 rails#41004 Thanks

@frantisekrokusekpa frantisekrokusekpa force-pushed the enable_custom_blob_key_configuration branch from 372a803 to d2a92a8 Compare January 4, 2021 13:44
@frantisekrokusekpa frantisekrokusekpa force-pushed the enable_custom_blob_key_configuration branch from e912bbf to 80dfe91 Compare September 26, 2021 21:44
@frantisekrokusekpa frantisekrokusekpa force-pushed the enable_custom_blob_key_configuration branch from 1892071 to 1ac0891 Compare November 26, 2021 16:41
kamipo and others added 13 commits February 9, 2022 17:33
Fix empty request inside helpers test
…on-assert-nothing-raised

Increment assertions count on assert_nothing_raised
In cache stores prepending `LocalCache`, serialized `Entry`
instances are stored in `LocalCache::LocalStore`. This
speeds up hot key lookups without a network roundtrip in a
context of a single given request.

However, with these entries being stored in their serialized
form, `#read_multi_entries` returns them directly to cache
consumers.

Instead, we will now deserialize these entries first.
This commit adds documentation to the constants and methods that are
part of Active Model's Attributes API. So far this API has been hidden
with the :nodoc: flag since its inception in Active Record and
subsequent move to Active Model (rails#30920 and rails#30985); as the API matures
and gets ready for public usage, visible documentation for its endpoints
becomes necessary.

The classes and modules being documented and publicized by this commit
are the main `Attributes` module, the `Type` namespace, and all the
standard attribute type classes included in the current API, which users
will be able to extend and replicate to suit their customization needs.

Some private modules are also receiving documetation, although they will
continue using the :nodoc: flag. Those are `Attribute` and
`AttributeSet`. Although they remain private I found useful to add some
comments to describe their responsibilities.
Documentation for Active Model Attributes [ci-skip]
…g-warning-in-test

Fix ruby 2.7 keyword args warning for postgresql reconnection_error test
The `filter_parameters` configuration includes a list of filters in the
latest `filter_parameter_logging` initializer template.
This updates the guides to reflect those changes.
Running the Action Pack tests outputs a warning:

    ./actionpack/test/controller/test_case_test.rb:1007: warning: instance variable @counter not initialized

Surrounding the line with silence_warnings cleans up the output.
Update parameter filter logging guides [ci-skip]
rafaelfranca and others added 30 commits February 24, 2022 15:12
Also fix AbstractMysqlAdapterTest
…_state

Use finish_with_state inside Rack::Logger
The database server discards prepared statements when the client disconnects
This is unfortunately going to force a change into all external adapter
subclasses, but I'm going to be making other changes too, so it's not
the worst of them... and it's just so confusing at the moment to have
"@connection" sometimes mean the AR adapter and sometimes the inner
connection, depending on exactly which class we're in.
Rename the raw connection ivar to `@raw_connection`
This gets an annoyingly verbose comment, because otherwise it could be
removed and continue passing almost all the time.
This adds a very awkward test-private method that attempts to detect
whether a raw connection physically has a transaction open -- which is
quite Not Great, but this seems important to get right.
Modern browsers don't render this HTML so it goes unused in practice.
The delivered bytes are therefore a small waste (although very small)
and unnecessary and could be optimized away.

Additionally, the HTML fails validation. Using the W3C v.Nu, we see the
following errors:

    Warning: Consider adding a lang attribute to the html start tag to declare the language of this document.

    Error: Start tag seen without seeing a doctype first. Expected <!DOCTYPE html>.

    Error: Element head is missing a required instance of child element title.

These errors may surface in site-wide compliance tests (either internal
tests or external contractual tests). Avoid the false positives by
removing the HTML.

While these warnings and errors could be resolved, it would be simpler
on future maintenance to remove the body altogether (especially as it
isn't rendered by the browser). As the same string is copied around a
few places, this removes multiple touch points to resolve the current
validation errors as well as new ones.

Many other frameworks and web servers don't include an HTML body on
redirect, so there isn't a reason for Rails to do so. By removing the
custom Rails HTML, there are fewing "fingerprints" that a malicious bot
could use to identify the backend technologies.

Application controllers that wish to add a response body after calling
redirect_to can continue to do so.
Remove body content from redirect responses
Ruby 2.6 added block argument processing to `Enumerable#to_h`.
https://bugs.ruby-lang.org/issues/15143

Rails 7 requires Ruby 2.7.0 or higher, so the new feature can use it.
`Style/MapToHash` cop will detect it. And this cop in the `Style` department,
but this seems to improve performance as follows:

```ruby
# map_to_hash.rb
require 'benchmark/ips'

ARRAY = (1..100).to_a
HASH = {foo: 1, bar: 2}

Benchmark.ips do |x|
  x.report('array.map.to_h') { ARRAY.map { |v| [v, v * 2] }.to_h }
  x.report('array.to_h')     { ARRAY.to_h { |v| [v, v * 2] } }

  x.compare!
end

Benchmark.ips do |x|
  x.report('hash.map.to_h') { HASH.map { |k, v| [k.to_s, v * 2] }.to_h }
  x.report('hash.to_h')     { HASH.to_h { |k, v| [k.to_s, v * 2] } }

  x.compare!
end
```

```console
% ruby map_to_hash.rb
Warming up --------------------------------------
      array.map.to_h     9.063k i/100ms
          array.to_h     9.609k i/100ms
Calculating -------------------------------------
      array.map.to_h     89.063k (± 3.9%) i/s -    453.150k in  5.096572s
          array.to_h     96.449k (± 1.7%) i/s -    490.059k in  5.082529s

Comparison:
          array.to_h:    96448.7 i/s
      array.map.to_h:    89063.4 i/s - 1.08x  (± 0.00) slower

Warming up --------------------------------------
       hash.map.to_h   106.284k i/100ms
           hash.to_h   149.354k i/100ms
Calculating -------------------------------------
       hash.map.to_h      1.102M (± 2.2%) i/s -      5.527M in   5.019657s
           hash.to_h      1.490M (± 0.9%) i/s -      7.468M in   5.013264s

Comparison:
           hash.to_h:  1489707.0 i/s
       hash.map.to_h:  1101561.5 i/s - 1.35x  (± 0.00) slower
```

`Style/MapToHash` cop ... https://docs.rubocop.org/rubocop/1.25/cops_style.html#stylemaptohash
…roller_doc

Edit ActionController Base API docs [ci-skip]
…n-activemodel-testcases

Fixed inconsistencies in ActiveModel test cases
…uide

Add Permissions-Policy header to the security guide [ci-skip]
Return the blob/blobs when #attach is able to save the record
Switch from fork to queue_classic beta release
It's not a common operation, but still, no point running two queries
when one will do.
This allows us to simulate an immediately-nested transaction without
ever needing to begin or commit the child: those operations are no-ops,
and we need only run a command if the child rolls back.

Further, we can use a single query to implement that rollback: where the
parent is a savepoint, the standard behaviour is to roll back to
immediately _after_ the savepoint was set. Where the parent is a
top-level transaction, most databases support ROLLBACK AND CHAIN.
Calling `skip_forgery_protection` without first calling
`protect_from_forgery`--either manually or through default
settings--raises an `ArgumentError` because `verify_authenticity_token`
has not been defined as a callback.

Since Rails 7.0 adds `skip_forgery_protection` to the
`Rails::WelcomeController` (PR rails#42864), this behavior means that setting
`default_protect_from_forgery` to false and visiting the Rails Welcome
page (`/`) raises an error.

This behavior also created an issue for `ActionMailbox` that was
previously fixed in the Mailbox controller by running
`skip_forgery_protection` only if `default_protect_from_forgery` was
true (PR rails#35935).

This PR addresses the underlying issue by setting the `raise` option for
`skip_before_action` to default to false inside
`skip_forgery_protection`.

The fix is implemented in `request_forgery_protection.rb`. The change to
`ActionMailbox`'s `base_controller.rb` removes the now-unnecessary
check of `default_protect_from_forgery`.

The tests added in `request_forgery_protection_test.rb` and
`routing_test.rb` both raise an error when run against the current
codebase and pass with the changes noted above.
Rollback nested transactions by restarting the parent where possible
Allow skip_forgery_protection if no protection set
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.