Class: Luoma::RenderContext

Inherits:
Object
  • Object
show all
Defined in:
lib/luoma/context.rb,
sig/luoma/context.rbs

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(template, globals: nil, disabled_tags: nil, context_depth: nil, assign_score_carry: nil, render_score_carry: nil) ⇒ RenderContext

(Template, ?globals: _Namespace?) -> void

Parameters:

  • template (Template)
  • globals: (_Namespace, nil) (defaults to: nil)
  • disabled_tags: (Set[String], nil) (defaults to: nil)
  • context_depth: (Integer, nil) (defaults to: nil)
  • assign_score_carry: (Integer, nil) (defaults to: nil)
  • render_score_carry: (Integer, nil) (defaults to: nil)


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
# File 'lib/luoma/context.rb', line 13

def initialize(
  template,
  globals: nil,
  disabled_tags: nil,
  context_depth: nil,
  assign_score_carry: nil,
  render_score_carry: nil
)
  # The template being rendered.
  @template = template

  # The environment this render context and associated template is
  # bound to.
  @env = template.env

  # Developer-defined template variables passed down from the environment
  # and template.
  @globals = globals || {} # steep:ignore

  # The namespace for variables defined with `{% assign %}` and
  # `{% capture %}`.
  @locals = {} #: t_namespace

  # A namespace for `{% increment %}` and `{% decrement %}`.
  @counters = {} #: t_namespace

  # The current template scope including `locals`, `globals` and `counters`.
  # New block-scoped namespaces get pushed onto and popped off this chain
  # map.
  #
  # Scopes are searched from right to left. New scopes are push on the
  # right.
  @scopes = ChainHash.new(@counters, @globals, @locals)

  # Names of tags that are disallowed in this context.
  @disabled_tags = disabled_tags

  # The number of times this render context has been extended or copied.
  @context_depth = context_depth || 0

  # A non-specific indicator of template local scope usage.
  @assign_score = 0

  # A non-specific indicator of template local scope usage for the current
  # template and all partial templates combined.
  @assign_score_cumulative = assign_score_carry || 0

  # The number of nodes rendered for the current template.
  @render_score = 0

  # The number of nodes rendered for the current template and all partial
  # templates.
  @render_score_cumulative = render_score_carry || 0

  # A stack of interrupt signals used by `{% break %}` and
  # `{% continue %}`, for example.
  @interrupts = [] #: Array[Symbol]

  # Registers supporting stateful tags. It's OK to use this map for storing
  # custom tag state.
  @registers = {}
end

Instance Attribute Details

#assign_scoreInteger (readonly)

Returns the value of attribute assign_score.

Returns:

  • (Integer)


7
8
9
# File 'lib/luoma/context.rb', line 7

def assign_score
  @assign_score
end

#assign_score_cumulativeInteger (readonly)

Returns the value of attribute assign_score_cumulative.

Returns:

  • (Integer)


7
8
9
# File 'lib/luoma/context.rb', line 7

def assign_score_cumulative
  @assign_score_cumulative
end

#context_depthInteger (readonly)

Returns the value of attribute context_depth.

Returns:

  • (Integer)


7
8
9
# File 'lib/luoma/context.rb', line 7

def context_depth
  @context_depth
end

#disabled_tagsSet[String]? (readonly)

Returns the value of attribute disabled_tags.

Returns:

  • (Set[String], nil)


7
8
9
# File 'lib/luoma/context.rb', line 7

def disabled_tags
  @disabled_tags
end

#envEnvironment (readonly)

Returns the value of attribute env.

Returns:



7
8
9
# File 'lib/luoma/context.rb', line 7

def env
  @env
end

#globals_Namespace (readonly)

Returns the value of attribute globals.

Returns:



7
8
9
# File 'lib/luoma/context.rb', line 7

def globals
  @globals
end

#interruptsArray[Symbol]

Returns the value of attribute interrupts.

Returns:

  • (Array[Symbol])


10
11
12
# File 'lib/luoma/context.rb', line 10

def interrupts
  @interrupts
end

#localsHash[String, untyped] (readonly)

Returns the value of attribute locals.

Returns:

  • (Hash[String, untyped])


7
8
9
# File 'lib/luoma/context.rb', line 7

def locals
  @locals
end

#registersHash[Symbol, untyped] (readonly)

Returns the value of attribute registers.

Returns:

  • (Hash[Symbol, untyped])


7
8
9
# File 'lib/luoma/context.rb', line 7

def registers
  @registers
end

#render_scoreInteger

Returns the value of attribute render_score.

Returns:

  • (Integer)


10
11
12
# File 'lib/luoma/context.rb', line 10

def render_score
  @render_score
end

#render_score_cumulativeInteger

Returns the value of attribute render_score_cumulative.

Returns:

  • (Integer)


10
11
12
# File 'lib/luoma/context.rb', line 10

def render_score_cumulative
  @render_score_cumulative
end

#scopesChainHash (readonly)

Returns the value of attribute scopes.

Returns:



7
8
9
# File 'lib/luoma/context.rb', line 7

def scopes
  @scopes
end

#templateTemplate (readonly)

Returns the value of attribute template.

Returns:



7
8
9
# File 'lib/luoma/context.rb', line 7

def template
  @template
end

Instance Method Details

#assign(name, value) ⇒ void

This method returns an undefined value.

(String, untyped) -> void

Parameters:

  • name (String)
  • value (Object)


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/luoma/context.rb', line 115

def assign(name, value)
  if @env.max_assign_score || @env.max_assign_score_cumulative
    score = assign_score_of(value)
    @assign_score += score
    @assign_score_cumulative += score

    if (@env.max_assign_score && @assign_score > @env.max_assign_score) || # steep:ignore
       (@env.max_assign_score_cumulative &&
        @assign_score_cumulative > @env.max_assign_score_cumulative) # steep:ignore
      raise ResourceLimitError.new("memory limits reached")
    end

  end

  @locals[name] = value
end

#assign_score_of(value) ⇒ Integer

(untyped) -> Integer

Parameters:

  • value (Object)

Returns:

  • (Integer)


192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/luoma/context.rb', line 192

def assign_score_of(value)
  case value
  when String
    value.bytesize
  when Array
    value.sum { |i| assign_score_of(i) } + 1
  when Hash
    value.reduce(1) { |a, p| a + assign_score_of(p.first) + assign_score_of(p.last) }
  else
    1
  end
end

#copy(namespace, block_scope: nil, disabled_tags: nil, template: nil) ⇒ RenderContext

(t_namespace, bool?, Set?, Template?) -> RenderContext

Parameters:

  • namespace (t_namespace)
  • block_scope: (Boolean, nil) (defaults to: nil)
  • disabled_tags: (Set[String], nil) (defaults to: nil)
  • template: (Template, nil) (defaults to: nil)

Returns:



137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/luoma/context.rb', line 137

def copy(namespace, block_scope: nil, disabled_tags: nil, template: nil)
  raise_for_context_depth

  ctx = RenderContext.new(
    template || @template,
    globals: block_scope ? ChainHash.new(namespace, @scopes) : ChainHash.new(namespace, @globals),
    context_depth: @context_depth + 1,
    assign_score_carry: @assign_score_cumulative,
    render_score_carry: @render_score_cumulative
  )

  @env.persistent_registers.each { |r| ctx.registers[r] = @registers[r] if @registers.include?(r) }
  ctx
end

#extends(namespace, template: nil) { ... } ⇒ void

This method returns an undefined value.

(t_namespace, ?template: Template?) { () -> untyped } -> void

Parameters:

  • namespace (t_namespace)
  • template: (Template, nil) (defaults to: nil)

Yields:

Yield Returns:

  • (Object)


156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/luoma/context.rb', line 156

def extends(namespace, template: nil)
  raise_for_context_depth

  assign_score_ = @assign_score
  render_score_ = @render_score
  template_ = @template

  @template = template if template
  @scopes << namespace
  @context_depth += 1
  @assign_score = 0
  @render_score = 0

  begin
    yield
  ensure
    @template = template_ if template
    @scopes.pop
    @context_depth -= 1
    @assign_score = assign_score_
    @render_score = render_score_
  end
end

#raise_for_context_depthvoid

This method returns an undefined value.

Signature:

  • () -> void



183
184
185
186
187
188
189
# File 'lib/luoma/context.rb', line 183

def raise_for_context_depth
  if @context_depth + 1 > @env.max_context_depth
    raise ContextDepthError.new(
      "maximum context depth reached, possible recursive render"
    )
  end
end

#resolve(name) ⇒ Object

(String) -> untyped

Parameters:

  • name (String)

Returns:

  • (Object)


77
78
79
# File 'lib/luoma/context.rb', line 77

def resolve(name)
  @scopes[name]
end

#resolve_array_segment(obj, segment) ⇒ Object

(Array, untyped) -> untyped

Parameters:

  • obj (Array[untyped])
  • segment (Object)

Returns:

  • (Object)


206
207
208
209
210
211
212
213
214
215
216
# File 'lib/luoma/context.rb', line 206

def resolve_array_segment(obj, segment)
  index = if segment.is_a?(Integer)
            segment.negative? && obj.length >= segment.abs ? obj.length + segment : segment
          end

  if index && index < obj.length
    obj[index]
  else
    :nothing
  end
end

#resolve_hash_segment(obj, segment) ⇒ Object

(Hash[untyped, untyped], untyped) -> untyped

Parameters:

  • obj (Hash[untyped, untyped])
  • segment (Object)

Returns:

  • (Object)


219
220
221
222
223
224
225
# File 'lib/luoma/context.rb', line 219

def resolve_hash_segment(obj, segment)
  if obj.key?(segment)
    obj[segment]
  else
    :nothing
  end
end

#resolve_path(obj, segments) ⇒ Object

(untyped, Array) -> untyped

Parameters:

  • obj (Object)
  • segments (Object)

Returns:

  • (Object)


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
# File 'lib/luoma/context.rb', line 86

def resolve_path(obj, segments)
  segment_index = -1

  segments.each do |segment|
    segment_index += 1

    return [segment.call(self, obj), segment_index] if segment.is_a?(PredicateFunction)

    obj = case obj
          when Drop
            segment = segment.to_primitive(:string, self) if segment.is_a?(Drop)
            obj.fetch(segment, self, default: :nothing)
          when Array
            segment = segment.to_primitive(:numeric, self) if segment.is_a?(Drop)
            resolve_array_segment(obj, segment)
          when Hash
            segment = segment.to_primitive(:data, self) if segment.is_a?(Drop)
            resolve_hash_segment(obj, segment)
          else
            :nothing
          end

    return [:nothing, segment_index] if obj == :nothing
  end

  [obj, segment_index]
end