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.
To compile your assets locally, run the assets:precompile task locally on your app. Make sure to use the production environment so that the production version of your assets are generated. A public/assets directory will be created. Inside this directory you'll find a manifest.
The clean it removes the old versions of the precompiled assets while leaving the new assets in place. Show activity on this post. rake assets:clean removes compiled assets. It is run by cap deploy:assets:clean to remove compiled assets, generally from a remote server.
The require_tree directive tells Sprockets to recursively include all JavaScript files in the specified directory into the output. These paths must be specified relative to the manifest file.
The asset pipeline provides a framework to concatenate and minify or compress JavaScript and CSS assets. It also adds the ability to write these assets in other languages such as CoffeeScript, Sass and ERB. Prior to Rails 3.1 these features were added through third-party Ruby libraries such as Jammit and Sprockets.
Ok, after hours of searching I came upon the list of available helper methods in Sprockets - it would have saved me a lot of time had this been linked to from the Sprockets man page on GitHub (there is a link, but it points to #FIXME). From the Sprockets API docs:
(Object) evaluate(path, options = {})
path
and runs processors on the file. <%= evaluate "bar.js" %>
Bingo! I changed my include directive to:
<%= evaluate(Rails.root.join("app/assets/some_other_identity/stylesheets/overrides.css.erb")) %>
and the CSS gets processed and the results inserted, just the way I wanted it to work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With