Class: Fiasco::Template::RenderContext

Inherits:
Object
  • Object
show all
Defined in:
lib/fiasco/template/render_context.rb

Defined Under Namespace

Classes: Entry, NullOutput

Instance Method Summary collapse

Constructor Details

#initializeRenderContext

Returns a new instance of RenderContext.



10
11
12
13
14
15
16
17
18
19
# File 'lib/fiasco/template/render_context.rb', line 10

def initialize
  @content_blocks = Hash.new {|h,k| h[k] = [] }
  @template_locals = Hash.new {|h,k| h[k] = [] }
  @templates = {}
  @compiled = Set.new
  display_value = lambda do |outvar, literal|
    "__tmp = (#{literal}); #{outvar} << display_value(__tmp)"
  end
  @compiler = Compiler.new(display_value: display_value)
end

Instance Method Details

#_compile(name, entry, locals = []) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/fiasco/template/render_context.rb', line 62

def _compile(name, entry, locals = [])
  src = "params ||= {}; @render_output ||= ''; "
  locals.each {|var| src += "#{var} = params[:#{var}]; "}
  src << "\n"
  src << @compiler.compile(entry.body)
  src << "\n@render_output"

  meth = <<-EOS
#coding:UTF-8
define_singleton_method(:'__view__#{name}') do |params|
#{src}
end
EOS
  eval(meth, binding, entry.filename || "(TEMPLATE:#{name})", -2)
  @compiled << name
end

#_declare(options) {|entry| ... } ⇒ Object

Yields:

  • (entry)


91
92
93
94
95
96
97
98
99
100
101
# File 'lib/fiasco/template/render_context.rb', line 91

def _declare(options)
  contents = options[:path] ? File.read(options[:path]) : options[:contents]

  if contents.nil?
    raise ArgumentError.new("Need either path or contents")
  end

  entry = Entry.new(contents, options[:path])

  yield(entry)
end

#_process_locals(name, locals) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
# File 'lib/fiasco/template/render_context.rb', line 79

def _process_locals(name, locals)
  seen_variables = @template_locals[name]
  diff = locals.keys - seen_variables

  unless diff.empty?
    seen_variables += diff
    @compiled.delete(name)
  end

  seen_variables
end

#_render(name, locals = {}) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/fiasco/template/render_context.rb', line 108

def _render(name, locals = {})
  name = name.to_s
  variables = _process_locals(name, locals)

  unless @compiled.include?(name)
    entry = @templates.fetch(name) do
      raise ArgumentError, "template `#{name}` not declared"
    end

    _compile(name, entry, variables)
  end

  send("__view__#{name}", locals)

  if @extends
    parent, pargs = @extends
    @extends = nil
    @render_output, @original_render_output = @original_render_output, nil
    _render(parent, *pargs)
  end

  @render_output
end

#block(key, &b) ⇒ Object



21
22
23
# File 'lib/fiasco/template/render_context.rb', line 21

def block(key, &b)
  @content_blocks[key.to_s] << b
end

#declare(name, options = {}) ⇒ Object



103
104
105
106
# File 'lib/fiasco/template/render_context.rb', line 103

def declare(name, options = {})
  name = name.to_s
  _declare(options) {|e| @templates[name] = e}
end

#display_value(value) ⇒ Object



141
142
143
144
# File 'lib/fiasco/template/render_context.rb', line 141

def display_value(value)
  str = value.to_s
  value.tainted? ? Rack::Utils.escape_html(str) : str
end

#extends(base, *args) ⇒ Object



55
56
57
58
59
60
# File 'lib/fiasco/template/render_context.rb', line 55

def extends(base, *args)
  # Store render output remporarily because we don't want to
  # render any output of a child template until we render the parent
  @render_output, @original_render_output = NullOutput.new, @render_output
  @extends = [base, args]
end

#load_macros(options) ⇒ Object



161
162
163
164
165
166
167
168
169
# File 'lib/fiasco/template/render_context.rb', line 161

def load_macros(options)
  @render_output, tmp = NullOutput.new, @render_output
  b = binding
  _declare(options) do |e|
    eval(@compiler.compile(e.body), b, e.filename || 'MACROS')
  end
ensure
  @render_output = tmp
end

#macro(mname, defaults = {}, &b) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/fiasco/template/render_context.rb', line 146

def macro(mname, defaults = {}, &b)
  arguments = b.parameters
  define_singleton_method mname do |named = defaults, &block|
    args = arguments.select{|t| t[0] != :block}.map do |type, name|
      named.fetch(name) do
        defaults.fetch(name) do
          msg = "Macro invocation '#{mname}' is missing a required argument: #{name}"
          raise ArgumentError, msg, caller(10)
        end
      end
    end
    b.call(*args, &block)
  end
end

#render(name, locals = {}) ⇒ Object Also known as: []



132
133
134
135
136
137
# File 'lib/fiasco/template/render_context.rb', line 132

def render(name, locals = {})
  _render(name, locals)
ensure
  @content_blocks.clear
  @render_output = nil
end

#superblock(*args) ⇒ Object



25
26
27
28
29
30
31
32
# File 'lib/fiasco/template/render_context.rb', line 25

def superblock(*args)
  # for a blocklevel N, we can find the superblock (block with the
  # same name defined on a parent template) in N+1
  @blocklevel += 1
  @content_blocks[@blockname][@blocklevel].call(*args)
ensure
  @blocklevel -= 1
end

#yield_block(key, *args, &b) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/fiasco/template/render_context.rb', line 34

def yield_block(key, *args, &b)
  key = key.to_s
  @content_blocks[key] << b if b

  if @content_blocks[key].length > 0
    begin
      old_blocklevel, @blocklevel = @blocklevel, -1
      old_blockname, @blockname = @blockname, key

      # @blocklevel starts at -1, making this call render
      # the block at level 0 (the last one defined in the
      # inheritance chain)
      superblock(*args)
    ensure
      @blocklevel, @blockname = old_blocklevel, old_blockname
    end
  else
    ''
  end
end