Skip to content

Instantly share code, notes, and snippets.

@manuelmeurer
Last active March 5, 2018 12:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save manuelmeurer/2214445 to your computer and use it in GitHub Desktop.
Save manuelmeurer/2214445 to your computer and use it in GitHub Desktop.
How to compile custom Sass stylesheets during runtime
# lib/store_stylesheet.rb
class StoreStylesheet
def initialize(store)
@store = store
end
# The path of the compiled stylesheet, i.e. stores/id_timestamp.css
def stylesheet_file
filename = [
@store.id,
@store.updated_at.to_s(:number)
].join('_')
File.join \
'stores',
"#{filename}.css"
end
# The path of the uncompiled Sass file, i.e. /path/to/app/app/assets/stylesheets/stores/id_timestamp.css.scss
def sass_file_path
Rails.root.join('app', 'assets', 'stylesheets', "#{self.stylesheet_file}.scss")
end
# The styles which are rendered through app/views/stores/styles.scss.erb
# You can supply local variables which can be accessed in the style view.
def styles
StoresController.new.render_to_string 'styles',
formats: [:scss],
layout: false,
locals: { store: @store }
end
# Check if this stylesheet has been compiled or needs to be recompiled
def compiled?
if Rails.application.config.assets.compile
# If assets are compiled dynamically, check if the Sass file exists and is not empty
File.exists?(self.sass_file_path) && !File.zero?(self.sass_file_path)
else
# Otherwise check if the digested file is registered as an asset
Rails.application.config.assets.digests[self.stylesheet_file].present?
end
end
def compile
# Compile app/views/stores/styles.scss.erb into app/assets/stylesheets/stores/id-timestamp.css.scss
File.open(self.sass_file_path, 'w') { |f| f.write(self.styles) }
# Create and register digested file only if assets are not compiled dynamically
unless Rails.application.config.assets.compile
# Use Sprockets::Environment instead of Sprockets::Index to find the dynamically created asset.
# Rails.application.assets might be a Sprockets::Index (in production) or a Sprockets::Environment (in development)
# We need to access the Sprockets::Environment to find the file that was just compiled. Sprockets::Index caches everything and wouldn't find this file.
# TODO: Is there an easier way to access the Sprockets::Environment?
env = Rails.application.assets.is_a?(Sprockets::Index) ? Rails.application.assets.instance_variable_get('@environment') : Rails.application.assets
# Compile asset
Sprockets::StaticCompiler.new(
env,
File.join(Rails.public_path, Rails.application.config.assets.prefix),
[self.stylesheet_file],
digest: true,
manifest: false
).compile
# Register digested file as an asset
Rails.application.config.assets.digests[self.stylesheet_file] = env[self.stylesheet_file].digest_path
end
# Delete old files
Dir[self.sass_file_path.sub(/\d+.css.scss$/, '*')].each do |file|
File.delete file unless file == self.sass_file_path.to_s
end
end
end
# app/views/layouts/stores.html.haml
# This could be in the application layout or somewhere else instead, depending on your app.
-# Compile the stylesheet if necessary
- store_stylesheet = StoreStylesheet.new(@store)
- store_stylesheet.compile unless store_stylesheet.compiled?
-# At this point you can access your compiled stylesheet.
-# You can either render it directly on the page or reference it via stylesheet_link_tag
-# The latter can only be done if you're serving assets directly via your app.
-# If you use the AssetSync gem or similar to upload your assets to S3, you should render it inline because it hasn't been uploaded.
-# Render on page:
-# Again, use Sprockets::Environment instead of Sprockets::Index to find the dynamically created asset.
- env = Rails.application.assets.is_a?(Sprockets::Index) ? Rails.application.assets.instance_variable_get('@environment') : Rails.application.assets
= env[store_stylesheet.stylesheet_file].to_s.html_safe
-# Reference:
= stylesheet_link_tag store_stylesheet.stylesheet_file
# app/views/stores/styles.scss.erb
# Use Sass and ERB in here (local variables can be supplied by StoreStylesheet#styles)
@import "compass/css3";
@import "fancy-buttons";
$gray: #ccc;
.store {
background-color: $gray;
&[data-id="<%= store.id %>"] {
background-color: <%= store.background_color %>;
button {
@include fancy-button(<%= store.button_color %>, 16px, 0.5em);
}
}
}
@mszyndel
Copy link

mszyndel commented Jul 3, 2014

This method doesn't work anymore (probably with Sprockets > 2.0). This worked for me: http://matteodepalo.github.io/blog/2013/01/31/how-to-create-custom-stylesheets-dynamically-with-rails-and-sass/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment