lib/mustache/sinatra.rb
require 'sinatra/base' require 'mustache' class Mustache # Support for Mustache in your Sinatra app. # # require 'mustache/sinatra' # # class Hurl < Sinatra::Base # register Mustache::Sinatra # # # Should be the path to your .mustache template files. # set :views, "path/to/mustache/templates" # # # Should be the path to your .rb Mustache view files. # # Only needed if different from the `views` setting # set :mustaches, "path/to/mustache/views" # # # This tells Mustache where to look for the Views module, # # under which your View classes should live. By default it's # # the class of your app - in this case `Hurl`. That is, for an :index # # view Mustache will expect Hurl::Views::Index by default. # # # If our Sinatra::Base subclass was instead Hurl::App, # # we'd want to do `set :namespace, Hurl::App` # set :namespace, Hurl # # get '/stats' do # mustache :stats # end # end # # As noted above, Mustache will look for `Hurl::Views::Index` when # `mustache :index` is called. # # If no `Views::Stats` class exists Mustache will render the template # file directly. # # You can indeed use layouts with this library. Where you'd normally # <%= yield %> you instead {{{yield}}} - the body of the subview is # set to the `yield` variable and made available to you. module Sinatra module Helpers # Call this in your Sinatra routes. def mustache(template, options={}, locals={}) render :mustache, template, options, locals end # This is called by Sinatra's `render` with the proper paths # and, potentially, a block containing a sub-view def render_mustache(template, data, opts, locals, &block) name = Mustache.classify(template.to_s) # This is a horrible hack but we need it to know under which namespace # Views is located. If you have Hurl::App::Views, namespace should be # set to Hurl:App. namespace = options.namespace if namespace.const_defined?(:Views) && namespace::Views.const_defined?(name) # First try to find the existing view, # e.g. Hurl::Views::Index klass = namespace::Views.const_get(name) elsif File.exists?(file = "#{options.mustaches}/#{template}.rb") # Couldn't find it - try to require the file if it exists, then # load in the view. require "#{file}".chomp('.rb') klass = namespace::Views.const_get(name) # compile and cache the template klass.template = data else # Still nothing. Use the stache. klass = Mustache end # Tell the view class its extension and path so finding partials # works as expected. if klass.template_extension != 'mustache' klass.template_extension = 'mustache' end # Confusingly Sinatra's `views` setting tells Mustache where the # templates are found. It's fine, blame Chris. if klass.template_path != options.views klass.template_path = options.views end # Create a new instance for playing with instance = klass.new # Copy instance variables set in Sinatra to the view instance_variables.each do |name| instance.instance_variable_set(name, instance_variable_get(name)) end # Locals get added to the view's context locals.each do |local, value| instance[local] = value end # If we're paseed a block it's a subview. Sticking it in yield # lets us use {{yield}} in layout.html to render the actual page. instance[:yield] = block.call if block instance.template = data unless instance.compiled? instance.to_html end end def self.registered(app) app.helpers Mustache::Sinatra::Helpers app.set :mustaches, ::Sinatra::Base.views app.set :namespace, app end end end Sinatra.register Mustache::Sinatra