I've converted this multi skinned app of mine to make use of the assets pipeline introduced in Rails 3.1. For the most part it's been surprisingly easy and I'm in love with the preprocessing ability which allows you to use inline Ruby in your CSS/JS files.
I have run into a major problem though, which despite the power of Sprockets seems tricky to solve. My app can be run with any number of skins (or "identities" rather) which is chosen at runtime. This "identity" parameter sets up stuff like cache directory, database connection, view paths - and indeed asset paths. While all "identities" can have their own stylesheet there is also a shared one which is used across all instances. So the assets folder structure looks something like this:
In /app/assets/stylesheets/aplication.css.erb:
<% require_asset("shared.css") %>
<% require_asset("overrides.css") %>
This loads two stylesheets, and crucially it uses the configured asset paths to resolve them (that's why i use require_assets
instead of the standard require
and include
directives, as they don't hit the resolver). It returns the first matches found and allows me to very easily override part or whole of the default styling. So
/app/assets/stylesheets/shared.css
can be overridden by putting a file with the same name in the instance assets folder
/app/assets/[identity]/stylesheets/shared.css
and if no such file exists it silently falls back to the default shared.css.
It all works brilliantly - I use the same technique for JavaScripts, images and fonts and everything gets neatly processed and packaged during precompilation. BUT. There is a type of (sideways) inheritance that I'm unable to achieve; sometimes the skin for an identity is so similar to another one that only a few dozen lines differ (e.g. identical layout but with a different color scheme) and I really want to be able to do something like this:
assets/stylesheets/application.css.erb:
<% require_asset("shared.css") %>
<% require_asset("overrides.css") %>
assets/current_identity/stylesheets/overrides.css:
<% require_asset("../../some_other_identity/stylesheets/overrides.css") %>
/* followed by the dozen or so lines that differ for this skin */
...
This FAILS because in the current context "some_other_identity" is not in the asset paths - Rails does not find the file in dev mode, and of course it's not included during precompilation either. And if I do include it in the assets path it loads the wrong overrides.css (there can be only one). So I've been experimenting with putting something like this at the top of overrides.css:
<%= File.read(Rails.root.join("app/assets/some_other_identity/stylesheets/overrides.css")) %>
/* rest of CSS */
...
And indeed that works just as expected. BUT. Because I'm now using the assets pipeline to serve all assets I can no longer reference images in the CSS with a fixed path - I have to use <%= asset_path("some_image.png") %>
so that the path resolver can work its magic. This means my overrides.css
is really overrides.css.erb
, and of course the ERB preprocessing doesn't happen when you do File.read()
. So, I'm stuck! Help! Anyone?
Edit: If I use
<%= ERB.new(File.read(Rails.root.join("app/assets/some_other_identity/stylesheets/overrides.css.erb"))).result %>
it does try to parse the ERB, but I get
undefined method `asset_path' for main:Object
which of course is due to me using asset_path("some_image.png")
etc in the file I'm trying to include.