If - for whatever reason - you have to render stylesheets dynamically, the following snippet might be of help. It emulates what "sprockets" would to when precompiling your assets, and give your stylesheets access to all the regular bells and whistles (like asset_path
, proper @import
s etc):
class DynamicStylesheetsController < ApplicationController
def show
logical_path = RELATIVE_PATH_TO_YOUR_TEMPLATE
path = File.join(Rails.root, logical_path)
template = Sass::Rails::SassTemplate.new(path)
environment = YourApp::Application.assets
context = environment.context_class.new(environment, logical_path, Pathname.new(path))
output = template.render(context)
render :text => output, :content_type => 'text/css'
end
end
Injecting information
I needed to inject some color values into my stylesheets, and did not want to run everything through ERB first (which is also hard, because of @import statements etc). So I did basically this:
-
Before the
template.render
, insert:context.singleton_class.send(:define_method, :theme_colors) do HASH_OF_COLOR_VALUES # e.g. { 'primary' => [100, 250, 10] } end
-
In an initializer, add
module Sass::Script::Functions def color(color_name) assert_type color_name, :String context = @options[:custom][:resolver].context Sass::Script::Color.new(context.theme_colors[color_name.to_s]) end declare :color, :args => [:string] end
-
In your sass template, use something like
$PRIMARY_COLOR = color(primary)
Performance
Sass is not especially fast (and it usually does not have to be). For my medium-sized stylesheet, rendering took about 0.5 seconds. It's probably still a good idea to
- use some form of action caching or page caching
- make sure you set proper expiration and cache headers
Caveat
This solution is probably somewhat brittle, since the methods used are not exactly part of a public API. If you know of a cleaner way to do this, please drop me a line.