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
Source
# File lib/liquid/context.rb, line 18 def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil) @environments = [environments].flatten @scopes = [(outer_scope || {})] @registers = registers @errors = [] @partial = false @strict_variables = false @resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits) squash_instance_assigns_with_environments @this_stack_used = false self.exception_renderer = Template.default_exception_renderer if rethrow_errors self.exception_renderer = ->(e) { raise } end @interrupts = [] @filters = [] @global_filter = nil end
Public Instance Methods
Source
# File lib/liquid/context.rb, line 150 def [](expression) evaluate(Expression.parse(expression)) end
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?
Source
# File lib/liquid/context.rb, line 134 def []=(key, value) unless @this_stack_used @this_stack_used = true push({}) end @scopes[0][key] = value end
Only allow String
, Numeric
, Hash
, Array
, Proc, Boolean or Liquid::Drop
Source
# File lib/liquid/context.rb, line 52 def add_filters(filters) filters = [filters].flatten.compact @filters += filters @strainer = nil 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
Source
# File lib/liquid/context.rb, line 58 def apply_global_filter(obj) global_filter.nil? ? obj : global_filter.call(obj) end
Source
# File lib/liquid/context.rb, line 129 def clear_instance_assigns @scopes[0] = {} end
Source
# File lib/liquid/context.rb, line 158 def evaluate(object) object.respond_to?(:evaluate) ? object.evaluate(self) : object end
Source
# File lib/liquid/context.rb, line 163 def find_variable(key, raise_on_not_found: true) # 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.key?(key) } scope = @scopes[index] if index variable = nil if scope.nil? @environments.each do |e| variable = lookup_and_evaluate(e, key, raise_on_not_found: raise_on_not_found) # When lookup returned a value OR there is no value but the lookup also did not raise # then it is the value we are looking for. if !variable.nil? || @strict_variables && raise_on_not_found scope = e break end end end scope ||= @environments.last || @scopes.last variable ||= lookup_and_evaluate(scope, key, raise_on_not_found: raise_on_not_found) variable = variable.to_liquid variable.context = self if variable.respond_to?(:context=) variable end
Fetches an object starting at the local scope and then moving up the hierachy
Source
# File lib/liquid/context.rb, line 77 def handle_error(e, line_number = nil) e = internal_error unless e.is_a?(Liquid::Error) e.template_name ||= template_name e.line_number ||= line_number errors.push(e) exception_renderer.call(e).to_s end
Source
# File lib/liquid/context.rb, line 63 def interrupt? !@interrupts.empty? end
are there any not handled interrupts?
Source
# File lib/liquid/context.rb, line 85 def invoke(method, *args) strainer.invoke(method, *args).to_liquid end
Source
# File lib/liquid/context.rb, line 192 def lookup_and_evaluate(obj, key, raise_on_not_found: true) if @strict_variables && raise_on_not_found && obj.respond_to?(:key?) && !obj.key?(key) raise Liquid::UndefinedVariable, "undefined variable #{key}" end value = obj[key] if value.is_a?(Proc) && obj.respond_to?(:[]=) obj[key] = (value.arity == 0) ? value.call : value.call(self) else value end end
Source
# File lib/liquid/context.rb, line 96 def merge(new_scopes) @scopes[0].merge!(new_scopes) end
Merge a hash of variables in the current local scope
Source
# File lib/liquid/context.rb, line 101 def pop raise ContextError if @scopes.size == 1 @scopes.shift end
Pop from the stack. use Context#stack
instead
Source
# File lib/liquid/context.rb, line 73 def pop_interrupt @interrupts.pop end
pop an interrupt from the stack
Source
# File lib/liquid/context.rb, line 90 def push(new_scope = {}) @scopes.unshift(new_scope) raise StackLevelError, "Nesting too deep".freeze if @scopes.length > Block::MAX_DEPTH end
Push new local scope on the stack. use Context#stack
instead
Source
# File lib/liquid/context.rb, line 68 def push_interrupt(e) @interrupts.push(e) end
push an interrupt to the stack. this interrupt is considered not handled.
Source
# File lib/liquid/context.rb, line 114 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
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
Source
# File lib/liquid/context.rb, line 44 def strainer @strainer ||= Strainer.create(self, @filters) end
Private Instance Methods
Source
# File lib/liquid/context.rb, line 208 def internal_error # raise and catch to set backtrace and cause on exception raise Liquid::InternalError, 'internal' rescue Liquid::InternalError => exc exc end
Source
# File lib/liquid/context.rb, line 215 def squash_instance_assigns_with_environments @scopes.last.each_key do |k| @environments.each do |env| if env.key?(k) scopes.last[k] = lookup_and_evaluate(env, k) break end end end end