Reputation: 12653
This issue occurs in a Prawn PDF generator in my Rails app, where I have the following line:
image open(@user.avatar.url)
In tests this line began failing with the following error:
No such file or directory @ rb_sysopen - /images/fallback/default.png
Avatar is a Carrierwave uploader, with a default image (see here)
def default_url(*args)
ActionController::Base.helpers.asset_path("images/fallback/default.png" )
end
This seemed like it would be an easy fix — define the asset host in test.rb
config.asset_host = Rails.root.join('app', 'assets').to_s
but then all my JS enabled feature tests began failing
Capybara::Poltergeist::JavascriptError:
One or more errors were raised in the Javascript code on the page.
ReferenceError: Can't find variable: SomeVariable
because the paths are being constructed with two assets
, e.g.
...app/assets/assets/jquery..
I am clearly doing something "unconventional" that goes against the Rails way of doing things. So I'm wondering what the convention is here.
One solution would be to move where I'm defining the asset folder from test.rb to the uploader.
#config/environments/test.rb
config.asset_host = Rails.root.join('app').to_s
#app/uploaders/avatar.eb
def default_url(*args)
ActionController::Base.helpers.asset_path("assets/images/fallback/default.png" )
end
but this would mean that helpers.asset_path
is not in fact calling the asset path but the app folder, which again seems to go against Rails convention.
I had thought the localhost would work
config.asset_host = "http://localhost"
but again JS files are failing
I'm sure I'm overlooking something obvious here. Grateful for pointers in the right direction.
Upvotes: 0
Views: 565
Reputation: 84114
I think this boils down to mixing up of various meanings of path and URL.
First off asset_host
is used when you want to serve your asset files from a different server to your application (ie from a cdn). If you look at the source to asset_path
rails first computes the path to the asset and then prepends the host. This creates a URL that the browser will later try to fetch, so prepending a path like /Users/andy/some_app
doesn't make sense
Setting it to http://localhost
doesn't work because that assumes the default http port, whereas capybara runs your app on a random port. You shouldn't ever need to set asset_host
in the test environment.
Moving on to carrierwave, avatar.url
also provides the URL (in your case just the path portion) from the point of view of the browser - it's designed to be embedded in an img tag for example. The open
method on the other hand know nothing about browsers and servers and just wants a path to a file on the local filesystem.
If you want a local filesystem path then you need avatar.path
method. As far as I know, there is no equivalent of default_url
for path, so you need to be prepared for it to return nil (and replace it with the path to your fallback instead)
#config/environments/test.rb
# do not set config.asset_host
#pdf_generator.rb
image open(@user.avatar.path)
#app/uploaders/avatar.rb
def path(*args)
if model.avatar?
super
else
"#{Rails.root}/app/assets/images/fallback/" + ["default_",version_name, ".png"].compact.join('')
end
end
Upvotes: 2