Andy Harvey
Andy Harvey

Reputation: 12653

Why do Carrierwave and asset pipeline appear to interpret config.asset_host differently?

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

Answers (1)

Frederick Cheung
Frederick Cheung

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

Related Questions