Broken precompiled assets in Rails 3.1 when deployed in a sub-URI

I am trying to upgrade a Rails 3 application to use Rails 3.1, and as part of this, I am using a new asset pipeline. Until now, everything works for me separately from one rather annoying problem that I can not solve.

The application and all its assets work fine in development, but during production, it is deployed in a sub-URI using Passenger ( http://the-host/sub-uri/ ). The problem is that the resources are precompiled during deployment, and one of my CSS files (well, this is a .css.scss file) uses the image-url helper from the sass-rails gem. Because during the pre-compilation process, the paths are hard-coded into a pre-compiled CSS file, sub-uri are not taken into account:

In my .css.scss file:

 body { background-image: image-url("bg.png"); } 

The result in the compiled file application-<md5-hash-here>.css :

 body { background-image: url(/assets/bg.png); } 

What you need to do to make it work correctly:

 body { background-image: url(/sub-uri/assets/bg.png); } 

Is this scenario too much? If so, I will need to revert to the old non-asset path and just execute my images and CSS with public . However, it seems that something had to be thought and decided ...? Am I missing a solution?


Edit 1: It should be noted that using the erb solution gives the same result as expected.


Edit 2: in response to Benoit Garret's comment

No, the problem is not related to config.assets.prefix . I tried setting this value (to /sub-uri/assets , not the default /assets ), but it turned out that it was wrong - it looks like this parameter already refers to the root of the Rails application, and not to the server, Removing this (and thus returning to the default) fixed all the strange problems that caused (and there were many, all the assets ended up in /sub-uri/sub-uri/assets ), it was very strange). The only problem is that the image-url helper and friends do not pick up sub-URIs when they are precompiled. Needless to say, this is logical, because when it is precompiled, he could not know that when he works under the Passenger, he will be configured in this way. My question is how to report this and thus lead to the correct paths in the precompiled result. If it’s really possible,

My current workaround is to link to iamge in CSS as follows: url(../images/bg.png) and put it in a non-pipelined location public/images . It is hardly ideal, since it does not benefit from fingerprints and everything that the pipeline provides.

+8
passenger asset-pipeline sprockets assets
source share
4 answers

Finally, I developed some workarounds / solutions.

1) From https://github.com/rails/sass-rails/issues/17 it looks like this can be fixed in sass-rails. I have monkey-corrected helpers.rb themselves along the lines of the proposed patch in the link above. I just set the required environment variable in the asset precompilation line in deploy.rb .

I am doing all my corrections in a single config/initializers/gem_patches.rb . In this file, I fixed this method as:

 module Sass module Rails module Helpers protected def public_path(asset, kind) path = options[:custom][:resolver].public_path(asset, kind.pluralize) path = ENV['PRODUCTION_URI'] + path if ENV['PRODUCTION_URI'] path end end end end 

2) Alternatively, if you can embed images in CSS, changing the stylesheet with the extension .erb and replacing image-url("bg.png") with url(<%= asset_data_uri "bg.png" %>) will work unnecessarily change sass-rails. asset-data-uri does not exist as a pure Sass function, so you need to use the Rails asset_data_uri helper.

+4
source share

In the latest version of Rails 3.1.3, you need to install the monkey on another module for it to work

This is what I did

 module Sprockets module Helpers module RailsHelper def asset_path(source, options = {}) source = source.logical_path if source.respond_to?(:logical_path) path = asset_paths.compute_public_path(source, asset_prefix, options.merge(:body => true)) path = options[:body] ? "#{path}?body=1" : path if !asset_paths.send(:has_request?) path = ENV['RAILS_RELATIVE_URL_ROOT'] + path if ENV['RAILS_RELATIVE_URL_ROOT'] end path end end end end 

And in my deploy.rb I have:

 desc "precompile the assets" namespace :assets do task :precompile_assets do run "cd #{release_path} && rm -rf public/assets/* && RAILS_ENV=production bundle exec rake assets:precompile RAILS_RELATIVE_URL_ROOT='/my_sub_uri'" end end before "deploy:symlink", "assets:precompile_assets" 
+2
source share

I am using Rails 3.1.3 and am successfully implementing myself in a sub-URI. I don't have monkey patches.

The main issues with this setting are discussed more here . As you can see, the solution was applied to Rails 3.2 and was never reversed in 3.1.4.

But I came up with a solution using Rails 3.1.3 , which works for my installation.

Try the following: (I'm not an expert, just trying to help solve a problem that bothered me for hours ...)

environment.rb:

 #at top: ENV['RAILS_RELATIVE_URL_ROOT'] = '/rais' 

production.rb:

 config.assets.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] ? ENV['RAILS_RELATIVE_URL_ROOT'] + '/assets' : '/assets' 

routes.rb:

  Rais::Application.routes.draw do scope ENV['RAILS_RELATIVE_URL_ROOT'] || '/' do #see config/environment.rb <<resources here>> end end 

As you can see, I put assets.prefix inside production.rb and not in application.rb. After that, you will run:

 rake assets:clear rake assets:precompile 

and than, test the console:

 RAILS_ENV=production rails console 

Results:

 foo = ActionView::Base.new foo.stylesheet_link_tag 'application' => "<link href=\"/rais/assets/layout.css?body=1\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />\n<link href=\"/rais/assets/application.css?body=1\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />" foo.image_tag('arrow-up.png') => "<img alt=\"Arrow-up\" src=\"/rais/assets/arrow-up-ca314ad9b991768ad2b9dcbeeb8760de.png\" />" 
+2
source share

After a little digging, I found a problem. The problem is Rails, in particular Sprockets :: Helpers :: RailsHelper :: AssetPaths # compute_public_path. Sprockets :: Helpers :: RailsHelper :: AssetPaths inherits ActionView :: AssetPaths and overrides a number of methods. When compute_public_path is called through the Sass :: Rails :: Resolver # public_path method, this is sass-rails, the rails star helper takes on the task of resolving the asset. Sprockets :: Helpers :: RailsHelper :: AssetPaths # compute_public_path drops super, which is ActionView :: AssetPaths # compute_public_path. Is there a has_request condition in this method? on rewrite_relative_url_root as 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 infer it from a controller variable to resolve the relative root of the URL. The problem is that when asterisks resolve these assets to sass, it does not have a controller and therefore has no request.

The solution above did not work in development mode for me. Here is the solution I'm using to get it working 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 
0
source share

All Articles