I have been working on an upgrade to Rails 3.1 from 2.3.11. One of the main hurdles to clear is the conversion to the asset pipeline. In this process I decided to convert my css to sass (scss). In rails 3.1 all assets delivered through the pipeline receive a hash appended to the filename in production. Because of this, all the images referenced in my css now needed to use the image-path or image-url helpers in sass. The issue is, that even though I have set my ENV['RAILS_RELATIVE_URL_ROOT'] in my environment.rb file sass asset helper fail to include the relative_url_root.
Just for clarity, to add the relative_url_root in rails 3.1, I added the following line to my environment.rb file:
ENV['RAILS_RELATIVE_URL_ROOT'] = '/foo'
and add the following line to my virtual host:
RailsBaseURI /foo
This strategy seems to work fine for all links and such. It is just the asset helpers in sass that don't seem to work properly. Any ideas would be appreciated.
After a bit of digging around, I have found the issue. The issue is in Rails, specifically Sprockets::Helpers::RailsHelper::AssetPaths#compute_public_path. Sprockets::Helpers::RailsHelper::AssetPaths inherits from ActionView::AssetPaths and overrides a number of methods. When compute_public_path is called through the Sass::Rails::Resolver#public_path method is sass-rails, the rails sprocket helper picks up the task of resolving the asset. Sprockets::Helpers::RailsHelper::AssetPaths#compute_public_path defers to super which is ActionView::AssetPaths#compute_public_path. In this method there is a condition of has_request? on rewrite_relative_url_root as seen below:
def compute_public_path(source, dir, ext = nil, include_host = true, protocol = nil)
...
source = rewrite_relative_url_root(source, relative_url_root) if has_request?
...
end
def relative_url_root
config = controller.config if controller.respond_to?(:config)
config ||= config.action_controller if config.action_controller.present?
config ||= config
config.relative_url_root
end
If you look at the internals of rewrite_relative_url_root it relies on a request to be present and the ability to derive it from the controller variable in order to resolve the relative url root. The issue is that when sprockets resolves these assets for sass it does not have a controller present and therefore no request.
The solution above didn't work in development mode for me. Here is the solution that I am using to make it work for now:
module Sass
module Rails
module Helpers
protected
def public_path(asset, kind)
resolver = options[:custom][:resolver]
asset_paths = resolver.context.asset_paths
path = resolver.public_path(asset, kind.pluralize)
if !asset_paths.send(:has_request?) && ENV['RAILS_RELATIVE_URL_ROOT']
path = ENV['RAILS_RELATIVE_URL_ROOT'] + path
end
path
end
end
end
end