summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib
diff options
context:
space:
mode:
authorBenjamin Sanders <ben@Benjamins-MacBook-Pro.local>2025-10-11 10:34:24 -0400
committerBenjamin Sanders <ben@Benjamins-MacBook-Pro.local>2025-10-11 10:34:24 -0400
commit5571dc766e2143e39762ff0b47c41d1c2e154f4c (patch)
treef4f124d314a7e76c04484515aa0fd1f2e271a601 /vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib
parent359f3309c97fc894b63e0a295c76ab298504d635 (diff)
Vendor bundled gems for deployment
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib')
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid.rb80
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/block.rb77
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/block_body.rb143
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/condition.rb151
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/context.rb226
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/document.rb27
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/drop.rb78
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/errors.rb56
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/expression.rb49
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/extensions.rb74
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/file_system.rb73
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/forloop_drop.rb42
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/i18n.rb39
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/interrupts.rb16
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/lexer.rb55
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/locales/en.yml26
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parse_context.rb38
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parse_tree_visitor.rb42
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parser.rb90
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parser_switching.rb31
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/profiler.rb158
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/profiler/hooks.rb23
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/range_lookup.rb37
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/resource_limits.rb23
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/standardfilters.rb506
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/strainer.rb66
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tablerowloop_drop.rb62
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tag.rb43
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/assign.rb59
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/break.rb18
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/capture.rb38
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/case.rb94
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/comment.rb16
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/continue.rb18
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/cycle.rb65
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/decrement.rb35
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/for.rb203
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/if.rb122
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/ifchanged.rb18
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/include.rb124
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/increment.rb31
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/raw.rb47
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/table_row.rb62
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/unless.rb30
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/template.rb252
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tokenizer.rb31
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/utils.rb83
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/variable.rb125
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/variable_lookup.rb88
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/version.rb5
50 files changed, 3895 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid.rb
new file mode 100644
index 0000000..770d2f9
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid.rb
@@ -0,0 +1,80 @@
+# Copyright (c) 2005 Tobias Luetke
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+module Liquid
+ FilterSeparator = /\|/
+ ArgumentSeparator = ','.freeze
+ FilterArgumentSeparator = ':'.freeze
+ VariableAttributeSeparator = '.'.freeze
+ WhitespaceControl = '-'.freeze
+ TagStart = /\{\%/
+ TagEnd = /\%\}/
+ VariableSignature = /\(?[\w\-\.\[\]]\)?/
+ VariableSegment = /[\w\-]/
+ VariableStart = /\{\{/
+ VariableEnd = /\}\}/
+ VariableIncompleteEnd = /\}\}?/
+ QuotedString = /"[^"]*"|'[^']*'/
+ QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o
+ TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/o
+ AnyStartingTag = /#{TagStart}|#{VariableStart}/o
+ PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
+ TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
+ VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o
+
+ singleton_class.send(:attr_accessor, :cache_classes)
+ self.cache_classes = true
+end
+
+require "liquid/version"
+require 'liquid/parse_tree_visitor'
+require 'liquid/lexer'
+require 'liquid/parser'
+require 'liquid/i18n'
+require 'liquid/drop'
+require 'liquid/tablerowloop_drop'
+require 'liquid/forloop_drop'
+require 'liquid/extensions'
+require 'liquid/errors'
+require 'liquid/interrupts'
+require 'liquid/strainer'
+require 'liquid/expression'
+require 'liquid/context'
+require 'liquid/parser_switching'
+require 'liquid/tag'
+require 'liquid/block'
+require 'liquid/block_body'
+require 'liquid/document'
+require 'liquid/variable'
+require 'liquid/variable_lookup'
+require 'liquid/range_lookup'
+require 'liquid/file_system'
+require 'liquid/resource_limits'
+require 'liquid/template'
+require 'liquid/standardfilters'
+require 'liquid/condition'
+require 'liquid/utils'
+require 'liquid/tokenizer'
+require 'liquid/parse_context'
+
+# Load all the tags of the standard library
+#
+Dir["#{__dir__}/liquid/tags/*.rb"].each { |f| require f }
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/block.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/block.rb
new file mode 100644
index 0000000..00c59b2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/block.rb
@@ -0,0 +1,77 @@
+module Liquid
+ class Block < Tag
+ MAX_DEPTH = 100
+
+ def initialize(tag_name, markup, options)
+ super
+ @blank = true
+ end
+
+ def parse(tokens)
+ @body = BlockBody.new
+ while parse_body(@body, tokens)
+ end
+ end
+
+ def render(context)
+ @body.render(context)
+ end
+
+ def blank?
+ @blank
+ end
+
+ def nodelist
+ @body.nodelist
+ end
+
+ def unknown_tag(tag, _params, _tokens)
+ if tag == 'else'.freeze
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.unexpected_else".freeze,
+ block_name: block_name))
+ elsif tag.start_with?('end'.freeze)
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.invalid_delimiter".freeze,
+ tag: tag,
+ block_name: block_name,
+ block_delimiter: block_delimiter))
+ else
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.unknown_tag".freeze, tag: tag))
+ end
+ end
+
+ def block_name
+ @tag_name
+ end
+
+ def block_delimiter
+ @block_delimiter ||= "end#{block_name}"
+ end
+
+ protected
+
+ def parse_body(body, tokens)
+ if parse_context.depth >= MAX_DEPTH
+ raise StackLevelError, "Nesting too deep".freeze
+ end
+ parse_context.depth += 1
+ begin
+ body.parse(tokens, parse_context) do |end_tag_name, end_tag_params|
+ @blank &&= body.blank?
+
+ return false if end_tag_name == block_delimiter
+ unless end_tag_name
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
+ end
+
+ # this tag is not registered with the system
+ # pass it to the current block for special handling or error reporting
+ unknown_tag(end_tag_name, end_tag_params, tokens)
+ end
+ ensure
+ parse_context.depth -= 1
+ end
+
+ true
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/block_body.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/block_body.rb
new file mode 100644
index 0000000..ba29415
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/block_body.rb
@@ -0,0 +1,143 @@
+module Liquid
+ class BlockBody
+ FullToken = /\A#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
+ ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
+ WhitespaceOrNothing = /\A\s*\z/
+ TAGSTART = "{%".freeze
+ VARSTART = "{{".freeze
+
+ attr_reader :nodelist
+
+ def initialize
+ @nodelist = []
+ @blank = true
+ end
+
+ def parse(tokenizer, parse_context)
+ parse_context.line_number = tokenizer.line_number
+ while token = tokenizer.shift
+ next if token.empty?
+ case
+ when token.start_with?(TAGSTART)
+ whitespace_handler(token, parse_context)
+ unless token =~ FullToken
+ raise_missing_tag_terminator(token, parse_context)
+ end
+ tag_name = $1
+ markup = $2
+ # fetch the tag from registered blocks
+ unless tag = registered_tags[tag_name]
+ # end parsing if we reach an unknown tag and let the caller decide
+ # determine how to proceed
+ return yield tag_name, markup
+ end
+ new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
+ @blank &&= new_tag.blank?
+ @nodelist << new_tag
+ when token.start_with?(VARSTART)
+ whitespace_handler(token, parse_context)
+ @nodelist << create_variable(token, parse_context)
+ @blank = false
+ else
+ if parse_context.trim_whitespace
+ token.lstrip!
+ end
+ parse_context.trim_whitespace = false
+ @nodelist << token
+ @blank &&= !!(token =~ WhitespaceOrNothing)
+ end
+ parse_context.line_number = tokenizer.line_number
+ end
+
+ yield nil, nil
+ end
+
+ def whitespace_handler(token, parse_context)
+ if token[2] == WhitespaceControl
+ previous_token = @nodelist.last
+ if previous_token.is_a? String
+ previous_token.rstrip!
+ end
+ end
+ parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
+ end
+
+ def blank?
+ @blank
+ end
+
+ def render(context)
+ output = []
+ context.resource_limits.render_score += @nodelist.length
+
+ idx = 0
+ while node = @nodelist[idx]
+ case node
+ when String
+ check_resources(context, node)
+ output << node
+ when Variable
+ render_node_to_output(node, output, context)
+ when Block
+ render_node_to_output(node, output, context, node.blank?)
+ break if context.interrupt? # might have happened in a for-block
+ when Continue, Break
+ # If we get an Interrupt that means the block must stop processing. An
+ # Interrupt is any command that stops block execution such as {% break %}
+ # or {% continue %}
+ context.push_interrupt(node.interrupt)
+ break
+ else # Other non-Block tags
+ render_node_to_output(node, output, context)
+ break if context.interrupt? # might have happened through an include
+ end
+ idx += 1
+ end
+
+ output.join
+ end
+
+ private
+
+ def render_node_to_output(node, output, context, skip_output = false)
+ node_output = node.render(context)
+ node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s
+ check_resources(context, node_output)
+ output << node_output unless skip_output
+ rescue MemoryError => e
+ raise e
+ rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
+ context.handle_error(e, node.line_number)
+ output << nil
+ rescue ::StandardError => e
+ line_number = node.is_a?(String) ? nil : node.line_number
+ output << context.handle_error(e, line_number)
+ end
+
+ def check_resources(context, node_output)
+ context.resource_limits.render_length += node_output.length
+ return unless context.resource_limits.reached?
+ raise MemoryError.new("Memory limits exceeded".freeze)
+ end
+
+ def create_variable(token, parse_context)
+ token.scan(ContentOfVariable) do |content|
+ markup = content.first
+ return Variable.new(markup, parse_context)
+ end
+ raise_missing_variable_terminator(token, parse_context)
+ end
+
+ def raise_missing_tag_terminator(token, parse_context)
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_termination".freeze, token: token, tag_end: TagEnd.inspect))
+ end
+
+ def raise_missing_variable_terminator(token, parse_context)
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.variable_termination".freeze, token: token, tag_end: VariableEnd.inspect))
+ end
+
+ def registered_tags
+ Template.tags
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/condition.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/condition.rb
new file mode 100644
index 0000000..3b51682
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/condition.rb
@@ -0,0 +1,151 @@
+module Liquid
+ # Container for liquid nodes which conveniently wraps decision making logic
+ #
+ # Example:
+ #
+ # c = Condition.new(1, '==', 1)
+ # c.evaluate #=> true
+ #
+ class Condition #:nodoc:
+ @@operators = {
+ '=='.freeze => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
+ '!='.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
+ '<>'.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
+ '<'.freeze => :<,
+ '>'.freeze => :>,
+ '>='.freeze => :>=,
+ '<='.freeze => :<=,
+ 'contains'.freeze => lambda do |cond, left, right|
+ if left && right && left.respond_to?(:include?)
+ right = right.to_s if left.is_a?(String)
+ left.include?(right)
+ else
+ false
+ end
+ end
+ }
+
+ def self.operators
+ @@operators
+ end
+
+ attr_reader :attachment, :child_condition
+ attr_accessor :left, :operator, :right
+
+ def initialize(left = nil, operator = nil, right = nil)
+ @left = left
+ @operator = operator
+ @right = right
+ @child_relation = nil
+ @child_condition = nil
+ end
+
+ def evaluate(context = Context.new)
+ condition = self
+ result = nil
+ loop do
+ result = interpret_condition(condition.left, condition.right, condition.operator, context)
+
+ case condition.child_relation
+ when :or
+ break if result
+ when :and
+ break unless result
+ else
+ break
+ end
+ condition = condition.child_condition
+ end
+ result
+ end
+
+ def or(condition)
+ @child_relation = :or
+ @child_condition = condition
+ end
+
+ def and(condition)
+ @child_relation = :and
+ @child_condition = condition
+ end
+
+ def attach(attachment)
+ @attachment = attachment
+ end
+
+ def else?
+ false
+ end
+
+ def inspect
+ "#<Condition #{[@left, @operator, @right].compact.join(' '.freeze)}>"
+ end
+
+ protected
+
+ attr_reader :child_relation
+
+ private
+
+ def equal_variables(left, right)
+ if left.is_a?(Liquid::Expression::MethodLiteral)
+ if right.respond_to?(left.method_name)
+ return right.send(left.method_name)
+ else
+ return nil
+ end
+ end
+
+ if right.is_a?(Liquid::Expression::MethodLiteral)
+ if left.respond_to?(right.method_name)
+ return left.send(right.method_name)
+ else
+ return nil
+ end
+ end
+
+ left == right
+ end
+
+ def interpret_condition(left, right, op, context)
+ # If the operator is empty this means that the decision statement is just
+ # a single variable. We can just poll this variable from the context and
+ # return this as the result.
+ return context.evaluate(left) if op.nil?
+
+ left = context.evaluate(left)
+ right = context.evaluate(right)
+
+ operation = self.class.operators[op] || raise(Liquid::ArgumentError.new("Unknown operator #{op}"))
+
+ if operation.respond_to?(:call)
+ operation.call(self, left, right)
+ elsif left.respond_to?(operation) && right.respond_to?(operation) && !left.is_a?(Hash) && !right.is_a?(Hash)
+ begin
+ left.send(operation, right)
+ rescue ::ArgumentError => e
+ raise Liquid::ArgumentError.new(e.message)
+ end
+ end
+ end
+
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
+ def children
+ [
+ @node.left, @node.right,
+ @node.child_condition, @node.attachment
+ ].compact
+ end
+ end
+ end
+
+ class ElseCondition < Condition
+ def else?
+ true
+ end
+
+ def evaluate(_context)
+ true
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/context.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/context.rb
new file mode 100644
index 0000000..2dcc6af
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/context.rb
@@ -0,0 +1,226 @@
+module Liquid
+ # 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
+ class Context
+ attr_reader :scopes, :errors, :registers, :environments, :resource_limits
+ attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
+
+ 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
+
+ def warnings
+ @warnings ||= []
+ end
+
+ def strainer
+ @strainer ||= Strainer.create(self, @filters)
+ end
+
+ # Adds filters to this context.
+ #
+ # Note that this does not register the filters with the main Template object. see <tt>Template.register_filter</tt>
+ # for that
+ def add_filters(filters)
+ filters = [filters].flatten.compact
+ @filters += filters
+ @strainer = nil
+ end
+
+ def apply_global_filter(obj)
+ global_filter.nil? ? obj : global_filter.call(obj)
+ end
+
+ # are there any not handled interrupts?
+ def interrupt?
+ !@interrupts.empty?
+ end
+
+ # push an interrupt to the stack. this interrupt is considered not handled.
+ def push_interrupt(e)
+ @interrupts.push(e)
+ end
+
+ # pop an interrupt from the stack
+ def pop_interrupt
+ @interrupts.pop
+ end
+
+ 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 invoke(method, *args)
+ strainer.invoke(method, *args).to_liquid
+ end
+
+ # Push new local scope on the stack. use <tt>Context#stack</tt> instead
+ def push(new_scope = {})
+ @scopes.unshift(new_scope)
+ raise StackLevelError, "Nesting too deep".freeze if @scopes.length > Block::MAX_DEPTH
+ end
+
+ # Merge a hash of variables in the current local scope
+ def merge(new_scopes)
+ @scopes[0].merge!(new_scopes)
+ end
+
+ # Pop from the stack. use <tt>Context#stack</tt> instead
+ def pop
+ raise ContextError if @scopes.size == 1
+ @scopes.shift
+ 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
+ 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
+
+ def clear_instance_assigns
+ @scopes[0] = {}
+ end
+
+ # Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
+ def []=(key, value)
+ unless @this_stack_used
+ @this_stack_used = true
+ push({})
+ end
+ @scopes[0][key] = value
+ 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?
+ def [](expression)
+ evaluate(Expression.parse(expression))
+ end
+
+ def key?(key)
+ self[key] != nil
+ end
+
+ 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
+ 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
+
+ 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
+
+ private
+
+ def internal_error
+ # raise and catch to set backtrace and cause on exception
+ raise Liquid::InternalError, 'internal'
+ rescue Liquid::InternalError => exc
+ exc
+ end
+
+ 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
+ end # Context
+end # Liquid
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/document.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/document.rb
new file mode 100644
index 0000000..d035dd4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/document.rb
@@ -0,0 +1,27 @@
+module Liquid
+ class Document < BlockBody
+ def self.parse(tokens, parse_context)
+ doc = new
+ doc.parse(tokens, parse_context)
+ doc
+ end
+
+ def parse(tokens, parse_context)
+ super do |end_tag_name, end_tag_params|
+ unknown_tag(end_tag_name, parse_context) if end_tag_name
+ end
+ rescue SyntaxError => e
+ e.line_number ||= parse_context.line_number
+ raise
+ end
+
+ def unknown_tag(tag, parse_context)
+ case tag
+ when 'else'.freeze, 'end'.freeze
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.unexpected_outer_tag".freeze, tag: tag))
+ else
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.unknown_tag".freeze, tag: tag))
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/drop.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/drop.rb
new file mode 100644
index 0000000..6b5aa99
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/drop.rb
@@ -0,0 +1,78 @@
+require 'set'
+
+module Liquid
+ # A drop in liquid is a class which allows you to export DOM like things to liquid.
+ # Methods of drops are callable.
+ # The main use for liquid drops is to implement lazy loaded objects.
+ # If you would like to make data available to the web designers which you don't want loaded unless needed then
+ # a drop is a great way to do that.
+ #
+ # Example:
+ #
+ # class ProductDrop < Liquid::Drop
+ # def top_sales
+ # Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
+ # end
+ # end
+ #
+ # tmpl = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {%endfor%} ' )
+ # tmpl.render('product' => ProductDrop.new ) # will invoke top_sales query.
+ #
+ # Your drop can either implement the methods sans any parameters
+ # or implement the liquid_method_missing(name) method which is a catch all.
+ class Drop
+ attr_writer :context
+
+ # Catch all for the method
+ def liquid_method_missing(method)
+ return nil unless @context && @context.strict_variables
+ raise Liquid::UndefinedDropMethod, "undefined method #{method}"
+ end
+
+ # called by liquid to invoke a drop
+ def invoke_drop(method_or_key)
+ if self.class.invokable?(method_or_key)
+ send(method_or_key)
+ else
+ liquid_method_missing(method_or_key)
+ end
+ end
+
+ def key?(_name)
+ true
+ end
+
+ def inspect
+ self.class.to_s
+ end
+
+ def to_liquid
+ self
+ end
+
+ def to_s
+ self.class.name
+ end
+
+ alias_method :[], :invoke_drop
+
+ # Check for method existence without invoking respond_to?, which creates symbols
+ def self.invokable?(method_name)
+ invokable_methods.include?(method_name.to_s)
+ end
+
+ def self.invokable_methods
+ @invokable_methods ||= begin
+ blacklist = Liquid::Drop.public_instance_methods + [:each]
+
+ if include?(Enumerable)
+ blacklist += Enumerable.public_instance_methods
+ blacklist -= [:sort, :count, :first, :min, :max, :include?]
+ end
+
+ whitelist = [:to_liquid] + (public_instance_methods - blacklist)
+ Set.new(whitelist.map(&:to_s))
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/errors.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/errors.rb
new file mode 100644
index 0000000..defa5ea
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/errors.rb
@@ -0,0 +1,56 @@
+module Liquid
+ class Error < ::StandardError
+ attr_accessor :line_number
+ attr_accessor :template_name
+ attr_accessor :markup_context
+
+ def to_s(with_prefix = true)
+ str = ""
+ str << message_prefix if with_prefix
+ str << super()
+
+ if markup_context
+ str << " "
+ str << markup_context
+ end
+
+ str
+ end
+
+ private
+
+ def message_prefix
+ str = ""
+ if is_a?(SyntaxError)
+ str << "Liquid syntax error"
+ else
+ str << "Liquid error"
+ end
+
+ if line_number
+ str << " ("
+ str << template_name << " " if template_name
+ str << "line " << line_number.to_s << ")"
+ end
+
+ str << ": "
+ str
+ end
+ end
+
+ ArgumentError = Class.new(Error)
+ ContextError = Class.new(Error)
+ FileSystemError = Class.new(Error)
+ StandardError = Class.new(Error)
+ SyntaxError = Class.new(Error)
+ StackLevelError = Class.new(Error)
+ TaintedError = Class.new(Error)
+ MemoryError = Class.new(Error)
+ ZeroDivisionError = Class.new(Error)
+ FloatDomainError = Class.new(Error)
+ UndefinedVariable = Class.new(Error)
+ UndefinedDropMethod = Class.new(Error)
+ UndefinedFilter = Class.new(Error)
+ MethodOverrideError = Class.new(Error)
+ InternalError = Class.new(Error)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/expression.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/expression.rb
new file mode 100644
index 0000000..98be6db
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/expression.rb
@@ -0,0 +1,49 @@
+module Liquid
+ class Expression
+ class MethodLiteral
+ attr_reader :method_name, :to_s
+
+ def initialize(method_name, to_s)
+ @method_name = method_name
+ @to_s = to_s
+ end
+
+ def to_liquid
+ to_s
+ end
+ end
+
+ LITERALS = {
+ nil => nil, 'nil'.freeze => nil, 'null'.freeze => nil, ''.freeze => nil,
+ 'true'.freeze => true,
+ 'false'.freeze => false,
+ 'blank'.freeze => MethodLiteral.new(:blank?, '').freeze,
+ 'empty'.freeze => MethodLiteral.new(:empty?, '').freeze
+ }.freeze
+
+ SINGLE_QUOTED_STRING = /\A'(.*)'\z/m
+ DOUBLE_QUOTED_STRING = /\A"(.*)"\z/m
+ INTEGERS_REGEX = /\A(-?\d+)\z/
+ FLOATS_REGEX = /\A(-?\d[\d\.]+)\z/
+ RANGES_REGEX = /\A\((\S+)\.\.(\S+)\)\z/
+
+ def self.parse(markup)
+ if LITERALS.key?(markup)
+ LITERALS[markup]
+ else
+ case markup
+ when SINGLE_QUOTED_STRING, DOUBLE_QUOTED_STRING
+ $1
+ when INTEGERS_REGEX
+ $1.to_i
+ when RANGES_REGEX
+ RangeLookup.parse($1, $2)
+ when FLOATS_REGEX
+ $1.to_f
+ else
+ VariableLookup.parse(markup)
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/extensions.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/extensions.rb
new file mode 100644
index 0000000..0907819
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/extensions.rb
@@ -0,0 +1,74 @@
+require 'time'
+require 'date'
+
+class String # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Symbol # :nodoc:
+ def to_liquid
+ to_s
+ end
+end
+
+class Array # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Hash # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Numeric # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Range # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Time # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class DateTime < Date # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class Date # :nodoc:
+ def to_liquid
+ self
+ end
+end
+
+class TrueClass
+ def to_liquid # :nodoc:
+ self
+ end
+end
+
+class FalseClass
+ def to_liquid # :nodoc:
+ self
+ end
+end
+
+class NilClass
+ def to_liquid # :nodoc:
+ self
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/file_system.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/file_system.rb
new file mode 100644
index 0000000..13f1f46
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/file_system.rb
@@ -0,0 +1,73 @@
+module Liquid
+ # A Liquid file system is a way to let your templates retrieve other templates for use with the include tag.
+ #
+ # You can implement subclasses that retrieve templates from the database, from the file system using a different
+ # path structure, you can provide them as hard-coded inline strings, or any manner that you see fit.
+ #
+ # You can add additional instance variables, arguments, or methods as needed.
+ #
+ # Example:
+ #
+ # Liquid::Template.file_system = Liquid::LocalFileSystem.new(template_path)
+ # liquid = Liquid::Template.parse(template)
+ #
+ # This will parse the template with a LocalFileSystem implementation rooted at 'template_path'.
+ class BlankFileSystem
+ # Called by Liquid to retrieve a template file
+ def read_template_file(_template_path)
+ raise FileSystemError, "This liquid context does not allow includes."
+ end
+ end
+
+ # This implements an abstract file system which retrieves template files named in a manner similar to Rails partials,
+ # ie. with the template name prefixed with an underscore. The extension ".liquid" is also added.
+ #
+ # For security reasons, template paths are only allowed to contain letters, numbers, and underscore.
+ #
+ # Example:
+ #
+ # file_system = Liquid::LocalFileSystem.new("/some/path")
+ #
+ # file_system.full_path("mypartial") # => "/some/path/_mypartial.liquid"
+ # file_system.full_path("dir/mypartial") # => "/some/path/dir/_mypartial.liquid"
+ #
+ # Optionally in the second argument you can specify a custom pattern for template filenames.
+ # The Kernel::sprintf format specification is used.
+ # Default pattern is "_%s.liquid".
+ #
+ # Example:
+ #
+ # file_system = Liquid::LocalFileSystem.new("/some/path", "%s.html")
+ #
+ # file_system.full_path("index") # => "/some/path/index.html"
+ #
+ class LocalFileSystem
+ attr_accessor :root
+
+ def initialize(root, pattern = "_%s.liquid".freeze)
+ @root = root
+ @pattern = pattern
+ end
+
+ def read_template_file(template_path)
+ full_path = full_path(template_path)
+ raise FileSystemError, "No such template '#{template_path}'" unless File.exist?(full_path)
+
+ File.read(full_path)
+ end
+
+ def full_path(template_path)
+ raise FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ /\A[^.\/][a-zA-Z0-9_\/]+\z/
+
+ full_path = if template_path.include?('/'.freeze)
+ File.join(root, File.dirname(template_path), @pattern % File.basename(template_path))
+ else
+ File.join(root, @pattern % template_path)
+ end
+
+ raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path).start_with?(File.expand_path(root))
+
+ full_path
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/forloop_drop.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/forloop_drop.rb
new file mode 100644
index 0000000..81b2d1a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/forloop_drop.rb
@@ -0,0 +1,42 @@
+module Liquid
+ class ForloopDrop < Drop
+ def initialize(name, length, parentloop)
+ @name = name
+ @length = length
+ @parentloop = parentloop
+ @index = 0
+ end
+
+ attr_reader :name, :length, :parentloop
+
+ def index
+ @index + 1
+ end
+
+ def index0
+ @index
+ end
+
+ def rindex
+ @length - @index
+ end
+
+ def rindex0
+ @length - @index - 1
+ end
+
+ def first
+ @index == 0
+ end
+
+ def last
+ @index == @length - 1
+ end
+
+ protected
+
+ def increment!
+ @index += 1
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/i18n.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/i18n.rb
new file mode 100644
index 0000000..2671507
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/i18n.rb
@@ -0,0 +1,39 @@
+require 'yaml'
+
+module Liquid
+ class I18n
+ DEFAULT_LOCALE = File.join(File.expand_path(__dir__), "locales", "en.yml")
+
+ TranslationError = Class.new(StandardError)
+
+ attr_reader :path
+
+ def initialize(path = DEFAULT_LOCALE)
+ @path = path
+ end
+
+ def translate(name, vars = {})
+ interpolate(deep_fetch_translation(name), vars)
+ end
+ alias_method :t, :translate
+
+ def locale
+ @locale ||= YAML.load_file(@path)
+ end
+
+ private
+
+ def interpolate(name, vars)
+ name.gsub(/%\{(\w+)\}/) do
+ # raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym]
+ (vars[$1.to_sym]).to_s
+ end
+ end
+
+ def deep_fetch_translation(name)
+ name.split('.'.freeze).reduce(locale) do |level, cur|
+ level[cur] or raise TranslationError, "Translation for #{name} does not exist in locale #{path}"
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/interrupts.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/interrupts.rb
new file mode 100644
index 0000000..41359d7
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/interrupts.rb
@@ -0,0 +1,16 @@
+module Liquid
+ # An interrupt is any command that breaks processing of a block (ex: a for loop).
+ class Interrupt
+ attr_reader :message
+
+ def initialize(message = nil)
+ @message = message || "interrupt".freeze
+ end
+ end
+
+ # Interrupt that is thrown whenever a {% break %} is called.
+ class BreakInterrupt < Interrupt; end
+
+ # Interrupt that is thrown whenever a {% continue %} is called.
+ class ContinueInterrupt < Interrupt; end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/lexer.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/lexer.rb
new file mode 100644
index 0000000..f290744
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/lexer.rb
@@ -0,0 +1,55 @@
+require "strscan"
+module Liquid
+ class Lexer
+ SPECIALS = {
+ '|'.freeze => :pipe,
+ '.'.freeze => :dot,
+ ':'.freeze => :colon,
+ ','.freeze => :comma,
+ '['.freeze => :open_square,
+ ']'.freeze => :close_square,
+ '('.freeze => :open_round,
+ ')'.freeze => :close_round,
+ '?'.freeze => :question,
+ '-'.freeze => :dash
+ }.freeze
+ IDENTIFIER = /[a-zA-Z_][\w-]*\??/
+ SINGLE_STRING_LITERAL = /'[^\']*'/
+ DOUBLE_STRING_LITERAL = /"[^\"]*"/
+ NUMBER_LITERAL = /-?\d+(\.\d+)?/
+ DOTDOT = /\.\./
+ COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
+ WHITESPACE_OR_NOTHING = /\s*/
+
+ def initialize(input)
+ @ss = StringScanner.new(input)
+ end
+
+ def tokenize
+ @output = []
+
+ until @ss.eos?
+ @ss.skip(WHITESPACE_OR_NOTHING)
+ break if @ss.eos?
+ tok = case
+ when t = @ss.scan(COMPARISON_OPERATOR) then [:comparison, t]
+ when t = @ss.scan(SINGLE_STRING_LITERAL) then [:string, t]
+ when t = @ss.scan(DOUBLE_STRING_LITERAL) then [:string, t]
+ when t = @ss.scan(NUMBER_LITERAL) then [:number, t]
+ when t = @ss.scan(IDENTIFIER) then [:id, t]
+ when t = @ss.scan(DOTDOT) then [:dotdot, t]
+ else
+ c = @ss.getch
+ if s = SPECIALS[c]
+ [s, c]
+ else
+ raise SyntaxError, "Unexpected character #{c}"
+ end
+ end
+ @output << tok
+ end
+
+ @output << [:end_of_string]
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/locales/en.yml b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/locales/en.yml
new file mode 100644
index 0000000..48b3b1d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/locales/en.yml
@@ -0,0 +1,26 @@
+---
+ errors:
+ syntax:
+ tag_unexpected_args: "Syntax Error in '%{tag}' - Valid syntax: %{tag}"
+ assign: "Syntax Error in 'assign' - Valid syntax: assign [var] = [source]"
+ capture: "Syntax Error in 'capture' - Valid syntax: capture [var]"
+ case: "Syntax Error in 'case' - Valid syntax: case [condition]"
+ case_invalid_when: "Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %}"
+ case_invalid_else: "Syntax Error in tag 'case' - Valid else condition: {% else %} (no parameters) "
+ cycle: "Syntax Error in 'cycle' - Valid syntax: cycle [name :] var [, var2, var3 ...]"
+ for: "Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]"
+ for_invalid_in: "For loops require an 'in' clause"
+ for_invalid_attribute: "Invalid attribute in for loop. Valid attributes are limit and offset"
+ if: "Syntax Error in tag 'if' - Valid syntax: if [expression]"
+ include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"
+ unknown_tag: "Unknown tag '%{tag}'"
+ invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
+ unexpected_else: "%{block_name} tag does not expect 'else' tag"
+ unexpected_outer_tag: "Unexpected outer '%{tag}' tag"
+ tag_termination: "Tag '%{token}' was not properly terminated with regexp: %{tag_end}"
+ variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}"
+ tag_never_closed: "'%{block_name}' tag was never closed"
+ meta_syntax_error: "Liquid syntax error: #{e.message}"
+ table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
+ argument:
+ include: "Argument error in tag 'include' - Illegal template name"
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parse_context.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parse_context.rb
new file mode 100644
index 0000000..abcdaeb
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parse_context.rb
@@ -0,0 +1,38 @@
+module Liquid
+ class ParseContext
+ attr_accessor :locale, :line_number, :trim_whitespace, :depth
+ attr_reader :partial, :warnings, :error_mode
+
+ def initialize(options = {})
+ @template_options = options ? options.dup : {}
+ @locale = @template_options[:locale] ||= I18n.new
+ @warnings = []
+ self.depth = 0
+ self.partial = false
+ end
+
+ def [](option_key)
+ @options[option_key]
+ end
+
+ def partial=(value)
+ @partial = value
+ @options = value ? partial_options : @template_options
+ @error_mode = @options[:error_mode] || Template.error_mode
+ value
+ end
+
+ def partial_options
+ @partial_options ||= begin
+ dont_pass = @template_options[:include_options_blacklist]
+ if dont_pass == true
+ { locale: locale }
+ elsif dont_pass.is_a?(Array)
+ @template_options.reject { |k, v| dont_pass.include?(k) }
+ else
+ @template_options
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parse_tree_visitor.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parse_tree_visitor.rb
new file mode 100644
index 0000000..74f5563
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parse_tree_visitor.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Liquid
+ class ParseTreeVisitor
+ def self.for(node, callbacks = Hash.new(proc {}))
+ if defined?(node.class::ParseTreeVisitor)
+ node.class::ParseTreeVisitor
+ else
+ self
+ end.new(node, callbacks)
+ end
+
+ def initialize(node, callbacks)
+ @node = node
+ @callbacks = callbacks
+ end
+
+ def add_callback_for(*classes, &block)
+ callback = block
+ callback = ->(node, _) { yield node } if block.arity.abs == 1
+ callback = ->(_, _) { yield } if block.arity.zero?
+ classes.each { |klass| @callbacks[klass] = callback }
+ self
+ end
+
+ def visit(context = nil)
+ children.map do |node|
+ item, new_context = @callbacks[node.class].call(node, context)
+ [
+ item,
+ ParseTreeVisitor.for(node, @callbacks).visit(new_context || context)
+ ]
+ end
+ end
+
+ protected
+
+ def children
+ @node.respond_to?(:nodelist) ? Array(@node.nodelist) : []
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parser.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parser.rb
new file mode 100644
index 0000000..6954343
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parser.rb
@@ -0,0 +1,90 @@
+module Liquid
+ class Parser
+ def initialize(input)
+ l = Lexer.new(input)
+ @tokens = l.tokenize
+ @p = 0 # pointer to current location
+ end
+
+ def jump(point)
+ @p = point
+ end
+
+ def consume(type = nil)
+ token = @tokens[@p]
+ if type && token[0] != type
+ raise SyntaxError, "Expected #{type} but found #{@tokens[@p].first}"
+ end
+ @p += 1
+ token[1]
+ end
+
+ # Only consumes the token if it matches the type
+ # Returns the token's contents if it was consumed
+ # or false otherwise.
+ def consume?(type)
+ token = @tokens[@p]
+ return false unless token && token[0] == type
+ @p += 1
+ token[1]
+ end
+
+ # Like consume? Except for an :id token of a certain name
+ def id?(str)
+ token = @tokens[@p]
+ return false unless token && token[0] == :id
+ return false unless token[1] == str
+ @p += 1
+ token[1]
+ end
+
+ def look(type, ahead = 0)
+ tok = @tokens[@p + ahead]
+ return false unless tok
+ tok[0] == type
+ end
+
+ def expression
+ token = @tokens[@p]
+ if token[0] == :id
+ variable_signature
+ elsif [:string, :number].include? token[0]
+ consume
+ elsif token.first == :open_round
+ consume
+ first = expression
+ consume(:dotdot)
+ last = expression
+ consume(:close_round)
+ "(#{first}..#{last})"
+ else
+ raise SyntaxError, "#{token} is not a valid expression"
+ end
+ end
+
+ def argument
+ str = ""
+ # might be a keyword argument (identifier: expression)
+ if look(:id) && look(:colon, 1)
+ str << consume << consume << ' '.freeze
+ end
+
+ str << expression
+ str
+ end
+
+ def variable_signature
+ str = consume(:id)
+ while look(:open_square)
+ str << consume
+ str << expression
+ str << consume(:close_square)
+ end
+ if look(:dot)
+ str << consume
+ str << variable_signature
+ end
+ str
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parser_switching.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parser_switching.rb
new file mode 100644
index 0000000..3aa664a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/parser_switching.rb
@@ -0,0 +1,31 @@
+module Liquid
+ module ParserSwitching
+ def parse_with_selected_parser(markup)
+ case parse_context.error_mode
+ when :strict then strict_parse_with_error_context(markup)
+ when :lax then lax_parse(markup)
+ when :warn
+ begin
+ return strict_parse_with_error_context(markup)
+ rescue SyntaxError => e
+ parse_context.warnings << e
+ return lax_parse(markup)
+ end
+ end
+ end
+
+ private
+
+ def strict_parse_with_error_context(markup)
+ strict_parse(markup)
+ rescue SyntaxError => e
+ e.line_number = line_number
+ e.markup_context = markup_context(markup)
+ raise e
+ end
+
+ def markup_context(markup)
+ "in \"#{markup.strip}\""
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/profiler.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/profiler.rb
new file mode 100644
index 0000000..dc9db60
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/profiler.rb
@@ -0,0 +1,158 @@
+require 'liquid/profiler/hooks'
+
+module Liquid
+ # Profiler enables support for profiling template rendering to help track down performance issues.
+ #
+ # To enable profiling, first require 'liquid/profiler'.
+ # Then, to profile a parse/render cycle, pass the <tt>profile: true</tt> option to <tt>Liquid::Template.parse</tt>.
+ # After <tt>Liquid::Template#render</tt> is called, the template object makes available an instance of this
+ # class via the <tt>Liquid::Template#profiler</tt> method.
+ #
+ # template = Liquid::Template.parse(template_content, profile: true)
+ # output = template.render
+ # profile = template.profiler
+ #
+ # This object contains all profiling information, containing information on what tags were rendered,
+ # where in the templates these tags live, and how long each tag took to render.
+ #
+ # This is a tree structure that is Enumerable all the way down, and keeps track of tags and rendering times
+ # inside of <tt>{% include %}</tt> tags.
+ #
+ # profile.each do |node|
+ # # Access to the node itself
+ # node.code
+ #
+ # # Which template and line number of this node.
+ # # If top level, this will be "<root>".
+ # node.partial
+ # node.line_number
+ #
+ # # Render time in seconds of this node
+ # node.render_time
+ #
+ # # If the template used {% include %}, this node will also have children.
+ # node.children.each do |child2|
+ # # ...
+ # end
+ # end
+ #
+ # Profiler also exposes the total time of the template's render in <tt>Liquid::Profiler#total_render_time</tt>.
+ #
+ # All render times are in seconds. There is a small performance hit when profiling is enabled.
+ #
+ class Profiler
+ include Enumerable
+
+ class Timing
+ attr_reader :code, :partial, :line_number, :children
+
+ def initialize(node, partial)
+ @code = node.respond_to?(:raw) ? node.raw : node
+ @partial = partial
+ @line_number = node.respond_to?(:line_number) ? node.line_number : nil
+ @children = []
+ end
+
+ def self.start(node, partial)
+ new(node, partial).tap(&:start)
+ end
+
+ def start
+ @start_time = Time.now
+ end
+
+ def finish
+ @end_time = Time.now
+ end
+
+ def render_time
+ @end_time - @start_time
+ end
+ end
+
+ def self.profile_node_render(node)
+ if Profiler.current_profile && node.respond_to?(:render)
+ Profiler.current_profile.start_node(node)
+ output = yield
+ Profiler.current_profile.end_node(node)
+ output
+ else
+ yield
+ end
+ end
+
+ def self.profile_children(template_name)
+ if Profiler.current_profile
+ Profiler.current_profile.push_partial(template_name)
+ output = yield
+ Profiler.current_profile.pop_partial
+ output
+ else
+ yield
+ end
+ end
+
+ def self.current_profile
+ Thread.current[:liquid_profiler]
+ end
+
+ def initialize
+ @partial_stack = ["<root>"]
+
+ @root_timing = Timing.new("", current_partial)
+ @timing_stack = [@root_timing]
+
+ @render_start_at = Time.now
+ @render_end_at = @render_start_at
+ end
+
+ def start
+ Thread.current[:liquid_profiler] = self
+ @render_start_at = Time.now
+ end
+
+ def stop
+ Thread.current[:liquid_profiler] = nil
+ @render_end_at = Time.now
+ end
+
+ def total_render_time
+ @render_end_at - @render_start_at
+ end
+
+ def each(&block)
+ @root_timing.children.each(&block)
+ end
+
+ def [](idx)
+ @root_timing.children[idx]
+ end
+
+ def length
+ @root_timing.children.length
+ end
+
+ def start_node(node)
+ @timing_stack.push(Timing.start(node, current_partial))
+ end
+
+ def end_node(_node)
+ timing = @timing_stack.pop
+ timing.finish
+
+ @timing_stack.last.children << timing
+ end
+
+ def current_partial
+ @partial_stack.last
+ end
+
+ def push_partial(partial_name)
+ @partial_stack.push(partial_name)
+ end
+
+ def pop_partial
+ @partial_stack.pop
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/profiler/hooks.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/profiler/hooks.rb
new file mode 100644
index 0000000..cb11cd7
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/profiler/hooks.rb
@@ -0,0 +1,23 @@
+module Liquid
+ class BlockBody
+ def render_node_with_profiling(node, output, context, skip_output = false)
+ Profiler.profile_node_render(node) do
+ render_node_without_profiling(node, output, context, skip_output)
+ end
+ end
+
+ alias_method :render_node_without_profiling, :render_node_to_output
+ alias_method :render_node_to_output, :render_node_with_profiling
+ end
+
+ class Include < Tag
+ def render_with_profiling(context)
+ Profiler.profile_children(context.evaluate(@template_name_expr).to_s) do
+ render_without_profiling(context)
+ end
+ end
+
+ alias_method :render_without_profiling, :render
+ alias_method :render, :render_with_profiling
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/range_lookup.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/range_lookup.rb
new file mode 100644
index 0000000..93bb420
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/range_lookup.rb
@@ -0,0 +1,37 @@
+module Liquid
+ class RangeLookup
+ def self.parse(start_markup, end_markup)
+ start_obj = Expression.parse(start_markup)
+ end_obj = Expression.parse(end_markup)
+ if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
+ new(start_obj, end_obj)
+ else
+ start_obj.to_i..end_obj.to_i
+ end
+ end
+
+ def initialize(start_obj, end_obj)
+ @start_obj = start_obj
+ @end_obj = end_obj
+ end
+
+ def evaluate(context)
+ start_int = to_integer(context.evaluate(@start_obj))
+ end_int = to_integer(context.evaluate(@end_obj))
+ start_int..end_int
+ end
+
+ private
+
+ def to_integer(input)
+ case input
+ when Integer
+ input
+ when NilClass, String
+ input.to_i
+ else
+ Utils.to_integer(input)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/resource_limits.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/resource_limits.rb
new file mode 100644
index 0000000..08b359b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/resource_limits.rb
@@ -0,0 +1,23 @@
+module Liquid
+ class ResourceLimits
+ attr_accessor :render_length, :render_score, :assign_score,
+ :render_length_limit, :render_score_limit, :assign_score_limit
+
+ def initialize(limits)
+ @render_length_limit = limits[:render_length_limit]
+ @render_score_limit = limits[:render_score_limit]
+ @assign_score_limit = limits[:assign_score_limit]
+ reset
+ end
+
+ def reached?
+ (@render_length_limit && @render_length > @render_length_limit) ||
+ (@render_score_limit && @render_score > @render_score_limit) ||
+ (@assign_score_limit && @assign_score > @assign_score_limit)
+ end
+
+ def reset
+ @render_length = @render_score = @assign_score = 0
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/standardfilters.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/standardfilters.rb
new file mode 100644
index 0000000..fffee4d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/standardfilters.rb
@@ -0,0 +1,506 @@
+require 'cgi'
+require 'bigdecimal'
+
+module Liquid
+ module StandardFilters
+ HTML_ESCAPE = {
+ '&'.freeze => '&amp;'.freeze,
+ '>'.freeze => '&gt;'.freeze,
+ '<'.freeze => '&lt;'.freeze,
+ '"'.freeze => '&quot;'.freeze,
+ "'".freeze => '&#39;'.freeze
+ }.freeze
+ HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
+ STRIP_HTML_BLOCKS = Regexp.union(
+ /<script.*?<\/script>/m,
+ /<!--.*?-->/m,
+ /<style.*?<\/style>/m
+ )
+ STRIP_HTML_TAGS = /<.*?>/m
+
+ # Return the size of an array or of an string
+ def size(input)
+ input.respond_to?(:size) ? input.size : 0
+ end
+
+ # convert an input string to DOWNCASE
+ def downcase(input)
+ input.to_s.downcase
+ end
+
+ # convert an input string to UPCASE
+ def upcase(input)
+ input.to_s.upcase
+ end
+
+ # capitalize words in the input centence
+ def capitalize(input)
+ input.to_s.capitalize
+ end
+
+ def escape(input)
+ CGI.escapeHTML(input.to_s) unless input.nil?
+ end
+ alias_method :h, :escape
+
+ def escape_once(input)
+ input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
+ end
+
+ def url_encode(input)
+ CGI.escape(input.to_s) unless input.nil?
+ end
+
+ def url_decode(input)
+ return if input.nil?
+
+ result = CGI.unescape(input.to_s)
+ raise Liquid::ArgumentError, "invalid byte sequence in #{result.encoding}" unless result.valid_encoding?
+
+ result
+ end
+
+ def slice(input, offset, length = nil)
+ offset = Utils.to_integer(offset)
+ length = length ? Utils.to_integer(length) : 1
+
+ if input.is_a?(Array)
+ input.slice(offset, length) || []
+ else
+ input.to_s.slice(offset, length) || ''
+ end
+ end
+
+ # Truncate a string down to x characters
+ def truncate(input, length = 50, truncate_string = "...".freeze)
+ return if input.nil?
+ input_str = input.to_s
+ length = Utils.to_integer(length)
+ truncate_string_str = truncate_string.to_s
+ l = length - truncate_string_str.length
+ l = 0 if l < 0
+ input_str.length > length ? input_str[0...l] + truncate_string_str : input_str
+ end
+
+ def truncatewords(input, words = 15, truncate_string = "...".freeze)
+ return if input.nil?
+ wordlist = input.to_s.split
+ words = Utils.to_integer(words)
+ l = words - 1
+ l = 0 if l < 0
+ wordlist.length > l ? wordlist[0..l].join(" ".freeze) + truncate_string.to_s : input
+ end
+
+ # Split input string into an array of substrings separated by given pattern.
+ #
+ # Example:
+ # <div class="summary">{{ post | split '//' | first }}</div>
+ #
+ def split(input, pattern)
+ input.to_s.split(pattern.to_s)
+ end
+
+ def strip(input)
+ input.to_s.strip
+ end
+
+ def lstrip(input)
+ input.to_s.lstrip
+ end
+
+ def rstrip(input)
+ input.to_s.rstrip
+ end
+
+ def strip_html(input)
+ empty = ''.freeze
+ result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
+ result.gsub!(STRIP_HTML_TAGS, empty)
+ result
+ end
+
+ # Remove all newlines from the string
+ def strip_newlines(input)
+ input.to_s.gsub(/\r?\n/, ''.freeze)
+ end
+
+ # Join elements of the array with certain character between them
+ def join(input, glue = ' '.freeze)
+ InputIterator.new(input).join(glue)
+ end
+
+ # Sort elements of the array
+ # provide optional property with which to sort an array of hashes or drops
+ def sort(input, property = nil)
+ ary = InputIterator.new(input)
+
+ return [] if ary.empty?
+
+ if property.nil?
+ ary.sort do |a, b|
+ nil_safe_compare(a, b)
+ end
+ elsif ary.all? { |el| el.respond_to?(:[]) }
+ begin
+ ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
+ rescue TypeError
+ raise_property_error(property)
+ end
+ end
+ end
+
+ # Sort elements of an array ignoring case if strings
+ # provide optional property with which to sort an array of hashes or drops
+ def sort_natural(input, property = nil)
+ ary = InputIterator.new(input)
+
+ return [] if ary.empty?
+
+ if property.nil?
+ ary.sort do |a, b|
+ nil_safe_casecmp(a, b)
+ end
+ elsif ary.all? { |el| el.respond_to?(:[]) }
+ begin
+ ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
+ rescue TypeError
+ raise_property_error(property)
+ end
+ end
+ end
+
+ # Filter the elements of an array to those with a certain property value.
+ # By default the target is any truthy value.
+ def where(input, property, target_value = nil)
+ ary = InputIterator.new(input)
+
+ if ary.empty?
+ []
+ elsif ary.first.respond_to?(:[]) && target_value.nil?
+ begin
+ ary.select { |item| item[property] }
+ rescue TypeError
+ raise_property_error(property)
+ end
+ elsif ary.first.respond_to?(:[])
+ begin
+ ary.select { |item| item[property] == target_value }
+ rescue TypeError
+ raise_property_error(property)
+ end
+ end
+ end
+
+ # Remove duplicate elements from an array
+ # provide optional property with which to determine uniqueness
+ def uniq(input, property = nil)
+ ary = InputIterator.new(input)
+
+ if property.nil?
+ ary.uniq
+ elsif ary.empty? # The next two cases assume a non-empty array.
+ []
+ elsif ary.first.respond_to?(:[])
+ begin
+ ary.uniq { |a| a[property] }
+ rescue TypeError
+ raise_property_error(property)
+ end
+ end
+ end
+
+ # Reverse the elements of an array
+ def reverse(input)
+ ary = InputIterator.new(input)
+ ary.reverse
+ end
+
+ # map/collect on a given property
+ def map(input, property)
+ InputIterator.new(input).map do |e|
+ e = e.call if e.is_a?(Proc)
+
+ if property == "to_liquid".freeze
+ e
+ elsif e.respond_to?(:[])
+ r = e[property]
+ r.is_a?(Proc) ? r.call : r
+ end
+ end
+ rescue TypeError
+ raise_property_error(property)
+ end
+
+ # Remove nils within an array
+ # provide optional property with which to check for nil
+ def compact(input, property = nil)
+ ary = InputIterator.new(input)
+
+ if property.nil?
+ ary.compact
+ elsif ary.empty? # The next two cases assume a non-empty array.
+ []
+ elsif ary.first.respond_to?(:[])
+ begin
+ ary.reject { |a| a[property].nil? }
+ rescue TypeError
+ raise_property_error(property)
+ end
+ end
+ end
+
+ # Replace occurrences of a string with another
+ def replace(input, string, replacement = ''.freeze)
+ input.to_s.gsub(string.to_s, replacement.to_s)
+ end
+
+ # Replace the first occurrences of a string with another
+ def replace_first(input, string, replacement = ''.freeze)
+ input.to_s.sub(string.to_s, replacement.to_s)
+ end
+
+ # remove a substring
+ def remove(input, string)
+ input.to_s.gsub(string.to_s, ''.freeze)
+ end
+
+ # remove the first occurrences of a substring
+ def remove_first(input, string)
+ input.to_s.sub(string.to_s, ''.freeze)
+ end
+
+ # add one string to another
+ def append(input, string)
+ input.to_s + string.to_s
+ end
+
+ def concat(input, array)
+ unless array.respond_to?(:to_ary)
+ raise ArgumentError.new("concat filter requires an array argument")
+ end
+ InputIterator.new(input).concat(array)
+ end
+
+ # prepend a string to another
+ def prepend(input, string)
+ string.to_s + input.to_s
+ end
+
+ # Add <br /> tags in front of all newlines in input string
+ def newline_to_br(input)
+ input.to_s.gsub(/\n/, "<br />\n".freeze)
+ end
+
+ # Reformat a date using Ruby's core Time#strftime( string ) -> string
+ #
+ # %a - The abbreviated weekday name (``Sun'')
+ # %A - The full weekday name (``Sunday'')
+ # %b - The abbreviated month name (``Jan'')
+ # %B - The full month name (``January'')
+ # %c - The preferred local date and time representation
+ # %d - Day of the month (01..31)
+ # %H - Hour of the day, 24-hour clock (00..23)
+ # %I - Hour of the day, 12-hour clock (01..12)
+ # %j - Day of the year (001..366)
+ # %m - Month of the year (01..12)
+ # %M - Minute of the hour (00..59)
+ # %p - Meridian indicator (``AM'' or ``PM'')
+ # %s - Number of seconds since 1970-01-01 00:00:00 UTC.
+ # %S - Second of the minute (00..60)
+ # %U - Week number of the current year,
+ # starting with the first Sunday as the first
+ # day of the first week (00..53)
+ # %W - Week number of the current year,
+ # starting with the first Monday as the first
+ # day of the first week (00..53)
+ # %w - Day of the week (Sunday is 0, 0..6)
+ # %x - Preferred representation for the date alone, no time
+ # %X - Preferred representation for the time alone, no date
+ # %y - Year without a century (00..99)
+ # %Y - Year with century
+ # %Z - Time zone name
+ # %% - Literal ``%'' character
+ #
+ # See also: http://www.ruby-doc.org/core/Time.html#method-i-strftime
+ def date(input, format)
+ return input if format.to_s.empty?
+
+ return input unless date = Utils.to_date(input)
+
+ date.strftime(format.to_s)
+ end
+
+ # Get the first element of the passed in array
+ #
+ # Example:
+ # {{ product.images | first | to_img }}
+ #
+ def first(array)
+ array.first if array.respond_to?(:first)
+ end
+
+ # Get the last element of the passed in array
+ #
+ # Example:
+ # {{ product.images | last | to_img }}
+ #
+ def last(array)
+ array.last if array.respond_to?(:last)
+ end
+
+ # absolute value
+ def abs(input)
+ result = Utils.to_number(input).abs
+ result.is_a?(BigDecimal) ? result.to_f : result
+ end
+
+ # addition
+ def plus(input, operand)
+ apply_operation(input, operand, :+)
+ end
+
+ # subtraction
+ def minus(input, operand)
+ apply_operation(input, operand, :-)
+ end
+
+ # multiplication
+ def times(input, operand)
+ apply_operation(input, operand, :*)
+ end
+
+ # division
+ def divided_by(input, operand)
+ apply_operation(input, operand, :/)
+ rescue ::ZeroDivisionError => e
+ raise Liquid::ZeroDivisionError, e.message
+ end
+
+ def modulo(input, operand)
+ apply_operation(input, operand, :%)
+ rescue ::ZeroDivisionError => e
+ raise Liquid::ZeroDivisionError, e.message
+ end
+
+ def round(input, n = 0)
+ result = Utils.to_number(input).round(Utils.to_number(n))
+ result = result.to_f if result.is_a?(BigDecimal)
+ result = result.to_i if n == 0
+ result
+ rescue ::FloatDomainError => e
+ raise Liquid::FloatDomainError, e.message
+ end
+
+ def ceil(input)
+ Utils.to_number(input).ceil.to_i
+ rescue ::FloatDomainError => e
+ raise Liquid::FloatDomainError, e.message
+ end
+
+ def floor(input)
+ Utils.to_number(input).floor.to_i
+ rescue ::FloatDomainError => e
+ raise Liquid::FloatDomainError, e.message
+ end
+
+ def at_least(input, n)
+ min_value = Utils.to_number(n)
+
+ result = Utils.to_number(input)
+ result = min_value if min_value > result
+ result.is_a?(BigDecimal) ? result.to_f : result
+ end
+
+ def at_most(input, n)
+ max_value = Utils.to_number(n)
+
+ result = Utils.to_number(input)
+ result = max_value if max_value < result
+ result.is_a?(BigDecimal) ? result.to_f : result
+ end
+
+ def default(input, default_value = ''.freeze)
+ if !input || input.respond_to?(:empty?) && input.empty?
+ default_value
+ else
+ input
+ end
+ end
+
+ private
+
+ def raise_property_error(property)
+ raise Liquid::ArgumentError.new("cannot select the property '#{property}'")
+ end
+
+ def apply_operation(input, operand, operation)
+ result = Utils.to_number(input).send(operation, Utils.to_number(operand))
+ result.is_a?(BigDecimal) ? result.to_f : result
+ end
+
+ def nil_safe_compare(a, b)
+ if !a.nil? && !b.nil?
+ a <=> b
+ else
+ a.nil? ? 1 : -1
+ end
+ end
+
+ def nil_safe_casecmp(a, b)
+ if !a.nil? && !b.nil?
+ a.to_s.casecmp(b.to_s)
+ else
+ a.nil? ? 1 : -1
+ end
+ end
+
+ class InputIterator
+ include Enumerable
+
+ def initialize(input)
+ @input = if input.is_a?(Array)
+ input.flatten
+ elsif input.is_a?(Hash)
+ [input]
+ elsif input.is_a?(Enumerable)
+ input
+ else
+ Array(input)
+ end
+ end
+
+ def join(glue)
+ to_a.join(glue.to_s)
+ end
+
+ def concat(args)
+ to_a.concat(args)
+ end
+
+ def reverse
+ reverse_each.to_a
+ end
+
+ def uniq(&block)
+ to_a.uniq(&block)
+ end
+
+ def compact
+ to_a.compact
+ end
+
+ def empty?
+ @input.each { return false }
+ true
+ end
+
+ def each
+ @input.each do |e|
+ yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
+ end
+ end
+ end
+ end
+
+ Template.register_filter(StandardFilters)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/strainer.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/strainer.rb
new file mode 100644
index 0000000..76d56d2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/strainer.rb
@@ -0,0 +1,66 @@
+require 'set'
+
+module Liquid
+ # Strainer is the parent class for the filters system.
+ # New filters are mixed into the strainer class which is then instantiated for each liquid template render run.
+ #
+ # The Strainer only allows method calls defined in filters given to it via Strainer.global_filter,
+ # Context#add_filters or Template.register_filter
+ class Strainer #:nodoc:
+ @@global_strainer = Class.new(Strainer) do
+ @filter_methods = Set.new
+ end
+ @@strainer_class_cache = Hash.new do |hash, filters|
+ hash[filters] = Class.new(@@global_strainer) do
+ @filter_methods = @@global_strainer.filter_methods.dup
+ filters.each { |f| add_filter(f) }
+ end
+ end
+
+ def initialize(context)
+ @context = context
+ end
+
+ class << self
+ attr_reader :filter_methods
+ end
+
+ def self.add_filter(filter)
+ raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
+ unless self.include?(filter)
+ invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
+ if invokable_non_public_methods.any?
+ raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
+ else
+ send(:include, filter)
+ @filter_methods.merge(filter.public_instance_methods.map(&:to_s))
+ end
+ end
+ end
+
+ def self.global_filter(filter)
+ @@strainer_class_cache.clear
+ @@global_strainer.add_filter(filter)
+ end
+
+ def self.invokable?(method)
+ @filter_methods.include?(method.to_s)
+ end
+
+ def self.create(context, filters = [])
+ @@strainer_class_cache[filters].new(context)
+ end
+
+ def invoke(method, *args)
+ if self.class.invokable?(method)
+ send(method, *args)
+ elsif @context && @context.strict_filters
+ raise Liquid::UndefinedFilter, "undefined filter #{method}"
+ else
+ args.first
+ end
+ rescue ::ArgumentError => e
+ raise Liquid::ArgumentError, e.message, e.backtrace
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tablerowloop_drop.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tablerowloop_drop.rb
new file mode 100644
index 0000000..cda4a1e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tablerowloop_drop.rb
@@ -0,0 +1,62 @@
+module Liquid
+ class TablerowloopDrop < Drop
+ def initialize(length, cols)
+ @length = length
+ @row = 1
+ @col = 1
+ @cols = cols
+ @index = 0
+ end
+
+ attr_reader :length, :col, :row
+
+ def index
+ @index + 1
+ end
+
+ def index0
+ @index
+ end
+
+ def col0
+ @col - 1
+ end
+
+ def rindex
+ @length - @index
+ end
+
+ def rindex0
+ @length - @index - 1
+ end
+
+ def first
+ @index == 0
+ end
+
+ def last
+ @index == @length - 1
+ end
+
+ def col_first
+ @col == 1
+ end
+
+ def col_last
+ @col == @cols
+ end
+
+ protected
+
+ def increment!
+ @index += 1
+
+ if @col == @cols
+ @col = 1
+ @row += 1
+ else
+ @col += 1
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tag.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tag.rb
new file mode 100644
index 0000000..06970c1
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tag.rb
@@ -0,0 +1,43 @@
+module Liquid
+ class Tag
+ attr_reader :nodelist, :tag_name, :line_number, :parse_context
+ alias_method :options, :parse_context
+ include ParserSwitching
+
+ class << self
+ def parse(tag_name, markup, tokenizer, options)
+ tag = new(tag_name, markup, options)
+ tag.parse(tokenizer)
+ tag
+ end
+
+ private :new
+ end
+
+ def initialize(tag_name, markup, parse_context)
+ @tag_name = tag_name
+ @markup = markup
+ @parse_context = parse_context
+ @line_number = parse_context.line_number
+ end
+
+ def parse(_tokens)
+ end
+
+ def raw
+ "#{@tag_name} #{@markup}"
+ end
+
+ def name
+ self.class.name.downcase
+ end
+
+ def render(_context)
+ ''.freeze
+ end
+
+ def blank?
+ false
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/assign.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/assign.rb
new file mode 100644
index 0000000..c8d0574
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/assign.rb
@@ -0,0 +1,59 @@
+module Liquid
+ # Assign sets a variable in your template.
+ #
+ # {% assign foo = 'monkey' %}
+ #
+ # You can then use the variable later in the page.
+ #
+ # {{ foo }}
+ #
+ class Assign < Tag
+ Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
+
+ attr_reader :to, :from
+
+ def initialize(tag_name, markup, options)
+ super
+ if markup =~ Syntax
+ @to = $1
+ @from = Variable.new($2, options)
+ else
+ raise SyntaxError.new options[:locale].t("errors.syntax.assign".freeze)
+ end
+ end
+
+ def render(context)
+ val = @from.render(context)
+ context.scopes.last[@to] = val
+ context.resource_limits.assign_score += assign_score_of(val)
+ ''.freeze
+ end
+
+ def blank?
+ true
+ end
+
+ private
+
+ def assign_score_of(val)
+ if val.instance_of?(String)
+ val.length
+ elsif val.instance_of?(Array) || val.instance_of?(Hash)
+ sum = 1
+ # Uses #each to avoid extra allocations.
+ val.each { |child| sum += assign_score_of(child) }
+ sum
+ else
+ 1
+ end
+ end
+
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
+ def children
+ [@node.from]
+ end
+ end
+ end
+
+ Template.register_tag('assign'.freeze, Assign)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/break.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/break.rb
new file mode 100644
index 0000000..6fe0969
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/break.rb
@@ -0,0 +1,18 @@
+module Liquid
+ # Break tag to be used to break out of a for loop.
+ #
+ # == Basic Usage:
+ # {% for item in collection %}
+ # {% if item.condition %}
+ # {% break %}
+ # {% endif %}
+ # {% endfor %}
+ #
+ class Break < Tag
+ def interrupt
+ BreakInterrupt.new
+ end
+ end
+
+ Template.register_tag('break'.freeze, Break)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/capture.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/capture.rb
new file mode 100644
index 0000000..8674356
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/capture.rb
@@ -0,0 +1,38 @@
+module Liquid
+ # Capture stores the result of a block into a variable without rendering it inplace.
+ #
+ # {% capture heading %}
+ # Monkeys!
+ # {% endcapture %}
+ # ...
+ # <h1>{{ heading }}</h1>
+ #
+ # Capture is useful for saving content for use later in your template, such as
+ # in a sidebar or footer.
+ #
+ class Capture < Block
+ Syntax = /(#{VariableSignature}+)/o
+
+ def initialize(tag_name, markup, options)
+ super
+ if markup =~ Syntax
+ @to = $1
+ else
+ raise SyntaxError.new(options[:locale].t("errors.syntax.capture"))
+ end
+ end
+
+ def render(context)
+ output = super
+ context.scopes.last[@to] = output
+ context.resource_limits.assign_score += output.length
+ ''.freeze
+ end
+
+ def blank?
+ true
+ end
+ end
+
+ Template.register_tag('capture'.freeze, Capture)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/case.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/case.rb
new file mode 100644
index 0000000..5036b27
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/case.rb
@@ -0,0 +1,94 @@
+module Liquid
+ class Case < Block
+ Syntax = /(#{QuotedFragment})/o
+ WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om
+
+ attr_reader :blocks, :left
+
+ def initialize(tag_name, markup, options)
+ super
+ @blocks = []
+
+ if markup =~ Syntax
+ @left = Expression.parse($1)
+ else
+ raise SyntaxError.new(options[:locale].t("errors.syntax.case".freeze))
+ end
+ end
+
+ def parse(tokens)
+ body = BlockBody.new
+ while parse_body(body, tokens)
+ body = @blocks.last.attachment
+ end
+ end
+
+ def nodelist
+ @blocks.map(&:attachment)
+ end
+
+ def unknown_tag(tag, markup, tokens)
+ case tag
+ when 'when'.freeze
+ record_when_condition(markup)
+ when 'else'.freeze
+ record_else_condition(markup)
+ else
+ super
+ end
+ end
+
+ def render(context)
+ context.stack do
+ execute_else_block = true
+
+ output = ''
+ @blocks.each do |block|
+ if block.else?
+ return block.attachment.render(context) if execute_else_block
+ elsif block.evaluate(context)
+ execute_else_block = false
+ output << block.attachment.render(context)
+ end
+ end
+ output
+ end
+ end
+
+ private
+
+ def record_when_condition(markup)
+ body = BlockBody.new
+
+ while markup
+ unless markup =~ WhenSyntax
+ raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_when".freeze))
+ end
+
+ markup = $2
+
+ block = Condition.new(@left, '=='.freeze, Expression.parse($1))
+ block.attach(body)
+ @blocks << block
+ end
+ end
+
+ def record_else_condition(markup)
+ unless markup.strip.empty?
+ raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_else".freeze))
+ end
+
+ block = ElseCondition.new
+ block.attach(BlockBody.new)
+ @blocks << block
+ end
+
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
+ def children
+ [@node.left] + @node.blocks
+ end
+ end
+ end
+
+ Template.register_tag('case'.freeze, Case)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/comment.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/comment.rb
new file mode 100644
index 0000000..c57c9cd
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/comment.rb
@@ -0,0 +1,16 @@
+module Liquid
+ class Comment < Block
+ def render(_context)
+ ''.freeze
+ end
+
+ def unknown_tag(_tag, _markup, _tokens)
+ end
+
+ def blank?
+ true
+ end
+ end
+
+ Template.register_tag('comment'.freeze, Comment)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/continue.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/continue.rb
new file mode 100644
index 0000000..9c81ec2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/continue.rb
@@ -0,0 +1,18 @@
+module Liquid
+ # Continue tag to be used to break out of a for loop.
+ #
+ # == Basic Usage:
+ # {% for item in collection %}
+ # {% if item.condition %}
+ # {% continue %}
+ # {% endif %}
+ # {% endfor %}
+ #
+ class Continue < Tag
+ def interrupt
+ ContinueInterrupt.new
+ end
+ end
+
+ Template.register_tag('continue'.freeze, Continue)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/cycle.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/cycle.rb
new file mode 100644
index 0000000..17aa860
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/cycle.rb
@@ -0,0 +1,65 @@
+module Liquid
+ # Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
+ #
+ # {% for item in items %}
+ # <div class="{% cycle 'red', 'green', 'blue' %}"> {{ item }} </div>
+ # {% end %}
+ #
+ # <div class="red"> Item one </div>
+ # <div class="green"> Item two </div>
+ # <div class="blue"> Item three </div>
+ # <div class="red"> Item four </div>
+ # <div class="green"> Item five</div>
+ #
+ class Cycle < Tag
+ SimpleSyntax = /\A#{QuotedFragment}+/o
+ NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
+
+ attr_reader :variables
+
+ def initialize(tag_name, markup, options)
+ super
+ case markup
+ when NamedSyntax
+ @variables = variables_from_string($2)
+ @name = Expression.parse($1)
+ when SimpleSyntax
+ @variables = variables_from_string(markup)
+ @name = @variables.to_s
+ else
+ raise SyntaxError.new(options[:locale].t("errors.syntax.cycle".freeze))
+ end
+ end
+
+ def render(context)
+ context.registers[:cycle] ||= {}
+
+ context.stack do
+ key = context.evaluate(@name)
+ iteration = context.registers[:cycle][key].to_i
+ result = context.evaluate(@variables[iteration])
+ iteration += 1
+ iteration = 0 if iteration >= @variables.size
+ context.registers[:cycle][key] = iteration
+ result
+ end
+ end
+
+ private
+
+ def variables_from_string(markup)
+ markup.split(',').collect do |var|
+ var =~ /\s*(#{QuotedFragment})\s*/o
+ $1 ? Expression.parse($1) : nil
+ end.compact
+ end
+
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
+ def children
+ Array(@node.variables)
+ end
+ end
+ end
+
+ Template.register_tag('cycle', Cycle)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/decrement.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/decrement.rb
new file mode 100644
index 0000000..b5cdaaa
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/decrement.rb
@@ -0,0 +1,35 @@
+module Liquid
+ # decrement is used in a place where one needs to insert a counter
+ # into a template, and needs the counter to survive across
+ # multiple instantiations of the template.
+ # NOTE: decrement is a pre-decrement, --i,
+ # while increment is post: i++.
+ #
+ # (To achieve the survival, the application must keep the context)
+ #
+ # if the variable does not exist, it is created with value 0.
+
+ # Hello: {% decrement variable %}
+ #
+ # gives you:
+ #
+ # Hello: -1
+ # Hello: -2
+ # Hello: -3
+ #
+ class Decrement < Tag
+ def initialize(tag_name, markup, options)
+ super
+ @variable = markup.strip
+ end
+
+ def render(context)
+ value = context.environments.first[@variable] ||= 0
+ value -= 1
+ context.environments.first[@variable] = value
+ value.to_s
+ end
+ end
+
+ Template.register_tag('decrement'.freeze, Decrement)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/for.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/for.rb
new file mode 100644
index 0000000..b69aa78
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/for.rb
@@ -0,0 +1,203 @@
+module Liquid
+ # "For" iterates over an array or collection.
+ # Several useful variables are available to you within the loop.
+ #
+ # == Basic usage:
+ # {% for item in collection %}
+ # {{ forloop.index }}: {{ item.name }}
+ # {% endfor %}
+ #
+ # == Advanced usage:
+ # {% for item in collection %}
+ # <div {% if forloop.first %}class="first"{% endif %}>
+ # Item {{ forloop.index }}: {{ item.name }}
+ # </div>
+ # {% else %}
+ # There is nothing in the collection.
+ # {% endfor %}
+ #
+ # You can also define a limit and offset much like SQL. Remember
+ # that offset starts at 0 for the first item.
+ #
+ # {% for item in collection limit:5 offset:10 %}
+ # {{ item.name }}
+ # {% end %}
+ #
+ # To reverse the for loop simply use {% for item in collection reversed %} (note that the flag's spelling is different to the filter `reverse`)
+ #
+ # == Available variables:
+ #
+ # forloop.name:: 'item-collection'
+ # forloop.length:: Length of the loop
+ # forloop.index:: The current item's position in the collection;
+ # forloop.index starts at 1.
+ # This is helpful for non-programmers who start believe
+ # the first item in an array is 1, not 0.
+ # forloop.index0:: The current item's position in the collection
+ # where the first item is 0
+ # forloop.rindex:: Number of items remaining in the loop
+ # (length - index) where 1 is the last item.
+ # forloop.rindex0:: Number of items remaining in the loop
+ # where 0 is the last item.
+ # forloop.first:: Returns true if the item is the first item.
+ # forloop.last:: Returns true if the item is the last item.
+ # forloop.parentloop:: Provides access to the parent loop, if present.
+ #
+ class For < Block
+ Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
+
+ attr_reader :collection_name, :variable_name, :limit, :from
+
+ def initialize(tag_name, markup, options)
+ super
+ @from = @limit = nil
+ parse_with_selected_parser(markup)
+ @for_block = BlockBody.new
+ @else_block = nil
+ end
+
+ def parse(tokens)
+ return unless parse_body(@for_block, tokens)
+ parse_body(@else_block, tokens)
+ end
+
+ def nodelist
+ @else_block ? [@for_block, @else_block] : [@for_block]
+ end
+
+ def unknown_tag(tag, markup, tokens)
+ return super unless tag == 'else'.freeze
+ @else_block = BlockBody.new
+ end
+
+ def render(context)
+ segment = collection_segment(context)
+
+ if segment.empty?
+ render_else(context)
+ else
+ render_segment(context, segment)
+ end
+ end
+
+ protected
+
+ def lax_parse(markup)
+ if markup =~ Syntax
+ @variable_name = $1
+ collection_name = $2
+ @reversed = !!$3
+ @name = "#{@variable_name}-#{collection_name}"
+ @collection_name = Expression.parse(collection_name)
+ markup.scan(TagAttributes) do |key, value|
+ set_attribute(key, value)
+ end
+ else
+ raise SyntaxError.new(options[:locale].t("errors.syntax.for".freeze))
+ end
+ end
+
+ def strict_parse(markup)
+ p = Parser.new(markup)
+ @variable_name = p.consume(:id)
+ raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_in".freeze)) unless p.id?('in'.freeze)
+ collection_name = p.expression
+ @name = "#{@variable_name}-#{collection_name}"
+ @collection_name = Expression.parse(collection_name)
+ @reversed = p.id?('reversed'.freeze)
+
+ while p.look(:id) && p.look(:colon, 1)
+ unless attribute = p.id?('limit'.freeze) || p.id?('offset'.freeze)
+ raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_attribute".freeze))
+ end
+ p.consume
+ set_attribute(attribute, p.expression)
+ end
+ p.consume(:end_of_string)
+ end
+
+ private
+
+ def collection_segment(context)
+ offsets = context.registers[:for] ||= {}
+
+ from = if @from == :continue
+ offsets[@name].to_i
+ else
+ context.evaluate(@from).to_i
+ end
+
+ collection = context.evaluate(@collection_name)
+ collection = collection.to_a if collection.is_a?(Range)
+
+ limit = context.evaluate(@limit)
+ to = limit ? limit.to_i + from : nil
+
+ segment = Utils.slice_collection(collection, from, to)
+ segment.reverse! if @reversed
+
+ offsets[@name] = from + segment.length
+
+ segment
+ end
+
+ def render_segment(context, segment)
+ for_stack = context.registers[:for_stack] ||= []
+ length = segment.length
+
+ result = ''
+
+ context.stack do
+ loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
+
+ for_stack.push(loop_vars)
+
+ begin
+ context['forloop'.freeze] = loop_vars
+
+ segment.each do |item|
+ context[@variable_name] = item
+ result << @for_block.render(context)
+ loop_vars.send(:increment!)
+
+ # Handle any interrupts if they exist.
+ if context.interrupt?
+ interrupt = context.pop_interrupt
+ break if interrupt.is_a? BreakInterrupt
+ next if interrupt.is_a? ContinueInterrupt
+ end
+ end
+ ensure
+ for_stack.pop
+ end
+ end
+
+ result
+ end
+
+ def set_attribute(key, expr)
+ case key
+ when 'offset'.freeze
+ @from = if expr == 'continue'.freeze
+ :continue
+ else
+ Expression.parse(expr)
+ end
+ when 'limit'.freeze
+ @limit = Expression.parse(expr)
+ end
+ end
+
+ def render_else(context)
+ @else_block ? @else_block.render(context) : ''.freeze
+ end
+
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
+ def children
+ (super + [@node.limit, @node.from, @node.collection_name]).compact
+ end
+ end
+ end
+
+ Template.register_tag('for'.freeze, For)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/if.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/if.rb
new file mode 100644
index 0000000..02da42b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/if.rb
@@ -0,0 +1,122 @@
+module Liquid
+ # If is the conditional block
+ #
+ # {% if user.admin %}
+ # Admin user!
+ # {% else %}
+ # Not admin user
+ # {% endif %}
+ #
+ # There are {% if count < 5 %} less {% else %} more {% endif %} items than you need.
+ #
+ class If < Block
+ Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
+ ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
+ BOOLEAN_OPERATORS = %w(and or).freeze
+
+ attr_reader :blocks
+
+ def initialize(tag_name, markup, options)
+ super
+ @blocks = []
+ push_block('if'.freeze, markup)
+ end
+
+ def nodelist
+ @blocks.map(&:attachment)
+ end
+
+ def parse(tokens)
+ while parse_body(@blocks.last.attachment, tokens)
+ end
+ end
+
+ def unknown_tag(tag, markup, tokens)
+ if ['elsif'.freeze, 'else'.freeze].include?(tag)
+ push_block(tag, markup)
+ else
+ super
+ end
+ end
+
+ def render(context)
+ context.stack do
+ @blocks.each do |block|
+ if block.evaluate(context)
+ return block.attachment.render(context)
+ end
+ end
+ ''.freeze
+ end
+ end
+
+ private
+
+ def push_block(tag, markup)
+ block = if tag == 'else'.freeze
+ ElseCondition.new
+ else
+ parse_with_selected_parser(markup)
+ end
+
+ @blocks.push(block)
+ block.attach(BlockBody.new)
+ end
+
+ def lax_parse(markup)
+ expressions = markup.scan(ExpressionsAndOperators)
+ raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop =~ Syntax
+
+ condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
+
+ until expressions.empty?
+ operator = expressions.pop.to_s.strip
+
+ raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop.to_s =~ Syntax
+
+ new_condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
+ raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless BOOLEAN_OPERATORS.include?(operator)
+ new_condition.send(operator, condition)
+ condition = new_condition
+ end
+
+ condition
+ end
+
+ def strict_parse(markup)
+ p = Parser.new(markup)
+ condition = parse_binary_comparisons(p)
+ p.consume(:end_of_string)
+ condition
+ end
+
+ def parse_binary_comparisons(p)
+ condition = parse_comparison(p)
+ first_condition = condition
+ while op = (p.id?('and'.freeze) || p.id?('or'.freeze))
+ child_condition = parse_comparison(p)
+ condition.send(op, child_condition)
+ condition = child_condition
+ end
+ first_condition
+ end
+
+ def parse_comparison(p)
+ a = Expression.parse(p.expression)
+ if op = p.consume?(:comparison)
+ b = Expression.parse(p.expression)
+ Condition.new(a, op, b)
+ else
+ Condition.new(a)
+ end
+ end
+
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
+ def children
+ @node.blocks
+ end
+ end
+ end
+
+ Template.register_tag('if'.freeze, If)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/ifchanged.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/ifchanged.rb
new file mode 100644
index 0000000..d70cbe1
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/ifchanged.rb
@@ -0,0 +1,18 @@
+module Liquid
+ class Ifchanged < Block
+ def render(context)
+ context.stack do
+ output = super
+
+ if output != context.registers[:ifchanged]
+ context.registers[:ifchanged] = output
+ output
+ else
+ ''.freeze
+ end
+ end
+ end
+ end
+
+ Template.register_tag('ifchanged'.freeze, Ifchanged)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/include.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/include.rb
new file mode 100644
index 0000000..c9f2a28
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/include.rb
@@ -0,0 +1,124 @@
+module Liquid
+ # Include allows templates to relate with other templates
+ #
+ # Simply include another template:
+ #
+ # {% include 'product' %}
+ #
+ # Include a template with a local variable:
+ #
+ # {% include 'product' with products[0] %}
+ #
+ # Include a template for a collection:
+ #
+ # {% include 'product' for products %}
+ #
+ class Include < Tag
+ Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o
+
+ attr_reader :template_name_expr, :variable_name_expr, :attributes
+
+ def initialize(tag_name, markup, options)
+ super
+
+ if markup =~ Syntax
+
+ template_name = $1
+ variable_name = $3
+
+ @variable_name_expr = variable_name ? Expression.parse(variable_name) : nil
+ @template_name_expr = Expression.parse(template_name)
+ @attributes = {}
+
+ markup.scan(TagAttributes) do |key, value|
+ @attributes[key] = Expression.parse(value)
+ end
+
+ else
+ raise SyntaxError.new(options[:locale].t("errors.syntax.include".freeze))
+ end
+ end
+
+ def parse(_tokens)
+ end
+
+ def render(context)
+ template_name = context.evaluate(@template_name_expr)
+ raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
+
+ partial = load_cached_partial(template_name, context)
+ context_variable_name = template_name.split('/'.freeze).last
+
+ variable = if @variable_name_expr
+ context.evaluate(@variable_name_expr)
+ else
+ context.find_variable(template_name, raise_on_not_found: false)
+ end
+
+ old_template_name = context.template_name
+ old_partial = context.partial
+ begin
+ context.template_name = template_name
+ context.partial = true
+ context.stack do
+ @attributes.each do |key, value|
+ context[key] = context.evaluate(value)
+ end
+
+ if variable.is_a?(Array)
+ variable.collect do |var|
+ context[context_variable_name] = var
+ partial.render(context)
+ end
+ else
+ context[context_variable_name] = variable
+ partial.render(context)
+ end
+ end
+ ensure
+ context.template_name = old_template_name
+ context.partial = old_partial
+ end
+ end
+
+ private
+
+ alias_method :parse_context, :options
+ private :parse_context
+
+ def load_cached_partial(template_name, context)
+ cached_partials = context.registers[:cached_partials] || {}
+
+ if cached = cached_partials[template_name]
+ return cached
+ end
+ source = read_template_from_file_system(context)
+ begin
+ parse_context.partial = true
+ partial = Liquid::Template.parse(source, parse_context)
+ ensure
+ parse_context.partial = false
+ end
+ cached_partials[template_name] = partial
+ context.registers[:cached_partials] = cached_partials
+ partial
+ end
+
+ def read_template_from_file_system(context)
+ file_system = context.registers[:file_system] || Liquid::Template.file_system
+
+ file_system.read_template_file(context.evaluate(@template_name_expr))
+ end
+
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
+ def children
+ [
+ @node.template_name_expr,
+ @node.variable_name_expr
+ ] + @node.attributes.values
+ end
+ end
+ end
+
+ Template.register_tag('include'.freeze, Include)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/increment.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/increment.rb
new file mode 100644
index 0000000..baa0cbb
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/increment.rb
@@ -0,0 +1,31 @@
+module Liquid
+ # increment is used in a place where one needs to insert a counter
+ # into a template, and needs the counter to survive across
+ # multiple instantiations of the template.
+ # (To achieve the survival, the application must keep the context)
+ #
+ # if the variable does not exist, it is created with value 0.
+ #
+ # Hello: {% increment variable %}
+ #
+ # gives you:
+ #
+ # Hello: 0
+ # Hello: 1
+ # Hello: 2
+ #
+ class Increment < Tag
+ def initialize(tag_name, markup, options)
+ super
+ @variable = markup.strip
+ end
+
+ def render(context)
+ value = context.environments.first[@variable] ||= 0
+ context.environments.first[@variable] = value + 1
+ value.to_s
+ end
+ end
+
+ Template.register_tag('increment'.freeze, Increment)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/raw.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/raw.rb
new file mode 100644
index 0000000..6b461bd
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/raw.rb
@@ -0,0 +1,47 @@
+module Liquid
+ class Raw < Block
+ Syntax = /\A\s*\z/
+ FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
+
+ def initialize(tag_name, markup, parse_context)
+ super
+
+ ensure_valid_markup(tag_name, markup, parse_context)
+ end
+
+ def parse(tokens)
+ @body = ''
+ while token = tokens.shift
+ if token =~ FullTokenPossiblyInvalid
+ @body << $1 if $1 != "".freeze
+ return if block_delimiter == $2
+ end
+ @body << token unless token.empty?
+ end
+
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
+ end
+
+ def render(_context)
+ @body
+ end
+
+ def nodelist
+ [@body]
+ end
+
+ def blank?
+ @body.empty?
+ end
+
+ protected
+
+ def ensure_valid_markup(tag_name, markup, parse_context)
+ unless markup =~ Syntax
+ raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_unexpected_args".freeze, tag: tag_name))
+ end
+ end
+ end
+
+ Template.register_tag('raw'.freeze, Raw)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/table_row.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/table_row.rb
new file mode 100644
index 0000000..7f391cf
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/table_row.rb
@@ -0,0 +1,62 @@
+module Liquid
+ class TableRow < Block
+ Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
+
+ attr_reader :variable_name, :collection_name, :attributes
+
+ def initialize(tag_name, markup, options)
+ super
+ if markup =~ Syntax
+ @variable_name = $1
+ @collection_name = Expression.parse($2)
+ @attributes = {}
+ markup.scan(TagAttributes) do |key, value|
+ @attributes[key] = Expression.parse(value)
+ end
+ else
+ raise SyntaxError.new(options[:locale].t("errors.syntax.table_row".freeze))
+ end
+ end
+
+ def render(context)
+ collection = context.evaluate(@collection_name) or return ''.freeze
+
+ from = @attributes.key?('offset'.freeze) ? context.evaluate(@attributes['offset'.freeze]).to_i : 0
+ to = @attributes.key?('limit'.freeze) ? from + context.evaluate(@attributes['limit'.freeze]).to_i : nil
+
+ collection = Utils.slice_collection(collection, from, to)
+
+ length = collection.length
+
+ cols = context.evaluate(@attributes['cols'.freeze]).to_i
+
+ result = "<tr class=\"row1\">\n"
+ context.stack do
+ tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
+ context['tablerowloop'.freeze] = tablerowloop
+
+ collection.each do |item|
+ context[@variable_name] = item
+
+ result << "<td class=\"col#{tablerowloop.col}\">" << super << '</td>'
+
+ if tablerowloop.col_last && !tablerowloop.last
+ result << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
+ end
+
+ tablerowloop.send(:increment!)
+ end
+ end
+ result << "</tr>\n"
+ result
+ end
+
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
+ def children
+ super + @node.attributes.values + [@node.collection_name]
+ end
+ end
+ end
+
+ Template.register_tag('tablerow'.freeze, TableRow)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/unless.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/unless.rb
new file mode 100644
index 0000000..1d4280d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tags/unless.rb
@@ -0,0 +1,30 @@
+require_relative 'if'
+
+module Liquid
+ # Unless is a conditional just like 'if' but works on the inverse logic.
+ #
+ # {% unless x < 0 %} x is greater than zero {% endunless %}
+ #
+ class Unless < If
+ def render(context)
+ context.stack do
+ # First condition is interpreted backwards ( if not )
+ first_block = @blocks.first
+ unless first_block.evaluate(context)
+ return first_block.attachment.render(context)
+ end
+
+ # After the first condition unless works just like if
+ @blocks[1..-1].each do |block|
+ if block.evaluate(context)
+ return block.attachment.render(context)
+ end
+ end
+
+ ''.freeze
+ end
+ end
+ end
+
+ Template.register_tag('unless'.freeze, Unless)
+end
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
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tokenizer.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tokenizer.rb
new file mode 100644
index 0000000..d03657e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/tokenizer.rb
@@ -0,0 +1,31 @@
+module Liquid
+ class Tokenizer
+ attr_reader :line_number
+
+ def initialize(source, line_numbers = false)
+ @source = source
+ @line_number = line_numbers ? 1 : nil
+ @tokens = tokenize
+ end
+
+ def shift
+ token = @tokens.shift
+ @line_number += token.count("\n") if @line_number && token
+ token
+ end
+
+ private
+
+ def tokenize
+ @source = @source.source if @source.respond_to?(:source)
+ return [] if @source.to_s.empty?
+
+ tokens = @source.split(TemplateParser)
+
+ # removes the rogue empty element at the beginning of the array
+ tokens.shift if tokens[0] && tokens[0].empty?
+
+ tokens
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/utils.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/utils.rb
new file mode 100644
index 0000000..516ac0c
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/utils.rb
@@ -0,0 +1,83 @@
+module Liquid
+ module Utils
+ def self.slice_collection(collection, from, to)
+ if (from != 0 || !to.nil?) && collection.respond_to?(:load_slice)
+ collection.load_slice(from, to)
+ else
+ slice_collection_using_each(collection, from, to)
+ end
+ end
+
+ def self.slice_collection_using_each(collection, from, to)
+ segments = []
+ index = 0
+
+ # Maintains Ruby 1.8.7 String#each behaviour on 1.9
+ if collection.is_a?(String)
+ return collection.empty? ? [] : [collection]
+ end
+ return [] unless collection.respond_to?(:each)
+
+ collection.each do |item|
+ if to && to <= index
+ break
+ end
+
+ if from <= index
+ segments << item
+ end
+
+ index += 1
+ end
+
+ segments
+ end
+
+ def self.to_integer(num)
+ return num if num.is_a?(Integer)
+ num = num.to_s
+ begin
+ Integer(num)
+ rescue ::ArgumentError
+ raise Liquid::ArgumentError, "invalid integer"
+ end
+ end
+
+ def self.to_number(obj)
+ case obj
+ when Float
+ BigDecimal(obj.to_s)
+ when Numeric
+ obj
+ when String
+ (obj.strip =~ /\A-?\d+\.\d+\z/) ? BigDecimal(obj) : obj.to_i
+ else
+ if obj.respond_to?(:to_number)
+ obj.to_number
+ else
+ 0
+ end
+ end
+ end
+
+ def self.to_date(obj)
+ return obj if obj.respond_to?(:strftime)
+
+ if obj.is_a?(String)
+ return nil if obj.empty?
+ obj = obj.downcase
+ end
+
+ case obj
+ when 'now'.freeze, 'today'.freeze
+ Time.now
+ when /\A\d+\z/, Integer
+ Time.at(obj.to_i)
+ when String
+ Time.parse(obj)
+ end
+ rescue ::ArgumentError
+ nil
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/variable.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/variable.rb
new file mode 100644
index 0000000..8d63eb1
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/variable.rb
@@ -0,0 +1,125 @@
+module Liquid
+ # Holds variables. Variables are only loaded "just in time"
+ # and are not evaluated as part of the render stage
+ #
+ # {{ monkey }}
+ # {{ user.name }}
+ #
+ # Variables can be combined with filters:
+ #
+ # {{ user | link }}
+ #
+ class Variable
+ FilterMarkupRegex = /#{FilterSeparator}\s*(.*)/om
+ FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
+ FilterArgsRegex = /(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o
+ JustTagAttributes = /\A#{TagAttributes}\z/o
+ MarkupWithQuotedFragment = /(#{QuotedFragment})(.*)/om
+
+ attr_accessor :filters, :name, :line_number
+ attr_reader :parse_context
+ alias_method :options, :parse_context
+
+ include ParserSwitching
+
+ def initialize(markup, parse_context)
+ @markup = markup
+ @name = nil
+ @parse_context = parse_context
+ @line_number = parse_context.line_number
+
+ parse_with_selected_parser(markup)
+ end
+
+ def raw
+ @markup
+ end
+
+ def markup_context(markup)
+ "in \"{{#{markup}}}\""
+ end
+
+ def lax_parse(markup)
+ @filters = []
+ return unless markup =~ MarkupWithQuotedFragment
+
+ name_markup = $1
+ filter_markup = $2
+ @name = Expression.parse(name_markup)
+ if filter_markup =~ FilterMarkupRegex
+ filters = $1.scan(FilterParser)
+ filters.each do |f|
+ next unless f =~ /\w+/
+ filtername = Regexp.last_match(0)
+ filterargs = f.scan(FilterArgsRegex).flatten
+ @filters << parse_filter_expressions(filtername, filterargs)
+ end
+ end
+ end
+
+ def strict_parse(markup)
+ @filters = []
+ p = Parser.new(markup)
+
+ @name = Expression.parse(p.expression)
+ while p.consume?(:pipe)
+ filtername = p.consume(:id)
+ filterargs = p.consume?(:colon) ? parse_filterargs(p) : []
+ @filters << parse_filter_expressions(filtername, filterargs)
+ end
+ p.consume(:end_of_string)
+ end
+
+ def parse_filterargs(p)
+ # first argument
+ filterargs = [p.argument]
+ # followed by comma separated others
+ filterargs << p.argument while p.consume?(:comma)
+ filterargs
+ end
+
+ def render(context)
+ obj = @filters.inject(context.evaluate(@name)) do |output, (filter_name, filter_args, filter_kwargs)|
+ filter_args = evaluate_filter_expressions(context, filter_args, filter_kwargs)
+ context.invoke(filter_name, output, *filter_args)
+ end
+
+ context.apply_global_filter(obj)
+ end
+
+ private
+
+ def parse_filter_expressions(filter_name, unparsed_args)
+ filter_args = []
+ keyword_args = {}
+ unparsed_args.each do |a|
+ if matches = a.match(JustTagAttributes)
+ keyword_args[matches[1]] = Expression.parse(matches[2])
+ else
+ filter_args << Expression.parse(a)
+ end
+ end
+ result = [filter_name, filter_args]
+ result << keyword_args unless keyword_args.empty?
+ result
+ end
+
+ def evaluate_filter_expressions(context, filter_args, filter_kwargs)
+ parsed_args = filter_args.map{ |expr| context.evaluate(expr) }
+ if filter_kwargs
+ parsed_kwargs = {}
+ filter_kwargs.each do |key, expr|
+ parsed_kwargs[key] = context.evaluate(expr)
+ end
+ parsed_args << parsed_kwargs
+ end
+ parsed_args
+ end
+
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
+ def children
+ [@node.name] + @node.filters.flatten
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/variable_lookup.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/variable_lookup.rb
new file mode 100644
index 0000000..62f4877
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/variable_lookup.rb
@@ -0,0 +1,88 @@
+module Liquid
+ class VariableLookup
+ SQUARE_BRACKETED = /\A\[(.*)\]\z/m
+ COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze].freeze
+
+ attr_reader :name, :lookups
+
+ def self.parse(markup)
+ new(markup)
+ end
+
+ def initialize(markup)
+ lookups = markup.scan(VariableParser)
+
+ name = lookups.shift
+ if name =~ SQUARE_BRACKETED
+ name = Expression.parse($1)
+ end
+ @name = name
+
+ @lookups = lookups
+ @command_flags = 0
+
+ @lookups.each_index do |i|
+ lookup = lookups[i]
+ if lookup =~ SQUARE_BRACKETED
+ lookups[i] = Expression.parse($1)
+ elsif COMMAND_METHODS.include?(lookup)
+ @command_flags |= 1 << i
+ end
+ end
+ end
+
+ def evaluate(context)
+ name = context.evaluate(@name)
+ object = context.find_variable(name)
+
+ @lookups.each_index do |i|
+ key = context.evaluate(@lookups[i])
+
+ # If object is a hash- or array-like object we look for the
+ # presence of the key and if its available we return it
+ if object.respond_to?(:[]) &&
+ ((object.respond_to?(:key?) && object.key?(key)) ||
+ (object.respond_to?(:fetch) && key.is_a?(Integer)))
+
+ # if its a proc we will replace the entry with the proc
+ res = context.lookup_and_evaluate(object, key)
+ object = res.to_liquid
+
+ # Some special cases. If the part wasn't in square brackets and
+ # no key with the same name was found we interpret following calls
+ # as commands and call them on the current object
+ elsif @command_flags & (1 << i) != 0 && object.respond_to?(key)
+ object = object.send(key).to_liquid
+
+ # No key was present with the desired value and it wasn't one of the directly supported
+ # keywords either. The only thing we got left is to return nil or
+ # raise an exception if `strict_variables` option is set to true
+ else
+ return nil unless context.strict_variables
+ raise Liquid::UndefinedVariable, "undefined variable #{key}"
+ end
+
+ # If we are dealing with a drop here we have to
+ object.context = context if object.respond_to?(:context=)
+ end
+
+ object
+ end
+
+ def ==(other)
+ self.class == other.class && state == other.state
+ end
+
+ protected
+
+ def state
+ [@name, @lookups, @command_flags]
+ end
+
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
+ def children
+ @node.lookups
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/version.rb b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/version.rb
new file mode 100644
index 0000000..9799863
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/liquid-4.0.4/lib/liquid/version.rb
@@ -0,0 +1,5 @@
+# encoding: utf-8
+
+module Liquid
+ VERSION = "4.0.4".freeze
+end