4

I am trying to find a way to generate Javascript automatically from a CoffeeScript file as you would easily do in Sinatra like this:

require 'sinatra'
require 'coffee-script'
get '/*.js' do
  name = params[:splat][0]
  file_name = File.join("#{name}.coffee")
  pass unless File.exists?(file_name)
  content_type(:js, :charset => 'utf-8')
  coffee(IO.read(file_name))
end

This way, in my code, I could act like .js files are present even if I only got .coffee files. It's less particular and less buggy than using a client-side CoffeeScript compiler...

Josh Voigts
  • 4,114
  • 1
  • 18
  • 43
Andreas
  • 119
  • 1
  • 6

2 Answers2

2

Thanks leucos !!!

I though about a Rack middleware too but wonder if there wasn't a more "ramaze" solution (like with innate).

Anyway, this works and it's great !

Here is a modified version of your code :

require 'coffee-script'

# An attempt to allow Ramaze to generate Js file from coffee files
module Rack
  class BrewedCoffee
    def initialize(app, options = {})
      @app = app
      @lookup_path = options[:lookup_path].nil? ? [__DIR__] : options[:lookup_path]
    end

    def call(env)
      name = env['PATH_INFO']

      # Continue processing if requested file is not js
      return @app.call(env) if File.extname(name) != '.js'

      coffee_name = "#{name[0...-3]}.coffee"

      @lookup_path.each do |p|
        coffee_file = File.join(p, coffee_name)
        next unless File.file?(coffee_file)

        response_body = CoffeeScript.compile(File.read(coffee_file))
        headers = {}
        headers["Content-Type"] = 'application/javascript'
        headers["Content-Length"] = response_body.length.to_s

        return [200, headers, [response_body]]
      end

      @app.call(env)
    end
  end
end

I clean things a bit and remove the Ramaze dependency, this way it's a pure Rack middleware :).

You can use it calling :

m.use Rack::BrewedCoffee, :lookup_path => Ramaze.options.roots

Bye, Andreas

Andreas
  • 119
  • 1
  • 6
  • 1
    In fact, we're probably both reinventing the weel : https://github.com/mattly/rack-coffee – leucos Jul 04 '12 at 08:44
1

The best way to handle this is probably to write your own rack middleware, and use it like below. You probably need to use some sort of caching so you don't rebuild your .js files from .coffee ones at every call.

require 'ramaze'

class BrewedCoffee
  def initialize(app)
    @app = app
  end

  def call(env)
    name = env['PATH_INFO']

    # Continue processing if requested file is not js
    return @app.call(env) if name !~ /\.js$/

    # Caching & freshness checks here...
    # ...

    # Brewing coffee
    name = name.split('.')[0..-2].join('.') + ".coffee"

    Ramaze.options.roots.each do |p|
      file_name = File.join("#{name}.coffee")
      next unless File.exists?(file_name)

      response_body = coffee(IO.read(file_name))
      headers["Content-Type"] = 'application/javascript'
      headers["Content-Length"] = response_body.length.to_s


      [status, headers, response_body]
    end

    @app.call(env)
  end
end


class MyController < Ramaze::Controller
  map '/'

  ...
end

Ramaze.middleware! :dev do |m|
  m.use(BrewedCoffee)
  m.run(Ramaze::AppMap)
end

Ramaze.start

This is quickly hacked and requires more love, but you get the idea. And you probably don't want to do this in production, and just prebuild you coffee stuff, or you'll get in trouble with your sysops :D

leucos
  • 17,661
  • 1
  • 44
  • 34