Class: Liquid::Context
- Inherits:
-
Object
- Object
- Liquid::Context
- Defined in:
- lib/liquid/context.rb
Overview
Context keeps the variable stack and resolves variables, as well as keywords
context['variable'] = 'testing'
context['variable'] #=> 'testing'
context['true'] #=> true
context['10.2232'] #=> 10.2232
context.stack do
context['bob'] = 'bobsen'
end
context['bob'] #=> nil class Context
Instance Attribute Summary collapse
-
#environments ⇒ Object
readonly
Returns the value of attribute environments.
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
-
#exception_handler ⇒ Object
Returns the value of attribute exception_handler.
-
#registers ⇒ Object
readonly
Returns the value of attribute registers.
-
#resource_limits ⇒ Object
readonly
Returns the value of attribute resource_limits.
-
#scopes ⇒ Object
readonly
Returns the value of attribute scopes.
Instance Method Summary collapse
-
#[](expression) ⇒ Object
Look up variable, either resolve directly after considering the name.
-
#[]=(key, value) ⇒ Object
Only allow String, Numeric, Hash, Array, Proc, Boolean or
Liquid::Drop
. -
#add_filters(filters) ⇒ Object
Adds filters to this context.
- #clear_instance_assigns ⇒ Object
- #evaluate(object) ⇒ Object
-
#find_variable(key) ⇒ Object
Fetches an object starting at the local scope and then moving up the hierachy.
- #handle_error(e, token = nil) ⇒ Object
-
#has_interrupt? ⇒ Boolean
are there any not handled interrupts?.
- #has_key?(key) ⇒ Boolean
- #increment_used_resources(key, obj) ⇒ Object
-
#initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil) ⇒ Context
constructor
A new instance of Context.
- #invoke(method, *args) ⇒ Object
- #lookup_and_evaluate(obj, key) ⇒ Object
-
#merge(new_scopes) ⇒ Object
Merge a hash of variables in the current local scope.
-
#pop ⇒ Object
Pop from the stack.
-
#pop_interrupt ⇒ Object
pop an interrupt from the stack.
-
#push(new_scope = {}) ⇒ Object
Push new local scope on the stack.
-
#push_interrupt(e) ⇒ Object
push an interrupt to the stack.
- #resource_limits_reached? ⇒ Boolean
-
#stack(new_scope = nil) ⇒ Object
Pushes a new local scope on the stack, pops it at the end of the block.
- #strainer ⇒ Object
Constructor Details
#initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil) ⇒ Context
Returns a new instance of Context.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/liquid/context.rb', line 19 def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil) @environments = [environments].flatten @scopes = [(outer_scope || {})] @registers = registers @errors = [] @resource_limits = resource_limits || Template.default_resource_limits.dup @resource_limits[:render_score_current] = 0 @resource_limits[:assign_score_current] = 0 @parsed_expression = Hash.new{ |cache, markup| cache[markup] = Expression.parse(markup) } squash_instance_assigns_with_environments @this_stack_used = false if rethrow_errors self.exception_handler = ->(e) { true } end @interrupts = [] @filters = [] end |
Instance Attribute Details
#environments ⇒ Object (readonly)
Returns the value of attribute environments.
16 17 18 |
# File 'lib/liquid/context.rb', line 16 def environments @environments end |
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
16 17 18 |
# File 'lib/liquid/context.rb', line 16 def errors @errors end |
#exception_handler ⇒ Object
Returns the value of attribute exception_handler.
17 18 19 |
# File 'lib/liquid/context.rb', line 17 def exception_handler @exception_handler end |
#registers ⇒ Object (readonly)
Returns the value of attribute registers.
16 17 18 |
# File 'lib/liquid/context.rb', line 16 def registers @registers end |
#resource_limits ⇒ Object (readonly)
Returns the value of attribute resource_limits.
16 17 18 |
# File 'lib/liquid/context.rb', line 16 def resource_limits @resource_limits end |
#scopes ⇒ Object (readonly)
Returns the value of attribute scopes.
16 17 18 |
# File 'lib/liquid/context.rb', line 16 def scopes @scopes end |
Instance Method Details
#[](expression) ⇒ Object
Look up variable, either resolve directly after considering the name. We can directly handle Strings, digits, floats and booleans (true,false). If no match is made we lookup the variable in the current scope and later move up to the parent blocks to see if we can resolve the variable somewhere up the tree. Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions
Example:
products == empty #=> products.empty?
172 173 174 |
# File 'lib/liquid/context.rb', line 172 def [](expression) evaluate(@parsed_expression[expression]) end |
#[]=(key, value) ⇒ Object
Only allow String, Numeric, Hash, Array, Proc, Boolean or Liquid::Drop
156 157 158 159 160 161 162 |
# File 'lib/liquid/context.rb', line 156 def []=(key, value) unless @this_stack_used @this_stack_used = true push({}) end @scopes[0][key] = value end |
#add_filters(filters) ⇒ Object
Adds filters to this context.
Note that this does not register the filters with the main Template object. see Template.register_filter
for that
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/liquid/context.rb', line 62 def add_filters(filters) filters = [filters].flatten.compact filters.each do |f| raise ArgumentError, "Expected module but got: #{f.class}" unless f.is_a?(Module) Strainer.add_known_filter(f) end # If strainer is already setup then there's no choice but to use a runtime # extend call. If strainer is not yet created, we can utilize strainers # cached class based API, which avoids busting the method cache. if @strainer filters.each do |f| strainer.extend(f) end else @filters.concat filters end end |
#clear_instance_assigns ⇒ Object
151 152 153 |
# File 'lib/liquid/context.rb', line 151 def clear_instance_assigns @scopes[0] = {} end |
#evaluate(object) ⇒ Object
180 181 182 |
# File 'lib/liquid/context.rb', line 180 def evaluate(object) object.respond_to?(:evaluate) ? object.evaluate(self) : object end |
#find_variable(key) ⇒ Object
Fetches an object starting at the local scope and then moving up the hierachy
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/liquid/context.rb', line 185 def find_variable(key) # This was changed from find() to find_index() because this is a very hot # path and find_index() is optimized in MRI to reduce object allocation index = @scopes.find_index { |s| s.has_key?(key) } scope = @scopes[index] if index variable = nil if scope.nil? @environments.each do |e| variable = lookup_and_evaluate(e, key) unless variable.nil? scope = e break end end end scope ||= @environments.last || @scopes.last variable ||= lookup_and_evaluate(scope, key) variable = variable.to_liquid variable.context = self if variable.respond_to?(:context=) return variable end |
#handle_error(e, token = nil) ⇒ Object
97 98 99 100 101 102 103 104 105 |
# File 'lib/liquid/context.rb', line 97 def handle_error(e, token=nil) if e.is_a?(Liquid::Error) e.set_line_number_from_token(token) end errors.push(e) raise if exception_handler && exception_handler.call(e) Liquid::Error.render(e) end |
#has_interrupt? ⇒ Boolean
are there any not handled interrupts?
82 83 84 |
# File 'lib/liquid/context.rb', line 82 def has_interrupt? !@interrupts.empty? end |
#has_key?(key) ⇒ Boolean
176 177 178 |
# File 'lib/liquid/context.rb', line 176 def has_key?(key) self[key] != nil end |
#increment_used_resources(key, obj) ⇒ Object
40 41 42 43 44 45 46 |
# File 'lib/liquid/context.rb', line 40 def increment_used_resources(key, obj) @resource_limits[key] += if obj.kind_of?(String) || obj.kind_of?(Array) || obj.kind_of?(Hash) obj.length else 1 end end |
#invoke(method, *args) ⇒ Object
107 108 109 |
# File 'lib/liquid/context.rb', line 107 def invoke(method, *args) strainer.invoke(method, *args).to_liquid end |
#lookup_and_evaluate(obj, key) ⇒ Object
213 214 215 216 217 218 219 |
# File 'lib/liquid/context.rb', line 213 def lookup_and_evaluate(obj, key) if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=) obj[key] = (value.arity == 0) ? value.call : value.call(self) else value end end |
#merge(new_scopes) ⇒ Object
Merge a hash of variables in the current local scope
118 119 120 |
# File 'lib/liquid/context.rb', line 118 def merge(new_scopes) @scopes[0].merge!(new_scopes) end |
#pop ⇒ Object
Pop from the stack. use Context#stack
instead
123 124 125 126 |
# File 'lib/liquid/context.rb', line 123 def pop raise ContextError if @scopes.size == 1 @scopes.shift end |
#pop_interrupt ⇒ Object
pop an interrupt from the stack
92 93 94 |
# File 'lib/liquid/context.rb', line 92 def pop_interrupt @interrupts.pop end |
#push(new_scope = {}) ⇒ Object
Push new local scope on the stack. use Context#stack
instead
112 113 114 115 |
# File 'lib/liquid/context.rb', line 112 def push(new_scope={}) @scopes.unshift(new_scope) raise StackLevelError, "Nesting too deep".freeze if @scopes.length > 100 end |
#push_interrupt(e) ⇒ Object
push an interrupt to the stack. this interrupt is considered not handled.
87 88 89 |
# File 'lib/liquid/context.rb', line 87 def push_interrupt(e) @interrupts.push(e) end |
#resource_limits_reached? ⇒ Boolean
48 49 50 51 52 |
# File 'lib/liquid/context.rb', line 48 def resource_limits_reached? (@resource_limits[:render_length_limit] && @resource_limits[:render_length_current] > @resource_limits[:render_length_limit]) || (@resource_limits[:render_score_limit] && @resource_limits[:render_score_current] > @resource_limits[:render_score_limit] ) || (@resource_limits[:assign_score_limit] && @resource_limits[:assign_score_current] > @resource_limits[:assign_score_limit] ) end |
#stack(new_scope = nil) ⇒ Object
Pushes a new local scope on the stack, pops it at the end of the block
Example:
context.stack do
context['var'] = 'hi'
end
context['var] #=> nil
136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/liquid/context.rb', line 136 def stack(new_scope=nil) old_stack_used = @this_stack_used if new_scope push(new_scope) @this_stack_used = true else @this_stack_used = false end yield ensure pop if @this_stack_used @this_stack_used = old_stack_used end |