Rails 3.1 SASS asset helpers not not including RAI

2019-07-02 12:25发布

问题:

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.

回答1:

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