File: template.rb

package info (click to toggle)
ruby-mustache 1.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid, trixie
  • size: 480 kB
  • sloc: ruby: 2,267; makefile: 2
file content (128 lines) | stat: -rw-r--r-- 4,230 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
require '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. Options may be passed.
    def initialize(source, options = {})
      @source = source
      @options = options
    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(@options).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(@options).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


    # 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