rails: `ActiveStorage::Current.host` not set causing disk service to throw `URI::InvalidURIError`
Steps to reproduce
config/storage.yml
:
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
local:
service: Disk
root: <%= Rails.root.join("storage") %>
public: true
config/environments/development.rb
:
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
app/models/user.rb
:
class User < ApplicationRecord
validates :username, presence: true
has_one_attached :avatar
Expected behavior
In rails console:
irb> ActiveStorage::Current.host
=> nil
irb> User.find_by_username('david').avatar.url
=> [should generate permanent url]
Actual behavior
In rails console:
irb> ActiveStorage::Current.host
=> nil
irb> User.find_by_username('david').avatar.url
URI::InvalidURIError (bad URI(is not URI?): nil)
System configuration
Rails version:
Rails 6.1.0
Ruby version:
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19]
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 38
- Comments: 30 (14 by maintainers)
There’s no documents about setting
ActiveStorage::Current.host
before generating public permanent url using the Disk service.For those searching for this error from Google, note the proper usage in views is to use “url_for” such as:
url_for(profile.image)
and not
profile.image.url
Interestingly, Rails doesn’t see this in their test suite because they set this value during setup:
https://github.com/rails/rails/blob/9492339979e94570dee00d071be0ef255065837a/activestorage/test/test_helper.rb#L52-L54
For Rspec users, you can workaround this by setting it the same way in a spec helper:
For those looking for a quick fix, I’ve been able to workaround this by monkey patching
ActiveStorage::Blob#service_url
For those using ActionController to handle requests, there is a Concern available that will set
ActiveStorage::Current.host
torequest.base_url
as abefore_action
:https://github.com/rails/rails/blob/master/activestorage/app/controllers/concerns/active_storage/set_current.rb
But, this is only a partial solution as it still wouldn’t work outside of your controllers.
Since I’m using Grape for my request handling, my workaround right now is to just explicitly set
ActiveStorage::Current.host
before I use.url
. Luckily, I only use the Disk service in testing and development.It would be ideal if we could set this value in our config, similar to how we can set url options for ActionMailer:
config.action_mailer.default_url_options = { host: 'localhost:3000' }
Perhaps there is a better way?
Also seeing this issue. Is there any way to set this host globally e.g. via a configuration, as others have suggested? If that seems reasonable to the maintainers of Rails, I could also take a stab at implementing it myself.
2023 and this is still a problem. 😡
Presumably #39566 will resolve this by creating a system for URLs outside of the controller context. I’m not sure of a workaround for now, as we punted on our AS work once we ran into this issue.
^^^ This approach won’t work with my rSpec tests.
I added your lines in my
rails_helper.rb
but it is stillnil
. I also placedActiveStorage::Current.host = "http://example.com
in the model itself, without success: WON’T WORKONLY when I place that right before calling the
.url
method, then my tests pass:WORKS
What I’ve done now is placing
include ActiveStorage::SetCurrent
in myApplicationController
. But I am not sure if this how it should be…Normally I expect that this should be set magically in the background.
After giving it some thought I think the correct solution would be just replacing
ActiveStorage::Current
which is a per-thread config, for a global config in theDiskService
definition (it only applies here). Something like:thoughts?
I’ll leave here a reproduction script. @rafaelfranca If you agree this is something we should fix, I’m willing to help.
I fixed my rspec by adding
include ActiveStorage::SetCurrent
to my controller. Non of the other suggesions did anything
This actually works and lets my RSpec test suite pass.
@rafaelfranca - this really should not be necessary. There is absolutely no documentation on this hidden “feature” in ActiveStorage. Could you please prescribe how one is supposed to set
ActiveStorage::Current.url_options
without resorting to the above? I’m happy to even update the Rails Guide for Active Storage or issue a Pull Request for the defaultDisk
Service file that incorporates the above.Note: none of the solutions below worked in my Rails 7.0.5 application. In my opinion, these solutions are better and more intuitive than creating a custom Disk Service so why wouldn’t they work in the
test
environment?In
test.rb
environment fileIn
rails_helper.rb
- RSpec configuration file:This fixed it for me! Thank you so much!
Another clean solution is to implement a custom Active Storage service, extending
ActiveStorage::Service::DiskService
, and overriding#url_options
. For instance:I believe it is usually okay to do this in test or development environments.
@intrip Ah yeah, I totally missed that. Good find!