lib/cadenza/context.rb
# frozen_string_literal: true
require 'cadenza/context/stack'
require 'cadenza/context/loaders'
module Cadenza
# The {Context} class is an essential class in Cadenza that contains all the
# data necessary to render a template to it's output. The context holds all
# defined variable names (see {#stack}), filters (see {#filters}),
# functions (see {#functions}), generic blocks (see {#blocks}), loaders
# (see {#loaders}) and configuration data as well as all the methods you
# should need to define and evaluate those.
class Context
extend Cadenza::Library
include Cadenza::Context::Stack
include Cadenza::Context::Loaders
# looks up the given identifier name on the given ruby object using
# Cadenza's internal logic for doing so.
#
# {Array} objects allow identifiers in the form of numbers to retrieve
# the index specified. Example: alphabet.0 returns "a"
#
# {Hash} objects allow identifiers to be fetched as keys of that hash.
#
# Any object which is a subclass of {ContextObject} will have a value
# looked up according to the logic defined in {ContextObject#invoke_context_method}
#
# @param [Symbol|String] identifier the name of the value to look up on this object
# @param [Object] object the object to look up the value on
# @return [Object] the result of the lookup
def self.lookup_on_object(identifier, object)
sym_identifier = identifier.to_sym
# allow looking up array indexes with dot notation, example: alphabet.0 => "a"
return object[identifier.to_i] if object.respond_to?(:[]) && object.is_a?(Array) && identifier =~ /\A\d+\z/
# otherwise if it's a hash look up the string or symbolized key
if object.respond_to?(:[]) && object.is_a?(Hash) && (object.key?(identifier) || object.key?(sym_identifier))
return object[identifier] || object[sym_identifier]
end
# if the identifier is a callable method then call that
return object.send(:invoke_context_method, identifier) if object.is_a?(Cadenza::ContextObject)
nil
end
# creates a new context object with an empty stack, filter list, function
# list, block list, loaders list and default configuration options.
#
# When created you can push an optional scope onto as the initial stack
#
# @param [Hash] initial_scope the initial scope for the context
def initialize(initial_scope = {})
push initial_scope
end
# creates a new instance of the context with the stack, loaders, filters,
# functions and blocks shallow copied.
#
# @return [Context] the cloned context
def clone
copy = super
copy.instance_variable_set('@stack', stack.dup)
copy.instance_variable_set('@loaders', loaders.dup)
copy
end
def evaluate_filter(name, input, params = [])
self.class.evaluate_filter(name, input, params)
end
def evaluate_block(name, nodes, parameters)
self.class.evaluate_block(name, self, nodes, parameters)
end
def functional_variables
warn '`#functional_variables` has been deprecated. Use `#functions` instead.'
functions
end
def functions
self.class.functions
end
def evaluate_functional_variable(name, params = [])
warn '`#evaluate_functional_variable` has been deprecated. Use `#evaluate_function` instead.'
evaluate_function(name, params)
end
def evaluate_function(name, params = [])
self.class.evaluate_function(name, self, params)
end
end
end