I'm writing a library that has an embedded Sinatra app launched via Thor. I want to mount instances of Sprockets::Environment
at /css
and /js
and have the main app mapped to /
. This would be easy using Rack::URLMap
in a config.ru
file, but in this case there isn't one because I'm starting the Sinatra app programmatically with Sinatra::Application.run!
. How can I achieve this?
Actually, this is not that hard. All you need to do is assign an instance of Sprockets::Environment
to a Sinatra configuration variable and define some paths to look up the assets you're interested in.
Here's a basic example:
require "sass"
require "haml"
require "erubis"
require "sinatra"
require "sprockets"
set :assets, Sprockets::Environment.new
# Configure sprockets
settings.assets.append_path "app/javascripts"
settings.assets.append_path "app/stylesheets"
# For compressed JS and CSS output
require "yui/compressor"
settings.assets.js_compressor = YUI::JavaScriptCompressor.new
settings.assets.css_compressor = YUI::CssCompressor.new
get "/" do
haml :index
end
get "/javascripts/:file.js" do
content_type "application/javascript"
settings.assets["#{params[:file]}.js"]
end
get "/stylesheets/:file.css" do
content_type "text/css"
settings.assets["#{params[:file]}.css"]
end
Happy sprocketing!
I ended up doing it by writing a custom middleware with some of the functionality from Rack::URLMap
. It looks roughly like this:
require "sprockets"
require "sinatra/base"
class SprocketsMiddleware
attr_reader :app, :prefix, :sprockets
def initialize(app, prefix)
@app = app
@prefix = prefix
@sprockets = Sprockets::Environment.new
yield sprockets if block_given?
end
def call(env)
path_info = env["PATH_INFO"]
if path_info =~ prefix
env["PATH_INFO"].sub!(prefix, "")
sprockets.call(env)
else
app.call(env)
end
ensure
env["PATH_INFO"] = path_info
end
end
class App < Sinatra::Base
use SprocketsMiddleware, %r{/assets} do |env|
env.append_path "assets/css"
env.append_path "assets/js"
end
end
App.run!
Here is how I integrated Sprockets into Sinatra with Rails-like directory layout, helpers and minification for JS and CSS.
I chose to write a Sinatra extension. This extension encapsulates the configuration of sprockets (paths, minification, helpers) and can be registered by the application.
module Sinatra
module Assets
extend Sinatra::Extension
configure do
set :assets, Sprockets::Environment.new(root).tap { |assets|
%w(assets vendor/assets).each do |base|
%w(images javascripts stylesheets).each do |type|
assets.append_path File.join(base, type)
end
end
if production?
assets.js_compressor = Closure::Compiler.new
assets.css_compressor = YUI::CssCompressor.new
uid = Digest::MD5.hexdigest(File.dirname(__FILE__))[0,8]
assets.cache = Sprockets::Cache::FileStore.new("/tmp/sinatra-#{uid}")
else
assets.cache = nil
end
}
end
get "/assets/*" do
env["PATH_INFO"].sub!(%r{^/assets}, "")
expires Time.now + (365*24*60*60) if settings.production?
settings.assets.call(env)
end
helpers do
include Sprockets::Helpers
Sprockets::Helpers.configure do |config|
config.expand = development?
config.digest = production?
end
def assets_environment
settings.assets
end
end
end
end
Using the extension in your application is simple:
class App < Sinatra::Base
register Sinatra::Assets
# ...
end
Assets can be placed in assets
, or vendor/assets
. For example vendor/assets/jquery.js
can be referenced by logical name, i.e. http://localhost/assets/jquery.js
.
In the example above I'm using sprockets-helpers which provides helpers such as javascript_tag
. The configuration given above assumes that in development, you want to expand assets required by the referenced asset (resulting in multiple tags per asset).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With