summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/template.rb
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/template.rb')
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/template.rb252
1 files changed, 252 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/template.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/template.rb
new file mode 100644
index 0000000..ba429ec
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/template.rb
@@ -0,0 +1,252 @@
+module Liquid
+ # Templates are central to liquid.
+ # Interpretating templates is a two step process. First you compile the
+ # source code you got. During compile time some extensive error checking is performed.
+ # your code should expect to get some SyntaxErrors.
+ #
+ # After you have a compiled template you can then <tt>render</tt> it.
+ # You can use a compiled template over and over again and keep it cached.
+ #
+ # Example:
+ #
+ # template = Liquid::Template.parse(source)
+ # template.render('user_name' => 'bob')
+ #
+ class Template
+ attr_accessor :root
+ attr_reader :resource_limits, :warnings
+
+ @@file_system = BlankFileSystem.new
+
+ class TagRegistry
+ include Enumerable
+
+ def initialize
+ @tags = {}
+ @cache = {}
+ end
+
+ def [](tag_name)
+ return nil unless @tags.key?(tag_name)
+ return @cache[tag_name] if Liquid.cache_classes
+
+ lookup_class(@tags[tag_name]).tap { |o| @cache[tag_name] = o }
+ end
+
+ def []=(tag_name, klass)
+ @tags[tag_name] = klass.name
+ @cache[tag_name] = klass
+ end
+
+ def delete(tag_name)
+ @tags.delete(tag_name)
+ @cache.delete(tag_name)
+ end
+
+ def each(&block)
+ @tags.each(&block)
+ end
+
+ private
+
+ def lookup_class(name)
+ name.split("::").reject(&:empty?).reduce(Object) { |scope, const| scope.const_get(const) }
+ end
+ end
+
+ attr_reader :profiler
+
+ class << self
+ # Sets how strict the parser should be.
+ # :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
+ # :warn is the default and will give deprecation warnings when invalid syntax is used.
+ # :strict will enforce correct syntax.
+ attr_writer :error_mode
+
+ # Deprecated. No longer used. Removed in version 5
+ attr_writer :taint_mode
+
+ attr_accessor :default_exception_renderer
+ Template.default_exception_renderer = lambda do |exception|
+ exception
+ end
+
+ def file_system
+ @@file_system
+ end
+
+ def file_system=(obj)
+ @@file_system = obj
+ end
+
+ def register_tag(name, klass)
+ tags[name.to_s] = klass
+ end
+
+ def tags
+ @tags ||= TagRegistry.new
+ end
+
+ def error_mode
+ @error_mode ||= :lax
+ end
+
+ # Deprecated. Removed in version 5
+ def taint_mode
+ @taint_mode ||= :lax
+ end
+
+ # Pass a module with filter methods which should be available
+ # to all liquid views. Good for registering the standard library
+ def register_filter(mod)
+ Strainer.global_filter(mod)
+ end
+
+ def default_resource_limits
+ @default_resource_limits ||= {}
+ end
+
+ # creates a new <tt>Template</tt> object from liquid source code
+ # To enable profiling, pass in <tt>profile: true</tt> as an option.
+ # See Liquid::Profiler for more information
+ def parse(source, options = {})
+ template = Template.new
+ template.parse(source, options)
+ end
+ end
+
+ def initialize
+ @rethrow_errors = false
+ @resource_limits = ResourceLimits.new(self.class.default_resource_limits)
+ end
+
+ # Parse source code.
+ # Returns self for easy chaining
+ def parse(source, options = {})
+ @options = options
+ @profiling = options[:profile]
+ @line_numbers = options[:line_numbers] || @profiling
+ parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
+ @root = Document.parse(tokenize(source), parse_context)
+ @warnings = parse_context.warnings
+ self
+ end
+
+ def registers
+ @registers ||= {}
+ end
+
+ def assigns
+ @assigns ||= {}
+ end
+
+ def instance_assigns
+ @instance_assigns ||= {}
+ end
+
+ def errors
+ @errors ||= []
+ end
+
+ # Render takes a hash with local variables.
+ #
+ # if you use the same filters over and over again consider registering them globally
+ # with <tt>Template.register_filter</tt>
+ #
+ # if profiling was enabled in <tt>Template#parse</tt> then the resulting profiling information
+ # will be available via <tt>Template#profiler</tt>
+ #
+ # Following options can be passed:
+ #
+ # * <tt>filters</tt> : array with local filters
+ # * <tt>registers</tt> : hash with register variables. Those can be accessed from
+ # filters and tags and might be useful to integrate liquid more with its host application
+ #
+ def render(*args)
+ return ''.freeze if @root.nil?
+
+ context = case args.first
+ when Liquid::Context
+ c = args.shift
+
+ if @rethrow_errors
+ c.exception_renderer = ->(e) { raise }
+ end
+
+ c
+ when Liquid::Drop
+ drop = args.shift
+ drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
+ when Hash
+ Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
+ when nil
+ Context.new(assigns, instance_assigns, registers, @rethrow_errors, @resource_limits)
+ else
+ raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
+ end
+
+ case args.last
+ when Hash
+ options = args.pop
+
+ registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
+
+ apply_options_to_context(context, options)
+ when Module, Array
+ context.add_filters(args.pop)
+ end
+
+ # Retrying a render resets resource usage
+ context.resource_limits.reset
+
+ begin
+ # render the nodelist.
+ # for performance reasons we get an array back here. join will make a string out of it.
+ result = with_profiling(context) do
+ @root.render(context)
+ end
+ result.respond_to?(:join) ? result.join : result
+ rescue Liquid::MemoryError => e
+ context.handle_error(e)
+ ensure
+ @errors = context.errors
+ end
+ end
+
+ def render!(*args)
+ @rethrow_errors = true
+ render(*args)
+ end
+
+ private
+
+ def tokenize(source)
+ Tokenizer.new(source, @line_numbers)
+ end
+
+ def with_profiling(context)
+ if @profiling && !context.partial
+ raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler)
+
+ @profiler = Profiler.new
+ @profiler.start
+
+ begin
+ yield
+ ensure
+ @profiler.stop
+ end
+ else
+ yield
+ end
+ end
+
+ def apply_options_to_context(context, options)
+ context.add_filters(options[:filters]) if options[:filters]
+ context.global_filter = options[:global_filter] if options[:global_filter]
+ context.exception_renderer = options[:exception_renderer] if options[:exception_renderer]
+ context.strict_variables = options[:strict_variables] if options[:strict_variables]
+ context.strict_filters = options[:strict_filters] if options[:strict_filters]
+ end
+ end
+end