6

I want, from a helper, to render some variables in a .scss.erb template that makes use of the image-url() sass function:

// template.scss.erb

#<%= id %> {
  background-image: image-url('<%= image_file %>');
}

So far, the ERB part has been easy:
(leveraging this stack overflow answer)

vars_binding = OpenStruct.new(
                 id:         'foo', 
                 image_file: 'foo.jpg'
               ).instance_eval { binding }

template = File.read('path/to/template.scss.erb')

rendered_sass = ERB.new(template).result(vars_binding)

Running that code, sass is now equal to:

#foo {
  background-image: image-url('foo.jpg');
}

However, when I next try to run:

css = Sass::Engine.new(
  rendered_sass,
  syntax:     :scss,
  cache:      false,
  load_paths: view_context.assets.paths,
  read_cache: false,
  style:      :compressed
).render

It returns

NoMethodError: undefined method `[]' for nil:NilClass
from …/sprockets-3.2.0/lib/sprockets/sass_processor.rb:267:in `sprockets_context'

because the call to Sass::Engine doesn’t provide a Sprockets context.

If I remove image-url() from the .scss.erb template, and replace it with the native url(), it will then render correctly as CSS, no problem.

So how do I render this template within a sprockets context?

Community
  • 1
  • 1
elstgav
  • 1,001
  • 7
  • 20

2 Answers2

8

After digging through a similar question, and a lot of trial and error, I found my solution: I have to supply a :sprockets hash when calling Sass::Engine.new.

css = Sass::Engine.new(
  rendered_sass,
  syntax:     :scss,
  cache:      false,
  load_paths: view_context.assets.paths,
  read_cache: false,
  style:      :compressed,

  # The key ingredient…
  sprockets:  {
    context:     view_context,
    environment: view_context.assets
  }
).render

It should be noted that view_context was passed from a view file, but it could have also been ActionView::Base.new

Community
  • 1
  • 1
elstgav
  • 1,001
  • 7
  • 20
  • Thank you! Something else I am struggling with is that I'd like to make the `image-url()` SASS helper generate absolute paths. Any ideas? – Drew Mar 22 '17 at 02:25
  • Could you give a bit more information about the view_context? I get a no method error when trying to call #assets on an instance of ActionView::Base. I'm certain this is close to what I need, but I don't really understand what the sprockets context is doing here. – sammms Mar 29 '17 at 14:14
  • 1
    @sammms instead of calling `assets` on `view_context`, you can call it on `Rails.application` – bert bruynooghe May 23 '17 at 11:30
  • 1
    For recent versions of Rails, you need to set `config.assets.compile = true` in your `application.rb`, or `assets` will be `nil` in production. More info on it in https://github.com/rails/sprockets-rails/issues/237 – bert bruynooghe May 23 '17 at 11:32
2

After struggling with the same issue I have found a live example of the same functionality on a Github page, surprisingly not gemified.

https://github.com/BLauris/custom-css-for-user

There is an article of the author explaining the method on this link: http://www.diatomenterprises.com/dynamically-compile-stylesheets-with-rails-and-sass/

ErvalhouS
  • 4,178
  • 1
  • 22
  • 38