mustache-1.0.2/0000755000004100000410000000000012560357623013364 5ustar www-datawww-datamustache-1.0.2/Rakefile0000644000004100000410000000243412560357623015034 0ustar www-datawww-datarequire 'rake/testtask' require 'rdoc/task' # # Helpers # def command?(command) system("type #{command} &> /dev/null") end # # Tests # task :default => :test Rake::TestTask.new do |t| t.libs << 'lib' t.pattern = 'test/**/*_test.rb' t.verbose = false end # # Ron # if command? :ronn desc "Show the manual" task :man => "man:build" do exec "man man/mustache.1" end desc "Build the manual" task "man:build" do sh "ronn -br5 --organization=DEFUNKT --manual='Mustache Manual' man/*.ron" end end # # Gems # desc "Push a new version to Gemcutter and publish docs." task :publish do require File.dirname(__FILE__) + '/lib/mustache/version' system "git tag v#{Mustache::Version}" sh "gem build mustache.gemspec" sh "gem push mustache-#{Mustache::Version}.gem" sh "git push origin master --tags" sh "git clean -fd" exec "rake pages" end # # Documentation # desc "Publish to GitHub Pages" task :pages => [ "man:build" ] do Dir['man/*.html'].each do |f| cp f, File.basename(f).sub('.html', '.newhtml') end `git commit -am 'generated manual'` `git checkout site` Dir['*.newhtml'].each do |f| mv f, f.sub('.newhtml', '.html') end `git add .` `git commit -m updated` `git push site site:master` `git checkout master` puts :done end mustache-1.0.2/bin/0000755000004100000410000000000012560357623014134 5ustar www-datawww-datamustache-1.0.2/bin/mustache0000755000004100000410000000465412560357623015704 0ustar www-datawww-data#!/usr/bin/env ruby require 'yaml' require 'optparse' require 'mustache' require 'mustache/version' class Mustache class CLI # Return a structure describing the options. def self.parse_options(args) opts = OptionParser.new do |opts| opts.banner = "Usage: mustache [-c] [-t] [-r library] FILE ..." opts.separator " " opts.separator "Examples:" opts.separator " $ mustache data.yml template.mustache" opts.separator " $ cat data.yml | mustache - template.mustache" opts.separator " $ mustache -c template.mustache" opts.separator " " opts.separator " See mustache(1) or " + "http://mustache.github.com/mustache.1.html" opts.separator " for more details." opts.separator " " opts.separator "Options:" opts.on("-c", "--compile FILE", "Print the compiled Ruby for a given template.") do |file| puts Mustache::Template.new(File.read(file)).compile exit end opts.on("-t", "--tokens FILE", "Print the tokenized form of a given template.") do |file| require 'pp' pp Mustache::Template.new(File.read(file)).tokens exit end opts.on('-r', '--require LIB', 'Require a Ruby library before running.') do |lib| require lib end opts.separator "Common Options:" opts.on("-v", "--version", "Print the version") do |v| puts "Mustache v#{Mustache::VERSION}" exit end opts.on_tail("-h", "--help", "Show this message") do puts opts exit end end opts.separator "" opts.parse!(args) end # Does the dirty work of reading files from the command line then # processing them. The meat of this script, if you will. def self.process_files(input_files) doc = input_files.file.read yaml = nil begin yaml = YAML.load_stream(doc) rescue abort "Unable to parse yaml!" end if yaml.nil? puts Mustache.render(doc) else template = input_files.skip.file.read puts Mustache.render template, yaml.compact.reduce(&:merge) end end end end # Help is the default. ARGV << '-h' if ARGV.empty? && $stdin.tty? # Process options Mustache::CLI.parse_options(ARGV) if $stdin.tty? # Still here - process rest of ARGF Mustache::CLI.process_files(ARGF) mustache-1.0.2/lib/0000755000004100000410000000000012560357623014132 5ustar www-datawww-datamustache-1.0.2/lib/mustache.rb0000644000004100000410000001766112560357623016303 0ustar www-datawww-datarequire 'mustache/enumerable' require 'mustache/template' require 'mustache/context' require 'mustache/settings' require 'mustache/utils' # Mustache is the base class from which your Mustache subclasses # should inherit (though it can be used on its own). # # The typical Mustache workflow is as follows: # # * Create a Mustache subclass: class Stats < Mustache # * Create a template: stats.mustache # * Instantiate an instance: view = Stats.new # * Render that instance: view.render # # You can skip the instantiation by calling `Stats.render` directly. # # While Mustache will do its best to load and render a template for # you, this process is completely customizable using a few options. # # All settings can be overriden at the class level. # # For example, going with the above example, we can use # `Stats.template_path = "/usr/local/templates"` to specify the path # Mustache uses to find templates. # # Here are the available options: # # * template_path # # The `template_path` setting determines the path Mustache uses when # looking for a template. By default it is "." # Setting it to /usr/local/templates, for example, means (given all # other settings are default) a Mustache subclass `Stats` will try to # load /usr/local/templates/stats.mustache # # * template_extension # # The `template_extension` is the extension Mustache uses when looking # for template files. By default it is "mustache" # # * template_file # # You can tell Mustache exactly which template to use with this # setting. It can be a relative or absolute path. # # * template # # Sometimes you want Mustache to render a string, not a file. In those # cases you may set the `template` setting. For example: # # >> Mustache.render("Hello {{planet}}", :planet => "World!") # => "Hello World!" # # The `template` setting is also available on instances. # # view = Mustache.new # view.template = "Hi, {{person}}!" # view[:person] = 'Mom' # view.render # => Hi, mom! # # * view_namespace # # To make life easy on those developing Mustache plugins for web frameworks or # other libraries, Mustache will attempt to load view classes (i.e. Mustache # subclasses) using the `view_class` class method. The `view_namespace` tells # Mustache under which constant view classes live. By default it is `Object`. # # * view_path # # Similar to `template_path`, the `view_path` option tells Mustache where to look # for files containing view classes when using the `view_class` method. # class Mustache # Instantiates an instance of this class and calls `render` with # the passed args. # # @return A rendered String version of a template. def self.render(*args) new.render(*args) end # Parses our fancy pants template file and returns normal file with # all special {{tags}} and {{#sections}}replaced{{/sections}}. # # Examples # # @view.render("Hi {{thing}}!", :thing => :world) # # View.template = "Hi {{thing}}!" # @view = View.new # @view.render(:thing => :world) # # @param [String,Hash] data A String template or a Hash context. # If a Hash is given, we'll try to figure # out the template from the class. # @param [Hash] ctx A Hash context if `data` is a String template. # # @return [String] Returns a rendered version of a template. def render(data = template, ctx = {}) case data when Hash ctx = data when Symbol self.template_name = data end tpl = case data when Hash templateify(template) when Symbol templateify(template) else templateify(data) end return tpl.render(context) if ctx == {} begin context.push(ctx) tpl.render(context) ensure context.pop end end # Context accessors. # # Example: # view = Mustache.new # view[:name] = "Jon" # view.template = "Hi, {{name}}!" # view.render # => "Hi, Jon!" def [](key) context[key.to_sym] end def []=(key, value) context[key.to_sym] = value end # A helper method which gives access to the context at a given time. # Kind of a hack for now, but useful when you're in an iterating section # and want access to the hash currently being iterated over. def context @context ||= Context.new(self) end # Given a file name and an optional context, attempts to load and # render the file as a template. def self.render_file(name, context = {}) render(partial(name), context) end # Given a file name and an optional context, attempts to load and # render the file as a template. def render_file(name, context = {}) self.class.render_file(name, context) end # Given a name, attempts to read a file and return the contents as a # string. The file is not rendered, so it might contain # {{mustaches}}. # # Call `render` if you need to process it. def self.partial(name) self.new.partial(name) end # Override this in your subclass if you want to do fun things like # reading templates from a database. It will be rendered by the # context, so all you need to do is return a string. def partial(name) path = "#{template_path}/#{name}.#{template_extension}" begin File.read(path) rescue raise if raise_on_context_miss? "" end end # Override this to provide custom escaping. # # Example: # # class PersonView < Mustache # def escapeHTML(str) # my_html_escape_method(str) # end # end # # @param [String] str String to escape. # # @return [String] Escaped HTML. def escapeHTML(str) CGI.escapeHTML(str) end # Has this instance or its class already compiled a template? def compiled? (@template && @template.is_a?(Template)) || self.class.compiled? end private # When given a symbol or string representing a class, will try to produce an # appropriate view class. # e.g. # Mustache.view_namespace = Hurl::Views # Mustache.view_class(:Partial) # => Hurl::Views::Partial def self.view_class(name) name = classify(name.to_s) # Emptiness begets emptiness. return Mustache if name.to_s.empty? name = "#{view_namespace}::#{name}" const = rescued_const_get(name) return const if const const_from_file(name) end def self.rescued_const_get name const_get(name, true) || Mustache rescue NameError nil end def self.const_from_file name file_name = underscore(name) file_path = "#{view_path}/#{file_name}.rb" return Mustache unless File.exists?(file_path) require file_path.chomp('.rb') rescued_const_get(name) end # Has this template already been compiled? Compilation is somewhat # expensive so it may be useful to check this before attempting it. def self.compiled? @template.is_a? Template end # template_partial => TemplatePartial # template/partial => Template::Partial def self.classify(underscored) Mustache::Utils::String.new(underscored).classify end # TemplatePartial => template_partial # Template::Partial => template/partial # Takes a string but defaults to using the current class' name. def self.underscore(classified = name) classified = superclass.name if classified.to_s.empty? Mustache::Utils::String.new(classified).underscore(view_namespace) end # @param [Template,String] obj Turns `obj` into a template def self.templateify(obj) obj.is_a?(Template) ? obj : Template.new(obj) end def templateify(obj) self.class.templateify(obj) end # Return the value of the configuration setting on the superclass, or return # the default. # # @param [Symbol] attr_name Name of the attribute. It should match # the instance variable. # @param [Object] default Default value to use if the superclass does # not respond. # # @return Inherited or default configuration setting. def self.inheritable_config_for(attr_name, default) superclass.respond_to?(attr_name) ? superclass.send(attr_name) : default end end mustache-1.0.2/lib/mustache/0000755000004100000410000000000012560357623015743 5ustar www-datawww-datamustache-1.0.2/lib/mustache/utils.rb0000644000004100000410000000123712560357623017433 0ustar www-datawww-dataclass Mustache module Utils class String def initialize string @string = string end def classify @string.split('/').map do |namespace| namespace.split(/[-_]/).map do |part| part[0] = part.chars.first.upcase part end.join end.join('::') end def underscore(view_namespace) @string .dup .split("#{view_namespace}::") .last .split('::') .map do |part| part[0] = part[0].downcase part.gsub(/[A-Z]/) { |s| "_" << s.downcase } end .join('/') end end end end mustache-1.0.2/lib/mustache/context.rb0000644000004100000410000001140112560357623017751 0ustar www-datawww-datarequire 'mustache/context_miss' class Mustache # A Context represents the context which a Mustache template is # executed within. All Mustache tags reference keys in the Context. # class Context # Initializes a Mustache::Context. # # @param [Mustache] mustache A Mustache instance. # def initialize(mustache) @stack = [mustache] @partial_template_cache = {} end # A {{>partial}} tag translates into a call to the context's # `partial` method, which would be this sucker right here. # # If the Mustache view handling the rendering (e.g. the view # representing your profile page or some other template) responds # to `partial`, we call it and render the result. # def partial(name, indentation = '') # Look for the first Mustache in the stack. mustache = mustache_in_stack # Indent the partial template by the given indentation. part = mustache.partial(name).to_s.gsub(/^/, indentation) # Get a template object for the partial and render the result. template_for_partial(part).render(self) end def template_for_partial(partial) @partial_template_cache[partial] ||= Template.new(partial) end # Find the first Mustache in the stack. # # If we're being rendered inside a Mustache object as a context, # we'll use that one. # # @return [Mustache] First Mustache in the stack. # def mustache_in_stack @mustache_in_stack ||= @stack.find { |frame| frame.is_a?(Mustache) } end # Allows customization of how Mustache escapes things. # # @param [String] str String to escape. # # @return [String] Escaped HTML string. # def escapeHTML(str) mustache_in_stack.escapeHTML(str) end # Adds a new object to the context's internal stack. # # @param [Object] new_obj Object to be added to the internal stack. # # @return [Context] Returns the Context. # def push(new_obj) @stack.unshift(new_obj) @mustache_in_stack = nil self end # Removes the most recently added object from the context's # internal stack. # # @return [Context] Returns the Context. # def pop @stack.shift @mustache_in_stack = nil self end # Can be used to add a value to the context in a hash-like way. # # context[:name] = "Chris" def []=(name, value) push(name => value) end # Alias for `fetch`. def [](name) fetch(name, nil) end # Do we know about a particular key? In other words, will calling # `context[key]` give us a result that was set. Basically. def has_key?(key) !!fetch(key, false) rescue ContextMiss false end # Similar to Hash#fetch, finds a value by `name` in the context's # stack. You may specify the default return value by passing a # second parameter. # # If no second parameter is passed (or raise_on_context_miss is # set to true), will raise a ContextMiss exception on miss. def fetch(name, default = :__raise) @stack.each do |frame| # Prevent infinite recursion. next if frame == self value = find(frame, name, :__missing) return value if value != :__missing end if default == :__raise || mustache_in_stack.raise_on_context_miss? raise ContextMiss.new("Can't find #{name} in #{@stack.inspect}") else default end end # Finds a key in an object, using whatever method is most # appropriate. If the object is a hash, does a simple hash lookup. # If it's an object that responds to the key as a method call, # invokes that method. You get the idea. # # @param [Object] obj The object to perform the lookup on. # @param [String,Symbol] key The key whose value you want # @param [Object] default An optional default value, to return if the key is not found. # # @return [Object] The value of key in object if it is found, and default otherwise. # def find(obj, key, default = nil) return find_in_hash(obj.to_hash, key, default) if obj.respond_to?(:to_hash) key = to_tag(key) return default unless obj.respond_to?(key) meth = obj.method(key) rescue proc { obj.send(key) } meth.arity == 1 ? meth.to_proc : meth.call end def current @stack.first end private # If a class, we need to find tags (methods) per Parser::ALLOWED_CONTENT. def to_tag key key.to_s.include?('-') ? key.to_s.tr('-', '_') : key end # Fetches a hash key if it exists, or returns the given default. def find_in_hash(obj, key, default) return obj[key] if obj.has_key?(key) return obj[key.to_s] if obj.has_key?(key.to_s) obj.fetch(key, default) end end end mustache-1.0.2/lib/mustache/parser.rb0000644000004100000410000002523712560357623017575 0ustar www-datawww-datarequire 'strscan' class Mustache # The Parser is responsible for taking a string template and # converting it into an array of tokens and, really, expressions. It # raises SyntaxError if there is anything it doesn't understand and # knows which sigil corresponds to which tag type. # # For example, given this template: # # Hi {{thing}}! # # Run through the Parser we'll get these tokens: # # [:multi, # [:static, "Hi "], # [:mustache, :etag, "thing"], # [:static, "!\n"]] # # You can see the array of tokens for any template with the # mustache(1) command line tool: # # $ mustache --tokens test.mustache # [:multi, [:static, "Hi "], [:mustache, :etag, "thing"], [:static, "!\n"]] class Parser # A SyntaxError is raised when the Parser comes across unclosed # tags, sections, illegal content in tags, or anything of that # sort. class SyntaxError < StandardError def initialize(message, position) @message = message @lineno, @column, @line, _ = position @stripped_line = @line.strip @stripped_column = @column - (@line.size - @line.lstrip.size) end def to_s <<-EOF #{@message} Line #{@lineno} #{@stripped_line} #{' ' * @stripped_column}^ EOF end end # The sigil types which are valid after an opening `{{` VALID_TYPES = [ '#', '^', '/', '=', '!', '<', '>', '&', '{' ].map(&:freeze) def self.valid_types @valid_types ||= Regexp.new(VALID_TYPES.map { |t| Regexp.escape(t) }.join('|') ) end # Add a supported sigil type (with optional aliases) to the Parser. # # Requires a block, which will be sent the following parameters: # # * content - The raw content of the tag # * fetch- A mustache context fetch expression for the content # * padding - Indentation whitespace from the currently-parsed line # * pre_match_position - Location of the scanner before a match was made # # The provided block will be evaluated against the current instance of # Parser, and may append to the Parser's @result as needed. def self.add_type(*types, &block) types = types.map(&:to_s) type, *aliases = types method_name = "scan_tag_#{type}".to_sym define_method(method_name, &block) aliases.each { |a| alias_method "scan_tag_#{a}", method_name } types.each { |t| VALID_TYPES << t unless VALID_TYPES.include?(t) } @valid_types = nil end # After these types of tags, all whitespace until the end of the line will # be skipped if they are the first (and only) non-whitespace content on # the line. SKIP_WHITESPACE = [ '#', '^', '/', '<', '>', '=', '!' ].map(&:freeze) # The content allowed in a tag name. ALLOWED_CONTENT = /(\w|[?!\/.-])*/ # These types of tags allow any content, # the rest only allow ALLOWED_CONTENT. ANY_CONTENT = [ '!', '=' ].map(&:freeze) attr_writer :otag, :ctag # Accepts an options hash which does nothing but may be used in # the future. def initialize(options = {}) @options = {} end # The opening tag delimiter. This may be changed at runtime. def otag @otag ||= '{{' end # The closing tag delimiter. This too may be changed at runtime. def ctag @ctag ||= '}}' end # Given a string template, returns an array of tokens. def compile(template) @encoding = nil if template.respond_to?(:encoding) @encoding = template.encoding template = template.dup.force_encoding("BINARY") end # Keeps information about opened sections. @sections = [] @result = [:multi] @scanner = StringScanner.new(template) # Scan until the end of the template. until @scanner.eos? scan_tags || scan_text end if !@sections.empty? # We have parsed the whole file, but there's still opened sections. type, pos, _ = @sections.pop error "Unclosed section #{type.inspect}", pos end @result end private def content_tags type, current_ctag if ANY_CONTENT.include?(type) r = /\s*#{regexp(type)}?#{regexp(current_ctag)}/ scan_until_exclusive(r) else @scanner.scan(ALLOWED_CONTENT) end end def dispatch_based_on_type type, content, fetch, padding, pre_match_position send("scan_tag_#{type}", content, fetch, padding, pre_match_position) end def find_closing_tag scanner, current_ctag error "Unclosed tag" unless scanner.scan(regexp(current_ctag)) end # Find {{mustaches}} and add them to the @result array. def scan_tags # Scan until we hit an opening delimiter. start_of_line = @scanner.beginning_of_line? pre_match_position = @scanner.pos last_index = @result.length return unless @scanner.scan(/([ \t]*)?#{Regexp.escape(otag)}/) padding = @scanner[1] || '' # Don't touch the preceding whitespace unless we're matching the start # of a new line. unless start_of_line @result << [:static, padding] unless padding.empty? pre_match_position += padding.length padding = '' end # Since {{= rewrites ctag, we store the ctag which should be used # when parsing this specific tag. current_ctag = self.ctag type = @scanner.scan(self.class.valid_types) @scanner.skip(/\s*/) # ANY_CONTENT tags allow any character inside of them, while # other tags (such as variables) are more strict. content = content_tags(type, current_ctag) # We found {{ but we can't figure out what's going on inside. error "Illegal content in tag" if content.empty? fetch = [:mustache, :fetch, content.split('.')] prev = @result dispatch_based_on_type(type, content, fetch, padding, pre_match_position) # The closing } in unescaped tags is just a hack for # aesthetics. type = "}" if type == "{" # Skip whitespace and any balancing sigils after the content # inside this tag. @scanner.skip(/\s+/) @scanner.skip(regexp(type)) if type find_closing_tag(@scanner, current_ctag) # If this tag was the only non-whitespace content on this line, strip # the remaining whitespace. If not, but we've been hanging on to padding # from the beginning of the line, re-insert the padding as static text. if start_of_line && !@scanner.eos? if @scanner.peek(2) =~ /\r?\n/ && SKIP_WHITESPACE.include?(type) @scanner.skip(/\r?\n/) else prev.insert(last_index, [:static, padding]) unless padding.empty? end end # Store off the current scanner position now that we've closed the tag # and consumed any irrelevant whitespace. @sections.last[1] << @scanner.pos unless @sections.empty? return unless @result == [:multi] end # Try to find static text, e.g. raw HTML with no {{mustaches}}. def scan_text text = scan_until_exclusive(/(^[ \t]*)?#{Regexp.escape(otag)}/) if text.nil? # Couldn't find any otag, which means the rest is just static text. text = @scanner.rest # Mark as done. @scanner.terminate end text.force_encoding(@encoding) if @encoding @result << [:static, text] unless text.empty? end # Scans the string until the pattern is matched. Returns the substring # *excluding* the end of the match, advancing the scan pointer to that # location. If there is no match, nil is returned. def scan_until_exclusive(regexp) pos = @scanner.pos if @scanner.scan_until(regexp) @scanner.pos -= @scanner.matched.size @scanner.pre_match[pos..-1] end end def offset position[0, 2] end # Returns [lineno, column, line] def position # The rest of the current line rest = @scanner.check_until(/\n|\Z/).to_s.chomp # What we have parsed so far parsed = @scanner.string[0...@scanner.pos] lines = parsed.split("\n") [ lines.size, lines.last.size - 1, lines.last + rest ] end # Used to quickly convert a string into a regular expression # usable by the string scanner. def regexp(thing) /#{Regexp.escape(thing)}/ end # Raises a SyntaxError. The message should be the name of the # error - other details such as line number and position are # handled for you. def error(message, pos = position) raise SyntaxError.new(message, pos) end # # Scan tags # # These methods are called in `scan_tags`. Because they contain nonstandard # characters in their method names, they are aliased to # better named methods. # # This function handles the cases where the scanned tag does not have # a type. def scan_tag_ content, fetch, padding, pre_match_position @result << [:mustache, :etag, fetch, offset] end def scan_tag_block content, fetch, padding, pre_match_position block = [:multi] @result << [:mustache, :section, fetch, offset, block] @sections << [content, position, @result] @result = block end alias_method :'scan_tag_#', :scan_tag_block def scan_tag_inverted content, fetch, padding, pre_match_position block = [:multi] @result << [:mustache, :inverted_section, fetch, offset, block] @sections << [content, position, @result] @result = block end alias_method :'scan_tag_^', :scan_tag_inverted def scan_tag_close content, fetch, padding, pre_match_position section, pos, result = @sections.pop raw = @scanner.pre_match[pos[3]...pre_match_position] + padding (@result = result).last << raw << [self.otag, self.ctag] if section.nil? error "Closing unopened #{content.inspect}" elsif section != content error "Unclosed section #{section.inspect}", pos end end alias_method :'scan_tag_/', :scan_tag_close def scan_tag_comment content, fetch, padding, pre_match_position end alias_method :'scan_tag_!', :scan_tag_comment def scan_tag_delimiter content, fetch, padding, pre_match_position self.otag, self.ctag = content.split(' ', 2) end alias_method :'scan_tag_=', :scan_tag_delimiter def scan_tag_open_partial content, fetch, padding, pre_match_position @result << [:mustache, :partial, content, offset, padding] end alias_method :'scan_tag_<', :scan_tag_open_partial alias_method :'scan_tag_>', :scan_tag_open_partial def scan_tag_unescaped content, fetch, padding, pre_match_position @result << [:mustache, :utag, fetch, offset] end alias_method :'scan_tag_{', :'scan_tag_unescaped' alias_method :'scan_tag_&', :'scan_tag_unescaped' end end mustache-1.0.2/lib/mustache/version.rb0000644000004100000410000000004712560357623017756 0ustar www-datawww-dataclass Mustache VERSION = '1.0.2' end mustache-1.0.2/lib/mustache/context_miss.rb0000644000004100000410000000067012560357623021012 0ustar www-datawww-dataclass Mustache # A ContextMiss is raised whenever a tag's target can not be found # in the current context if `Mustache#raise_on_context_miss?` is # set to true. # # For example, if your View class does not respond to `music` but # your template contains a `{{music}}` tag this exception will be raised. # # By default it is not raised. See Mustache.raise_on_context_miss. # class ContextMiss < RuntimeError; end end mustache-1.0.2/lib/mustache/enumerable.rb0000644000004100000410000000005512560357623020407 0ustar www-datawww-dataclass Mustache Enumerable = Module.new end mustache-1.0.2/lib/mustache/generator.rb0000644000004100000410000001321012560357623020253 0ustar www-datawww-dataclass Mustache # The Generator is in charge of taking an array of Mustache tokens, # usually assembled by the Parser, and generating an interpolatable # Ruby string. This string is considered the "compiled" template # because at that point we're relying on Ruby to do the parsing and # run our code. # # For example, let's take this template: # # Hi {{thing}}! # # If we run this through the Parser we'll get these tokens: # # [:multi, # [:static, "Hi "], # [:mustache, :etag, "thing"], # [:static, "!\n"]] # # Now let's hand that to the Generator: # # >> puts Mustache::Generator.new.compile(tokens) # "Hi #{CGI.escapeHTML(ctx[:thing].to_s)}!\n" # # You can see the generated Ruby string for any template with the # mustache(1) command line tool: # # $ mustache --compile test.mustache # "Hi #{CGI.escapeHTML(ctx[:thing].to_s)}!\n" class Generator # Options are unused for now but may become useful in the future. def initialize(options = {}) @options = options end # Given an array of tokens, returns an interpolatable Ruby string. def compile(exp) "\"#{compile!(exp)}\"" end private # Given an array of tokens, converts them into Ruby code. In # particular there are three types of expressions we are concerned # with: # # :multi # Mixed bag of :static, :mustache, and whatever. # # :static # Normal HTML, the stuff outside of {{mustaches}}. # # :mustache # Any Mustache tag, from sections to partials. # # To give you an idea of what you'll be dealing with take this # template: # # Hello {{name}} # You have just won ${{value}}! # {{#in_ca}} # Well, ${{taxed_value}}, after taxes. # {{/in_ca}} # # If we run this through the Parser, we'll get back this array of # tokens: # # [:multi, # [:static, "Hello "], # [:mustache, :etag, # [:mustache, :fetch, ["name"]]], # [:static, "\nYou have just won $"], # [:mustache, :etag, # [:mustache, :fetch, ["value"]]], # [:static, "!\n"], # [:mustache, # :section, # [:mustache, :fetch, ["in_ca"]], # [:multi, # [:static, "Well, $"], # [:mustache, :etag, # [:mustache, :fetch, ["taxed_value"]]], # [:static, ", after taxes.\n"]], # "Well, ${{taxed_value}}, after taxes.\n", # ["{{", "}}"]]] def compile!(exp) case exp.first when :multi exp[1..-1].reduce("") { |sum, e| sum << compile!(e) } when :static str(exp[1]) when :mustache send("on_#{exp[1]}", *exp[2..-1]) else raise "Unhandled exp: #{exp.first}" end end # Callback fired when the compiler finds a section token. We're # passed the section name and the array of tokens. def on_section(name, offset, content, raw, delims) # Convert the tokenized content of this section into a Ruby # string we can use. code = compile(content) # Compile the Ruby for this section now that we know what's # inside the section. ev(<<-compiled) if v = #{compile!(name)} if v == true #{code} elsif v.is_a?(Proc) t = Mustache::Template.new(v.call(#{raw.inspect}).to_s) def t.tokens(src=@source) p = Parser.new p.otag, p.ctag = #{delims.inspect} p.compile(src) end t.render(ctx.dup) else # Shortcut when passed non-array v = [v] unless v.is_a?(Array) || v.is_a?(Mustache::Enumerable) || defined?(Enumerator) && v.is_a?(Enumerator) v.map { |h| ctx.push(h); r = #{code}; ctx.pop; r }.join end end compiled end # Fired when we find an inverted section. Just like `on_section`, # we're passed the inverted section name and the array of tokens. def on_inverted_section(name, offset, content, raw, delims) # Convert the tokenized content of this section into a Ruby # string we can use. code = compile(content) # Compile the Ruby for this inverted section now that we know # what's inside. ev(<<-compiled) v = #{compile!(name)} if v.nil? || v == false || v.respond_to?(:empty?) && v.empty? #{code} end compiled end # Fired when the compiler finds a partial. We want to return code # which calls a partial at runtime instead of expanding and # including the partial's body to allow for recursive partials. def on_partial(name, offset, indentation) ev("ctx.partial(#{name.to_sym.inspect}, #{indentation.inspect})") end # An unescaped tag. def on_utag(name, offset) ev(<<-compiled) v = #{compile!(name)} if v.is_a?(Proc) v = Mustache::Template.new(v.call.to_s).render(ctx.dup) end v.to_s compiled end # An escaped tag. def on_etag(name, offset) ev(<<-compiled) v = #{compile!(name)} if v.is_a?(Proc) v = Mustache::Template.new(v.call.to_s).render(ctx.dup) end ctx.escapeHTML(v.to_s) compiled end def on_fetch(names) return "ctx.current" if names.empty? names = names.map { |n| n.to_sym } initial, *rest = names <<-compiled #{rest.inspect}.reduce(ctx[#{initial.inspect}]) { |value, key| value && ctx.find(value, key) } compiled end # An interpolation-friendly version of a string, for use within a # Ruby string. def ev(s) "#\{#{s}}" end def str(s) s.inspect[1..-2] end end end mustache-1.0.2/lib/mustache/settings.rb0000644000004100000410000001226512560357623020136 0ustar www-datawww-data# Settings which can be configured for all view classes, a single # view class, or a single Mustache instance. class Mustache # # Template Path # # The template path informs your Mustache view where to look for its # corresponding template. By default it's the current directory (".") # # A class named Stat with a template_path of "app/templates" will look # for "app/templates/stat.mustache" def self.template_path @template_path ||= inheritable_config_for :template_path, '.' end def self.template_path=(path) @template_path = File.expand_path(path) @template = nil end def template_path @template_path ||= self.class.template_path end alias_method :path, :template_path def template_path=(path) @template_path = File.expand_path(path) @template = nil end # Alias for `template_path` def self.path template_path end # Alias for `template_path` def self.path=(path) self.template_path = path end # # Template Extension # # A Mustache template's default extension is 'mustache', but this can be changed. def self.template_extension @template_extension ||= inheritable_config_for :template_extension, 'mustache' end def self.template_extension=(template_extension) @template_extension = template_extension @template = nil end def template_extension @template_extension ||= self.class.template_extension end def template_extension=(template_extension) @template_extension = template_extension @template = nil end # # Template Name # # The template name is the Mustache template file without any # extension or other information. Defaults to `class_name`. # # You may want to change this if your class is named Stat but you want # to re-use another template. # # class Stat # self.template_name = "graphs" # use graphs.mustache # end def self.template_name @template_name || underscore end def self.template_name=(template_name) @template_name = template_name @template = nil end def template_name @template_name ||= self.class.template_name end def template_name=(template_name) @template_name = template_name @template = nil end # # Template File # # The template file is the absolute path of the file Mustache will # use as its template. By default it's ./class_name.mustache def self.template_file @template_file || "#{path}/#{template_name}.#{template_extension}" end def self.template_file=(template_file) @template_file = template_file @template = nil end # The template file is the absolute path of the file Mustache will # use as its template. By default it's ./class_name.mustache def template_file @template_file || "#{path}/#{template_name}.#{template_extension}" end def template_file=(template_file) @template_file = template_file @template = nil end # # Template # # The template is the actual string Mustache uses as its template. # There is a bit of magic here: what we get back is actually a # Mustache::Template object, but you can still safely use `template=` # with a string. def self.template @template ||= templateify(File.read(template_file)) end def self.template=(template) @template = templateify(template) end # The template can be set at the instance level. def template return @template if @template # If they sent any instance-level options use that instead of the class's. if @template_path || @template_extension || @template_name || @template_file @template = templateify(File.read(template_file)) else @template = self.class.template end end def template=(template) @template = templateify(template) end # # Raise on context miss # # Should an exception be raised when we cannot find a corresponding method # or key in the current context? By default this is false to emulate ctemplate's # behavior, but it may be useful to enable when debugging or developing. # # If set to true and there is a context miss, `Mustache::ContextMiss` will # be raised. def self.raise_on_context_miss? @raise_on_context_miss end def self.raise_on_context_miss=(boolean) @raise_on_context_miss = boolean end # Instance level version of `Mustache.raise_on_context_miss?` def raise_on_context_miss? self.class.raise_on_context_miss? || @raise_on_context_miss end def raise_on_context_miss=(boolean) @raise_on_context_miss = boolean end # # View Namespace # # The constant under which Mustache will look for views when autoloading. # By default the view namespace is `Object`, but it might be nice to set # it to something like `Hurl::Views` if your app's main namespace is `Hurl`. def self.view_namespace @view_namespace ||= inheritable_config_for(:view_namespace, Object) end def self.view_namespace=(namespace) @view_namespace = namespace end # # View Path # # Mustache searches the view path for .rb files to require when asked to find a # view class. Defaults to "." def self.view_path @view_path ||= inheritable_config_for(:view_path, '.') end def self.view_path=(path) @view_path = path end end mustache-1.0.2/lib/mustache/template.rb0000644000004100000410000001010212560357623020075 0ustar www-datawww-datarequire 'cgi' require 'mustache/parser' require 'mustache/generator' class Mustache # A Template represents a Mustache template. It compiles and caches # a raw string template into something usable. # # The idea is this: when handed a Mustache template, convert it into # a Ruby string by transforming Mustache tags into interpolated # Ruby. # # You shouldn't use this class directly, instead: # # >> Mustache.render(template, hash) class Template attr_reader :source # Expects a Mustache template as a string along with a template # path, which it uses to find partials. def initialize(source) @source = source end # Renders the `@source` Mustache template using the given # `context`, which should be a simple hash keyed with symbols. # # The first time a template is rendered, this method is overriden # and from then on it is "compiled". Subsequent calls will skip # the compilation step and run the Ruby version of the template # directly. def render(context) # Compile our Mustache template into a Ruby string compiled = "def render(ctx) #{compile} end" # Here we rewrite ourself with the interpolated Ruby version of # our Mustache template so subsequent calls are very fast and # can skip the compilation stage. instance_eval(compiled, __FILE__, __LINE__ - 1) # Call the newly rewritten version of #render render(context) end # Does the dirty work of transforming a Mustache template into an # interpolation-friendly Ruby string. def compile(src = @source) Generator.new.compile(tokens(src)) end alias_method :to_s, :compile # Returns an array of tokens for a given template. # # @return [Array] Array of tokens. # def tokens(src = @source) Parser.new.compile(src) end # Returns an array of tags. # # Tags that belong to sections will be of the form `section1.tag`. # # @return [Array] Returns an array of tags. # def tags Template.recursor(tokens, []) do |token, section| if [:etag, :utag].include?(token[1]) [ new_token=nil, new_section=nil, result=((section + [token[2][2][0]]).join('.')), stop=true ] elsif [:section, :inverted_section].include?(token[1]) [ new_token=token[4], new_section=(section + [token[2][2][0]]), result=nil, stop=false ] else [ new_token=token, new_section=section, result=nil, stop=false ] end end.flatten.reject(&:nil?).uniq end # Returns an array of sections. # # Sections that belong to other sections will be of the form `section1.childsection` # # @return [Array] Returns an array of section. # def sections Template.recursor(tokens, []) do |token, section| if [:section, :inverted_section].include?(token[1]) new_section=(section + [token[2][2][0]]) [ new_token=token[4], new_section, result=new_section.join('.'), stop=false ] else [ new_token=token, new_section=section, result=nil, stop=false ] end end.flatten.reject(&:nil?).uniq end # Returns an array of partials. # # Partials that belong to sections are included, but the section name is not preserved # # @return [Array] Returns an array of partials. # def partials Template.recursor(tokens, []) do |token, section| if token[1] == :partial [ new_token=token, new_section=section, result=token[2], stop=true ] else [ new_token=token, new_section=section, result=nil, stop=false ] end end.flatten.reject(&:nil?).uniq end private # Simple recursive iterator for tokens def self.recursor(toks, section, &block) toks.map do |token| next unless token.is_a? Array if token.first == :mustache new_token, new_section, result, stop = yield(token, section) [ result ] + ( stop ? [] : recursor(new_token, new_section, &block)) else recursor(token, section, &block) end end end end end mustache-1.0.2/metadata.yml0000644000004100000410000001367612560357623015704 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: mustache version: !ruby/object:Gem::Version version: 1.0.2 platform: ruby authors: - Chris Wanstrath - Magnus Holm - Pieter van de Bruggen - Ricardo Mendes autorequire: bindir: bin cert_chain: [] date: 2015-06-24 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: bundler requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.6' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '1.6' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '10.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '10.3' - !ruby/object:Gem::Dependency name: minitest requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '5.4' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '5.4' - !ruby/object:Gem::Dependency name: benchmark-ips requirement: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: ruby-prof requirement: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rdoc requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '4.1' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '4.1' - !ruby/object:Gem::Dependency name: ronn requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '0.7' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '0.7' description: ! 'Inspired by ctemplate, Mustache is a framework-agnostic way to render logic-free views. As ctemplates says, "It emphasizes separating logic from presentation: it is impossible to embed application logic in this template language. Think of Mustache as a replacement for your views. Instead of views consisting of ERB or HAML with random helpers and arbitrary logic, your views are broken into two parts: a Ruby class and an HTML template. ' email: rokusu@gmail.com executables: - mustache extensions: [] extra_rdoc_files: [] files: - LICENSE - README.md - Rakefile - bin/mustache - lib/mustache.rb - lib/mustache/context.rb - lib/mustache/context_miss.rb - lib/mustache/enumerable.rb - lib/mustache/generator.rb - lib/mustache/parser.rb - lib/mustache/settings.rb - lib/mustache/template.rb - lib/mustache/utils.rb - lib/mustache/version.rb - man/mustache.1 - man/mustache.1.html - man/mustache.1.ron - man/mustache.5 - man/mustache.5.html - man/mustache.5.ron - test/autoloading_test.rb - test/fixtures/comments.mustache - test/fixtures/comments.rb - test/fixtures/complex_view.mustache - test/fixtures/complex_view.rb - test/fixtures/crazy_recursive.mustache - test/fixtures/crazy_recursive.rb - test/fixtures/delimiters.mustache - test/fixtures/delimiters.rb - test/fixtures/dot_notation.mustache - test/fixtures/dot_notation.rb - test/fixtures/double_section.mustache - test/fixtures/double_section.rb - test/fixtures/escaped.mustache - test/fixtures/escaped.rb - test/fixtures/inner_partial.mustache - test/fixtures/inner_partial.txt - test/fixtures/inverted_section.mustache - test/fixtures/inverted_section.rb - test/fixtures/lambda.mustache - test/fixtures/lambda.rb - test/fixtures/liberal.mustache - test/fixtures/liberal.rb - test/fixtures/method_missing.rb - test/fixtures/namespaced.mustache - test/fixtures/namespaced.rb - test/fixtures/nested_objects.mustache - test/fixtures/nested_objects.rb - test/fixtures/node.mustache - test/fixtures/partial_with_module.mustache - test/fixtures/partial_with_module.rb - test/fixtures/passenger.conf - test/fixtures/passenger.rb - test/fixtures/recursive.mustache - test/fixtures/recursive.rb - test/fixtures/simple.mustache - test/fixtures/simple.rb - test/fixtures/simply_complicated.mustache - test/fixtures/template_partial.mustache - test/fixtures/template_partial.rb - test/fixtures/template_partial.txt - test/fixtures/unescaped.mustache - test/fixtures/unescaped.rb - test/fixtures/utf8.mustache - test/fixtures/utf8_partial.mustache - test/helper.rb - test/mustache_test.rb - test/parser_test.rb - test/partial_test.rb - test/spec_test.rb - test/template_test.rb homepage: https://github.com/mustache/mustache licenses: - MIT metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: '2.0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.4.5 signing_key: specification_version: 4 summary: Mustache is a framework-agnostic way to render logic-free views. test_files: [] mustache-1.0.2/test/0000755000004100000410000000000012560357623014343 5ustar www-datawww-datamustache-1.0.2/test/helper.rb0000644000004100000410000000025412560357623016150 0ustar www-datawww-datarequire 'minitest/autorun' require "codeclimate-test-reporter" CodeClimate::TestReporter.start Dir[File.dirname(__FILE__) + '/fixtures/*.rb'].each do |f| require f end mustache-1.0.2/test/spec_test.rb0000644000004100000410000000413112560357623016660 0ustar www-datawww-datarequire 'mustache' require 'tmpdir' require 'yaml' require 'minitest/autorun' # Calls appropriate method on YAML. See: https://gist.github.com/tenderlove/958999ab4240b93bd3cd YAML.add_domain_type(nil, 'code') { |_, val| eval(val['ruby']) } # A simple base class for Mustache specs. # Creates a partials directory, then points a (dynamic) subclass of Mustache at # that directory before each test; the partials directory is destroyed after # each test is run. class MustacheSpec < Minitest::Test def setup @partials = File.join(File.dirname(__FILE__), 'partials') Dir.mkdir(@partials) @Mustache = Class.new(Mustache) @Mustache.template_path = @partials end def teardown Dir[File.join(@partials, '*')].each { |file| File.delete(file) } Dir.rmdir(@partials) end # Extracts the partials from the test, and dumps them into the partials # directory for inclusion. def setup_partials(test) (test['partials'] || {}).each do |name, content| File.open(File.join(@partials, "#{name}.mustache"), 'w') do |f| f.print(content) end end end # Asserts equality between the rendered template and the expected value, # printing additional context data on failure. def assert_mustache_spec(test) actual = @Mustache.render(test['template'], test['data']) assert_equal test['expected'], actual, "" << "#{ test['desc'] }\n" << "Data: #{ test['data'].inspect }\n" << "Template: #{ test['template'].inspect }\n" << "Partials: #{ (test['partials'] || {}).inspect }\n" end def test_noop; assert(true); end end spec_files = File.join(File.dirname(__FILE__), '..', 'ext', 'spec', 'specs', '*.yml') Dir[spec_files].each do |file| spec = YAML.load_file(file) klass_name = "Test" + File.basename(file, ".yml").sub(/~/, '').capitalize instance_eval "class ::#{klass_name} < MustacheSpec; end" test_suite = Kernel.const_get(klass_name) test_suite.class_eval do spec['tests'].each do |test| define_method :"test_spec - #{test['name']}" do setup_partials(test) assert_mustache_spec(test) end end end end mustache-1.0.2/test/partial_test.rb0000644000004100000410000000677512560357623017402 0ustar www-datawww-datarequire_relative 'helper' class PartialTest < Minitest::Test def test_view_partial assert_equal <<-end_partial.strip, PartialWithModule.render

Welcome

Hello Bob You have just won $100000!

Fair enough, right?

end_partial end def test_partial_with_slashes klass = Class.new(Mustache) klass.template = '{{> test/fixtures/inner_partial}}' view = klass.new view[:title] = 'success' assert_equal "Again, success!", view.render end def test_view_partial_inherits_context klass = Class.new(TemplatePartial) view = klass.new view.template_path = File.dirname(__FILE__) + '/fixtures' view[:titles] = [{:title => :One}, {:title => :Two}] view.template = <<-end_template

Context Test

end_template assert_equal <<-end_partial, view.render

Context Test

end_partial end def test_view_partial_inherits_context_of_class_methods klass = Class.new(TemplatePartial) klass.template_path = File.dirname(__FILE__) + '/fixtures' klass.send(:define_method, :titles) do [{:title => :One}, {:title => :Two}] end view = klass.new view.template = <<-end_template

Context Test

end_template assert_equal <<-end_partial, view.render

Context Test

end_partial end def test_template_partial assert_equal <<-end_partial.strip, TemplatePartial.render

Welcome

Again, Welcome! end_partial end def test_template_partial_with_custom_extension partial = Class.new(TemplatePartial) partial.template_extension = 'txt' partial.template_path = File.dirname(__FILE__) + '/fixtures' assert_equal <<-end_partial.strip, partial.render.strip Welcome ------- ## Again, Welcome! ## end_partial end def test_recursive_partials assert_equal <<-end_partial, Recursive.render It works! end_partial end def test_crazy_recursive_partials assert_equal <<-end_partial.strip, CrazyRecursive.render end_partial end def test_partials_use_proper_context assert_equal "OuterThing OuterThing", OuterThing.render('{{name}} {{> p}}') assert_equal "InnerThing InnerThing", InnerThing.render('{{name}} {{> p}}') assert_equal "OuterThing InnerThing InnerThing", OuterThing.render('{{name}} {{#inner}}{{name}} {{> p}}{{/inner}}') end def test_partials_render_returned_strings assert_equal "ok", MiddleThing.render('{{> some_partial }}') end end class InnerThing < Mustache def partial(p) self.class end def name; self.class end end class OuterThing < Mustache def inner InnerThing.new end def partial(p) self.class end def name; self.class end end class MiddleThing < Mustache def partial(name) "{{#{name}}}" end def some_partial; "ok" end end mustache-1.0.2/test/fixtures/0000755000004100000410000000000012560357623016214 5ustar www-datawww-datamustache-1.0.2/test/fixtures/passenger.rb0000644000004100000410000000054412560357623020533 0ustar www-datawww-datarequire 'mustache' class Passenger < Mustache self.path = File.dirname(__FILE__) self.template_extension = 'conf' def server "example.com" end def deploy_to "/var/www/example.com" end def stage "production" end def timestamp Time.now.strftime('%Y%m%d%H%M%S') end end if $0 == __FILE__ puts Passenger.to_text end mustache-1.0.2/test/fixtures/nested_objects.mustache0000644000004100000410000000042412560357623022742 0ustar www-datawww-data

{{header}}

{{#list}} {{/list}} {{^list}}

The list is empty.

{{/list}}mustache-1.0.2/test/fixtures/double_section.mustache0000644000004100000410000000007112560357623022743 0ustar www-datawww-data{{#t}} * first {{/t}} * {{two}} {{#t}} * third {{/t}}mustache-1.0.2/test/fixtures/dot_notation.rb0000644000004100000410000000060412560357623021242 0ustar www-datawww-datarequire 'mustache' class DotNotation < Mustache self.path = File.dirname(__FILE__) def person return { :name => OpenStruct.new(:first => 'Chris', :last => 'Firescythe'), :age => 24, :hometown => { :city => "Cincinnati", :state => "OH" } } end def normal "Normal" end end if $0 == __FILE__ puts DotNotation.to_html end mustache-1.0.2/test/fixtures/simple.mustache0000644000004100000410000000015112560357623021235 0ustar www-datawww-dataHello {{name}} You have just won ${{value}}! {{#in_ca}} Well, ${{ taxed_value }}, after taxes. {{/in_ca}}mustache-1.0.2/test/fixtures/delimiters.rb0000644000004100000410000000057212560357623020706 0ustar www-datawww-datarequire 'mustache' class Delimiters < Mustache self.path = File.dirname(__FILE__) def start "It worked the first time." end def middle [ { :item => "And it worked the second time." }, { :item => "As well as the third." } ] end def final "Then, surprisingly, it worked the final time." end end if $0 == __FILE__ puts Delimiters.to_html end mustache-1.0.2/test/fixtures/partial_with_module.rb0000644000004100000410000000064212560357623022577 0ustar www-datawww-datarequire 'mustache' module SimpleView def name "Bob" end def value 100_000 end def taxed_value value - (value * 0.4) end def in_ca false end end class PartialWithModule < Mustache include SimpleView self.path = File.dirname(__FILE__) def greeting "Welcome" end def farewell "Fair enough, right?" end end if $0 == __FILE__ puts PartialWithModule.to_html end mustache-1.0.2/test/fixtures/unescaped.mustache0000644000004100000410000000002412560357623021712 0ustar www-datawww-data

{{{title}}}

mustache-1.0.2/test/fixtures/complex_view.mustache0000644000004100000410000000042412560357623022450 0ustar www-datawww-data

{{header}}

{{#list}} {{/list}} {{^list}}

The list is empty.

{{/list}}mustache-1.0.2/test/fixtures/template_partial.mustache0000644000004100000410000000004512560357623023275 0ustar www-datawww-data

{{title}}

{{>inner_partial}}mustache-1.0.2/test/fixtures/dot_notation.mustache0000644000004100000410000000071612560357623022454 0ustar www-datawww-data* {{person.name.first}} {{person.name.last}} * {{person.age}} * {{person.hometown.city}}, {{person.hometown.state}} * {{#person}}{{hometown.city}}, {{hometown.state}}{{/person}} * {{#person}}{{#hometown}}{{city}}, {{state}}{{/hometown}}{{/person}} * {{#person.hometown}}{{city}}, {{state}}{{/person.hometown}} * {{normal}} * {{{person.name.first}}} {{&person.name.last}} * {{^person.alien?}}{{person.hometown.city}}, {{person.hometown.state}}{{/person.alien?}} mustache-1.0.2/test/fixtures/crazy_recursive.mustache0000644000004100000410000000015712560357623023171 0ustar www-datawww-data mustache-1.0.2/test/fixtures/utf8.mustache0000644000004100000410000000005512560357623020635 0ustar www-datawww-data

中文 {{test}}

{{> utf8_partial}} mustache-1.0.2/test/fixtures/complex_view.rb0000644000004100000410000000103012560357623021234 0ustar www-datawww-datarequire 'mustache' class ComplexView < Mustache self.path = File.dirname(__FILE__) def header "Colors" end def item items = [] items << { :name => 'red', :current => true, :url => '#Red' } items << { :name => 'green', :current => false, :url => '#Green' } items << { :name => 'blue', :current => false, :url => '#Blue' } items end def link not self[:current] end def list not item.empty? end def empty item.empty? end end if $0 == __FILE__ puts ComplexView.to_html end mustache-1.0.2/test/fixtures/comments.mustache0000644000004100000410000000010212560357623021565 0ustar www-datawww-data

{{title}}{{! just something interesting... #or not... }}

mustache-1.0.2/test/fixtures/inner_partial.txt0000644000004100000410000000003012560357623021575 0ustar www-datawww-data## Again, {{title}}! ## mustache-1.0.2/test/fixtures/template_partial.rb0000644000004100000410000000034212560357623022067 0ustar www-datawww-datarequire 'mustache' class TemplatePartial < Mustache self.path = File.dirname(__FILE__) def title "Welcome" end def title_bars '-' * title.size end end if $0 == __FILE__ puts TemplatePartial.to_html end mustache-1.0.2/test/fixtures/simply_complicated.mustache0000644000004100000410000000065312560357623023634 0ustar www-datawww-dataHi there {{yourname}}. Your home directory is {{HOME}}. {{#friend}} Your friend is named {{name}} {{#morr}} Hey {{word}} {{up}} {{{awesomesauce}}}. {{/morr}} {{^morr}} Booooo. {{hiss}} {{/morr}} {{notinmorr}} {{> partial1}} {{/friend}} {{^friend}} You have no friends, {{person}}. You suck. {{/friend}} {{> partial2}} {{! comments are awesome }} {{={% %}=}} {%love%} {%={{ }}=%} {{{triplestash}}} mustache-1.0.2/test/fixtures/simple.rb0000644000004100000410000000041512560357623020032 0ustar www-datawww-datarequire 'mustache' class Simple < Mustache self.path = File.dirname(__FILE__) def name "Chris" end def value 10_000 end def taxed_value value - (value * 0.4) end def in_ca true end end if $0 == __FILE__ puts Simple.to_html end mustache-1.0.2/test/fixtures/nested_objects.rb0000644000004100000410000000112212560357623021530 0ustar www-datawww-datarequire 'mustache' require 'ostruct' class NestedObjects < Mustache self.path = File.dirname(__FILE__) def header "Colors" end def item items = [] items << OpenStruct.new(:name => 'red', :current => true, :url => '#Red') items << OpenStruct.new(:name => 'green', :current => false, :url => '#Green') items << OpenStruct.new(:name => 'blue', :current => false, :url => '#Blue') items end def link not self[:current] end def list not item.empty? end def empty item.empty? end end if $0 == __FILE__ puts NestedObjects.to_html end mustache-1.0.2/test/fixtures/comments.rb0000644000004100000410000000026212560357623020366 0ustar www-datawww-datarequire 'mustache' class Comments < Mustache self.path = File.dirname(__FILE__) def title "A Comedy of Errors" end end if $0 == __FILE__ puts Comments.to_html end mustache-1.0.2/test/fixtures/liberal.mustache0000644000004100000410000000005612560357623021362 0ustar www-datawww-data{{first-name}} {{middle_name!}} {{lastName?}} mustache-1.0.2/test/fixtures/lambda.mustache0000644000004100000410000000013212560357623021163 0ustar www-datawww-data{{#rendered}} Hi {{name}}. {{/rendered}} {{#not_rendered}} Hi {{name}}. {{/not_rendered}}mustache-1.0.2/test/fixtures/inverted_section.mustache0000644000004100000410000000007112560357623023311 0ustar www-datawww-data{{^t}} * first {{/t}} * {{two}} {{^t}} * third {{/t}}mustache-1.0.2/test/fixtures/namespaced.rb0000644000004100000410000000062112560357623020640 0ustar www-datawww-datarequire 'mustache' module TestViews class Namespaced < Mustache self.path = File.dirname(__FILE__) def title "Dragon < Tiger" end end class NamespacedWithPartial < Mustache self.path = File.dirname(__FILE__) self.template = "My opinion: {{>inner_partial}}" def title "Victory" end end end if $0 == __FILE__ puts TestViews::Namespaced.to_html end mustache-1.0.2/test/fixtures/partial_with_module.mustache0000644000004100000410000000007012560357623024000 0ustar www-datawww-data

{{greeting}}

{{>simple}}

{{farewell}}

mustache-1.0.2/test/fixtures/delimiters.mustache0000644000004100000410000000013712560357623022111 0ustar www-datawww-data{{=<% %>=}} * <% start %> <%=| |=%> |# middle | * | item | |/ middle | |={{ }}=| * {{ final }} mustache-1.0.2/test/fixtures/utf8_partial.mustache0000644000004100000410000000003112560357623022343 0ustar www-datawww-data

中文又来啦

mustache-1.0.2/test/fixtures/escaped.mustache0000644000004100000410000000002212560357623021345 0ustar www-datawww-data

{{title}}

mustache-1.0.2/test/fixtures/node.mustache0000644000004100000410000000013412560357623020672 0ustar www-datawww-data
  • {{contents}}
  • mustache-1.0.2/test/fixtures/method_missing.rb0000644000004100000410000000047312560357623021556 0ustar www-datawww-datarequire 'mustache' class MethodMissing < Mustache self.template = '[ {{#list}}{{.}} {{/list}}]' def method_missing(name, *args, &block) return (0..10).to_a if name == :list return super end def respond_to?(method) method == :list end end if $0 == __FILE__ puts MethodMissing.to_html end mustache-1.0.2/test/fixtures/inverted_section.rb0000644000004100000410000000022512560357623022104 0ustar www-datawww-datarequire 'mustache' class InvertedSection < Mustache self.path = File.dirname(__FILE__) def t false end def two "second" end end mustache-1.0.2/test/fixtures/inner_partial.mustache0000644000004100000410000000002112560357623022567 0ustar www-datawww-dataAgain, {{title}}!mustache-1.0.2/test/fixtures/lambda.rb0000644000004100000410000000071312560357623017762 0ustar www-datawww-datarequire 'mustache' class Lambda < Mustache self.path = File.dirname(__FILE__) attr_reader :calls def initialize(*args) super @calls = 0 @cached = nil end def rendered lambda do |text| return @cached if @cached @calls += 1 @cached = render(text) end end def not_rendered lambda { |text| "{{= | =}}#{text}" } end end if $0 == __FILE__ puts Lambda.to_html(Lambda.template, :name => "Jonny") end mustache-1.0.2/test/fixtures/double_section.rb0000644000004100000410000000022212560357623021533 0ustar www-datawww-datarequire 'mustache' class DoubleSection < Mustache self.path = File.dirname(__FILE__) def t true end def two "second" end end mustache-1.0.2/test/fixtures/namespaced.mustache0000644000004100000410000000002212560357623022041 0ustar www-datawww-data

    {{title}}

    mustache-1.0.2/test/fixtures/liberal.rb0000644000004100000410000000035612560357623020157 0ustar www-datawww-datarequire 'mustache' class Liberal < Mustache self.path = File.dirname(__FILE__) def first_name "kevin" end def middle_name! 'j' end def lastName? 'sheurs' end end if $0 == __FILE__ puts Liberal.to_html end mustache-1.0.2/test/fixtures/recursive.mustache0000644000004100000410000000005512560357623021756 0ustar www-datawww-dataIt works! {{#show}} {{>recursive}} {{/show}} mustache-1.0.2/test/fixtures/crazy_recursive.rb0000644000004100000410000000107612560357623021764 0ustar www-datawww-datarequire 'mustache' class CrazyRecursive < Mustache self.path = File.dirname(__FILE__) def top_nodes [{ :contents => "1", :children => [{ :contents => "2", :children => [{ :contents => "3", :children => [] }] }, { :contents => "4", :children => [{ :contents => "5", :children => [{ :contents => "6", :children => [] }] }] }] }] end end if $0 == __FILE__ puts CrazyRecursive.to_html end mustache-1.0.2/test/fixtures/unescaped.rb0000644000004100000410000000025612560357623020513 0ustar www-datawww-datarequire 'mustache' class Unescaped < Mustache self.path = File.dirname(__FILE__) def title "Bear > Shark" end end if $0 == __FILE__ puts Unescaped.to_html end mustache-1.0.2/test/fixtures/recursive.rb0000644000004100000410000000024412560357623020550 0ustar www-datawww-datarequire 'mustache' class Recursive < Mustache self.path = File.dirname(__FILE__) def show false end end if $0 == __FILE__ puts Recursive.to_html end mustache-1.0.2/test/fixtures/template_partial.txt0000644000004100000410000000005512560357623022304 0ustar www-datawww-data{{title}} {{title_bars}} {{>inner_partial}} mustache-1.0.2/test/fixtures/escaped.rb0000644000004100000410000000034012560357623020142 0ustar www-datawww-data$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib' require 'mustache' class Escaped < Mustache self.path = File.dirname(__FILE__) def title "Bear > Shark" end end if $0 == __FILE__ puts Escaped.to_html end mustache-1.0.2/test/fixtures/passenger.conf0000644000004100000410000000015112560357623021047 0ustar www-datawww-data ServerName {{server}} DocumentRoot {{deploy_to}} RailsEnv {{stage}} mustache-1.0.2/test/mustache_test.rb0000644000004100000410000004271712560357623017553 0ustar www-datawww-data# -*- coding: utf-8 -*- require_relative 'helper' class MustacheTest < Minitest::Test def test_instance_render klass = Class.new(Mustache) klass.template = "Hi {{thing}}!" assert_equal "Hi world!", klass.render(:thing => :world) assert_equal "Nice.", klass.render("{{compliment}}.", :compliment => "Nice") assert_equal <<-end_simple, Simple.new.render(:name => "yo", :in_ca => false) Hello yo You have just won $10000! end_simple end def test_passenger assert_equal <<-end_passenger, Passenger.render ServerName example.com DocumentRoot /var/www/example.com RailsEnv production end_passenger end def test_complex_view assert_equal <<-end_complex, ComplexView.render

    Colors

    end_complex end def test_nested_objects assert_equal <<-end_complex, NestedObjects.render

    Colors

    end_complex end def test_single_line_sections html = %(

    ) instance = Mustache.new instance.template = html instance[:no_flash] = true assert_equal %Q'

    {{contact}}
    {{/contact}}" assert_equal "
    Call 1-888-FLOWERS\nAsk for Johnson.
    ", instance.render end def test_sassy_single_line_sections instance = Mustache.new instance[:full_time] = true instance.template = "\n {{#full_time}}full time{{/full_time}}\n" assert_equal "\n full time\n", instance.render end def test_sassier_single_line_sections instance = Mustache.new instance.template = "\t{{#list}}\r\n\t{{/list}}" assert_equal "", instance.render end def test_padding_before_section instance = Mustache.new instance.template = "\t{{#list}}a{{/list}}" assert_equal "\taa", instance.render(:list => [1, 2]) end def test_padding_before_section_on_eos instance = Mustache.new instance.template = "{{#list}}\n\t{{/list}}" assert_equal "", instance.render(:list => [1, 2]) end def test_two_line_sections html = %(

    ) instance = Mustache.new instance.template = html instance[:no_flash] = true assert_equal %Q'