Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a digest-aware asset for default_url in rails4?

In rails 3, to use an asset from the pipeline as the default_url in carrierwave's uploader, you did something like the following:

class MyUploader
  # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
  include Sprockets::Helpers::RailsHelper
  include Sprockets::Helpers::IsolatedHelper

  def default_url
    # For Rails 3.1+ asset pipeline compatibility:
    asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
  end
end

In rails 4, the pipeline has been abstracted into the sprockets-rails gem/railtie, so the above will give you:

uninitialized constant Sprockets::Helpers

Looking at the sprockets-rails gem, the replacement helper would seem to be Sprockets::Rails::Helper. But with this module included, asset_path("fallback/default.png") returns, simply:

# wrong:
"/fallback/default.png"

not the asset- and digest-aware URL that I expect:

"/assets/fallback/default-b3beee1588afe2ae582c64888cd007db.png"

How can I get the correct asset_path behavior outside of a view?

like image 902
jemmons Avatar asked May 17 '13 13:05

jemmons


1 Answers

TL;DR:

Don't include anything. Use the helper proxy instead, like this:

def default_url
  ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
end

The Gory Details:

Sprockets::Rails::Helper makes use of a sprockets manifest to properly locate and digest assets. Once it got abstracted into a gem for rails 4, this manifest started getting set by the railtie. So simply include-ing the module's methods into our class wasn't enough to pull that manifest in, and asset_path (mis)behaved accordingly.

Thankfully, there is the oft-overlooked ActionController::Base.helpers class method:

Provides a proxy to access helpers methods from outside the view.

Like the docs say, it acts as a helper proxy and is probably what should have been used for this purpose from the begining (including helper modules into your namespace runs the risk of polluting your class with unused methods and potentially overwriting stuff, so using the proxy is the better alternative).

There might yet be some equivalent of Sprockets::Helpers::IsolatedHelper that hacks enough together to give Sprockets::Rails::Helper what it needs to get the job done. But the proxy is, IMHO, a much more elegant solution. So I've stopped searching.

like image 98
jemmons Avatar answered Oct 17 '22 16:10

jemmons