Instantly share code, notes, and snippets.

Embed
What would you like to do?
require 'action_view'
require 'active_support'
require 'mustache'
class Mustache
# Remember to use {{{yield}}} (3 mustaches) to skip escaping HTML
# Using {{{tag}}} will skip escaping HTML so if your mustache methods return
# HTML, be sure to interpolate them using 3 mustaches.
class Rails < Mustache
attr_accessor :view
def method_missing(method, *args, &block)
view.send(method, *args, &block)
end
def respond_to?(method, include_private=false)
super(method, include_private) || view.respond_to?(method, include_private)
end
# Redefine where Mustache::Rails templates locate their partials:
#
# (1) in the same directory as the current template file.
# (2) in the shared templates path (can be configured via Config.shared_path=(value))
#
def partial(name)
partial_name = "_#{name}.#{Config.template_extension}"
template_dir = Pathname.new(self.class.template_file).dirname
partial_path = File.expand_path("#{template_dir}/#{partial_name}")
unless File.file?(partial_path)
partial_path = "#{Config.shared_path}/#{partial_name}"
end
File.read(partial_path)
end
# You can change these defaults in, say, a Rails initializer or
# environment.rb, e.g.:
#
# Mustache::Rails::Config.template_base_path = Rails.root.join('app', 'templates')
module Config
def self.template_base_path
@template_base_path ||= ::Rails.root.join('app', 'templates')
end
def self.template_base_path=(value)
@template_base_path = value
end
def self.template_extension
@template_extension ||= 'html.mustache'
end
def self.template_extension=(value)
@template_extension = value
end
def self.shared_path
@shared_path ||= ::Rails.root.join('app', 'templates', 'shared')
end
def self.shared_path=(value)
@shared_path = value
end
end
class TemplateHandler < ActionView::Template::Handler
include ActionView::Template::Handlers::Compilable
self.default_format = :mustache
# @return [String] its evaled in the context of the action view
# hence the hack below
#
# @param [ActionView::Template]
def compile(template)
mustache_class = mustache_class_from_template(template)
mustache_class.template_file = mustache_template_file(template)
<<-MUSTACHE
mustache = ::#{mustache_class}.new
mustache.view = self
mustache[:yield] = content_for(:layout)
mustache.context.update(local_assigns)
variables = controller.instance_variable_names
variables -= %w[@template]
if controller.respond_to?(:protected_instance_variables)
variables -= controller.protected_instance_variables
end
variables.each do |name|
mustache.instance_variable_set(name, controller.instance_variable_get(name))
end
# Declaring an +attr_reader+ for each instance variable in the
# Mustache::Rails subclass makes them available to your templates.
mustache.class.class_eval do
attr_reader *variables.map { |name| name.sub(/^@/, '').to_sym }
end
mustache.render
MUSTACHE
end
private
def mustache_class_from_template(template)
const_name = ActiveSupport::Inflector.camelize(template.virtual_path.to_s)
defined?(const_name) ? const_name.constantize : Mustache
end
def mustache_template_file(template)
"#{Config.template_base_path}/#{template.virtual_path}.#{Config.template_extension}"
end
end
end
end
::ActiveSupport::Dependencies.load_paths << Rails.root.join("app", "views")
::ActionView::Template.register_template_handler(:rb, Mustache::Rails::TemplateHandler)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment