-
-
Save goodmike/449345 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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