class Liquid::Context
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
Attributes
Public Class Methods
# 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
Public Instance Methods
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?
# File lib/liquid/context.rb, line 159 def [](expression) evaluate(@parsed_expression[expression]) end
Adds filters to this context.
Note that this does not register the filters with the main Template object. see
Template.register_filter
for that
# File lib/liquid/context.rb, line 62 def add_filters(filters) filters = [filters].flatten.compact @filters += filters @strainer = nil end
# File lib/liquid/context.rb, line 138 def clear_instance_assigns @scopes[0] = {} end
# File lib/liquid/context.rb, line 167 def evaluate(object) object.respond_to?(:evaluate) ? object.evaluate(self) : object end
Fetches an object starting at the local scope and then moving up the hierachy
# File lib/liquid/context.rb, line 172 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
# File lib/liquid/context.rb, line 84 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
are there any not handled interrupts?
# File lib/liquid/context.rb, line 69 def has_interrupt? !@interrupts.empty? end
# File lib/liquid/context.rb, line 163 def has_key?(key) self[key] != nil end
# 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
# File lib/liquid/context.rb, line 94 def invoke(method, *args) strainer.invoke(method, *args).to_liquid end
# File lib/liquid/context.rb, line 200 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 a hash of variables in the current local scope
# File lib/liquid/context.rb, line 105 def merge(new_scopes) @scopes[0].merge!(new_scopes) end
Pop from the stack. use Context#stack
instead
# File lib/liquid/context.rb, line 110 def pop raise ContextError if @scopes.size == 1 @scopes.shift end
pop an interrupt from the stack
# File lib/liquid/context.rb, line 79 def pop_interrupt @interrupts.pop end
Push new local scope on the stack. use Context#stack
instead
# File lib/liquid/context.rb, line 99 def push(new_scope={}) @scopes.unshift(new_scope) raise StackLevelError, "Nesting too deep".freeze if @scopes.length > 100 end
push an interrupt to the stack. this interrupt is considered not handled.
# File lib/liquid/context.rb, line 74 def push_interrupt(e) @interrupts.push(e) end
# 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
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
# File lib/liquid/context.rb, line 123 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
# File lib/liquid/context.rb, line 54 def strainer @strainer ||= Strainer.create(self, @filters) end
Private Instance Methods
# File lib/liquid/context.rb, line 209 def squash_instance_assigns_with_environments @scopes.last.each_key do |k| @environments.each do |env| if env.has_key?(k) scopes.last[k] = lookup_and_evaluate(env, k) break end end end end