class Liquid::Context

context #=> nil class Context<br><br>end<br>context = ‘bobsen’
context.stack do<br><br>context #=> 10.2232<br>context #=> true<br>context #=> ‘testing’<br>context = ‘testing’
Context keeps the variable stack and resolves variables, as well as keywords

def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_environments: {}, &block)

rubocop:disable Metrics/ParameterLists
def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_environments: {}, &block)
  new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_environments, &block)
end

def [](expression)

products == empty #=> products.empty?
Example:

Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions
later move up to the parent blocks to see if we can resolve the variable somewhere up the tree.
If no match is made we lookup the variable in the current scope and
Strings, digits, floats and booleans (true,false).
Look up variable, either resolve directly after considering the name. We can directly handle
def [](expression)
  evaluate(Expression.parse(expression))
end

def []=(key, value)

Only allow String, Numeric, Hash, Array, Proc, Boolean or Liquid::Drop
def []=(key, value)
  @scopes[0][key] = value
end

def add_filters(filters)

for that
Note that this does not register the filters with the main Template object. see Template.register_filter

Adds filters to this context.
def add_filters(filters)
  filters = [filters].flatten.compact
  @filters += filters
  @strainer = nil
end

def apply_global_filter(obj)

def apply_global_filter(obj)
  global_filter.nil? ? obj : global_filter.call(obj)
end

def check_overflow

def check_overflow
  raise StackLevelError, "Nesting too deep" if overflow?
end

def clear_instance_assigns

def clear_instance_assigns
  @scopes[0] = {}
end

def evaluate(object)

def evaluate(object)
  object.respond_to?(:evaluate) ? object.evaluate(self) : object
end

def find_variable(key, raise_on_not_found: true)

Fetches an object starting at the local scope and then moving up the hierachy
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) }
  variable = if index
    lookup_and_evaluate(@scopes[index], key, raise_on_not_found: raise_on_not_found)
  else
    try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found)
  end
  # update variable's context before invoking #to_liquid
  variable.context = self if variable.respond_to?(:context=)
  liquid_variable = variable.to_liquid
  liquid_variable.context = self if variable != liquid_variable && liquid_variable.respond_to?(:context=)
  liquid_variable
end

def handle_error(e, line_number = nil)

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

def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {})

def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {})
  @environments = [environments]
  @environments.flatten!
  @static_environments = [static_environments].flatten(1).freeze
  @scopes              = [outer_scope || {}]
  @registers           = registers.is_a?(Registers) ? registers : Registers.new(registers)
  @errors              = []
  @partial             = false
  @strict_variables    = false
  @resource_limits     = resource_limits || ResourceLimits.new(Template.default_resource_limits)
  @base_scope_depth    = 0
  @interrupts          = []
  @filters             = []
  @global_filter       = nil
  @disabled_tags       = {}
  @registers.static[:cached_partials] ||= {}
  @registers.static[:file_system] ||= Liquid::Template.file_system
  @registers.static[:template_factory] ||= Liquid::TemplateFactory.new
  self.exception_renderer = Template.default_exception_renderer
  if rethrow_errors
    self.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
  end
  yield self if block_given?
  # Do this last, since it could result in this object being passed to a Proc in the environment
  squash_instance_assigns_with_environments
end

def internal_error

def internal_error
  # raise and catch to set backtrace and cause on exception
  raise Liquid::InternalError, 'internal'
rescue Liquid::InternalError => exc
  exc
end

def interrupt?

are there any not handled interrupts?
def interrupt?
  !@interrupts.empty?
end

def invoke(method, *args)

def invoke(method, *args)
  strainer.invoke(method, *args).to_liquid
end

def key?(key)

def key?(key)
  self[key] != nil
end

def lookup_and_evaluate(obj, key, raise_on_not_found: true)

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

def merge(new_scopes)

Merge a hash of variables in the current local scope
def merge(new_scopes)
  @scopes[0].merge!(new_scopes)
end

def new_isolated_subcontext

but with an isolated scope.
Creates a new context inheriting resource limits, filters, environment etc.,
def new_isolated_subcontext
  check_overflow
  self.class.build(
    resource_limits: resource_limits,
    static_environments: static_environments,
    registers: Registers.new(registers),
  ).tap do |subcontext|
    subcontext.base_scope_depth   = base_scope_depth + 1
    subcontext.exception_renderer = exception_renderer
    subcontext.filters  = @filters
    subcontext.strainer = nil
    subcontext.errors   = errors
    subcontext.warnings = warnings
    subcontext.disabled_tags = @disabled_tags
  end
end

def overflow?

def overflow?
  base_scope_depth + @scopes.length > Block::MAX_DEPTH
end

def pop

Pop from the stack. use Context#stack instead
def pop
  raise ContextError if @scopes.size == 1
  @scopes.shift
end

def pop_interrupt

pop an interrupt from the stack
def pop_interrupt
  @interrupts.pop
end

def push(new_scope = {})

Push new local scope on the stack. use Context#stack instead
def push(new_scope = {})
  @scopes.unshift(new_scope)
  check_overflow
end

def push_interrupt(e)

push an interrupt to the stack. this interrupt is considered not handled.
def push_interrupt(e)
  @interrupts.push(e)
end

def squash_instance_assigns_with_environments

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 # squash_instance_assigns_with_environments

def stack(new_scope = {})

context['var'] #=> nil

end
context['var'] = 'hi'
context.stack do
Example:

Pushes a new local scope on the stack, pops it at the end of the block
def stack(new_scope = {})
  push(new_scope)
  yield
ensure
  pop
end

def strainer

def strainer
  @strainer ||= StrainerFactory.create(self, @filters)
end

def tag_disabled?(tag_name)

def tag_disabled?(tag_name)
  @disabled_tags.fetch(tag_name, 0) > 0
end

def try_variable_find_in_environments(key, raise_on_not_found:)

def try_variable_find_in_environments(key, raise_on_not_found:)
  @environments.each do |environment|
    found_variable = lookup_and_evaluate(environment, key, raise_on_not_found: raise_on_not_found)
    if !found_variable.nil? || @strict_variables && raise_on_not_found
      return found_variable
    end
  end
  @static_environments.each do |environment|
    found_variable = lookup_and_evaluate(environment, key, raise_on_not_found: raise_on_not_found)
    if !found_variable.nil? || @strict_variables && raise_on_not_found
      return found_variable
    end
  end
  nil
end

def warnings

def warnings
  @warnings ||= []
end

def with_disabled_tags(tag_names)

def with_disabled_tags(tag_names)
  tag_names.each do |name|
    @disabled_tags[name] = @disabled_tags.fetch(name, 0) + 1
  end
  yield
ensure
  tag_names.each do |name|
    @disabled_tags[name] -= 1
  end
end