Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Using Sprockets 2 in Rails 3.0.x with CoffeeScript & SASS
UPDATE: Please see some of the forks for an updated version of this guide. I
myself have moved onto the Rails 3.1 betas to get the asset pipeline. But if
you want to stay on stable there are other folks who are keeping this guide
relevant despite the changes constantly occurring on Sprockets 2. The comments
on this gist will lead you to the right forks. :)
Some brief instructions on how to use Sprocket 2 in Rails to get CoffeeScript
powered JS and SASS powered CSS with YUI compression all via the magic of rack.
This stuff will be native in Rails 3.1 and the layout of the files on the
filesystem will be different but this guide will get you working with it
while we wait for all that to finalize.
Ignore the number prefixes on each file. This is just to ensure proper order in the Gist.
gem 'coffee-script'
gem 'yui-compressor', :require => 'yui/compressor'
gem 'sass'
gem 'json' # sprocket dependency for Ruby 1.8 only
gem 'sprockets', :git => 'git://github.com/sstephenson/sprockets.git'
# Config a Sprockets::Environment to mount as a Rack end-point. I like to use a subclass
# as it allows the config to be easily reusable. Since I use the same instance for
# all mount points I make it a singleton class. I just add this as an initializer to my
# project since it is really just configuration.
class AssetServer < Sprockets::Environment
include Singleton
def initialize
super Rails.public_path
paths << 'javascripts' << 'stylesheets'
if Rails.env.production?
self.js_compressor = YUI::JavaScriptCompressor.new :munge => true, :optimize => true
self.css_compressor = YUI::CssCompressor.new
end
end
end
# Mount the rack end-point for JavaScript and CSS.
MyApp::Application.routes.draw do
mount AssetServer.instance => 'javascripts'
mount AssetServer.instance => 'stylesheets'
end
# Put this in your public/javascripts directory and call /javascripts/application.js in your browser
alert 'hello world'
// Put this in your public/stylesheets directory and call /stylesheets/application.css in your browser
body {margin: 2px + 5px}
Sprockets 2 has a lot more under the hood but this gets you started.
A few things not covered:
1. Anything supported by Tilt can be used as a template engine
(not just Sass and CoffeeScript).
2. Although Sass has native abilities to include other files, Sprockets 2
gives the ability to all formats through special comments like:
// =require "foo"
It's special commands can be fairly powerful (like requiring an entire
directory or tree). NOTE: Use the comment character relevant for the
language. So coffescript should be:
# =require 'foo.js'
Then you can create 'foo.js.coffee' and when served it will be as one
file.
3. Sprockets 2 has the ability to pre-compile the assets for maximum speed.
Also useful when the deployment environment doesn't support a template
language (like CoffeeScript).
@TrevorBurnham

This comment has been minimized.

Show comment Hide comment
@TrevorBurnham

TrevorBurnham Apr 9, 2011

CoffeeScript doesn't have the native ability to include other files, unlike Sass, and the only way to insert // comments is by backtick-escaping them. Does Sprockets 2 have improved CoffeeScript support?

CoffeeScript doesn't have the native ability to include other files, unlike Sass, and the only way to insert // comments is by backtick-escaping them. Does Sprockets 2 have improved CoffeeScript support?

@eric1234

This comment has been minimized.

Show comment Hide comment
@eric1234

eric1234 Apr 9, 2011

@TrevorBurnham - You are correct. I was mistaken. I was thinking you could "require" a file but that is only for Node.js on the server side. So you will need to use Sprocket requires in your coffeescript. The comment character is not important. So put the following in your coffeescript:

# =require 'foo.js'

Then create the file foo.js.coffee. When sent out to the client it is combined all into one file.

Owner

eric1234 commented Apr 9, 2011

@TrevorBurnham - You are correct. I was mistaken. I was thinking you could "require" a file but that is only for Node.js on the server side. So you will need to use Sprocket requires in your coffeescript. The comment character is not important. So put the following in your coffeescript:

# =require 'foo.js'

Then create the file foo.js.coffee. When sent out to the client it is combined all into one file.

@TrevorBurnham

This comment has been minimized.

Show comment Hide comment
@TrevorBurnham

TrevorBurnham Apr 9, 2011

Interesting! I'd love to see a full fleshed-out example of resolving dependencies (and handling compilation as well as concatenation) in a project with multiple CoffeeScript and JavaScript files.

Interesting! I'd love to see a full fleshed-out example of resolving dependencies (and handling compilation as well as concatenation) in a project with multiple CoffeeScript and JavaScript files.

@Whazor

This comment has been minimized.

Show comment Hide comment
@Whazor

Whazor Apr 13, 2011

A tip for beginning CoffeeScript users (like me), defining a function on the global namespace can be done with the following code:
this.a_global_function = () -> alert 'test'

Whazor commented Apr 13, 2011

A tip for beginning CoffeeScript users (like me), defining a function on the global namespace can be done with the following code:
this.a_global_function = () -> alert 'test'

@eric1234

This comment has been minimized.

Show comment Hide comment
@eric1234

eric1234 Apr 13, 2011

Useful to know. I have always done:

window.global_function = -> alert 'test'

But I like your use of this more. In fact I guess we could use the @ syntax to shorten it to just:

@global_function = -> alert 'test'

Will have to try that on my next script to see if it works as I expect.

Owner

eric1234 commented Apr 13, 2011

Useful to know. I have always done:

window.global_function = -> alert 'test'

But I like your use of this more. In fact I guess we could use the @ syntax to shorten it to just:

@global_function = -> alert 'test'

Will have to try that on my next script to see if it works as I expect.

@sgruhier

This comment has been minimized.

Show comment Hide comment
@sgruhier

sgruhier Apr 17, 2011

I have done a fork to have a setup "a la Rails 3.1"
https://gist.github.com/923998

I have done a fork to have a setup "a la Rails 3.1"
https://gist.github.com/923998

@eric1234

This comment has been minimized.

Show comment Hide comment
@eric1234

eric1234 Apr 18, 2011

@sgruhier - Very cool. I put together my guide before seeing the "Rails 3.1" way. Will be adopting that on my next project.

Owner

eric1234 commented Apr 18, 2011

@sgruhier - Very cool. I put together my guide before seeing the "Rails 3.1" way. Will be adopting that on my next project.

@sgruhier

This comment has been minimized.

Show comment Hide comment
@sgruhier

sgruhier Apr 18, 2011

You're welcome, thanks for your gist! it helped me a lot to use coffee-script + sprockets on a project

You're welcome, thanks for your gist! it helped me a lot to use coffee-script + sprockets on a project

@victusfate

This comment has been minimized.

Show comment Hide comment
@victusfate

victusfate May 11, 2011

Thanks @eric1234 and @sgruhier!

Thanks @eric1234 and @sgruhier!

@ep-wac

This comment has been minimized.

Show comment Hide comment
@ep-wac

ep-wac May 17, 2011

My Rails (3.0.3) on Ruby 1.8.7 p302 complains about

initializers/coffee_sass_compiler.rb:10:in 'initialize': undefined local variable or method 'paths'

so I tried adding a

paths = []

at the start of the 'def initialize' but that just made Rails complain about

action_dispatch/routing/mapper.rb:292:in 'mount': A rack application must be specified

what am I doing wrong?

ep-wac commented May 17, 2011

My Rails (3.0.3) on Ruby 1.8.7 p302 complains about

initializers/coffee_sass_compiler.rb:10:in 'initialize': undefined local variable or method 'paths'

so I tried adding a

paths = []

at the start of the 'def initialize' but that just made Rails complain about

action_dispatch/routing/mapper.rb:292:in 'mount': A rack application must be specified

what am I doing wrong?

@eric1234

This comment has been minimized.

Show comment Hide comment
@eric1234

eric1234 May 17, 2011

@ep-wac - Are you inheriting from Sprockets::Environment. That is where the paths accessor is defined. Also it is what makes this a Rack application.

Owner

eric1234 commented May 17, 2011

@ep-wac - Are you inheriting from Sprockets::Environment. That is where the paths accessor is defined. Also it is what makes this a Rack application.

@ep-wac

This comment has been minimized.

Show comment Hide comment
@ep-wac

ep-wac May 17, 2011

well - I rather bluntly cp'ed your work <:)

class CoffeeSassCompiler < Sprockets::Environment
  include Singleton

  def initialize
    super Rails.root.join('app')
    paths << 'javascripts' << 'stylesheets'
    if Rails.env.production?
      self.js_compressor = YUI::JavaScriptCompressor.new :munge => true, :optimize => true
      self.css_compressor = YUI::CssCompressor.new
    end
  end

end

all I attributed was renaming it (assets mean something entirely different to me) :) - and removed the 'asset' path in the initial join - but I did test that part - the join statement - in a console just to be sure and then I renamed the mounts in my routes.rb too, off cause ;)

mount CoffeeSassCompiler.instance => 'javascripts'
mount CoffeeSassCompiler.instance => 'stylesheets'

I loaded/installed all the gem's mentioned and without the mounts, my rails is in pristine condition

It's great of you to get back to me so fast - been stuck here for far too long (quite a deroute from my early morning intention to 'just add coffescript' to my belt <:)

cheers,
walther

ep-wac commented May 17, 2011

well - I rather bluntly cp'ed your work <:)

class CoffeeSassCompiler < Sprockets::Environment
  include Singleton

  def initialize
    super Rails.root.join('app')
    paths << 'javascripts' << 'stylesheets'
    if Rails.env.production?
      self.js_compressor = YUI::JavaScriptCompressor.new :munge => true, :optimize => true
      self.css_compressor = YUI::CssCompressor.new
    end
  end

end

all I attributed was renaming it (assets mean something entirely different to me) :) - and removed the 'asset' path in the initial join - but I did test that part - the join statement - in a console just to be sure and then I renamed the mounts in my routes.rb too, off cause ;)

mount CoffeeSassCompiler.instance => 'javascripts'
mount CoffeeSassCompiler.instance => 'stylesheets'

I loaded/installed all the gem's mentioned and without the mounts, my rails is in pristine condition

It's great of you to get back to me so fast - been stuck here for far too long (quite a deroute from my early morning intention to 'just add coffescript' to my belt <:)

cheers,
walther

@eric1234

This comment has been minimized.

Show comment Hide comment
@eric1234

eric1234 May 17, 2011

@ep-wac - I wrote this guide shortly before it was announced that Rails 3.1 would be using Sprockets. Ever since that announcement I have noticed a high level of activity on the Sprockets projects so it may be that they re-factored stuff rendering my guide obsolete (the price of bleeding edge). If you don't have time to mess with Sprockets I would suggest 'barista' as a more stable way of getting CoffeeScript in your application. If you have the time then you can of course dig into the Sprockets code and find out what they changed.

Owner

eric1234 commented May 17, 2011

@ep-wac - I wrote this guide shortly before it was announced that Rails 3.1 would be using Sprockets. Ever since that announcement I have noticed a high level of activity on the Sprockets projects so it may be that they re-factored stuff rendering my guide obsolete (the price of bleeding edge). If you don't have time to mess with Sprockets I would suggest 'barista' as a more stable way of getting CoffeeScript in your application. If you have the time then you can of course dig into the Sprockets code and find out what they changed.

@ep-wac

This comment has been minimized.

Show comment Hide comment
@ep-wac

ep-wac May 17, 2011

just a thought -

navigated to my sprockets gem in

~/.rvm/gems/ruby-1.8.7-p302/gems/sprockets-1.0.2/lib/sprockets

and did a

grep paths *.rb

and all he gave me back was

preprocessor.rb:    attr_reader :environment, :concatenation, :source_files, :asset_paths
preprocessor.rb:      @asset_paths = []
preprocessor.rb:      return if !asset_path || asset_paths.include?(asset_path)
preprocessor.rb:      asset_paths << asset_path
secretary.rb:      :expand_paths => true
secretary.rb:      expand_paths(load_locations, options).each do |load_location|
secretary.rb:      expand_paths(source_files, options).each do |source_file|
secretary.rb:        preprocessor.asset_paths.each do |asset_path|
secretary.rb:      def expand_paths(paths, options = {})
secretary.rb:        if options.has_key?(:expand_paths) ? options[:expand_paths] : @options[:expand_paths]
secretary.rb:          paths.map { |path| Dir[from_root(path)].sort }.flatten.compact
secretary.rb:          paths.map { |path| from_root(path) }
secretary.rb:        relative_file_paths_beneath(asset_path).each do |filename|
secretary.rb:      def relative_file_paths_beneath(path)

that does not look right? Does it?

Cheers,
Walther

ep-wac commented May 17, 2011

just a thought -

navigated to my sprockets gem in

~/.rvm/gems/ruby-1.8.7-p302/gems/sprockets-1.0.2/lib/sprockets

and did a

grep paths *.rb

and all he gave me back was

preprocessor.rb:    attr_reader :environment, :concatenation, :source_files, :asset_paths
preprocessor.rb:      @asset_paths = []
preprocessor.rb:      return if !asset_path || asset_paths.include?(asset_path)
preprocessor.rb:      asset_paths << asset_path
secretary.rb:      :expand_paths => true
secretary.rb:      expand_paths(load_locations, options).each do |load_location|
secretary.rb:      expand_paths(source_files, options).each do |source_file|
secretary.rb:        preprocessor.asset_paths.each do |asset_path|
secretary.rb:      def expand_paths(paths, options = {})
secretary.rb:        if options.has_key?(:expand_paths) ? options[:expand_paths] : @options[:expand_paths]
secretary.rb:          paths.map { |path| Dir[from_root(path)].sort }.flatten.compact
secretary.rb:          paths.map { |path| from_root(path) }
secretary.rb:        relative_file_paths_beneath(asset_path).each do |filename|
secretary.rb:      def relative_file_paths_beneath(path)

that does not look right? Does it?

Cheers,
Walther

@eric1234

This comment has been minimized.

Show comment Hide comment
@eric1234

eric1234 May 17, 2011

Yes, this guide was written for Sprockets 2 which is a complete re-write of Sprockets 1. Sprockets 1 was focused on building a JavaScript project (for things like scripty2, prototype, etc) while Sprockets 2 is for many languages and focused on serving the files instead of pre-compiling. You need to work off the Git repo as I don't think Sprockets 2 is released.

Owner

eric1234 commented May 17, 2011

Yes, this guide was written for Sprockets 2 which is a complete re-write of Sprockets 1. Sprockets 1 was focused on building a JavaScript project (for things like scripty2, prototype, etc) while Sprockets 2 is for many languages and focused on serving the files instead of pre-compiling. You need to work off the Git repo as I don't think Sprockets 2 is released.

@ep-wac

This comment has been minimized.

Show comment Hide comment
@ep-wac

ep-wac May 17, 2011

Well - if I just could start to doing what the gist said!
(don't know how that

, :git => 'git://github.com/sstephenson/sprockets.git'

got "lost in the fire"

apologies and thanx a bunch - you got me on my way!

Cheers,
Walther

ep-wac commented May 17, 2011

Well - if I just could start to doing what the gist said!
(don't know how that

, :git => 'git://github.com/sstephenson/sprockets.git'

got "lost in the fire"

apologies and thanx a bunch - you got me on my way!

Cheers,
Walther

@superchris

This comment has been minimized.

Show comment Hide comment
@superchris

superchris May 21, 2011

Does this mean that sprockets 2 doesn't do precompiling at all?

Does this mean that sprockets 2 doesn't do precompiling at all?

@eric1234

This comment has been minimized.

Show comment Hide comment
@eric1234

eric1234 May 23, 2011

@superchris - Sprockets 2 does support pre-compiling. I just preferred serving through Rack for my use case. If you are on Rails 3.1 I think there is even a rake task now that makes pre-compiling easy.

Owner

eric1234 commented May 23, 2011

@superchris - Sprockets 2 does support pre-compiling. I just preferred serving through Rack for my use case. If you are on Rails 3.1 I think there is even a rake task now that makes pre-compiling easy.

@emk

This comment has been minimized.

Show comment Hide comment
@emk

emk Jun 3, 2011

For better Rails 3.1 compatibility, you can change:

super Rails.public_path

...to:

super File.expand_path('app/assets', Rails.root)

...and move your javascripts/ and stylesheets/ directories into app/assets.

emk commented Jun 3, 2011

For better Rails 3.1 compatibility, you can change:

super Rails.public_path

...to:

super File.expand_path('app/assets', Rails.root)

...and move your javascripts/ and stylesheets/ directories into app/assets.

@sgruhier

This comment has been minimized.

Show comment Hide comment
@sgruhier

sgruhier Jun 3, 2011

correct
I do this
super Rails.root.join('app', 'assets')
in my apps (https://gist.github.com/923998)

sgruhier commented Jun 3, 2011

correct
I do this
super Rails.root.join('app', 'assets')
in my apps (https://gist.github.com/923998)

@joevandyk

This comment has been minimized.

Show comment Hide comment
@joevandyk

joevandyk Jun 9, 2011

A few things:

  1. If you do includes in sass or coffeescript, if you modify the included files, the changes won't be picked up until the main file is changed.
  2. The cache busting timestamps won't appear.

A few things:

  1. If you do includes in sass or coffeescript, if you modify the included files, the changes won't be picked up until the main file is changed.
  2. The cache busting timestamps won't appear.
@suranyami

This comment has been minimized.

Show comment Hide comment
@suranyami

suranyami Aug 18, 2011

I had problems getting this to work with sprockets (2.0.0.beta.13) because the trail.paths array was being duplicated before returning, so appending paths to it didn't work. The solution was to use append_path.

I've forked your gist here:

https://gist.github.com/1154048

And done a sample Rails 3 app here:

https://github.com/suranyami/sprocket_sample

I had problems getting this to work with sprockets (2.0.0.beta.13) because the trail.paths array was being duplicated before returning, so appending paths to it didn't work. The solution was to use append_path.

I've forked your gist here:

https://gist.github.com/1154048

And done a sample Rails 3 app here:

https://github.com/suranyami/sprocket_sample
@eric1234

This comment has been minimized.

Show comment Hide comment
@eric1234

eric1234 Aug 18, 2011

@suranyami - Awesome to see you and other people keeping this gist relevant for Rails 3 despite all the changes affecting sprockets 2 that are constantly making this gist obsolete. Thanks for keeping this guide from going obsolete. I keep hoping Rails 3.1 hits soon so people can use the asset pipeline without rigging it themselves (or using beta software).

Owner

eric1234 commented Aug 18, 2011

@suranyami - Awesome to see you and other people keeping this gist relevant for Rails 3 despite all the changes affecting sprockets 2 that are constantly making this gist obsolete. Thanks for keeping this guide from going obsolete. I keep hoping Rails 3.1 hits soon so people can use the asset pipeline without rigging it themselves (or using beta software).

@suranyami

This comment has been minimized.

Show comment Hide comment
@suranyami

suranyami Aug 19, 2011

@eric1234 Glad you like it.

I wish I could start using Rails 3.1 now, too, because the asset pipeline looks sweet.

I've also updated the sprocket_sample to have some detailed examples using a couple of different templates and require statements.

@eric1234 Glad you like it.

I wish I could start using Rails 3.1 now, too, because the asset pipeline looks sweet.

I've also updated the sprocket_sample to have some detailed examples using a couple of different templates and require statements.

@bradrobertson

This comment has been minimized.

Show comment Hide comment
@bradrobertson

bradrobertson Dec 14, 2011

@suranyami, this is gold thanks! The Sprockets example of specifically mounting the Sprocket endpoint and then the Rails App endpoint didn't work for me in integration tests. This solved my issue on getting the JS loaded properly with capybara-webkit. Thanks so much!

@suranyami, this is gold thanks! The Sprockets example of specifically mounting the Sprocket endpoint and then the Rails App endpoint didn't work for me in integration tests. This solved my issue on getting the JS loaded properly with capybara-webkit. Thanks so much!

@suranyami

This comment has been minimized.

Show comment Hide comment
@suranyami

suranyami Dec 14, 2011

Glad to hear, @bradrobertson! Happy to help.

Glad to hear, @bradrobertson! Happy to help.

@bradrobertson

This comment has been minimized.

Show comment Hide comment
@bradrobertson

bradrobertson Dec 15, 2011

Any chance anyone has the digest option of sprockets working with this setup? I haven't really had a chance to dig into it too much, but I'd love for an MD5 to be appended to my files in production.

Any chance anyone has the digest option of sprockets working with this setup? I haven't really had a chance to dig into it too much, but I'd love for an MD5 to be appended to my files in production.

@MohitSharma

This comment has been minimized.

Show comment Hide comment
@MohitSharma

MohitSharma Apr 12, 2012

Does this is compatible with rails 3.0.11

Does this is compatible with rails 3.0.11

@eric1234

This comment has been minimized.

Show comment Hide comment
@eric1234

eric1234 Apr 12, 2012

@MohitSharma - Things have changed a lot in Sprockets 2 since I wrote this guide. You will likely need to change some things. Also some forks of this guide were made to accomidate changes in Sprockets 2. Unsure if those are still maintained or if their editor has moved onto Rails 3.1+. It should be possible to use Sprockets 2 in a Rails 3.0.11 app (since it is just rack based). You may just have to dig a bit.

Owner

eric1234 commented Apr 12, 2012

@MohitSharma - Things have changed a lot in Sprockets 2 since I wrote this guide. You will likely need to change some things. Also some forks of this guide were made to accomidate changes in Sprockets 2. Unsure if those are still maintained or if their editor has moved onto Rails 3.1+. It should be possible to use Sprockets 2 in a Rails 3.0.11 app (since it is just rack based). You may just have to dig a bit.

@MohitSharma

This comment has been minimized.

Show comment Hide comment
@MohitSharma

MohitSharma Apr 13, 2012

Thanks @eric1234

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