Full Code of Shopify/liquid for AI

main dd37353cca46 cached
204 files
720.9 KB
203.7k tokens
2031 symbols
1 requests
Download .txt
Showing preview only (773K chars total). Download the full file or copy to clipboard to get everything.
Repository: Shopify/liquid
Branch: main
Commit: dd37353cca46
Files: 204
Total size: 720.9 KB

Directory structure:
gitextract_1on2h7q1/

├── .github/
│   ├── dependabot.yaml
│   └── workflows/
│       ├── cla.yml
│       └── liquid.yml
├── .gitignore
├── .rubocop.yml
├── .rubocop_todo.yml
├── .ruby-version
├── CONTRIBUTING.md
├── Gemfile
├── History.md
├── LICENSE
├── README.md
├── Rakefile
├── bin/
│   └── render
├── example/
│   └── server/
│       ├── example_servlet.rb
│       ├── liquid_servlet.rb
│       ├── server.rb
│       └── templates/
│           ├── index.liquid
│           └── products.liquid
├── lib/
│   ├── liquid/
│   │   ├── block.rb
│   │   ├── block_body.rb
│   │   ├── condition.rb
│   │   ├── const.rb
│   │   ├── context.rb
│   │   ├── deprecations.rb
│   │   ├── document.rb
│   │   ├── drop.rb
│   │   ├── environment.rb
│   │   ├── errors.rb
│   │   ├── expression.rb
│   │   ├── extensions.rb
│   │   ├── file_system.rb
│   │   ├── forloop_drop.rb
│   │   ├── i18n.rb
│   │   ├── interrupts.rb
│   │   ├── lexer.rb
│   │   ├── locales/
│   │   │   └── en.yml
│   │   ├── parse_context.rb
│   │   ├── parse_tree_visitor.rb
│   │   ├── parser.rb
│   │   ├── parser_switching.rb
│   │   ├── partial_cache.rb
│   │   ├── profiler/
│   │   │   └── hooks.rb
│   │   ├── profiler.rb
│   │   ├── range_lookup.rb
│   │   ├── registers.rb
│   │   ├── resource_limits.rb
│   │   ├── standardfilters.rb
│   │   ├── strainer_template.rb
│   │   ├── tablerowloop_drop.rb
│   │   ├── tag/
│   │   │   ├── disableable.rb
│   │   │   └── disabler.rb
│   │   ├── tag.rb
│   │   ├── tags/
│   │   │   ├── assign.rb
│   │   │   ├── break.rb
│   │   │   ├── capture.rb
│   │   │   ├── case.rb
│   │   │   ├── comment.rb
│   │   │   ├── continue.rb
│   │   │   ├── cycle.rb
│   │   │   ├── decrement.rb
│   │   │   ├── doc.rb
│   │   │   ├── echo.rb
│   │   │   ├── for.rb
│   │   │   ├── if.rb
│   │   │   ├── ifchanged.rb
│   │   │   ├── include.rb
│   │   │   ├── increment.rb
│   │   │   ├── inline_comment.rb
│   │   │   ├── raw.rb
│   │   │   ├── render.rb
│   │   │   ├── table_row.rb
│   │   │   └── unless.rb
│   │   ├── tags.rb
│   │   ├── template.rb
│   │   ├── template_factory.rb
│   │   ├── tokenizer.rb
│   │   ├── usage.rb
│   │   ├── utils.rb
│   │   ├── variable.rb
│   │   ├── variable_lookup.rb
│   │   └── version.rb
│   └── liquid.rb
├── liquid.gemspec
├── performance/
│   ├── benchmark.rb
│   ├── memory_profile.rb
│   ├── profile.rb
│   ├── shopify/
│   │   ├── comment_form.rb
│   │   ├── database.rb
│   │   ├── json_filter.rb
│   │   ├── liquid.rb
│   │   ├── money_filter.rb
│   │   ├── paginate.rb
│   │   ├── shop_filter.rb
│   │   ├── tag_filter.rb
│   │   ├── vision.database.yml
│   │   └── weight_filter.rb
│   ├── tests/
│   │   ├── dropify/
│   │   │   ├── article.liquid
│   │   │   ├── blog.liquid
│   │   │   ├── cart.liquid
│   │   │   ├── collection.liquid
│   │   │   ├── index.liquid
│   │   │   ├── page.liquid
│   │   │   ├── product.liquid
│   │   │   └── theme.liquid
│   │   ├── ripen/
│   │   │   ├── article.liquid
│   │   │   ├── blog.liquid
│   │   │   ├── cart.liquid
│   │   │   ├── collection.liquid
│   │   │   ├── index.liquid
│   │   │   ├── page.liquid
│   │   │   ├── product.liquid
│   │   │   └── theme.liquid
│   │   ├── tribble/
│   │   │   ├── 404.liquid
│   │   │   ├── article.liquid
│   │   │   ├── blog.liquid
│   │   │   ├── cart.liquid
│   │   │   ├── collection.liquid
│   │   │   ├── index.liquid
│   │   │   ├── page.liquid
│   │   │   ├── product.liquid
│   │   │   ├── search.liquid
│   │   │   └── theme.liquid
│   │   └── vogue/
│   │       ├── article.liquid
│   │       ├── blog.liquid
│   │       ├── cart.liquid
│   │       ├── collection.liquid
│   │       ├── index.liquid
│   │       ├── page.liquid
│   │       ├── product.liquid
│   │       └── theme.liquid
│   ├── theme_runner.rb
│   └── unit/
│       ├── expression_benchmark.rb
│       └── lexer_benchmark.rb
├── spec/
│   ├── ruby_liquid.rb
│   ├── ruby_liquid_lax.rb
│   ├── ruby_liquid_with_active_support.rb
│   └── ruby_liquid_yjit.rb
└── test/
    ├── fixtures/
    │   └── en_locale.yml
    ├── integration/
    │   ├── assign_test.rb
    │   ├── blank_test.rb
    │   ├── block_test.rb
    │   ├── capture_test.rb
    │   ├── context_test.rb
    │   ├── document_test.rb
    │   ├── drop_test.rb
    │   ├── error_handling_test.rb
    │   ├── expression_test.rb
    │   ├── filter_kwarg_test.rb
    │   ├── filter_test.rb
    │   ├── hash_ordering_test.rb
    │   ├── hash_rendering_test.rb
    │   ├── output_test.rb
    │   ├── parsing_quirks_test.rb
    │   ├── profiler_test.rb
    │   ├── security_test.rb
    │   ├── standard_filter_test.rb
    │   ├── tag/
    │   │   └── disableable_test.rb
    │   ├── tag_test.rb
    │   ├── tags/
    │   │   ├── break_tag_test.rb
    │   │   ├── continue_tag_test.rb
    │   │   ├── cycle_tag_test.rb
    │   │   ├── echo_test.rb
    │   │   ├── for_tag_test.rb
    │   │   ├── if_else_tag_test.rb
    │   │   ├── include_tag_test.rb
    │   │   ├── increment_tag_test.rb
    │   │   ├── inline_comment_test.rb
    │   │   ├── liquid_tag_test.rb
    │   │   ├── raw_tag_test.rb
    │   │   ├── render_tag_test.rb
    │   │   ├── standard_tag_test.rb
    │   │   ├── statements_test.rb
    │   │   ├── table_row_test.rb
    │   │   └── unless_else_tag_test.rb
    │   ├── template_test.rb
    │   ├── trim_mode_test.rb
    │   └── variable_test.rb
    ├── test_helper.rb
    └── unit/
        ├── block_unit_test.rb
        ├── condition_unit_test.rb
        ├── environment_filter_test.rb
        ├── environment_test.rb
        ├── file_system_unit_test.rb
        ├── i18n_unit_test.rb
        ├── lexer_unit_test.rb
        ├── parse_context_unit_test.rb
        ├── parse_tree_visitor_test.rb
        ├── parser_unit_test.rb
        ├── partial_cache_unit_test.rb
        ├── regexp_unit_test.rb
        ├── registers_unit_test.rb
        ├── resource_limits_unit_test.rb
        ├── strainer_template_unit_test.rb
        ├── tag_unit_test.rb
        ├── tags/
        │   ├── case_tag_unit_test.rb
        │   ├── comment_tag_unit_test.rb
        │   ├── doc_tag_unit_test.rb
        │   ├── for_tag_unit_test.rb
        │   └── if_tag_unit_test.rb
        ├── template_factory_unit_test.rb
        ├── template_unit_test.rb
        ├── tokenizer_unit_test.rb
        └── variable_unit_test.rb

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/dependabot.yaml
================================================
version: 2
updates:
  - package-ecosystem: github-actions
    directory: "/"
    schedule:
      interval: weekly


================================================
FILE: .github/workflows/cla.yml
================================================
name: Contributor License Agreement (CLA)

on:
  pull_request_target:
    types: [opened, synchronize]
  issue_comment:
    types: [created]

jobs:
  cla:
    runs-on: ubuntu-latest
    if: |
      (github.event.issue.pull_request 
        && !github.event.issue.pull_request.merged_at
        && contains(github.event.comment.body, 'signed')
      ) 
      || (github.event.pull_request && !github.event.pull_request.merged)
    steps:
      - uses: Shopify/shopify-cla-action@v1
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          cla-token: ${{ secrets.CLA_TOKEN }}


================================================
FILE: .github/workflows/liquid.yml
================================================
name: Liquid
on: [push]

env:
  BUNDLE_JOBS: 4
  BUNDLE_RETRY: 3

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        entry:
          - { ruby: 3.3, allowed-failure: false } # minimum supported
          - { ruby: 3.4, allowed-failure: false, rubyopt: "--yjit" }
          - { ruby: 4.0, allowed-failure: false } # latest stable
          - {
              ruby: 4.0,
              allowed-failure: false,
              rubyopt: "--enable-frozen-string-literal",
            }
          - { ruby: 4.0, allowed-failure: false, rubyopt: "--yjit" }
          - { ruby: 4.0, allowed-failure: false, rubyopt: "--zjit" }

          # Head can have failures due to being in development
          - { ruby: head, allowed-failure: true }
          - {
              ruby: head,
              allowed-failure: true,
              rubyopt: "--enable-frozen-string-literal",
            }
          - { ruby: head, allowed-failure: true, rubyopt: "--yjit" }
          - { ruby: head, allowed-failure: true, rubyopt: "--zjit" }
    name: Test Ruby ${{ matrix.entry.ruby }} ${{ matrix.entry.rubyopt }} --${{ matrix.entry.allowed-failure && 'allowed-failure' || 'strict' }}
    steps:
      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
      - uses: ruby/setup-ruby@319994f95fa847cf3fb3cd3dbe89f6dcde9f178f # v1.295.0
        with:
          ruby-version: ${{ matrix.entry.ruby }}
          bundler-cache: true
          bundler: latest
      - run: bundle exec rake
        continue-on-error: ${{ matrix.entry.allowed-failure }}
        env:
          RUBYOPT: ${{ matrix.entry.rubyopt }}

  spec:
    runs-on: ubuntu-latest
    env:
      BUNDLE_WITH: spec
    steps:
      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
      - uses: ruby/setup-ruby@319994f95fa847cf3fb3cd3dbe89f6dcde9f178f # v1.295.0
        with:
          bundler-cache: true
          bundler: latest
      - name: Run liquid-spec for all adapters
        run: |
          for adapter in spec/*.rb; do
            echo "=== Running $adapter ==="
            bundle exec liquid-spec run "$adapter" --no-max-failures
          done

  memory_profile:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
      - uses: ruby/setup-ruby@319994f95fa847cf3fb3cd3dbe89f6dcde9f178f # v1.295.0
        with:
          bundler-cache: true
      - run: bundle exec rake memory_profile:run


================================================
FILE: .gitignore
================================================
*~
*.gem
*.swp
pkg
*.rbc
.rvmrc
.bundle
.byebug_history
Gemfile.lock


================================================
FILE: .rubocop.yml
================================================
inherit_gem:
  rubocop-shopify: rubocop.yml

inherit_from:
  - .rubocop_todo.yml

require: rubocop-performance

Performance:
  Enabled: true

AllCops:
  NewCops: disable
  SuggestExtensions: false
  Exclude:
    - 'vendor/bundle/**/*'

Naming/MethodName:
  Exclude:
    - 'example/server/liquid_servlet.rb'

Style/ClassMethodsDefinitions:
  Enabled: false

# liquid filter calls were being mistaken to be calls on arrays
Style/ConcatArrayLiterals:
  Exclude:
    - 'test/integration/standard_filter_test.rb'


================================================
FILE: .rubocop_todo.yml
================================================
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2022-05-18 19:25:47 UTC using RuboCop version 1.29.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 1
# This cop supports safe auto-correction (--auto-correct).
# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
# Include: **/*.gemspec
Gemspec/OrderedDependencies:
  Exclude:
    - 'liquid.gemspec'

# Offense count: 6
# This cop supports safe auto-correction (--auto-correct).
Layout/ClosingHeredocIndentation:
  Exclude:
    - 'test/integration/tags/for_tag_test.rb'

# Offense count: 34
# This cop supports safe auto-correction (--auto-correct).
Layout/EmptyLineAfterGuardClause:
  Exclude:
    - 'lib/liquid/block.rb'
    - 'lib/liquid/block_body.rb'
    - 'lib/liquid/context.rb'
    - 'lib/liquid/drop.rb'
    - 'lib/liquid/lexer.rb'
    - 'lib/liquid/parser.rb'
    - 'lib/liquid/profiler/hooks.rb'
    - 'lib/liquid/standardfilters.rb'
    - 'lib/liquid/tags/for.rb'
    - 'lib/liquid/tags/if.rb'
    - 'lib/liquid/utils.rb'
    - 'lib/liquid/variable.rb'
    - 'lib/liquid/variable_lookup.rb'
    - 'performance/shopify/money_filter.rb'
    - 'performance/shopify/paginate.rb'

# Offense count: 8
# This cop supports safe auto-correction (--auto-correct).
# Configuration parameters: AllowAliasSyntax, AllowedMethods.
# AllowedMethods: alias_method, public, protected, private
Layout/EmptyLinesAroundAttributeAccessor:
  Exclude:
    - 'lib/liquid/template.rb'
    - 'test/integration/filter_test.rb'
    - 'test/integration/tags/include_tag_test.rb'
    - 'test/unit/strainer_template_unit_test.rb'

# Offense count: 17
# This cop supports safe auto-correction (--auto-correct).
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: aligned, indented
Layout/LineEndStringConcatenationIndentation:
  Exclude:
    - 'test/integration/tags/for_tag_test.rb'
    - 'test/integration/tags/increment_tag_test.rb'

# Offense count: 1
# This cop supports safe auto-correction (--auto-correct).
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: aligned, indented
Layout/MultilineOperationIndentation:
  Exclude:
    - 'lib/liquid/expression.rb'

# Offense count: 9
Lint/MissingSuper:
  Exclude:
    - 'lib/liquid/forloop_drop.rb'
    - 'lib/liquid/tablerowloop_drop.rb'
    - 'test/integration/assign_test.rb'
    - 'test/integration/context_test.rb'
    - 'test/integration/filter_test.rb'
    - 'test/integration/standard_filter_test.rb'
    - 'test/integration/tags/for_tag_test.rb'
    - 'test/integration/tags/table_row_test.rb'

# Offense count: 44
Naming/ConstantName:
  Exclude:
    - 'lib/liquid.rb'
    - 'lib/liquid/block_body.rb'
    - 'lib/liquid/tags/assign.rb'
    - 'lib/liquid/tags/capture.rb'
    - 'lib/liquid/tags/case.rb'
    - 'lib/liquid/tags/cycle.rb'
    - 'lib/liquid/tags/for.rb'
    - 'lib/liquid/tags/if.rb'
    - 'lib/liquid/tags/raw.rb'
    - 'lib/liquid/tags/table_row.rb'
    - 'lib/liquid/variable.rb'
    - 'performance/shopify/comment_form.rb'
    - 'performance/shopify/paginate.rb'
    - 'test/integration/tags/include_tag_test.rb'

# Offense count: 9
# Configuration parameters: CheckIdentifiers, CheckConstants, CheckVariables, CheckStrings, CheckSymbols, CheckComments, CheckFilepaths, FlaggedTerms.
Naming/InclusiveLanguage:
  Exclude:
    - 'lib/liquid/drop.rb'
    - 'lib/liquid/parse_context.rb'
    - 'test/integration/drop_test.rb'
    - 'test/integration/tags/if_else_tag_test.rb'

# Offense count: 2
Style/ClassVars:
  Exclude:
    - 'lib/liquid/condition.rb'

# Offense count: 3
# This cop supports safe auto-correction (--auto-correct).
Style/ExplicitBlockArgument:
  Exclude:
    - 'test/integration/context_test.rb'
    - 'test/integration/tag/disableable_test.rb'
    - 'test/integration/tags/for_tag_test.rb'

# Offense count: 2982
# This cop supports safe auto-correction (--auto-correct).
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiterals:
  Enabled: false

# Offense count: 20
# This cop supports safe auto-correction (--auto-correct).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiteralsInInterpolation:
  Exclude:
    - 'lib/liquid/condition.rb'
    - 'lib/liquid/strainer_template.rb'
    - 'lib/liquid/tag/disableable.rb'
    - 'performance/shopify/shop_filter.rb'
    - 'performance/shopify/tag_filter.rb'

# Offense count: 6
# This cop supports safe auto-correction (--auto-correct).
# Configuration parameters: EnforcedStyleForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
Style/TrailingCommaInArrayLiteral:
  Exclude:
    - 'example/server/example_servlet.rb'
    - 'lib/liquid/condition.rb'
    - 'test/integration/context_test.rb'
    - 'test/integration/standard_filter_test.rb'
    - 'test/unit/parse_tree_visitor_test.rb'

# Offense count: 1
# This cop supports safe auto-correction (--auto-correct).
# Configuration parameters: EnforcedStyleForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
Style/TrailingCommaInHashLiteral:
  Exclude:
    - 'lib/liquid/expression.rb'

# Offense count: 19
# This cop supports safe auto-correction (--auto-correct).
# Configuration parameters: EnforcedStyle, MinSize, WordRegex.
# SupportedStyles: percent, brackets
Style/WordArray:
  Exclude:
    - 'lib/liquid/tags/if.rb'
    - 'liquid.gemspec'
    - 'test/integration/assign_test.rb'
    - 'test/integration/context_test.rb'
    - 'test/integration/drop_test.rb'
    - 'test/integration/standard_filter_test.rb'

# Offense count: 117
# This cop supports safe auto-correction (--auto-correct).
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, AllowCopDirectives, AllowedPatterns.
# URISchemes: http, https
Layout/LineLength:
  Max: 260

Naming/PredicatePrefix:
  Enabled: false

# Offense count: 1
# This is intentional - early return from begin/rescue in assignment context
Lint/NoReturnInBeginEndBlocks:
  Exclude:
    - 'lib/liquid/standardfilters.rb'


================================================
FILE: .ruby-version
================================================
3.4.1


================================================
FILE: CONTRIBUTING.md
================================================
# How to contribute

## Things we will merge

* Bugfixes
* Performance improvements
* Features that are likely to be useful to the majority of Liquid users
* Documentation updates that are concise and likely to be useful to the majority of Liquid users

## Things we won't merge

* Code that introduces considerable performance degrations
* Code that touches performance-critical parts of Liquid and comes without benchmarks
* Features that are not important for most people (we want to keep the core Liquid code small and tidy)
* Features that can easily be implemented on top of Liquid (for example as a custom filter or custom filesystem)
* Code that does not include tests
* Code that breaks existing tests
* Documentation changes that are verbose, incorrect or not important to most people (we want to keep it simple and easy to understand)

## Workflow

* [Sign the CLA](https://cla.shopify.com/) if you haven't already
* Fork the Liquid repository
* Create a new branch in your fork
  * For updating [Liquid documentation](https://shopify.github.io/liquid/), create it from `gh-pages` branch. (You can skip tests.)
* If it makes sense, add tests for your code and/or run a performance benchmark
* Make sure all tests pass (`bundle exec rake`)
* Create a pull request

## Releasing

* Bump the version in `lib/liquid/version.rb`
* Update the `History.md` file
* Open a PR like [this one](https://github.com/Shopify/liquid/pull/1894) and merge it to `main`
* Create a new release using the [GitHub UI](https://github.com/Shopify/liquid/releases/new)



================================================
FILE: Gemfile
================================================
# frozen_string_literal: true

source 'https://rubygems.org'
git_source(:github) do |repo_name|
  "https://github.com/#{repo_name}.git"
end

gemspec

gem "base64"

group :benchmark, :test do
  gem 'benchmark-ips'
  gem 'memory_profiler'
  gem 'terminal-table'
  gem "lru_redux"

  install_if -> { RUBY_PLATFORM !~ /mingw|mswin|java/ && RUBY_ENGINE != 'truffleruby' } do
    gem 'stackprof'
  end
end

group :development do
  gem "webrick"
end

group :test do
  gem 'benchmark'
  gem 'rubocop', '~> 1.82.0'
  gem 'rubocop-shopify', '~> 2.18.0', require: false
  gem 'rubocop-performance', require: false
end

group :spec do
  gem 'liquid-spec', github: 'Shopify/liquid-spec', branch: 'main'
  gem 'activesupport', require: false
end


================================================
FILE: History.md
================================================
# Liquid Change Log

## 5.11.0
* Revert the Inline Snippets tag (#2001), treat its inclusion in the latest Liquid release as a bug, and allow for feedback on RFC#1916 to better support Liquid developers [Guilherme Carreiro]
* Rename the `:rigid` error mode to `:strict2` and display a warning when users attempt to use the `:rigid` mode [Guilherme Carreiro]

## 5.10.0
* Introduce support for Inline Snippets [Julia Boutin]

## 5.9.0
* Introduce `:rigid` error mode for stricter, safer parsing of all tags [CP Clermont, Guilherme Carreiro]

## 5.8.7
* Expose body content in the `Doc` tag [James Meng]

## 5.8.1

* Fix `{% doc %}` tag to be visitable [Guilherme Carreiro]

## 5.8.0

* Introduce the new `{% doc %}` tag [Guilherme Carreiro]

## 5.7.3

* Raise Liquid::SyntaxError when parsing invalidly encoded strings [Chris AtLee]

## 5.7.2 2025-01-31

* Fix array filters to not support nested properties [Guilherme Carreiro]

## 5.7.1 2025-01-24

* Fix the `find` and `find_index`filters to return `nil` when filtering empty arrays [Guilherme Carreiro]
* Fix the `has` filter to return `false` when filtering empty arrays [Guilherme Carreiro]

## 5.7.0 2025-01-16

### Features

* Add `find`, `find_index`, `has`, and `reject` filters to arrays [Guilherme Carreiro]
* Compatibility with Ruby 3.4 [Ian Ker-Seymer]

## 5.6.4 2025-01-14

### Fixes
* Add a default `string_scanner` to avoid errors with `Liquid::VariableLookup.parse("foo.bar")` [Ian Ker-Seymer]

## 5.6.3 2025-01-13
* Remove `lru_redux` dependency [Michael Go]

## 5.6.2 2025-01-13

### Fixes
* Preserve the old behavior of requiring floats to start with a digit [Michael Go]

## 5.6.1 2025-01-13

### Performance improvements
* Faster Expression parser / Tokenizer with StringScanner [Michael Go]

## 5.6.0 2024-12-19

### Architectural changes
* Added new `Environment` class to manage configuration and state that was previously stored in `Template` [Ian Ker-Seymer]
* Moved tag registration from `Template` to `Environment` [Ian Ker-Seymer]
* Removed `StrainerFactory` in favor of `Environment`-based strainer creation [Ian Ker-Seymer]
* Consolidated standard tags into a new `Tags` module with `STANDARD_TAGS` constant [Ian Ker-Seymer]

### Performance improvements
* Optimized `Lexer` with a new `Lexer2` implementation using jump tables for faster tokenization, requires Ruby 3.4 [Ian Ker-Seymer]
* Improved variable rendering with specialized handling for different types [Michael Go]
* Reduced array allocations by using frozen empty constants [Michael Go]

### API changes
* Deprecated several `Template` class methods in favor of `Environment` methods [Ian Ker-Seymer]
* Added deprecation warnings system [Ian Ker-Seymer]
* Changed how filters and tags are registered to use Environment [Ian Ker-Seymer]

### Fixes
* Fixed table row handling of break interrupts [Alex Coco]
* Improved variable output handling for arrays [Ian Ker-Seymer]
* Fix Tokenizer to handle null source value (#1873) [Bahar Pourazar]

## 5.5.0 2024-03-21

Please reference the GitHub release for more information.

## 5.4.0 2022-07-29

### Breaking Changes
* Drop support for end-of-life Ruby versions (2.5 and 2.6) (#1578) [Andy Waite]

### Features
* Allow `#` to be used as an inline comment tag (#1498) [CP Clermont]

### Fixes
* `PartialCache` now shares snippet cache with subcontexts by default (#1553) [Chris AtLee]
* Hash registers no longer leak into subcontexts as static registers (#1564) [Chris AtLee]
* Fix `ParseTreeVisitor` for `with` variable expressions in `Render` tag (#1596) [CP Clermont]

### Changed
* Liquid::Context#registers now always returns a Liquid::Registers object, though supports the most used Hash functions for compatibility (#1553)

## 5.3.0 2022-03-22

### Fixes
* StandardFilter: Fix missing @context on iterations (#1525) [Thierry Joyal]
* Fix warning about block and default value in `static_registers.rb` (#1531) [Peter Zhu]

### Deprecation
* Condition#evaluate to require mandatory context argument in Liquid 6.0.0 (#1527) [Thierry Joyal]

## 5.2.0 2022-03-01

### Features
* Add `remove_last`, and `replace_last` filters (#1422) [Anders Hagbard]
* Eagerly cache global filters (#1524) [Jean Boussier]

### Fixes
* Fix some internal errors in filters from invalid input (#1476) [Dylan Thacker-Smith]
* Allow dash in filter kwarg name for consistency with Liquid::C (#1518) [CP Clermont]

## 5.1.0 / 2021-09-09

### Features
* Add `base64_encode`, `base64_decode`, `base64_url_safe_encode`, and `base64_url_safe_decode` filters (#1450) [Daniel Insley]
* Introduce `to_liquid_value` in `Liquid::Drop` (#1441) [Michael Go]

### Fixes
* Fix support for using a String subclass for the liquid source (#1421) [Dylan Thacker-Smith]
* Add `ParseTreeVisitor` to `RangeLookup` (#1470) [CP Clermont]
* Translate `RangeError` to `Liquid::Error` for `truncatewords` with large int (#1431) [Dylan Thacker-Smith]

## 5.0.1 / 2021-03-24

### Fixes
* Add ParseTreeVisitor to Echo tag (#1414) [CP Clermont]
* Test with ruby 3.0 as the latest ruby version (#1398) [Dylan Thacker-Smith]
* Handle carriage return in newlines_to_br (#1391) [Unending]

### Performance Improvements
* Use split limit in truncatewords (#1361) [Dylan Thacker-Smith]

## 5.0.0 / 2021-01-06

### Features
* Add new `{% render %}` tag (#1122) [Samuel Doiron]
* Add support for `as` in `{% render %}` and `{% include %}` (#1181) [Mike Angell]
* Add `{% liquid %}` and `{% echo %}` tags (#1086) [Justin Li]
* Add [usage tracking](README.md#usage-tracking) [Mike Angell]
* Add `Tag.disable_tags` for disabling tags that prepend `Tag::Disableable` at render time (#1162, #1274, #1275) [Mike Angell]
* Support using a profiler for multiple renders (#1365, #1366) [Dylan Thacker-Smith]

### Fixes
* Fix catastrophic backtracking in `RANGES_REGEX` regular expression (#1357) [Dylan Thacker-Smith]
* Make sure the for tag's limit and offset are integers (#1094) [David Cornu]
* Invokable methods for enumerable reject include (#1151) [Thierry Joyal]
* Allow `default` filter to handle `false` as value (#1144) [Mike Angell]
* Fix render length resource limit so it doesn't multiply nested output (#1285) [Dylan Thacker-Smith]
* Fix duplication of text in raw tags (#1304) [Peter Zhu]
* Fix strict parsing of find variable with a name expression (#1317) [Dylan Thacker-Smith]
* Use monotonic time to measure durations in Liquid::Profiler (#1362) [Dylan Thacker-Smith]

### Breaking Changes
* Require Ruby >= 2.5 (#1131, #1310) [Mike Angell, Dylan Thacker-Smith]
* Remove support for taint checking (#1268) [Dylan Thacker-Smith]
* Split Strainer class into StrainerFactory and StrainerTemplate (#1208) [Thierry Joyal]
* Remove handling of a nil context in the Strainer class (#1218) [Thierry Joyal]
* Handle `BlockBody#blank?` at parse time (#1287) [Dylan Thacker-Smith]
* Pass the tag markup and tokenizer to `Document#unknown_tag` (#1290) [Dylan Thacker-Smith]
* And several internal changes

### Performance Improvements
* Reduce allocations (#1073, #1091, #1115, #1099, #1117, #1141, #1322, #1341) [Richard Monette, Florian Weingarten, Ashwin Maroli]
* Improve resources limits performance (#1093, #1323) [Florian Weingarten, Dylan Thacker-Smith]

## 4.0.3 / 2019-03-12

### Fixed
* Fix break and continue tags inside included templates in loops (#1072) [Justin Li]

## 4.0.2 / 2019-03-08

### Changed
* Add `where` filter (#1026) [Samuel Doiron]
* Add `ParseTreeVisitor` to iterate the Liquid AST (#1025) [Stephen Paul Weber]
* Improve `strip_html` performance (#1032) [printercu]

### Fixed
* Add error checking for invalid combinations of inputs to sort, sort_natural, where, uniq, map, compact filters (#1059) [Garland Zhang]
* Validate the character encoding in url_decode (#1070) [Clayton Smith]

## 4.0.1 / 2018-10-09

### Changed
* Add benchmark group in Gemfile (#855) [Jerry Liu]
* Allow benchmarks to benchmark render by itself (#851) [Jerry Liu]
* Avoid calling `line_number` on String node when rescuing a render error. (#860) [Dylan Thacker-Smith]
* Avoid duck typing to detect whether to call render on a node. [Dylan Thacker-Smith]
* Clarify spelling of `reversed` on `for` block tag (#843) [Mark Crossfield]
* Replace recursion with loop to avoid potential stack overflow from malicious input (#891, #892) [Dylan Thacker-Smith]
* Limit block tag nesting to 100 (#894) [Dylan Thacker-Smith]
* Replace `assert_equal nil` with `assert_nil` (#895) [Dylan Thacker-Smith]
* Remove Spy Gem (#896) [Dylan Thacker-Smith]
* Add `collection_name` and `variable_name` reader to `For` block (#909)
* Symbols render as strings (#920) [Justin Li]
* Remove default value from Hash objects (#932) [Maxime Bedard]
* Remove one level of nesting (#944) [Dylan Thacker-Smith]
* Update Rubocop version (#952) [Justin Li]
* Add `at_least` and `at_most` filters (#954, #958) [Nithin Bekal]
* Add a regression test for a liquid-c trim mode bug (#972) [Dylan Thacker-Smith]
* Use https rather than git protocol to fetch liquid-c [Dylan Thacker-Smith]
* Add tests against Ruby 2.4 (#963) and 2.5 (#981)
* Replace RegExp literals with constants (#988) [Ashwin Maroli]
* Replace unnecessary `#each_with_index` with `#each` (#992) [Ashwin Maroli]
* Improve the unexpected end delimiter message for block tags. (#1003) [Dylan Thacker-Smith]
* Refactor and optimize rendering (#1005) [Christopher Aue]
* Add installation instruction (#1006) [Ben Gift]
* Remove Circle CI (#1010)
* Rename deprecated `BigDecimal.new` to `BigDecimal` (#1024) [Koichi ITO]
* Rename deprecated Rubocop name (#1027) [Justin Li]

### Fixed
* Handle `join` filter on non String joiners (#857) [Richard Monette]
* Fix duplicate inclusion condition logic error of `Liquid::Strainer.add_filter` method (#861)
* Fix `escape`, `url_encode`, `url_decode` not handling non-string values (#898) [Thierry Joyal]
* Fix raise when variable is defined but nil when using `strict_variables` [Pascal Betz]
* Fix `sort` and `sort_natural` to handle arrays with nils (#930) [Eric Chan]

## 4.0.0 / 2016-12-14 / branch "4-0-stable"

### Changed
* Render an opaque internal error by default for non-Liquid::Error (#835) [Dylan Thacker-Smith]
* Ruby 2.0 support dropped (#832) [Dylan Thacker-Smith]
* Add to_number Drop method to allow custom drops to work with number filters (#731)
* Add strict_variables and strict_filters options to detect undefined references (#691)
* Improve loop performance (#681) [Florian Weingarten]
* Rename Drop method `before_method` to `liquid_method_missing` (#661) [Thierry Joyal]
* Add url_decode filter to invert url_encode (#645) [Larry Archer]
* Add global_filter to apply a filter to all output (#610) [Loren Hale]
* Add compact filter (#600) [Carson Reinke]
* Rename deprecated "has_key?" and "has_interrupt?" methods (#593) [Florian Weingarten]
* Include template name with line numbers in render errors (574) [Dylan Thacker-Smith]
* Add sort_natural filter (#554) [Martin Hanzel]
* Add forloop.parentloop as a reference to the parent loop (#520) [Justin Li]
* Block parsing moved to BlockBody class (#458) [Dylan Thacker-Smith]
* Add concat filter to concatenate arrays (#429) [Diogo Beato]
* Ruby 1.9 support dropped (#491) [Justin Li]
* Liquid::Template.file_system's read_template_file method is no longer passed the context. (#441) [James Reid-Smith]
* Remove `liquid_methods` (See https://github.com/Shopify/liquid/pull/568 for replacement)
* Liquid::Template.register_filter raises when the module overrides registered public methods as private or protected (#705) [Gaurav Chande]

### Fixed

* Fix variable names being detected as an operator when starting with contains (#788) [Michael Angell]
* Fix include tag used with strict_variables (#828) [QuickPay]
* Fix map filter when value is a Proc (#672) [Guillaume Malette]
* Fix truncate filter when value is not a string (#672) [Guillaume Malette]
* Fix behaviour of escape filter when input is nil (#665) [Tanel Jakobsoo]
* Fix sort filter behaviour with empty array input (#652) [Marcel Cary]
* Fix test failure under certain timezones (#631) [Dylan Thacker-Smith]
* Fix bug in uniq filter (#595) [Florian Weingarten]
* Fix bug when "blank" and "empty" are used as variable names (#592) [Florian Weingarten]
* Fix condition parse order in strict mode (#569) [Justin Li]
* Fix naming of the "context variable" when dynamically including a template (#559) [Justin Li]
* Gracefully accept empty strings in the date filter (#555) [Loren Hale]
* Fix capturing into variables with a hyphen in the name (#505) [Florian Weingarten]
* Fix case sensitivity regression in date standard filter (#499) [Kelley Reynolds]
* Disallow filters with no variable in strict mode (#475) [Justin Li]
* Disallow variable names in the strict parser that are not valid in the lax parser (#463) [Justin Li]
* Fix BlockBody#warnings taking exponential time to compute (#486) [Justin Li]

## 3.0.5 / 2015-07-23 / branch "3-0-stable"

* Fix test failure under certain timezones [Dylan Thacker-Smith]

## 3.0.4 / 2015-07-17

* Fix chained access to multi-dimensional hashes [Florian Weingarten]

## 3.0.3 / 2015-05-28

* Fix condition parse order in strict mode (#569) [Justin Li]

## 3.0.2 / 2015-04-24

* Expose VariableLookup private members (#551) [Justin Li]
* Documentation fixes

## 3.0.1 / 2015-01-23

* Remove duplicate `index0` key in TableRow tag (#502) [Alfred Xing]

## 3.0.0 / 2014-11-12

* Removed Block#end_tag. Instead, override parse with `super` followed by your code. See #446 [Dylan Thacker-Smith]
* Fixed condition with wrong data types (#423) [Bogdan Gusiev]
* Add url_encode to standard filters (#421) [Derrick Reimer]
* Add uniq to standard filters [Florian Weingarten]
* Add exception_handler feature (#397) and #254 [Bogdan Gusiev, Florian Weingarten]
* Optimize variable parsing to avoid repeated regex evaluation during template rendering #383 [Jason Hiltz-Laforge]
* Optimize checking for block interrupts to reduce object allocation #380 [Jason Hiltz-Laforge]
* Properly set context rethrow_errors on render! #349 [Thierry Joyal]
* Fix broken rendering of variables which are equal to false (#345) [Florian Weingarten]
* Remove ActionView template handler [Dylan Thacker-Smith]
* Freeze lots of string literals for new Ruby 2.1 optimization (#297) [Florian Weingarten]
* Allow newlines in tags and variables (#324) [Dylan Thacker-Smith]
* Tag#parse is called after initialize, which now takes options instead of tokens as the 3rd argument. See #321 [Dylan Thacker-Smith]
* Raise `Liquid::ArgumentError` instead of `::ArgumentError` when filter has wrong number of arguments #309 [Bogdan Gusiev]
* Add a to_s default for liquid drops (#306) [Adam Doeler]
* Add strip, lstrip, and rstrip to standard filters [Florian Weingarten]
* Make if, for & case tags return complete and consistent nodelists (#250) [Nick Jones]
* Prevent arbitrary method invocation on condition objects (#274) [Dylan Thacker-Smith]
* Don't call to_sym when creating conditions for security reasons (#273) [Bouke van der Bijl]
* Fix resource counting bug with respond_to?(:length) (#263) [Florian Weingarten]
* Allow specifying custom patterns for template filenames (#284) [Andrei Gladkyi]
* Allow drops to optimize loading a slice of elements (#282) [Tom Burns]
* Support for passing variables to snippets in subdirs (#271) [Joost Hietbrink]
* Add a class cache to avoid runtime extend calls (#249) [James Tucker]
* Remove some legacy Ruby 1.8 compatibility code (#276) [Florian Weingarten]
* Add default filter to standard filters (#267) [Derrick Reimer]
* Add optional strict parsing and warn parsing (#235) [Tristan Hume]
* Add I18n syntax error translation (#241) [Simon Hørup Eskildsen, Sirupsen]
* Make sort filter work on enumerable drops (#239) [Florian Weingarten]
* Fix clashing method names in enumerable drops (#238) [Florian Weingarten]
* Make map filter work on enumerable drops (#233) [Florian Weingarten]
* Improved whitespace stripping for blank blocks, related to #216 [Florian Weingarten]

## 2.6.3 / 2015-07-23 / branch "2-6-stable"

* Fix test failure under certain timezones [Dylan Thacker-Smith]

## 2.6.2 / 2015-01-23

* Remove duplicate hash key [Parker Moore]

## 2.6.1 / 2014-01-10

Security fix, cherry-picked from master (4e14a65):
* Don't call to_sym when creating conditions for security reasons (#273) [Bouke van der Bijl]
* Prevent arbitrary method invocation on condition objects (#274) [Dylan Thacker-Smith]

## 2.6.0 / 2013-11-25

IMPORTANT: Liquid 2.6 is going to be the last version of Liquid which maintains explicit Ruby 1.8 compatability.
The following releases will only be tested against Ruby 1.9 and Ruby 2.0 and are likely to break on Ruby 1.8.

* Bugfix for #106: fix example servlet [gnowoel]
* Bugfix for #97: strip_html filter supports multi-line tags [Jo Liss]
* Bugfix for #114: strip_html filter supports style tags [James Allardice]
* Bugfix for #117: 'now' support for date filter in Ruby 1.9 [Notre Dame Webgroup]
* Bugfix for #166: truncate filter on UTF-8 strings with Ruby 1.8 [Florian Weingarten]
* Bugfix for #204: 'raw' parsing bug [Florian Weingarten]
* Bugfix for #150: 'for' parsing bug [Peter Schröder]
* Bugfix for #126: Strip CRLF in strip_newline [Peter Schröder]
* Bugfix for #174, "can't convert Fixnum into String" for "replace" [jsw0528]
* Allow a Liquid::Drop to be passed into Template#render [Daniel Huckstep]
* Resource limits [Florian Weingarten]
* Add reverse filter [Jay Strybis]
* Add utf-8 support
* Use array instead of Hash to keep the registered filters [Tasos Stathopoulos]
* Cache tokenized partial templates [Tom Burns]
* Avoid warnings in Ruby 1.9.3 [Marcus Stollsteimer]
* Better documentation for 'include' tag (closes #163) [Peter Schröder]
* Use of BigDecimal on filters to have better precision (closes #155) [Arthur Nogueira Neves]

## 2.5.5 / 2014-01-10 / branch "2-5-stable"

Security fix, cherry-picked from master (4e14a65):
* Don't call to_sym when creating conditions for security reasons (#273) [Bouke van der Bijl]
* Prevent arbitrary method invocation on condition objects (#274) [Dylan Thacker-Smith]

## 2.5.4 / 2013-11-11

* Fix "can't convert Fixnum into String" for "replace" (#173), [jsw0528]

## 2.5.3 / 2013-10-09

* #232, #234, #237: Fix map filter bugs [Florian Weingarten]

## 2.5.2 / 2013-09-03 / deleted

Yanked from rubygems, as it contained too many changes that broke compatibility. Those changes will be on following major releases.

## 2.5.1 / 2013-07-24

* #230: Fix security issue with map filter, Use invoke_drop in map filter [Florian Weingarten]

## 2.5.0 / 2013-03-06

* Prevent Object methods from being called on drops
* Avoid symbol injection from liquid
* Added break and continue statements
* Fix filter parser for args without space separators
* Add support for filter keyword arguments


## 2.4.0 / 2012-08-03

* Performance improvements
* Allow filters in `assign`
* Add `modulo` filter
* Ruby 1.8, 1.9, and Rubinius compatibility fixes
* Add support for `quoted['references']` in `tablerow`
* Add support for Enumerable to `tablerow`
* `strip_html` filter removes html comments


## 2.3.0 / 2011-10-16

* Several speed/memory improvements
* Numerous bug fixes
* Added support for MRI 1.9, Rubinius, and JRuby
* Added support for integer drop parameters
* Added epoch support to `date` filter
* New `raw` tag that suppresses parsing
* Added `else` option to `for` tag
* New `increment` tag
* New `split` filter


## 2.2.1 / 2010-08-23

* Added support for literal tags


## 2.2.0 / 2010-08-22

* Compatible with Ruby 1.8.7, 1.9.1 and 1.9.2-p0
* Merged some changed made by the community


## 1.9.0 / 2008-03-04

* Fixed gem install rake task
* Improve Error encapsulation in liquid by maintaining a own set of exceptions instead of relying on ruby build ins


## Before 1.9.0

* Added If with or / and expressions
* Implemented .to_liquid for all objects which can be passed to liquid like Strings Arrays Hashes Numerics and Booleans. To export new objects to liquid just implement .to_liquid on them and return objects which themselves have .to_liquid methods.
* Added more tags to standard library
* Added include tag ( like partials in rails )
* [...] Gazillion of detail improvements
* Added strainers as filter hosts for better security [Tobias Luetke]
* Fixed that rails integration would call filter with the wrong "self" [Michael Geary]
* Fixed bad error reporting when a filter called a method which doesn't exist. Liquid told you that it couldn't find the filter which was obviously misleading [Tobias Luetke]
* Removed count helper from standard lib. use size [Tobias Luetke]
* Fixed bug with string filter parameters failing to tolerate commas in strings. [Paul Hammond]
* Improved filter parameters. Filter parameters are now context sensitive; Types are resolved according to the rules of the context. Multiple parameters are now separated by the Liquid::ArgumentSeparator: , by default [Paul Hammond]
    {{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }}
* Added Liquid::Drop. A base class which you can use for exporting proxy objects to liquid which can acquire more data when used in liquid. [Tobias Luetke]

  class ProductDrop < Liquid::Drop
    def top_sales
       Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
    end
  end
  t = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {% endfor %} '  )
  t.render('product' => ProductDrop.new )
* Added filter parameters support. Example: {{ date | format_date: "%Y" }} [Paul Hammond]


================================================
FILE: LICENSE
================================================
Copyright (c) 2005, 2006 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.


================================================
FILE: README.md
================================================
[![Build status](https://github.com/Shopify/liquid/actions/workflows/liquid.yml/badge.svg)](https://github.com/Shopify/liquid/actions/workflows/liquid.yml)
[![Inline docs](http://inch-ci.org/github/Shopify/liquid.svg?branch=master)](http://inch-ci.org/github/Shopify/liquid)

# Liquid template engine

* [Contributing guidelines](CONTRIBUTING.md)
* [Version history](History.md)
* [Liquid documentation from Shopify](https://shopify.dev/docs/api/liquid)
* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
* [Website](http://liquidmarkup.org/)

## Introduction

Liquid is a template engine which was written with very specific requirements:

* It has to have beautiful and simple markup. Template engines which don't produce good looking markup are no fun to use.
* It needs to be non evaling and secure. Liquid templates are made so that users can edit them. You don't want to run code on your server which your users wrote.
* It has to be stateless. Compile and render steps have to be separate so that the expensive parsing and compiling can be done once and later on you can just render it passing in a hash with local variables and objects.

## Why you should use Liquid

* You want to allow your users to edit the appearance of your application but don't want them to run **insecure code on your server**.
* You want to render templates directly from the database.
* You like smarty (PHP) style template engines.
* You need a template engine which does HTML just as well as emails.
* You don't like the markup of your current templating engine.

## What does it look like?

```html
<ul id="products">
  {% for product in products %}
    <li>
      <h2>{{ product.name }}</h2>
      Only {{ product.price | price }}

      {{ product.description | prettyprint | paragraph }}
    </li>
  {% endfor %}
</ul>
```

## How to use Liquid

Install Liquid by adding `gem 'liquid'` to your gemfile.

Liquid supports a very simple API based around the Liquid::Template class.
For standard use you can just pass it the content of a file and call render with a parameters hash.

```ruby
@template = Liquid::Template.parse("hi {{name}}") # Parses and compiles the template
@template.render('name' => 'tobi')                # => "hi tobi"
```

### Concept of Environments

In Liquid, a "Environment" is a scoped environment that encapsulates custom tags, filters, and other configurations. This allows you to define and isolate different sets of functionality for different contexts, avoiding global overrides that can lead to conflicts and unexpected behavior.

By using environments, you can:

1. **Encapsulate Logic**: Keep the logic for different parts of your application separate.
2. **Avoid Conflicts**: Prevent custom tags and filters from clashing with each other.
3. **Improve Maintainability**: Make it easier to manage and understand the scope of customizations.
4. **Enhance Security**: Limit the availability of certain tags and filters to specific contexts.

We encourage the use of Environments over globally overriding things because it promotes better software design principles such as modularity, encapsulation, and separation of concerns.

Here's an example of how you can define and use Environments in Liquid:

```ruby
user_environment = Liquid::Environment.build do |environment|
  environment.register_tag("renderobj", RenderObjTag)
end

Liquid::Template.parse(<<~LIQUID, environment: user_environment)
  {% renderobj src: "path/to/model.obj" %}
LIQUID
```

In this example, `RenderObjTag` is a custom tag that is only available within the `user_environment`.

Similarly, you can define another environment for a different context, such as email templates:

```ruby
email_environment = Liquid::Environment.build do |environment|
  environment.register_tag("unsubscribe_footer", UnsubscribeFooter)
end

Liquid::Template.parse(<<~LIQUID, environment: email_environment)
  {% unsubscribe_footer %}
LIQUID
```

By using Environments, you ensure that custom tags and filters are only available in the contexts where they are needed, making your Liquid templates more robust and easier to manage. For smaller projects, a global environment is available via `Liquid::Environment.default`.

### Error Modes

Setting the error mode of Liquid lets you specify how strictly you want your templates to be interpreted.
Normally the parser is very lax and will accept almost anything without error. Unfortunately this can make
it very hard to debug and can lead to unexpected behaviour.

Liquid also comes with different parsers that can be used when editing templates to give better error messages
when templates are invalid. You can enable this new parser like this:

```ruby
Liquid::Environment.default.error_mode = :strict2 # Raises a SyntaxError when invalid syntax is used in all tags
Liquid::Environment.default.error_mode = :strict  # Raises a SyntaxError when invalid syntax is used in some tags
Liquid::Environment.default.error_mode = :warn    # Adds strict errors to template.errors but continues as normal
Liquid::Environment.default.error_mode = :lax     # The default mode, accepts almost anything.
```

If you want to set the error mode only on specific templates you can pass `:error_mode` as an option to `parse`:
```ruby
Liquid::Template.parse(source, error_mode: :strict)
```
This is useful for doing things like enabling strict mode only in the theme editor.

It is recommended that you enable `:strict` or `:warn` mode on new apps to stop invalid templates from being created.
It is also recommended that you use it in the template editors of existing apps to give editors better error messages.

### Undefined variables and filters

By default, the renderer doesn't raise or in any other way notify you if some variables or filters are missing, i.e. not passed to the `render` method.
You can improve this situation by passing `strict_variables: true` and/or `strict_filters: true` options to the `render` method.
When one of these options is set to true, all errors about undefined variables and undefined filters will be stored in `errors` array of a `Liquid::Template` instance.
Here are some examples:

```ruby
template = Liquid::Template.parse("{{x}} {{y}} {{z.a}} {{z.b}}")
template.render({ 'x' => 1, 'z' => { 'a' => 2 } }, { strict_variables: true })
#=> '1  2 ' # when a variable is undefined, it's rendered as nil
template.errors
#=> [#<Liquid::UndefinedVariable: Liquid error: undefined variable y>, #<Liquid::UndefinedVariable: Liquid error: undefined variable b>]
```

```ruby
template = Liquid::Template.parse("{{x | filter1 | upcase}}")
template.render({ 'x' => 'foo' }, { strict_filters: true })
#=> '' # when at least one filter in the filter chain is undefined, a whole expression is rendered as nil
template.errors
#=> [#<Liquid::UndefinedFilter: Liquid error: undefined filter filter1>]
```

If you want to raise on a first exception instead of pushing all of them in `errors`, you can use `render!` method:

```ruby
template = Liquid::Template.parse("{{x}} {{y}}")
template.render!({ 'x' => 1}, { strict_variables: true })
#=> Liquid::UndefinedVariable: Liquid error: undefined variable y
```

### Usage tracking

To help track usages of a feature or code path in production, we have released opt-in usage tracking. To enable this, we provide an empty `Liquid:: Usage.increment` method which you can customize to your needs. The feature is well suited to https://github.com/Shopify/statsd-instrument. However, the choice of implementation is up to you.

Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.


================================================
FILE: Rakefile
================================================
# frozen_string_literal: true

require 'rake'
require 'rake/testtask'
$LOAD_PATH.unshift(File.expand_path("../lib", __FILE__))
require "liquid/version"

task(default: [:test, :rubocop])

desc('run test suite with default parser')
Rake::TestTask.new(:base_test) do |t|
  t.libs << 'lib' << 'test'
  t.test_files = FileList['test/{integration,unit}/**/*_test.rb']
  t.verbose    = false
end

Rake::TestTask.new(:integration_test) do |t|
  t.libs << 'lib' << 'test'
  t.test_files = FileList['test/integration/**/*_test.rb']
  t.verbose    = false
end

desc('run test suite with warn error mode')
task :warn_test do
  ENV['LIQUID_PARSER_MODE'] = 'warn'
  Rake::Task['base_test'].invoke
end

task :rubocop do
  if RUBY_ENGINE == 'ruby'
    require 'rubocop/rake_task'
    RuboCop::RakeTask.new
  end
end

desc('runs test suite with lax, strict, and strict2 parsers')
task :test do
  ENV['LIQUID_PARSER_MODE'] = 'lax'
  Rake::Task['base_test'].invoke

  ENV['LIQUID_PARSER_MODE'] = 'strict'
  Rake::Task['base_test'].reenable
  Rake::Task['base_test'].invoke

  ENV['LIQUID_PARSER_MODE'] = 'strict2'
  Rake::Task['base_test'].reenable
  Rake::Task['base_test'].invoke

  if RUBY_ENGINE == 'ruby' || RUBY_ENGINE == 'truffleruby'
    ENV['LIQUID_PARSER_MODE'] = 'lax'
    Rake::Task['integration_test'].reenable
    Rake::Task['integration_test'].invoke

    ENV['LIQUID_PARSER_MODE'] = 'strict'
    Rake::Task['integration_test'].reenable
    Rake::Task['integration_test'].invoke

    ENV['LIQUID_PARSER_MODE'] = 'strict2'
    Rake::Task['integration_test'].reenable
    Rake::Task['integration_test'].invoke
  end
end

task(gem: :build)
task :build do
  system "gem build liquid.gemspec"
end

task install: :build do
  system "gem install liquid-#{Liquid::VERSION}.gem"
end

task release: :build do
  system "git tag -a v#{Liquid::VERSION} -m 'Tagging #{Liquid::VERSION}'"
  system "git push --tags"
  system "gem push liquid-#{Liquid::VERSION}.gem"
  system "rm liquid-#{Liquid::VERSION}.gem"
end

namespace :benchmark do
  desc "Run the liquid benchmark with lax parsing"
  task :lax do
    ruby "./performance/benchmark.rb lax"
  end

  desc "Run the liquid benchmark with strict parsing"
  task :strict do
    ruby "./performance/benchmark.rb strict"
  end

  desc "Run the liquid benchmark with strict2 parsing"
  task :strict2 do
    ruby "./performance/benchmark.rb strict2"
  end

  desc "Run the liquid benchmark with lax, strict, and strict2 parsing"
  task run: [:lax, :strict, :strict2]

  desc "Run unit benchmarks"
  namespace :unit do
    task :all do
      Dir["./performance/unit/*_benchmark.rb"].each do |file|
        puts "🧪 Running #{file}"
        ruby file
      end
    end

    task :lexer do
      Dir["./performance/unit/lexer_benchmark.rb"].each do |file|
        puts "🧪 Running #{file}"
        ruby file
      end
    end

    task :expression do
      Dir["./performance/unit/expression_benchmark.rb"].each do |file|
        puts "🧪 Running #{file}"
        ruby file
      end
    end
  end
end

namespace :profile do
  desc "Run the liquid profile/performance coverage"
  task :run do
    ruby "./performance/profile.rb"
  end

  desc "Run the liquid profile/performance coverage with strict parsing"
  task :strict do
    ruby "./performance/profile.rb strict"
  end
end

namespace :memory_profile do
  desc "Run memory profiler"
  task :run do
    ruby "./performance/memory_profile.rb"
  end
end

desc("Run example")
task :example do
  ruby "-w -d -Ilib example/server/server.rb"
end

task :console do
  exec 'irb -I lib -r liquid'
end

desc('run liquid-spec suite across all adapters')
task :spec do
  adapters = Dir['./spec/*.rb'].join(',')
  sh "bundle exec liquid-spec matrix --adapters=#{adapters} --reference=ruby_liquid"
end


================================================
FILE: bin/render
================================================
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'bundler/setup'
require 'liquid'

class VirtualFileSystem
  def initialize
    snippet_1 = <<~LIQUID
      <h1>
        {{- greating | default: 'Hello' }}, {{ name | default: 'world' -}}!
      </h1>
    LIQUID
    snippet_2 = <<~LIQUID
      {%- for i in (1..5) -%}
        > {{ i }}
      {%- endfor -%}
    LIQUID

    @templates = {
      'snippet-1' => snippet_1,
      'snippet-2' => snippet_2,
    }
  end

  def read_template_file(key)
    @templates[key] || raise(Liquid::FileSystemError, "No such template '#{key}'")
  end
end

def source
  File.read(ARGV[0])
rescue StandardError
  'Usage: bin/render example/server/templates/index.liquid'
end

def assigns
  {
    'date' => Time.now,
  }
end

puts Liquid::Template
  .parse(source, error_mode: :strict2)
  .tap { |t| t.registers[:file_system] = VirtualFileSystem.new }
  .render(assigns)


================================================
FILE: example/server/example_servlet.rb
================================================
# frozen_string_literal: true

module ProductsFilter
  def price(integer)
    format("$%.2d USD", integer / 100.0)
  end

  def prettyprint(text)
    text.gsub(/\*(.*)\*/, '<b>\1</b>')
  end

  def count(array)
    array.size
  end

  def paragraph(p)
    "<p>#{p}</p>"
  end
end

class Servlet < LiquidServlet
  def index
    { 'date' => Time.now }
  end

  def products
    { 'products' => products_list, 'more_products' => more_products_list, 'description' => description, 'section' => 'Snowboards', 'cool_products' => true }
  end

  private

  def products_list
    [
      { 'name' => 'Arbor Draft', 'price' => 39900, 'description' => 'the *arbor draft* is a excellent product' },
      { 'name' => 'Arbor Element', 'price' => 40000, 'description' => 'the *arbor element* rocks for freestyling' },
      { 'name' => 'Arbor Diamond', 'price' => 59900, 'description' => 'the *arbor diamond* is a made up product because im obsessed with arbor and have no creativity' }
    ]
  end

  def more_products_list
    [
      { 'name' => 'Arbor Catalyst', 'price' => 39900, 'description' => 'the *arbor catalyst* is an advanced drop-through for freestyle and flatground performance and versatility' },
      { 'name' => 'Arbor Fish', 'price' => 40000, 'description' => 'the *arbor fish* is a compact pin that features an extended wheelbase and time-honored teardrop shape' }
    ]
  end

  def description
    "List of Products ~ This is a list of products with price and description."
  end
end


================================================
FILE: example/server/liquid_servlet.rb
================================================
# frozen_string_literal: true

class LiquidServlet < WEBrick::HTTPServlet::AbstractServlet
  def do_GET(req, res)
    handle(:get, req, res)
  end

  def do_POST(req, res)
    handle(:post, req, res)
  end

  private

  def handle(_type, req, res)
    @request  = req
    @response = res

    @request.path_info =~ /(\w+)\z/
    @action  = Regexp.last_match(1) || 'index'
    @assigns = send(@action) if respond_to?(@action)

    @response['Content-Type'] = "text/html"
    @response.status = 200
    @response.body   = Liquid::Template.parse(read_template).render(@assigns, filters: [ProductsFilter])
  end

  def read_template(filename = @action)
    File.read("#{__dir__}/templates/#{filename}.liquid")
  end
end


================================================
FILE: example/server/server.rb
================================================
# frozen_string_literal: true

require 'webrick'
require 'rexml/document'

require_relative '../../lib/liquid'
require_relative 'liquid_servlet'
require_relative 'example_servlet'

# Setup webrick
server = WEBrick::HTTPServer.new(Port: ARGV[1] || 3000)
server.mount('/', Servlet)
trap("INT") { server.shutdown }
server.start


================================================
FILE: example/server/templates/index.liquid
================================================
<p>Hello world!</p>

<p>It is {{date}}</p>


<p>Check out the <a href="/products">Products</a> screen </p>


================================================
FILE: example/server/templates/products.liquid
================================================
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta http-equiv="Content-Language" content="en-us" />

    <title>products</title>

    <meta name="ROBOTS" content="ALL" />
    <meta http-equiv="imagetoolbar" content="no" />
    <meta name="MSSmartTagsPreventParsing" content="true" />
    <meta name="Copyright" content="(c) 2005 Copyright content:  Copyright design: Tobias Luetke" />
    <!-- (c) Copyright 2005 by Tobias Luetke All Rights Reserved. -->
  </head>

  <body>
    {% assign all_products = products | concat: more_products  %}
    <h1>{{ description | split: '~' | first }}</h1>

    <h2>{{ description | split: '~' | last }}</h2>

    <h2>There are currently {{all_products | count}} products in the {{section}} catalog</h2>

    {% if cool_products %}
      Cool products :)
    {% else %}
      Uncool products :(
    {% endif %}

    <ul id="products">

      {% for product in all_products %}
        <li>
          <h2>{{product.name}}</h2>
          Only {{product.price | price }}

          {{product.description | prettyprint | paragraph }}

          {{ 'it rocks!' | paragraph }}

        </li>
      {% endfor %}

    </ul>

  </body>
</html>


================================================
FILE: lib/liquid/block.rb
================================================
# frozen_string_literal: true

module Liquid
  class Block < Tag
    MAX_DEPTH = 100

    def initialize(tag_name, markup, options)
      super
      @blank = true
    end

    def parse(tokens)
      @body = new_body
      while parse_body(@body, tokens)
      end
      @body.freeze
    end

    # For backwards compatibility
    def render(context)
      @body.render(context)
    end

    def blank?
      @blank
    end

    def nodelist
      @body.nodelist
    end

    def unknown_tag(tag_name, _markup, _tokenizer)
      Block.raise_unknown_tag(tag_name, block_name, block_delimiter, parse_context)
    end

    # @api private
    def self.raise_unknown_tag(tag, block_name, block_delimiter, parse_context)
      if tag == 'else'
        raise SyntaxError, parse_context.locale.t(
          "errors.syntax.unexpected_else",
          block_name: block_name,
        )
      elsif tag.start_with?('end')
        raise SyntaxError, parse_context.locale.t(
          "errors.syntax.invalid_delimiter",
          tag: tag,
          block_name: block_name,
          block_delimiter: block_delimiter,
        )
      else
        raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
      end
    end

    def raise_tag_never_closed(block_name)
      raise SyntaxError, parse_context.locale.t("errors.syntax.tag_never_closed", block_name: block_name)
    end

    def block_name
      @tag_name
    end

    def block_delimiter
      @block_delimiter ||= "end#{block_name}"
    end

    private

    # @api public
    def new_body
      parse_context.new_block_body
    end

    # @api public
    def parse_body(body, tokens)
      if parse_context.depth >= MAX_DEPTH
        raise StackLevelError, "Nesting too deep"
      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
          raise_tag_never_closed(block_name) unless end_tag_name

          # 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


================================================
FILE: lib/liquid/block_body.rb
================================================
# frozen_string_literal: true

require 'English'

module Liquid
  class BlockBody
    LiquidTagToken      = /\A\s*(#{TagName})\s*(.*?)\z/o
    FullToken           = /\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
    FullTokenPossiblyInvalid = /\A(.*)#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*)?#{WhitespaceControl}?#{TagEnd}\z/om
    ContentOfVariable   = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
    WhitespaceOrNothing = /\A\s*\z/
    TAGSTART            = "{%"
    VARSTART            = "{{"

    attr_reader :nodelist

    def initialize
      @nodelist = []
      @blank    = true
    end

    def parse(tokenizer, parse_context, &block)
      raise FrozenError, "can't modify frozen Liquid::BlockBody" if frozen?

      parse_context.line_number = tokenizer.line_number

      if tokenizer.for_liquid_tag
        parse_for_liquid_tag(tokenizer, parse_context, &block)
      else
        parse_for_document(tokenizer, parse_context, &block)
      end
    end

    def freeze
      @nodelist.freeze
      super
    end

    private def parse_for_liquid_tag(tokenizer, parse_context)
      while (token = tokenizer.shift)
        unless token.empty? || token.match?(WhitespaceOrNothing)
          unless token =~ LiquidTagToken
            # line isn't empty but didn't match tag syntax, yield and let the
            # caller raise a syntax error
            return yield token, token
          end
          tag_name = Regexp.last_match(1)
          markup   = Regexp.last_match(2)

          if tag_name == 'liquid'
            parse_context.line_number -= 1
            next parse_liquid_tag(markup, parse_context)
          end

          unless (tag = parse_context.environment.tag_for_name(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
        end
        parse_context.line_number = tokenizer.line_number
      end

      yield nil, nil
    end

    # @api private
    def self.unknown_tag_in_liquid_tag(tag, parse_context)
      Block.raise_unknown_tag(tag, 'liquid', '%}', parse_context)
    end

    # @api private
    def self.raise_missing_tag_terminator(token, parse_context)
      raise SyntaxError, parse_context.locale.t("errors.syntax.tag_termination", token: token, tag_end: TagEnd.inspect)
    end

    # @api private
    def self.raise_missing_variable_terminator(token, parse_context)
      raise SyntaxError, parse_context.locale.t("errors.syntax.variable_termination", token: token, tag_end: VariableEnd.inspect)
    end

    # @api private
    def self.render_node(context, output, node)
      node.render_to_output_buffer(context, output)
    rescue => exc
      blank_tag = !node.instance_of?(Variable) && node.blank?
      rescue_render_node(context, output, node.line_number, exc, blank_tag)
    end

    # @api private
    def self.rescue_render_node(context, output, line_number, exc, blank_tag)
      case exc
      when MemoryError
        raise
      when UndefinedVariable, UndefinedDropMethod, UndefinedFilter
        context.handle_error(exc, line_number)
      else
        error_message = context.handle_error(exc, line_number)
        unless blank_tag # conditional for backwards compatibility
          output << error_message
        end
      end
    end

    private def parse_liquid_tag(markup, parse_context)
      liquid_tag_tokenizer = parse_context.new_tokenizer(
        markup, start_line_number: parse_context.line_number, for_liquid_tag: true
      )
      parse_for_liquid_tag(liquid_tag_tokenizer, parse_context) do |end_tag_name, _end_tag_markup|
        if end_tag_name
          BlockBody.unknown_tag_in_liquid_tag(end_tag_name, parse_context)
        end
      end
    end

    private def handle_invalid_tag_token(token, parse_context)
      if token.end_with?('%}')
        yield token, token
      else
        BlockBody.raise_missing_tag_terminator(token, parse_context)
      end
    end

    private def parse_for_document(tokenizer, parse_context, &block)
      while (token = tokenizer.shift)
        next if token.empty?
        case
        when token.start_with?(TAGSTART)
          whitespace_handler(token, parse_context)
          unless token =~ FullToken
            return handle_invalid_tag_token(token, parse_context, &block)
          end
          tag_name = Regexp.last_match(2)
          markup   = Regexp.last_match(4)

          if parse_context.line_number
            # newlines inside the tag should increase the line number,
            # particularly important for multiline {% liquid %} tags
            parse_context.line_number += Regexp.last_match(1).count("\n") + Regexp.last_match(3).count("\n")
          end

          if tag_name == 'liquid'
            parse_liquid_tag(markup, parse_context)
            next
          end

          unless (tag = parse_context.environment.tag_for_name(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.match?(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)
          first_byte = previous_token.getbyte(0)
          previous_token.rstrip!
          if previous_token.empty? && parse_context[:bug_compatible_whitespace_trimming] && first_byte
            previous_token << first_byte
          end
        end
      end
      parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
    end

    def blank?
      @blank
    end

    # Remove blank strings in the block body for a control flow tag (e.g. `if`, `for`, `case`, `unless`)
    # with a blank body.
    #
    # For example, in a conditional assignment like the following
    #
    # ```
    # {% if size > max_size %}
    #   {% assign size = max_size %}
    # {% endif %}
    # ```
    #
    # we assume the intention wasn't to output the blank spaces in the `if` tag's block body, so this method
    # will remove them to reduce the render output size.
    #
    # Note that it is now preferred to use the `liquid` tag for this use case.
    def remove_blank_strings
      raise "remove_blank_strings only support being called on a blank block body" unless @blank
      @nodelist.reject! { |node| node.instance_of?(String) }
    end

    def render(context)
      render_to_output_buffer(context, +'')
    end

    def render_to_output_buffer(context, output)
      freeze unless frozen?

      context.resource_limits.increment_render_score(@nodelist.length)

      idx = 0
      while (node = @nodelist[idx])
        if node.instance_of?(String)
          output << node
        else
          render_node(context, output, node)
          # 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 %}. These tags may also occur through Block or Include tags.
          break if context.interrupt? # might have happened in a for-block
        end
        idx += 1

        context.resource_limits.increment_write_score(output)
      end

      output
    end

    private

    def render_node(context, output, node)
      BlockBody.render_node(context, output, node)
    end

    def create_variable(token, parse_context)
      if token.end_with?("}}")
        i = 2
        i = 3 if token[i] == "-"
        parse_end = token.length - 3
        parse_end -= 1 if token[parse_end] == "-"
        markup_end = parse_end - i + 1
        markup = markup_end <= 0 ? "" : token.slice(i, markup_end)

        return Variable.new(markup, parse_context)
      end

      BlockBody.raise_missing_variable_terminator(token, parse_context)
    end

    # @deprecated Use {.raise_missing_tag_terminator} instead
    def raise_missing_tag_terminator(token, parse_context)
      BlockBody.raise_missing_tag_terminator(token, parse_context)
    end

    # @deprecated Use {.raise_missing_variable_terminator} instead
    def raise_missing_variable_terminator(token, parse_context)
      BlockBody.raise_missing_variable_terminator(token, parse_context)
    end
  end
end


================================================
FILE: lib/liquid/condition.rb
================================================
# frozen_string_literal: true

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 = {
      '==' => ->(cond, left, right) {  cond.send(:equal_variables, left, right) },
      '!=' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
      '<>' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
      '<' => :<,
      '>' => :>,
      '>=' => :>=,
      '<=' => :<=,
      'contains' => 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
      rescue Encoding::CompatibilityError
        # "✅".b.include?("✅") raises Encoding::CompatibilityError despite being materially equal
        left.b.include?(right.b)
      end,
    }

    class MethodLiteral
      attr_reader :method_name, :to_s

      def initialize(method_name, to_s)
        @method_name = method_name
        @to_s = to_s
      end
    end

    @@method_literals = {
      'blank' => MethodLiteral.new(:blank?, '').freeze,
      'empty' => MethodLiteral.new(:empty?, '').freeze,
    }

    def self.operators
      @@operators
    end

    def self.parse_expression(parse_context, markup, safe: false)
      @@method_literals[markup] || parse_context.parse_expression(markup, safe: safe)
    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 = deprecated_default_context)
      condition = self
      result = nil
      loop do
        result = interpret_condition(condition.left, condition.right, condition.operator, context)

        case condition.child_relation
        when :or
          break if Liquid::Utils.to_liquid_value(result)
        when :and
          break unless Liquid::Utils.to_liquid_value(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(' ')}>"
    end

    protected

    attr_reader :child_relation

    private

    def equal_variables(left, right)
      if left.is_a?(MethodLiteral)
        return call_method_literal(left, right)
      end

      if right.is_a?(MethodLiteral)
        return call_method_literal(right, left)
      end

      left == right
    end

    def call_method_literal(literal, value)
      method_name = literal.method_name

      # If the object responds to the method (e.g., ActiveSupport is loaded), use it
      if value.respond_to?(method_name)
        value.send(method_name)
      else
        # Emulate ActiveSupport's blank?/empty? to make Liquid invariant
        # to whether ActiveSupport is loaded or not
        case method_name
        when :blank?
          liquid_blank?(value)
        when :empty?
          liquid_empty?(value)
        else
          false
        end
      end
    end

    # Implement blank? semantics matching ActiveSupport
    # blank? returns true for nil, false, empty strings, whitespace-only strings,
    # empty arrays, and empty hashes
    def liquid_blank?(value)
      case value
      when NilClass, FalseClass
        true
      when TrueClass, Numeric
        false
      when String
        # Blank if empty or whitespace only (matches ActiveSupport)
        value.empty? || value.match?(/\A\s*\z/)
      when Array, Hash
        value.empty?
      else
        # Fall back to empty? if available, otherwise false
        value.respond_to?(:empty?) ? value.empty? : false
      end
    end

    # Implement empty? semantics
    # Note: nil is NOT empty. empty? checks if a collection has zero elements.
    def liquid_empty?(value)
      case value
      when String, Array, Hash
        value.empty?
      else
        value.respond_to?(:empty?) ? value.empty? : false
      end
    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  = Liquid::Utils.to_liquid_value(context.evaluate(left))
      right = Liquid::Utils.to_liquid_value(context.evaluate(right))

      operation = self.class.operators[op] || raise(Liquid::ArgumentError, "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, e.message
        end
      end
    end

    def deprecated_default_context
      warn("DEPRECATION WARNING: Condition#evaluate without a context argument is deprecated " \
        "and will be removed from Liquid 6.0.0.")
      Context.new
    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


================================================
FILE: lib/liquid/const.rb
================================================
# frozen_string_literal: true

module Liquid
  module Const
    EMPTY_HASH = {}.freeze
    EMPTY_ARRAY = [].freeze
  end
end


================================================
FILE: lib/liquid/context.rb
================================================
# frozen_string_literal: true

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, :static_registers, :static_environments
    attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters, :environment

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

    def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {}, environment = Environment.default)
      @environment = environment
      @environments = [environments]
      @environments.flatten!

      @static_environments = [static_environments].flatten(1).freeze
      @scopes              = [outer_scope || {}]
      @registers           = registers.is_a?(Registers) ? registers : Registers.new(registers)
      @errors              = []
      @partial             = false
      @strict_variables    = false
      @resource_limits     = resource_limits || ResourceLimits.new(environment.default_resource_limits)
      @base_scope_depth    = 0
      @interrupts          = []
      @filters             = []
      @global_filter       = nil
      @disabled_tags       = {}

      # Instead of constructing new StringScanner objects for each Expression parse,
      # we recycle the same one.
      @string_scanner = StringScanner.new("")

      @registers.static[:cached_partials] ||= {}
      @registers.static[:file_system] ||= environment.file_system
      @registers.static[:template_factory] ||= Liquid::TemplateFactory.new

      self.exception_renderer = environment.exception_renderer
      if rethrow_errors
        self.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
      end

      yield self if block_given?

      # Do this last, since it could result in this object being passed to a Proc in the environment
      squash_instance_assigns_with_environments
    end
    # rubocop:enable Metrics/ParameterLists

    def warnings
      @warnings ||= []
    end

    def strainer
      @strainer ||= @environment.create_strainer(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)
      check_overflow
    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 = {})
      push(new_scope)
      yield
    ensure
      pop
    end

    # Creates a new context inheriting resource limits, filters, environment etc.,
    # but with an isolated scope.
    def new_isolated_subcontext
      check_overflow

      self.class.build(
        environment: @environment,
        resource_limits: resource_limits,
        static_environments: static_environments,
        registers: Registers.new(registers),
      ).tap do |subcontext|
        subcontext.base_scope_depth   = base_scope_depth + 1
        subcontext.exception_renderer = exception_renderer
        subcontext.filters  = @filters
        subcontext.strainer = nil
        subcontext.errors   = errors
        subcontext.warnings = warnings
        subcontext.disabled_tags = @disabled_tags
      end
    end

    def clear_instance_assigns
      @scopes[0] = {}
    end

    # Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
    def []=(key, value)
      @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, @string_scanner))
    end

    def key?(key)
      find_variable(key, raise_on_not_found: false) != 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) }

      variable = if index
        lookup_and_evaluate(@scopes[index], key, raise_on_not_found: raise_on_not_found)
      else
        try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found)
      end

      # update variable's context before invoking #to_liquid
      variable.context = self if variable.respond_to?(:context=)

      liquid_variable = variable.to_liquid

      liquid_variable.context = self if variable != liquid_variable && liquid_variable.respond_to?(:context=)

      liquid_variable
    end

    def lookup_and_evaluate(obj, key, raise_on_not_found: true)
      if @strict_variables && raise_on_not_found && obj.respond_to?(:key?) && !obj.key?(key)
        raise Liquid::UndefinedVariable, "undefined variable #{key}"
      end

      value = obj[key]

      if value.is_a?(Proc) && obj.respond_to?(:[]=)
        obj[key] = value.arity == 0 ? value.call : value.call(self)
      else
        value
      end
    end

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

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

    protected

    attr_writer :base_scope_depth, :warnings, :errors, :strainer, :filters, :disabled_tags

    private

    attr_reader :base_scope_depth

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

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

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

    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


================================================
FILE: lib/liquid/deprecations.rb
================================================
# frozen_string_literal: true

require "set"

module Liquid
  class Deprecations
    class << self
      attr_accessor :warned

      Deprecations.warned = Set.new

      def warn(name, alternative)
        return if warned.include?(name)

        warned << name

        caller_location = caller_locations(2, 1).first
        Warning.warn("[DEPRECATION] #{name} is deprecated. Use #{alternative} instead. Called from #{caller_location}\n")
      end
    end
  end
end


================================================
FILE: lib/liquid/document.rb
================================================
# frozen_string_literal: true

module Liquid
  class Document
    def self.parse(tokens, parse_context)
      doc = new(parse_context)
      doc.parse(tokens, parse_context)
      doc
    end

    attr_reader :parse_context, :body

    def initialize(parse_context)
      @parse_context = parse_context
      @body = new_body
    end

    def nodelist
      @body.nodelist
    end

    def parse(tokenizer, parse_context)
      while parse_body(tokenizer)
      end
      @body.freeze
    rescue SyntaxError => e
      e.line_number ||= parse_context.line_number
      raise
    end

    def unknown_tag(tag, _markup, _tokenizer)
      case tag
      when 'else', 'end'
        raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_outer_tag", tag: tag)
      else
        raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
      end
    end

    def render_to_output_buffer(context, output)
      @body.render_to_output_buffer(context, output)
    end

    def render(context)
      render_to_output_buffer(context, +'')
    end

    private

    def new_body
      parse_context.new_block_body
    end

    def parse_body(tokenizer)
      @body.parse(tokenizer, parse_context) do |unknown_tag_name, unknown_tag_markup|
        if unknown_tag_name
          unknown_tag(unknown_tag_name, unknown_tag_markup, tokenizer)
          true
        else
          false
        end
      end
    end
  end
end


================================================
FILE: lib/liquid/drop.rb
================================================
# frozen_string_literal: true

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

    def initialize
      @context = nil
    end

    # Catch all for the method
    def liquid_method_missing(method)
      return unless @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]
        end

        whitelist = [:to_liquid] + (public_instance_methods - blacklist)
        Set.new(whitelist.map(&:to_s))
      end
    end
  end
end


================================================
FILE: lib/liquid/environment.rb
================================================
# frozen_string_literal: true

module Liquid
  # The Environment is the container for all configuration options of Liquid, such as
  # the registered tags, filters, and the default error mode.
  class Environment
    # The default error mode for all templates. This can be overridden on a
    # per-template basis.
    attr_accessor :error_mode

    # The tags that are available to use in the template.
    attr_accessor :tags

    # The strainer template which is used to store filters that are available to
    # use in templates.
    attr_accessor :strainer_template

    # The exception renderer that is used to render exceptions that are raised
    # when rendering a template
    attr_accessor :exception_renderer

    # The default file system that is used to load templates from.
    attr_accessor :file_system

    # The default resource limits that are used to limit the resources that a
    # template can consume.
    attr_accessor :default_resource_limits

    class << self
      # Creates a new environment instance.
      #
      # @param tags [Hash] The tags that are available to use in
      #  the template.
      # @param file_system The default file system that is used
      #  to load templates from.
      # @param error_mode [Symbol] The default error mode for all templates
      #  (either :strict2, :strict, :warn, or :lax).
      # @param exception_renderer [Proc] The exception renderer that is used to
      #   render exceptions.
      # @yieldparam environment [Environment] The environment instance that is being built.
      # @return [Environment] The new environment instance.
      def build(tags: nil, file_system: nil, error_mode: nil, exception_renderer: nil)
        ret = new
        ret.tags = tags if tags
        ret.file_system = file_system if file_system
        ret.error_mode = error_mode if error_mode
        ret.exception_renderer = exception_renderer if exception_renderer
        yield ret if block_given?
        ret.freeze
      end

      # Returns the default environment instance.
      #
      # @return [Environment] The default environment instance.
      def default
        @default ||= new
      end

      # Sets the default environment instance for the duration of the block
      #
      # @param environment [Environment] The environment instance to use as the default for the
      #   duration of the block.
      # @yield
      # @return [Object] The return value of the block.
      def dangerously_override(environment)
        original_default = @default
        @default = environment
        yield
      ensure
        @default = original_default
      end
    end

    # Initializes a new environment instance.
    # @api private
    def initialize
      @tags = Tags::STANDARD_TAGS.dup
      @error_mode = :lax
      @strainer_template = Class.new(StrainerTemplate).tap do |klass|
        klass.add_filter(StandardFilters)
      end
      @exception_renderer = ->(exception) { exception }
      @file_system = BlankFileSystem.new
      @default_resource_limits = Const::EMPTY_HASH
      @strainer_template_class_cache = {}
    end

    # Registers a new tag with the environment.
    #
    # @param name [String] The name of the tag.
    # @param klass [Liquid::Tag] The class that implements the tag.
    # @return [void]
    def register_tag(name, klass)
      @tags[name] = klass
    end

    # Registers a new filter with the environment.
    #
    # @param filter [Module] The module that contains the filter methods.
    # @return [void]
    def register_filter(filter)
      @strainer_template_class_cache.clear
      @strainer_template.add_filter(filter)
    end

    # Registers multiple filters with this environment.
    #
    # @param filters [Array<Module>] The modules that contain the filter methods.
    # @return [self]
    def register_filters(filters)
      @strainer_template_class_cache.clear
      filters.each { |f| @strainer_template.add_filter(f) }
      self
    end

    # Creates a new strainer instance with the given filters, caching the result
    # for faster lookup.
    #
    # @param context [Liquid::Context] The context that the strainer will be
    #   used in.
    # @param filters [Array<Module>] The filters that the strainer will have
    #   access to.
    # @return [Liquid::Strainer] The new strainer instance.
    def create_strainer(context, filters = Const::EMPTY_ARRAY)
      return @strainer_template.new(context) if filters.empty?

      strainer_template = @strainer_template_class_cache[filters] ||= begin
        klass = Class.new(@strainer_template)
        filters.each { |f| klass.add_filter(f) }
        klass
      end

      strainer_template.new(context)
    end

    # Returns the names of all the filter methods that are available to use in
    # the strainer template.
    #
    # @return [Array<String>] The names of all the filter methods.
    def filter_method_names
      @strainer_template.filter_method_names
    end

    # Returns the tag class for the given tag name.
    #
    # @param name [String] The name of the tag.
    # @return [Liquid::Tag] The tag class.
    def tag_for_name(name)
      @tags[name]
    end

    def freeze
      @tags.freeze
      # TODO: freeze the tags, currently this is not possible because of liquid-c
      # @strainer_template.freeze
      super
    end
  end
end


================================================
FILE: lib/liquid/errors.rb
================================================
# frozen_string_literal: true

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 = +""
      str << if is_a?(SyntaxError)
        "Liquid syntax error"
      else
        "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)
  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)
  DisabledError         = Class.new(Error)
  InternalError         = Class.new(Error)
  TemplateEncodingError = Class.new(Error)
end


================================================
FILE: lib/liquid/expression.rb
================================================
# frozen_string_literal: true

module Liquid
  class Expression
    LITERALS = {
      nil => nil,
      'nil' => nil,
      'null' => nil,
      '' => nil,
      'true' => true,
      'false' => false,
      'blank' => '',
      'empty' => '',
      # in lax mode, minus sign can be a VariableLookup
      # For simplicity and performace, we treat it like a literal
      '-' => VariableLookup.parse("-", nil).freeze,
    }.freeze

    DOT = ".".ord
    ZERO = "0".ord
    NINE = "9".ord
    DASH = "-".ord

    # Use an atomic group (?>...) to avoid pathological backtracing from
    # malicious input as described in https://github.com/Shopify/liquid/issues/1357
    RANGES_REGEX = /\A\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\z/
    INTEGER_REGEX = /\A(-?\d+)\z/
    FLOAT_REGEX = /\A(-?\d+)\.\d+\z/

    class << self
      def safe_parse(parser, ss = StringScanner.new(""), cache = nil)
        parse(parser.expression, ss, cache)
      end

      def parse(markup, ss = StringScanner.new(""), cache = nil)
        return unless markup

        markup = markup.strip # markup can be a frozen string

        if (markup.start_with?('"') && markup.end_with?('"')) ||
          (markup.start_with?("'") && markup.end_with?("'"))
          return markup[1..-2]
        elsif LITERALS.key?(markup)
          return LITERALS[markup]
        end

        # Cache only exists during parsing
        if cache
          return cache[markup] if cache.key?(markup)

          cache[markup] = inner_parse(markup, ss, cache).freeze
        else
          inner_parse(markup, ss, nil).freeze
        end
      end

      def inner_parse(markup, ss, cache)
        if markup.start_with?("(") && markup.end_with?(")") && markup =~ RANGES_REGEX
          return RangeLookup.parse(
            Regexp.last_match(1),
            Regexp.last_match(2),
            ss,
            cache,
          )
        end

        if (num = parse_number(markup, ss))
          num
        else
          VariableLookup.parse(markup, ss, cache)
        end
      end

      def parse_number(markup, ss)
        # check if the markup is simple integer or float
        case markup
        when INTEGER_REGEX
          return Integer(markup, 10)
        when FLOAT_REGEX
          return markup.to_f
        end

        ss.string = markup
        # the first byte must be a digit or  a dash
        byte = ss.scan_byte

        return false if byte != DASH && (byte < ZERO || byte > NINE)

        if byte == DASH
          peek_byte = ss.peek_byte

          # if it starts with a dash, the next byte must be a digit
          return false if peek_byte.nil? || !(peek_byte >= ZERO && peek_byte <= NINE)
        end

        # The markup could be a float with multiple dots
        first_dot_pos = nil
        num_end_pos = nil

        while (byte = ss.scan_byte)
          return false if byte != DOT && (byte < ZERO || byte > NINE)

          # we found our number and now we are just scanning the rest of the string
          next if num_end_pos

          if byte == DOT
            if first_dot_pos.nil?
              first_dot_pos = ss.pos
            else
              # we found another dot, so we know that the number ends here
              num_end_pos = ss.pos - 1
            end
          end
        end

        num_end_pos = markup.length if ss.eos?

        if num_end_pos
          # number ends with a number "123.123"
          markup.byteslice(0, num_end_pos).to_f
        else
          # number ends with a dot "123."
          markup.byteslice(0, first_dot_pos).to_f
        end
      end
    end
  end
end


================================================
FILE: lib/liquid/extensions.rb
================================================
# frozen_string_literal: true

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


================================================
FILE: lib/liquid/file_system.rb
================================================
# frozen_string_literal: true

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")
      @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 %r{\A[^./][a-zA-Z0-9_/]+\z}.match?(template_path)

      full_path = if template_path.include?('/')
        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


================================================
FILE: lib/liquid/forloop_drop.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type object
  # @liquid_name forloop
  # @liquid_summary
  #   Information about a parent [`for` loop](/docs/api/liquid/tags/for).
  class ForloopDrop < Drop
    def initialize(name, length, parentloop)
      @name       = name
      @length     = length
      @parentloop = parentloop
      @index      = 0
    end

    # @liquid_public_docs
    # @liquid_name length
    # @liquid_summary
    #   The total number of iterations in the loop.
    # @liquid_return [number]
    attr_reader :length

    # @liquid_public_docs
    # @liquid_name parentloop
    # @liquid_summary
    #   The parent `forloop` object.
    # @liquid_description
    #   If the current `for` loop isn't nested inside another `for` loop, then `nil` is returned.
    # @liquid_return [forloop]
    attr_reader :parentloop

    attr_reader :name

    # @liquid_public_docs
    # @liquid_summary
    #   The 1-based index of the current iteration.
    # @liquid_return [number]
    def index
      @index + 1
    end

    # @liquid_public_docs
    # @liquid_summary
    #   The 0-based index of the current iteration.
    # @liquid_return [number]
    def index0
      @index
    end

    # @liquid_public_docs
    # @liquid_summary
    #   The 1-based index of the current iteration, in reverse order.
    # @liquid_return [number]
    def rindex
      @length - @index
    end

    # @liquid_public_docs
    # @liquid_summary
    #   The 0-based index of the current iteration, in reverse order.
    # @liquid_return [number]
    def rindex0
      @length - @index - 1
    end

    # @liquid_public_docs
    # @liquid_summary
    #   Returns `true` if the current iteration is the first. Returns `false` if not.
    # @liquid_return [boolean]
    def first
      @index == 0
    end

    # @liquid_public_docs
    # @liquid_summary
    #   Returns `true` if the current iteration is the last. Returns `false` if not.
    # @liquid_return [boolean]
    def last
      @index == @length - 1
    end

    protected

    def increment!
      @index += 1
    end
  end
end


================================================
FILE: lib/liquid/i18n.rb
================================================
# frozen_string_literal: true

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[Regexp.last_match(1).to_sym].to_s
      end
    end

    def deep_fetch_translation(name)
      name.split('.').reduce(locale) do |level, cur|
        level[cur] || raise(TranslationError, "Translation for #{name} does not exist in locale #{path}")
      end
    end
  end
end


================================================
FILE: lib/liquid/interrupts.rb
================================================
# frozen_string_literal: true

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"
    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


================================================
FILE: lib/liquid/lexer.rb
================================================
# frozen_string_literal: true

module Liquid
  class Lexer
    CLOSE_ROUND = [:close_round, ")"].freeze
    CLOSE_SQUARE = [:close_square, "]"].freeze
    COLON = [:colon, ":"].freeze
    COMMA = [:comma, ","].freeze
    COMPARISION_NOT_EQUAL = [:comparison, "!="].freeze
    COMPARISON_CONTAINS = [:comparison, "contains"].freeze
    COMPARISON_EQUAL = [:comparison, "=="].freeze
    COMPARISON_GREATER_THAN = [:comparison, ">"].freeze
    COMPARISON_GREATER_THAN_OR_EQUAL = [:comparison, ">="].freeze
    COMPARISON_LESS_THAN = [:comparison, "<"].freeze
    COMPARISON_LESS_THAN_OR_EQUAL = [:comparison, "<="].freeze
    COMPARISON_NOT_EQUAL_ALT = [:comparison, "<>"].freeze
    DASH = [:dash, "-"].freeze
    DOT = [:dot, "."].freeze
    DOTDOT = [:dotdot, ".."].freeze
    DOT_ORD = ".".ord
    DOUBLE_STRING_LITERAL = /"[^\"]*"/
    EOS = [:end_of_string].freeze
    IDENTIFIER            = /[a-zA-Z_][\w-]*\??/
    NUMBER_LITERAL        = /-?\d+(\.\d+)?/
    OPEN_ROUND = [:open_round, "("].freeze
    OPEN_SQUARE = [:open_square, "["].freeze
    PIPE = [:pipe, "|"].freeze
    QUESTION = [:question, "?"].freeze
    RUBY_WHITESPACE = [" ", "\t", "\r", "\n", "\f"].freeze
    SINGLE_STRING_LITERAL = /'[^\']*'/
    WHITESPACE_OR_NOTHING = /\s*/

    SINGLE_COMPARISON_TOKENS = [].tap do |table|
      table["<".ord] = COMPARISON_LESS_THAN
      table[">".ord] = COMPARISON_GREATER_THAN
      table.freeze
    end

    TWO_CHARS_COMPARISON_JUMP_TABLE = [].tap do |table|
      table["=".ord] = [].tap do |sub_table|
        sub_table["=".ord] = COMPARISON_EQUAL
        sub_table.freeze
      end
      table["!".ord] = [].tap do |sub_table|
        sub_table["=".ord] = COMPARISION_NOT_EQUAL
        sub_table.freeze
      end
      table.freeze
    end

    COMPARISON_JUMP_TABLE = [].tap do |table|
      table["<".ord] = [].tap do |sub_table|
        sub_table["=".ord] = COMPARISON_LESS_THAN_OR_EQUAL
        sub_table[">".ord] = COMPARISON_NOT_EQUAL_ALT
        sub_table.freeze
      end
      table[">".ord] = [].tap do |sub_table|
        sub_table["=".ord] = COMPARISON_GREATER_THAN_OR_EQUAL
        sub_table.freeze
      end
      table.freeze
    end

    NEXT_MATCHER_JUMP_TABLE = [].tap do |table|
      "a".upto("z") do |c|
        table[c.ord] = [:id, IDENTIFIER].freeze
        table[c.upcase.ord] = [:id, IDENTIFIER].freeze
      end
      table["_".ord] = [:id, IDENTIFIER].freeze

      "0".upto("9") do |c|
        table[c.ord] = [:number, NUMBER_LITERAL].freeze
      end
      table["-".ord] = [:number, NUMBER_LITERAL].freeze

      table["'".ord] = [:string, SINGLE_STRING_LITERAL].freeze
      table["\"".ord] = [:string, DOUBLE_STRING_LITERAL].freeze
      table.freeze
    end

    SPECIAL_TABLE = [].tap do |table|
      table["|".ord] = PIPE
      table[".".ord] = DOT
      table[":".ord] = COLON
      table[",".ord] = COMMA
      table["[".ord] = OPEN_SQUARE
      table["]".ord] = CLOSE_SQUARE
      table["(".ord] = OPEN_ROUND
      table[")".ord] = CLOSE_ROUND
      table["?".ord] = QUESTION
      table["-".ord] = DASH
    end

    NUMBER_TABLE = [].tap do |table|
      "0".upto("9") do |c|
        table[c.ord] = true
      end
      table.freeze
    end

    # rubocop:disable Metrics/BlockNesting
    class << self
      def tokenize(ss)
        output = []

        until ss.eos?
          ss.skip(WHITESPACE_OR_NOTHING)

          break if ss.eos?

          start_pos = ss.pos
          peeked = ss.peek_byte

          if (special = SPECIAL_TABLE[peeked])
            ss.scan_byte
            # Special case for ".."
            if special == DOT && ss.peek_byte == DOT_ORD
              ss.scan_byte
              output << DOTDOT
            elsif special == DASH
              # Special case for negative numbers
              if (peeked_byte = ss.peek_byte) && NUMBER_TABLE[peeked_byte]
                ss.pos -= 1
                output << [:number, ss.scan(NUMBER_LITERAL)]
              else
                output << special
              end
            else
              output << special
            end
          elsif (sub_table = TWO_CHARS_COMPARISON_JUMP_TABLE[peeked])
            ss.scan_byte
            if (peeked_byte = ss.peek_byte) && (found = sub_table[peeked_byte])
              output << found
              ss.scan_byte
            else
              raise_syntax_error(start_pos, ss)
            end
          elsif (sub_table = COMPARISON_JUMP_TABLE[peeked])
            ss.scan_byte
            if (peeked_byte = ss.peek_byte) && (found = sub_table[peeked_byte])
              output << found
              ss.scan_byte
            else
              output << SINGLE_COMPARISON_TOKENS[peeked]
            end
          else
            type, pattern = NEXT_MATCHER_JUMP_TABLE[peeked]

            if type && (t = ss.scan(pattern))
              # Special case for "contains"
              output << if type == :id && t == "contains" && output.last&.first != :dot
                COMPARISON_CONTAINS
              else
                [type, t]
              end
            else
              raise_syntax_error(start_pos, ss)
            end
          end
        end
        # rubocop:enable Metrics/BlockNesting
        output << EOS
      rescue ::ArgumentError => e
        if e.message == "invalid byte sequence in #{ss.string.encoding}"
          raise SyntaxError, "Invalid byte sequence in #{ss.string.encoding}"
        else
          raise
        end
      end

      def raise_syntax_error(start_pos, ss)
        ss.pos = start_pos
        # the character could be a UTF-8 character, use getch to get all the bytes
        raise SyntaxError, "Unexpected character #{ss.getch}"
      end
    end
  end
end


================================================
FILE: lib/liquid/locales/en.yml
================================================
---
  errors:
    syntax:
      tag_unexpected_args: "Syntax Error in '%{tag}' - Valid syntax: %{tag}"
      block_tag_unexpected_args: "Syntax Error in '%{tag}' - Valid syntax: {% %{tag} %}{% end%{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 ...]"
      doc_invalid_nested: "Syntax Error in 'doc' - Nested doc tags are not allowed"
      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]"
      inline_comment_invalid: "Syntax error in tag '#' - Each line of comments must be prefixed by the '#' character"
      invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
      invalid_template_encoding: "Invalid template encoding"
      render: "Syntax error in tag 'render' - Template name must be a quoted string"
      table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
      table_row_invalid_attribute: "Invalid attribute '%{attribute}' in tablerow loop. Valid attributes are cols, limit, offset, and range"
      tag_never_closed: "'%{block_name}' tag was never closed"
      tag_termination: "Tag '%{token}' was not properly terminated with regexp: %{tag_end}"
      unexpected_else: "%{block_name} tag does not expect 'else' tag"
      unexpected_outer_tag: "Unexpected outer '%{tag}' tag"
      unknown_tag: "Unknown tag '%{tag}'"
      variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}"
    argument:
      include: "Argument error in tag 'include' - Illegal template name"
    disabled:
      tag: "usage is not allowed in this context"


================================================
FILE: lib/liquid/parse_context.rb
================================================
# frozen_string_literal: true

module Liquid
  class ParseContext
    attr_accessor :locale, :line_number, :trim_whitespace, :depth
    attr_reader :partial, :warnings, :error_mode, :environment

    def initialize(options = Const::EMPTY_HASH)
      @environment = options.fetch(:environment, Environment.default)
      @template_options = options ? options.dup : {}

      @locale   = @template_options[:locale] ||= I18n.new
      @warnings = []

      # constructing new StringScanner in Lexer, Tokenizer, etc is expensive
      # This StringScanner will be shared by all of them
      @string_scanner = StringScanner.new("")

      @expression_cache = if options[:expression_cache].nil?
        {}
      elsif options[:expression_cache].respond_to?(:[]) && options[:expression_cache].respond_to?(:[]=)
        options[:expression_cache]
      elsif options[:expression_cache]
        {}
      end

      self.depth   = 0
      self.partial = false
    end

    def [](option_key)
      @options[option_key]
    end

    def new_block_body
      Liquid::BlockBody.new
    end

    def new_parser(input)
      @string_scanner.string = input
      Parser.new(@string_scanner)
    end

    def new_tokenizer(source, start_line_number: nil, for_liquid_tag: false)
      Tokenizer.new(
        source: source,
        string_scanner: @string_scanner,
        line_number: start_line_number,
        for_liquid_tag: for_liquid_tag,
      )
    end

    def safe_parse_expression(parser)
      Expression.safe_parse(parser, @string_scanner, @expression_cache)
    end

    def parse_expression(markup, safe: false)
      if !safe && @error_mode == :strict2
        # parse_expression is a widely used API. To maintain backward
        # compatibility while raising awareness about strict2 parser standards,
        # the safe flag supports API users make a deliberate decision.
        #
        # In strict2 mode, markup MUST come from a string returned by the parser
        # (e.g., parser.expression). We're not calling the parser here to
        # prevent redundant parser overhead.
        raise Liquid::InternalError, "unsafe parse_expression cannot be used in strict2 mode"
      end

      Expression.parse(markup, @string_scanner, @expression_cache)
    end

    def partial=(value)
      @partial = value
      @options = value ? partial_options : @template_options

      @error_mode = @options[:error_mode] || @environment.error_mode
    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


================================================
FILE: lib/liquid/parse_tree_visitor.rb
================================================
# 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) : Const::EMPTY_ARRAY
    end
  end
end


================================================
FILE: lib/liquid/parser.rb
================================================
# frozen_string_literal: true

module Liquid
  class Parser
    def initialize(input)
      ss = input.is_a?(StringScanner) ? input : StringScanner.new(input)
      @tokens = Lexer.tokenize(ss)
      @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]
      case token[0]
      when :id
        str = consume
        str << variable_lookups
      when :open_square
        str = consume.dup
        str << expression
        str << consume(:close_square)
        str << variable_lookups
      when :string, :number
        consume
      when :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 << ' '
      end

      str << expression
      str
    end

    def variable_lookups
      str = +""
      loop do
        if look(:open_square)
          str << consume
          str << expression
          str << consume(:close_square)
        elsif look(:dot)
          str << consume
          str << consume(:id)
        else
          break
        end
      end
      str
    end
  end
end


================================================
FILE: lib/liquid/parser_switching.rb
================================================
# frozen_string_literal: true

module Liquid
  module ParserSwitching
    # Do not use this.
    #
    # It's basically doing the same thing the {#parse_with_selected_parser},
    # except this will try the strict parser regardless of the error mode,
    # and fall back to the lax parser if the error mode is lax or warn,
    # except when in strict2 mode where it uses the strict2 parser.
    #
    # @deprecated Use {#parse_with_selected_parser} instead.
    def strict_parse_with_error_mode_fallback(markup)
      return strict2_parse_with_error_context(markup) if strict2_mode?

      strict_parse_with_error_context(markup)
    rescue SyntaxError => e
      case parse_context.error_mode
      when :rigid
        rigid_warn
        raise
      when :strict2
        raise
      when :strict
        raise
      when :warn
        parse_context.warnings << e
      end
      lax_parse(markup)
    end

    def parse_with_selected_parser(markup)
      case parse_context.error_mode
      when :rigid   then rigid_warn && strict2_parse_with_error_context(markup)
      when :strict2 then strict2_parse_with_error_context(markup)
      when :strict  then strict_parse_with_error_context(markup)
      when :lax     then lax_parse(markup)
      when :warn
        begin
          strict2_parse_with_error_context(markup)
        rescue SyntaxError => e
          parse_context.warnings << e
          lax_parse(markup)
        end
      end
    end

    def strict2_mode?
      parse_context.error_mode == :strict2 || parse_context.error_mode == :rigid
    end

    private

    def rigid_warn
      Deprecations.warn(':rigid', ':strict2')
    end

    def strict2_parse_with_error_context(markup)
      strict2_parse(markup)
    rescue SyntaxError => e
      e.line_number    = line_number
      e.markup_context = markup_context(markup)
      raise e
    end

    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


================================================
FILE: lib/liquid/partial_cache.rb
================================================
# frozen_string_literal: true

module Liquid
  class PartialCache
    def self.load(template_name, context:, parse_context:)
      cached_partials = context.registers[:cached_partials]
      cache_key = "#{template_name}:#{parse_context.error_mode}"
      cached = cached_partials[cache_key]
      return cached if cached

      file_system = context.registers[:file_system]
      source      = file_system.read_template_file(template_name)

      parse_context.partial = true

      template_factory = context.registers[:template_factory]
      template = template_factory.for(template_name)

      begin
        partial = template.parse(source, parse_context)
      rescue Liquid::Error => e
        e.template_name = template&.name || template_name
        raise e
      end

      partial.name ||= template_name

      cached_partials[cache_key] = partial
    ensure
      parse_context.partial = false
    end
  end
end


================================================
FILE: lib/liquid/profiler/hooks.rb
================================================
# frozen_string_literal: true

module Liquid
  module BlockBodyProfilingHook
    def render_node(context, output, node)
      if (profiler = context.profiler)
        profiler.profile_node(context.template_name, code: node.raw, line_number: node.line_number) do
          super
        end
      else
        super
      end
    end
  end
  BlockBody.prepend(BlockBodyProfilingHook)

  module DocumentProfilingHook
    def render_to_output_buffer(context, output)
      return super unless context.profiler
      context.profiler.profile(context.template_name) { super }
    end
  end
  Document.prepend(DocumentProfilingHook)

  module ContextProfilingHook
    attr_accessor :profiler

    def new_isolated_subcontext
      new_context = super
      new_context.profiler = profiler
      new_context
    end
  end
  Context.prepend(ContextProfilingHook)
end


================================================
FILE: lib/liquid/profiler.rb
================================================
# frozen_string_literal: true

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.
  #     # The top-level template name is `nil` by default, but can be set in the Liquid::Context before rendering.
  #     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, :template_name, :line_number, :children
      attr_accessor :total_time
      alias_method :render_time, :total_time
      alias_method :partial, :template_name

      def initialize(code: nil, template_name: nil, line_number: nil)
        @code = code
        @template_name = template_name
        @line_number = line_number
        @children = []
      end

      def self_time
        @self_time ||= begin
          total_children_time = 0.0
          @children.each do |child|
            total_children_time += child.total_time
          end
          @total_time - total_children_time
        end
      end
    end

    attr_reader :total_time
    alias_method :total_render_time, :total_time

    def initialize
      @root_children = []
      @current_children = nil
      @total_time = 0.0
    end

    def profile(template_name, &block)
      # nested renders are done from a tag that already has a timing node
      return yield if @current_children

      root_children = @root_children
      render_idx = root_children.length
      begin
        @current_children = root_children
        profile_node(template_name, &block)
      ensure
        @current_children = nil
        if (timing = root_children[render_idx])
          @total_time += timing.total_time
        end
      end
    end

    def children
      children = @root_children
      if children.length == 1
        children.first.children
      else
        children
      end
    end

    def each(&block)
      children.each(&block)
    end

    def [](idx)
      children[idx]
    end

    def length
      children.length
    end

    def profile_node(template_name, code: nil, line_number: nil)
      timing = Timing.new(code: code, template_name: template_name, line_number: line_number)
      parent_children = @current_children
      start_time = monotonic_time
      begin
        @current_children = timing.children
        yield
      ensure
        @current_children = parent_children
        timing.total_time = monotonic_time - start_time
        parent_children << timing
      end
    end

    private

    def monotonic_time
      Process.clock_gettime(Process::CLOCK_MONOTONIC)
    end
  end
end


================================================
FILE: lib/liquid/range_lookup.rb
================================================
# frozen_string_literal: true

module Liquid
  class RangeLookup
    def self.parse(start_markup, end_markup, string_scanner, cache = nil)
      start_obj = Expression.parse(start_markup, string_scanner, cache)
      end_obj   = Expression.parse(end_markup, string_scanner, cache)
      if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
        new(start_obj, end_obj)
      else
        begin
          start_obj.to_i..end_obj.to_i
        rescue NoMethodError
          invalid_expr = start_markup unless start_obj.respond_to?(:to_i)
          invalid_expr ||= end_markup unless end_obj.respond_to?(:to_i)
          if invalid_expr
            raise Liquid::SyntaxError, "Invalid expression type '#{invalid_expr}' in range expression"
          end

          raise
        end
      end
    end

    attr_reader :start_obj, :end_obj

    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

    class ParseTreeVisitor < Liquid::ParseTreeVisitor
      def children
        [@node.start_obj, @node.end_obj]
      end
    end
  end
end


================================================
FILE: lib/liquid/registers.rb
================================================
# frozen_string_literal: true

module Liquid
  class Registers
    attr_reader :static

    def initialize(registers = {})
      @static = registers.is_a?(Registers) ? registers.static : registers
      @changes = {}
    end

    def []=(key, value)
      @changes[key] = value
    end

    def [](key)
      if @changes.key?(key)
        @changes[key]
      else
        @static[key]
      end
    end

    def delete(key)
      @changes.delete(key)
    end

    UNDEFINED = Object.new

    def fetch(key, default = UNDEFINED, &block)
      if @changes.key?(key)
        @changes.fetch(key)
      elsif default != UNDEFINED
        if block_given?
          @static.fetch(key, &block)
        else
          @static.fetch(key, default)
        end
      else
        @static.fetch(key, &block)
      end
    end

    def key?(key)
      @changes.key?(key) || @static.key?(key)
    end
  end

  # Alias for backwards compatibility
  StaticRegisters = Registers
end


================================================
FILE: lib/liquid/resource_limits.rb
================================================
# frozen_string_literal: true

module Liquid
  class ResourceLimits
    attr_accessor :render_length_limit,
      :render_score_limit,
      :assign_score_limit,
      :cumulative_render_score_limit,
      :cumulative_assign_score_limit
    attr_reader :render_score,
      :assign_score,
      :cumulative_render_score,
      :cumulative_assign_score

    def initialize(limits)
      @render_length_limit           = limits[:render_length_limit]
      @render_score_limit            = limits[:render_score_limit]
      @assign_score_limit            = limits[:assign_score_limit]
      @cumulative_render_score_limit = limits[:cumulative_render_score_limit]
      @cumulative_assign_score_limit = limits[:cumulative_assign_score_limit]
      @cumulative_render_score = 0
      @cumulative_assign_score = 0
      reset
    end

    def increment_render_score(amount)
      @render_score += amount
      @cumulative_render_score += amount
      raise_limits_reached if @render_score_limit && @render_score > @render_score_limit
      raise_limits_reached if @cumulative_render_score_limit && @cumulative_render_score > @cumulative_render_score_limit
    end

    def increment_assign_score(amount)
      @assign_score += amount
      @cumulative_assign_score += amount
      raise_limits_reached if @assign_score_limit && @assign_score > @assign_score_limit
      raise_limits_reached if @cumulative_assign_score_limit && @cumulative_assign_score > @cumulative_assign_score_limit
    end

    # update either render_length or assign_score based on whether or not the writes are captured
    def increment_write_score(output)
      if (last_captured = @last_capture_length)
        captured = output.bytesize
        increment = captured - last_captured
        @last_capture_length = captured
        increment_assign_score(increment)
      elsif @render_length_limit && output.bytesize > @render_length_limit
        raise_limits_reached
      end
    end

    def raise_limits_reached
      @reached_limit = true
      raise MemoryError, "Memory limits exceeded"
    end

    def reached?
      @reached_limit
    end

    def reset
      @reached_limit = false
      @last_capture_length = nil
      @render_score = @assign_score = 0
      raise_limits_reached if @cumulative_render_score_limit && @cumulative_render_score > @cumulative_render_score_limit
      raise_limits_reached if @cumulative_assign_score_limit && @cumulative_assign_score > @cumulative_assign_score_limit
    end

    def with_capture
      old_capture_length = @last_capture_length
      begin
        @last_capture_length = 0
        yield
      ensure
        @last_capture_length = old_capture_length
      end
    end
  end
end


================================================
FILE: lib/liquid/standardfilters.rb
================================================
# frozen_string_literal: true

require 'cgi'
require 'base64'
require 'bigdecimal'
module Liquid
  module StandardFilters
    MAX_I32 = (1 << 31) - 1
    private_constant :MAX_I32

    MIN_I64 = -(1 << 63)
    MAX_I64 = (1 << 63) - 1
    I64_RANGE = MIN_I64..MAX_I64
    private_constant :MIN_I64, :MAX_I64, :I64_RANGE

    HTML_ESCAPE = {
      '&' => '&amp;',
      '>' => '&gt;',
      '<' => '&lt;',
      '"' => '&quot;',
      "'" => '&#39;',
    }.freeze
    HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
    STRIP_HTML_BLOCKS       = Regexp.union(
      %r{<script.*?</script>}m,
      /<!--.*?-->/m,
      %r{<style.*?</style>}m,
    )
    STRIP_HTML_TAGS = /<.*?>/m

    class << self
      def try_coerce_encoding(input, encoding:)
        original_encoding = input.encoding
        if input.encoding != encoding
          input.force_encoding(encoding)
          unless input.valid_encoding?
            input.force_encoding(original_encoding)
          end
        end
        input
      end
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Returns the size of a string or array.
    # @liquid_description
    #   The size of a string is the number of characters that the string includes. The size of an array is the number of items
    #   in the array.
    # @liquid_syntax variable | size
    # @liquid_return [number]
    def size(input)
      input.respond_to?(:size) ? input.size : 0
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Converts a string to all lowercase characters.
    # @liquid_syntax string | downcase
    # @liquid_return [string]
    def downcase(input)
      Utils.to_s(input).downcase
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Converts a string to all uppercase characters.
    # @liquid_syntax string | upcase
    # @liquid_return [string]
    def upcase(input)
      Utils.to_s(input).upcase
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Capitalizes the first word in a string and downcases the remaining characters.
    # @liquid_syntax string | capitalize
    # @liquid_return [string]
    def capitalize(input)
      Utils.to_s(input).capitalize
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Escapes special characters in HTML, such as `<>`, `'`, and `&`, and converts characters into escape sequences. The filter doesn't effect characters within the string that don’t have a corresponding escape sequence.".
    # @liquid_syntax string | escape
    # @liquid_return [string]
    def escape(input)
      CGI.escapeHTML(Utils.to_s(input)) unless input.nil?
    end
    alias_method :h, :escape

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Escapes a string without changing characters that have already been escaped.
    # @liquid_syntax string | escape_once
    # @liquid_return [string]
    def escape_once(input)
      Utils.to_s(input).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Converts any URL-unsafe characters in a string to the
    #   [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding) equivalent.
    # @liquid_description
    #   > Note:
    #   > Spaces are converted to a `+` character, instead of a percent-encoded character.
    # @liquid_syntax string | url_encode
    # @liquid_return [string]
    def url_encode(input)
      CGI.escape(Utils.to_s(input)) unless input.nil?
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Decodes any [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding) characters
    #   in a string.
    # @liquid_syntax string | url_decode
    # @liquid_return [string]
    def url_decode(input)
      return if input.nil?

      result = CGI.unescape(Utils.to_s(input))
      raise Liquid::ArgumentError, "invalid byte sequence in #{result.encoding}" unless result.valid_encoding?

      result
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Encodes a string to [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
    # @liquid_syntax string | base64_encode
    # @liquid_return [string]
    def base64_encode(input)
      Base64.strict_encode64(Utils.to_s(input))
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Decodes a string in [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
    # @liquid_syntax string | base64_decode
    # @liquid_return [string]
    def base64_decode(input)
      input = Utils.to_s(input)
      StandardFilters.try_coerce_encoding(Base64.strict_decode64(input), encoding: input.encoding)
    rescue ::ArgumentError
      raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Encodes a string to URL-safe [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
    # @liquid_syntax string | base64_url_safe_encode
    # @liquid_return [string]
    def base64_url_safe_encode(input)
      Base64.urlsafe_encode64(Utils.to_s(input))
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Decodes a string in URL-safe [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
    # @liquid_syntax string | base64_url_safe_decode
    # @liquid_return [string]
    def base64_url_safe_decode(input)
      input = Utils.to_s(input)
      StandardFilters.try_coerce_encoding(Base64.urlsafe_decode64(input), encoding: input.encoding)
    rescue ::ArgumentError
      raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Returns a substring or series of array items, starting at a given 0-based index.
    # @liquid_description
    #   By default, the substring has a length of one character, and the array series has one array item. However, you can
    #   provide a second parameter to specify the number of characters or array items.
    # @liquid_syntax string | slice
    # @liquid_return [string]
    def slice(input, offset, length = nil)
      offset = Utils.to_integer(offset)
      length = length ? Utils.to_integer(length) : 1

      begin
        if input.is_a?(Array)
          input.slice(offset, length) || []
        else
          Utils.to_s(input).slice(offset, length) || ''
        end
      rescue RangeError
        if I64_RANGE.cover?(length) && I64_RANGE.cover?(offset)
          raise # unexpected error
        end
        offset = offset.clamp(I64_RANGE)
        length = length.clamp(I64_RANGE)
        retry
      end
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Truncates a string down to a given number of characters.
    # @liquid_description
    #   If the specified number of characters is less than the length of the string, then an ellipsis (`...`) is appended to
    #   the truncated string. The ellipsis is included in the character count of the truncated string.
    # @liquid_syntax string | truncate: number
    # @liquid_return [string]
    def truncate(input, length = 50, truncate_string = "...")
      return if input.nil?
      input_str = Utils.to_s(input)
      length    = Utils.to_integer(length)

      truncate_string_str = Utils.to_s(truncate_string)

      l = length - truncate_string_str.length
      l = 0 if l < 0

      input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Truncates a string down to a given number of words.
    # @liquid_description
    #   If the specified number of words is less than the number of words in the string, then an ellipsis (`...`) is appended to
    #   the truncated string.
    #
    #   > Caution:
    #   > HTML tags are treated as words, so you should strip any HTML from truncated content. If you don't strip HTML, then
    #   > closing HTML tags can be removed, which can result in unexpected behavior.
    # @liquid_syntax string | truncatewords: number
    # @liquid_return [string]
    def truncatewords(input, words = 15, truncate_string = "...")
      return if input.nil?
      input = Utils.to_s(input)
      words = Utils.to_integer(words)
      words = 1 if words <= 0

      wordlist = begin
        input.split(" ", words + 1)
      rescue RangeError
        # integer too big for String#split, but we can semantically assume no truncation is needed
        return input if words + 1 > MAX_I32
        raise # unexpected error
      end
      return input if wordlist.length <= words

      wordlist.pop
      truncate_string = Utils.to_s(truncate_string)
      wordlist.join(" ").concat(truncate_string)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Splits a string into an array of substrings based on a given separator.
    # @liquid_syntax string | split: string
    # @liquid_return [array[string]]
    def split(input, pattern)
      pattern = Utils.to_s(pattern)
      input = Utils.to_s(input)
      input.split(pattern)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Removes leading and trailing whitespace and collapses consecutive whitespace to a single space.
    # @liquid_syntax string | squish
    # @liquid_return [string]
    def squish(input)
      return if input.nil?

      Utils.to_s(input).strip.gsub(/\s+/, ' ')
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Strips all whitespace from the left and right of a string.
    # @liquid_syntax string | strip
    # @liquid_return [string]
    def strip(input)
      input = Utils.to_s(input)
      input.strip
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Strips all whitespace from the left of a string.
    # @liquid_syntax string | lstrip
    # @liquid_return [string]
    def lstrip(input)
      input = Utils.to_s(input)
      input.lstrip
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Strips all whitespace from the right of a string.
    # @liquid_syntax string | rstrip
    # @liquid_return [string]
    def rstrip(input)
      input = Utils.to_s(input)
      input.rstrip
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Strips all HTML tags from a string.
    # @liquid_syntax string | strip_html
    # @liquid_return [string]
    def strip_html(input)
      input = Utils.to_s(input)
      empty  = ''
      result = input.gsub(STRIP_HTML_BLOCKS, empty)
      result.gsub!(STRIP_HTML_TAGS, empty)
      result
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Strips all newline characters (line breaks) from a string.
    # @liquid_syntax string | strip_newlines
    # @liquid_return [string]
    def strip_newlines(input)
      input = Utils.to_s(input)
      input.gsub(/\r?\n/, '')
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Combines all of the items in an array into a single string, separated by a space.
    # @liquid_syntax array | join
    # @liquid_return [string]
    def join(input, glue = ' ')
      glue = Utils.to_s(glue)
      InputIterator.new(input, context).join(glue)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Sorts the items in an array in case-sensitive alphabetical, or numerical, order.
    # @liquid_syntax array | sort
    # @liquid_return [array[untyped]]
    def sort(input, property = nil)
      ary = InputIterator.new(input, context)

      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

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Sorts the items in an array in case-insensitive alphabetical order.
    # @liquid_description
    #   > Caution:
    #   > You shouldn't use the `sort_natural` filter to sort numerical values. When comparing items an array, each item is converted to a
    #   > string, so sorting on numerical values can lead to unexpected results.
    # @liquid_syntax array | sort_natural
    # @liquid_return [array[untyped]]
    def sort_natural(input, property = nil)
      ary = InputIterator.new(input, context)

      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

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Filters an array to include only items with a specific property value.
    # @liquid_description
    #   This requires you to provide both the property name and the associated value.
    # @liquid_syntax array | where: string, string
    # @liquid_return [array[untyped]]
    def where(input, property, target_value = nil)
      filter_array(input, property, target_value) { |ary, &block| ary.select(&block) }
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Filters an array to exclude items with a specific property value.
    # @liquid_description
    #   This requires you to provide both the property name and the associated value.
    # @liquid_syntax array | reject: string, string
    # @liquid_return [array[untyped]]
    def reject(input, property, target_value = nil)
      filter_array(input, property, target_value) { |ary, &block| ary.reject(&block) }
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Tests if any item in an array has a specific property value.
    # @liquid_description
    #   This requires you to provide both the property name and the associated value.
    # @liquid_syntax array | has: string, string
    # @liquid_return [boolean]
    def has(input, property, target_value = nil)
      filter_array(input, property, target_value, false) { |ary, &block| ary.any?(&block) }
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Returns the first item in an array with a specific property value.
    # @liquid_description
    #   This requires you to provide both the property name and the associated value.
    # @liquid_syntax array | find: string, string
    # @liquid_return [untyped]
    def find(input, property, target_value = nil)
      filter_array(input, property, target_value, nil) { |ary, &block| ary.find(&block) }
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Returns the index of the first item in an array with a specific property value.
    # @liquid_description
    #   This requires you to provide both the property name and the associated value.
    # @liquid_syntax array | find_index: string, string
    # @liquid_return [number]
    def find_index(input, property, target_value = nil)
      filter_array(input, property, target_value, nil) { |ary, &block| ary.find_index(&block) }
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Removes any duplicate items in an array.
    # @liquid_syntax array | uniq
    # @liquid_return [array[untyped]]
    def uniq(input, property = nil)
      ary = InputIterator.new(input, context)

      if property.nil?
        ary.uniq
      elsif ary.empty? # The next two cases assume a non-empty array.
        []
      else
        ary.uniq do |item|
          item[property]
        rescue TypeError
          raise_property_error(property)
        rescue NoMethodError
          return nil unless item.respond_to?(:[])
          raise
        end
      end
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Reverses the order of the items in an array.
    # @liquid_syntax array | reverse
    # @liquid_return [array[untyped]]
    def reverse(input)
      ary = InputIterator.new(input, context)
      ary.reverse
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Creates an array of values from a specific property of the items in an array.
    # @liquid_syntax array | map: string
    # @liquid_return [array[untyped]]
    def map(input, property)
      InputIterator.new(input, context).map do |e|
        e = e.call if e.is_a?(Proc)

        if property == "to_liquid"
          e
        elsif e.respond_to?(:[])
          r = e[property]
          r.is_a?(Proc) ? r.call : r
        end
      end
    rescue TypeError
      raise_property_error(property)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Removes any `nil` items from an array.
    # @liquid_syntax array | compact
    # @liquid_return [array[untyped]]
    def compact(input, property = nil)
      ary = InputIterator.new(input, context)

      if property.nil?
        ary.compact
      elsif ary.empty? # The next two cases assume a non-empty array.
        []
      else
        ary.reject do |item|
          item[property].nil?
        rescue TypeError
          raise_property_error(property)
        rescue NoMethodError
          return nil unless item.respond_to?(:[])
          raise
        end
      end
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Replaces any instance of a substring inside a string with a given string.
    # @liquid_syntax string | replace: string, string
    # @liquid_return [string]
    def replace(input, string, replacement = '')
      string = Utils.to_s(string)
      replacement = Utils.to_s(replacement)
      input = Utils.to_s(input)
      input.gsub(string, replacement)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Replaces the first instance of a substring inside a string with a given string.
    # @liquid_syntax string | replace_first: string, string
    # @liquid_return [string]
    def replace_first(input, string, replacement = '')
      string = Utils.to_s(string)
      replacement = Utils.to_s(replacement)
      input = Utils.to_s(input)
      input.sub(string, replacement)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Replaces the last instance of a substring inside a string with a given string.
    # @liquid_syntax string | replace_last: string, string
    # @liquid_return [string]
    def replace_last(input, string, replacement)
      input = Utils.to_s(input)
      string = Utils.to_s(string)
      replacement = Utils.to_s(replacement)

      start_index = input.rindex(string)

      return input unless start_index

      output = input.dup
      output[start_index, string.length] = replacement
      output
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Removes any instance of a substring inside a string.
    # @liquid_syntax string | remove: string
    # @liquid_return [string]
    def remove(input, string)
      replace(input, string, '')
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Removes the first instance of a substring inside a string.
    # @liquid_syntax string | remove_first: string
    # @liquid_return [string]
    def remove_first(input, string)
      replace_first(input, string, '')
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Removes the last instance of a substring inside a string.
    # @liquid_syntax string | remove_last: string
    # @liquid_return [string]
    def remove_last(input, string)
      replace_last(input, string, '')
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Adds a given string to the end of a string.
    # @liquid_syntax string | append: string
    # @liquid_return [string]
    def append(input, string)
      input = Utils.to_s(input)
      string = Utils.to_s(string)
      input + string
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Concatenates (combines) two arrays.
    # @liquid_description
    #   > Note:
    #   > The `concat` filter won't filter out duplicates. If you want to remove duplicates, then you need to use the
    #   > [`uniq` filter](/docs/api/liquid/filters/uniq).
    # @liquid_syntax array | concat: array
    # @liquid_return [array[untyped]]
    def concat(input, array)
      unless array.respond_to?(:to_ary)
        raise ArgumentError, "concat filter requires an array argument"
      end
      InputIterator.new(input, context).concat(array)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Adds a given string to the beginning of a string.
    # @liquid_syntax string | prepend: string
    # @liquid_return [string]
    def prepend(input, string)
      input = Utils.to_s(input)
      string = Utils.to_s(string)
      string + input
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Converts newlines (`\n`) in a string to HTML line breaks (`<br>`).
    # @liquid_syntax string | newline_to_br
    # @liquid_return [string]
    def newline_to_br(input)
      input = Utils.to_s(input)
      input.gsub(/\r?\n/, "<br />\n")
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category date
    # @liquid_summary
    #   Formats a date according to a specified format string.
    # @liquid_description
    #   This filter formats a date using various format specifiers. If the format string is empty,
    #   the original input is returned. If the input cannot be converted to a date, the original input is returned.
    #
    #   The following format specifiers can be used:
    #
    #   %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
    # @liquid_syntax date | date: string
    # @liquid_return [string]
    def date(input, format)
      str_format = Utils.to_s(format)
      return input if str_format.empty?

      return input unless (date = Utils.to_date(input))

      date.strftime(str_format)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Returns the first item in an array.
    # @liquid_syntax array | first
    # @liquid_return [untyped]
    def first(array)
      # ActiveSupport returns "" for empty strings, not nil
      return array[0] || "" if array.is_a?(String)
      array.first if array.respond_to?(:first)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Returns the last item in an array.
    # @liquid_syntax array | last
    # @liquid_return [untyped]
    def last(array)
      # ActiveSupport returns "" for empty strings, not nil
      return array[-1] || "" if array.is_a?(String)
      array.last if array.respond_to?(:last)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Returns the absolute value of a number.
    # @liquid_syntax number | abs
    # @liquid_return [number]
    def abs(input)
      result = Utils.to_number(input).abs
      result.is_a?(BigDecimal) ? result.to_f : result
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Adds two numbers.
    # @liquid_syntax number | plus: number
    # @liquid_return [number]
    def plus(input, operand)
      apply_operation(input, operand, :+)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Subtracts a given number from another number.
    # @liquid_syntax number | minus: number
    # @liquid_return [number]
    def minus(input, operand)
      apply_operation(input, operand, :-)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Multiplies a number by a given number.
    # @liquid_syntax number | times: number
    # @liquid_return [number]
    def times(input, operand)
      apply_operation(input, operand, :*)
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Divides a number by a given number. The `divided_by` filter produces a result of the same type as the divisor. This means if you divide by an integer, the result will be an integer, and if you divide by a float, the result will be a float.
    # @liquid_syntax number | divided_by: number
    # @liquid_return [number]
    def divided_by(input, operand)
      apply_operation(input, operand, :/)
    rescue ::ZeroDivisionError => e
      raise Liquid::ZeroDivisionError, e.message
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Returns the remainder of dividing a number by a given number.
    # @liquid_syntax number | modulo: number
    # @liquid_return [number]
    def modulo(input, operand)
      apply_operation(input, operand, :%)
    rescue ::ZeroDivisionError => e
      raise Liquid::ZeroDivisionError, e.message
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Rounds a number to the nearest integer.
    # @liquid_syntax number | round
    # @liquid_return [number]
    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

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Rounds a number up to the nearest integer.
    # @liquid_syntax number | ceil
    # @liquid_return [number]
    def ceil(input)
      Utils.to_number(input).ceil.to_i
    rescue ::FloatDomainError => e
      raise Liquid::FloatDomainError, e.message
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Rounds a number down to the nearest integer.
    # @liquid_syntax number | floor
    # @liquid_return [number]
    def floor(input)
      Utils.to_number(input).floor.to_i
    rescue ::FloatDomainError => e
      raise Liquid::FloatDomainError, e.message
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Limits a number to a minimum value.
    # @liquid_syntax number | at_least
    # @liquid_return [number]
    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

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category math
    # @liquid_summary
    #   Limits a number to a maximum value.
    # @liquid_syntax number | at_most
    # @liquid_return [number]
    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

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category default
    # @liquid_summary
    #   Sets a default value for any variable whose value is one of the following:
    #
    #   - [`empty`](/docs/api/liquid/basics#empty)
    #   - [`false`](/docs/api/liquid/basics#truthy-and-falsy)
    #   - [`nil`](/docs/api/liquid/basics#nil)
    # @liquid_syntax variable | default: variable
    # @liquid_return [untyped]
    # @liquid_optional_param allow_false: [boolean] Whether to use false values instead of the default.
    def default(input, default_value = '', options = {})
      options = {} unless options.is_a?(Hash)
      false_check = options['allow_false'] ? input.nil? : !Liquid::Utils.to_liquid_value(input)
      false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category array
    # @liquid_summary
    #   Returns the sum of all elements in an array.
    # @liquid_syntax array | sum
    # @liquid_return [number]
    def sum(input, property = nil)
      ary = InputIterator.new(input, context)
      return 0 if ary.empty?

      values_for_sum = ary.map do |item|
        if property.nil?
          item
        elsif item.respond_to?(:[])
          item[property]
        else
          0
        end
      rescue TypeError
        raise_property_error(property)
      end

      result = InputIterator.new(values_for_sum, context).sum do |item|
        Utils.to_number(item)
      end

      result.is_a?(BigDecimal) ? result.to_f : result
    end

    private

    attr_reader :context

    def filter_array(input, property, target_value, default_value = [], &block)
      ary = InputIterator.new(input, context)

      return default_value if ary.empty?

      block.call(ary) do |item|
        if target_value.nil?
          item[property]
        else
          item[property] == target_value
        end
      rescue TypeError
        raise_property_error(property)
      rescue NoMethodError
        return nil unless item.respond_to?(:[])
        raise
      end
    end

    def raise_property_error(property)
      raise Liquid::ArgumentError, "cannot select the property '#{Utils.to_s(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)
      result = a <=> b

      if result
        result
      elsif a.nil?
        1
      elsif b.nil?
        -1
      else
        raise Liquid::ArgumentError, "cannot sort values of incompatible types"
      end
    end

    def nil_safe_casecmp(a, b)
      if !a.nil? && !b.nil?
        a.to_s.casecmp(b.to_s)
      elsif a.nil? && b.nil?
        0
      else
        a.nil? ? 1 : -1
      end
    end

    class InputIterator
      include Enumerable

      def initialize(input, context)
        @context = context
        @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)
        first = true
        output = +""
        each do |item|
          if first
            first = false
          else
            output << glue
          end

          output << Liquid::Utils.to_s(item)
        end
        output
      end

      def concat(args)
        to_a.concat(args)
      end

      def reverse
        reverse_each.to_a
      end

      def uniq(&block)
        to_a.uniq do |item|
          item = Utils.to_liquid_value(item)
          block ? yield(item) : item
        end
      end

      def compact
        to_a.compact
      end

      def empty?
        @input.each { return false }
        true
      end

      def each
        @input.each do |e|
          e = e.respond_to?(:to_liquid) ? e.to_liquid : e
          e.context = @context if e.respond_to?(:context=)
          yield(e)
        end
      end
    end
  end
end


================================================
FILE: lib/liquid/strainer_template.rb
================================================
# frozen_string_literal: true

require 'set'

module Liquid
  # StrainerTemplate is the computed 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 StrainerFactory.add_global_filter,
  # Context#add_filters or Template.register_filter
  class StrainerTemplate
    def initialize(context)
      @context = context
    end

    class << self
      def add_filter(filter)
        return if 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(', ')}"
        end

        include(filter)

        filter_methods.merge(filter.public_instance_methods.map(&:to_s))
      end

      def invokable?(method)
        filter_methods.include?(method.to_s)
      end

      def inherited(subclass)
        super
        subclass.instance_variable_set(:@filter_methods, @filter_methods.dup)
      end

      def filter_method_names
        filter_methods.map(&:to_s).to_a
      end

      private

      def filter_methods
        @filter_methods ||= Set.new
      end
    end

    def invoke(method, *args)
      if self.class.invokable?(method)
        send(method, *args)
      elsif @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


================================================
FILE: lib/liquid/tablerowloop_drop.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type object
  # @liquid_name tablerowloop
  # @liquid_summary
  #   Information about a parent [`tablerow` loop](/docs/api/liquid/tags/tablerow).
  class TablerowloopDrop < Drop
    def initialize(length, cols)
      @length = length
      @row    = 1
      @col    = 1
      @cols   = cols
      @index  = 0
    end

    # @liquid_public_docs
    # @liquid_summary
    #   The total number of iterations in the loop.
    # @liquid_return [number]
    attr_reader :length

    # @liquid_public_docs
    # @liquid_summary
    #   The 1-based index of the current column.
    # @liquid_return [number]
    attr_reader :col

    # @liquid_public_docs
    # @liquid_summary
    #   The 1-based index of current row.
    # @liquid_return [number]
    attr_reader :row

    # @liquid_public_docs
    # @liquid_summary
    #   The 1-based index of the current iteration.
    # @liquid_return [number]
    def index
      @index + 1
    end

    # @liquid_public_docs
    # @liquid_summary
    #   The 0-based index of the current iteration.
    # @liquid_return [number]
    def index0
      @index
    end

    # @liquid_public_docs
    # @liquid_summary
    #   The 0-based index of the current column.
    # @liquid_return [number]
    def col0
      @col - 1
    end

    # @liquid_public_docs
    # @liquid_summary
    #   The 1-based index of the current iteration, in reverse order.
    # @liquid_return [number]
    def rindex
      @length - @index
    end

    # @liquid_public_docs
    # @liquid_summary
    #   The 0-based index of the current iteration, in reverse order.
    # @liquid_return [number]
    def rindex0
      @length - @index - 1
    end

    # @liquid_public_docs
    # @liquid_summary
    #   Returns `true` if the current iteration is the first. Returns `false` if not.
    # @liquid_return [boolean]
    def first
      @index == 0
    end

    # @liquid_public_docs
    # @liquid_summary
    #   Returns `true` if the current iteration is the last. Returns `false` if not.
    # @liquid_return [boolean]
    def last
      @index == @length - 1
    end

    # @liquid_public_docs
    # @liquid_summary
    #   Returns `true` if the current column is the first in the row. Returns `false` if not.
    # @liquid_return [boolean]
    def col_first
      @col == 1
    end

    # @liquid_public_docs
    # @liquid_summary
    #   Returns `true` if the current column is the last in the row. Returns `false` if not.
    # @liquid_return [boolean]
    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


================================================
FILE: lib/liquid/tag/disableable.rb
================================================
# frozen_string_literal: true

module Liquid
  class Tag
    module Disableable
      def render_to_output_buffer(context, output)
        if context.tag_disabled?(tag_name)
          output << disabled_error(context)
          return
        end
        super
      end

      def disabled_error(context)
        # raise then rescue the exception so that the Context#exception_renderer can re-raise it
        raise DisabledError, "#{tag_name} #{parse_context[:locale].t('errors.disabled.tag')}"
      rescue DisabledError => exc
        context.handle_error(exc, line_number)
      end
    end
  end
end


================================================
FILE: lib/liquid/tag/disabler.rb
================================================
# frozen_string_literal: true

module Liquid
  class Tag
    module Disabler
      def render_to_output_buffer(context, output)
        context.with_disabled_tags(self.class.disabled_tags) do
          super
        end
      end
    end
  end
end


================================================
FILE: lib/liquid/tag.rb
================================================
# frozen_string_literal: true

require 'liquid/tag/disabler'
require 'liquid/tag/disableable'

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, parse_context)
        tag = new(tag_name, markup, parse_context)
        tag.parse(tokenizer)
        tag
      end

      def disable_tags(*tag_names)
        tag_names += disabled_tags
        define_singleton_method(:disabled_tags) { tag_names }
        prepend(Disabler)
      end

      private :new

      protected

      def disabled_tags
        []
      end
    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)
      ''
    end

    # For backwards compatibility with custom tags. In a future release, the semantics
    # of the `render_to_output_buffer` method will become the default and the `render`
    # method will be removed.
    def render_to_output_buffer(context, output)
      render_result = render(context)
      output << render_result if render_result
      output
    end

    def blank?
      false
    end

    private

    def safe_parse_expression(parser)
      parse_context.safe_parse_expression(parser)
    end

    def parse_expression(markup, safe: false)
      parse_context.parse_expression(markup, safe: safe)
    end
  end
end


================================================
FILE: lib/liquid/tags/assign.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category variable
  # @liquid_name assign
  # @liquid_summary
  #   Creates a new variable.
  # @liquid_description
  #   You can create variables of any [basic type](/docs/api/liquid/basics#types), [object](/docs/api/liquid/objects), or object property.
  #
  #   > Caution:
  #   > Predefined Liquid objects can be overridden by variables with the same name.
  #   > To make sure that you can access all Liquid objects, make sure that your variable name doesn't match a predefined object's name.
  # @liquid_syntax
  #   {% assign variable_name = value %}
  # @liquid_syntax_keyword variable_name The name of the variable being created.
  # @liquid_syntax_keyword value The value you want to assign to the variable.
  class Assign < Tag
    Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om

    # @api private
    def self.raise_syntax_error(parse_context)
      raise Liquid::SyntaxError, parse_context.locale.t('errors.syntax.assign')
    end

    attr_reader :to, :from

    def initialize(tag_name, markup, parse_context)
      super
      if markup =~ Syntax
        @to   = Regexp.last_match(1)
        @from = Variable.new(Regexp.last_match(2), parse_context)
      else
        self.class.raise_syntax_error(parse_context)
      end
    end

    def render_to_output_buffer(context, output)
      val = @from.render(context)
      context.scopes.last[@to] = val
      context.resource_limits.increment_assign_score(assign_score_of(val))
      output
    end

    def blank?
      true
    end

    private

    def assign_score_of(val)
      if val.instance_of?(String)
        val.bytesize
      elsif val.instance_of?(Array)
        sum = 1
        # Uses #each to avoid extra allocations.
        val.each { |child| sum += assign_score_of(child) }
        sum
      elsif val.instance_of?(Hash)
        sum = 1
        val.each do |key, entry_value|
          sum += assign_score_of(key)
          sum += assign_score_of(entry_value)
        end
        sum
      else
        1
      end
    end

    class ParseTreeVisitor < Liquid::ParseTreeVisitor
      def children
        [@node.from]
      end
    end
  end
end


================================================
FILE: lib/liquid/tags/break.rb
================================================
# frozen_string_literal: true

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 %}
  #
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category iteration
  # @liquid_name break
  # @liquid_summary
  #   Stops a [`for` loop](/docs/api/liquid/tags/for) from iterating.
  # @liquid_syntax
  #   {% break %}
  class Break < Tag
    INTERRUPT = BreakInterrupt.new.freeze

    def render_to_output_buffer(context, output)
      context.push_interrupt(INTERRUPT)
      output
    end
  end
end


================================================
FILE: lib/liquid/tags/capture.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category variable
  # @liquid_name capture
  # @liquid_summary
  #   Creates a new variable with a string value.
  # @liquid_description
  #   You can create complex strings with Liquid logic and variables.
  #
  #   > Caution:
  #   > Predefined Liquid objects can be overridden by variables with the same name.
  #   > To make sure that you can access all Liquid objects, make sure that your variable name doesn't match a predefined object's name.
  # @liquid_syntax
  #   {% capture variable %}
  #     value
  #   {% endcapture %}
  # @liquid_syntax_keyword variable The name of the variable being created.
  # @liquid_syntax_keyword value The value you want to assign to the variable.
  class Capture < Block
    Syntax = /(#{VariableSignature}+)/o

    def initialize(tag_name, markup, options)
      super
      if markup =~ Syntax
        @to = Regexp.last_match(1)
      else
        raise SyntaxError, options[:locale].t("errors.syntax.capture")
      end
    end

    def render_to_output_buffer(context, output)
      context.resource_limits.with_capture do
        capture_output = render(context)
        context.scopes.last[@to] = capture_output
      end
      output
    end

    def blank?
      true
    end
  end
end


================================================
FILE: lib/liquid/tags/case.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category conditional
  # @liquid_name case
  # @liquid_summary
  #   Renders a specific expression depending on the value of a specific variable.
  # @liquid_syntax
  #   {% case variable %}
  #     {% when first_value %}
  #       first_expression
  #     {% when second_value %}
  #       second_expression
  #     {% else %}
  #       third_expression
  #   {% endcase %}
  # @liquid_syntax_keyword variable The name of the variable you want to base your case statement on.
  # @liquid_syntax_keyword first_value A specific value to check for.
  # @liquid_syntax_keyword second_value A specific value to check for.
  # @liquid_syntax_keyword first_expression An expression to be rendered when the variable's value matches `first_value`.
  # @liquid_syntax_keyword second_expression An expression to be rendered when the variable's value matches `second_value`.
  # @liquid_syntax_keyword third_expression An expression to be rendered when the variable's value has no match.
  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 = []
      parse_with_selected_parser(markup)
    end

    def parse(tokens)
      body = case_body = new_body
      body = @blocks.last.attachment while parse_body(body, tokens)
      @blocks.reverse_each do |condition|
        body = condition.attachment
        unless body.frozen?
          body.remove_blank_strings if blank?
          body.freeze
        end
      end
      case_body.freeze
    end

    def nodelist
      @blocks.map(&:attachment)
    end

    def unknown_tag(tag, markup, tokens)
      case tag
      when 'when'
        record_when_condition(markup)
      when 'else'
        record_else_condition(markup)
      else
        super
      end
    end

    def render_to_output_buffer(context, output)
      execute_else_block = true

      @blocks.each do |block|
        if block.else?
          block.attachment.render_to_output_buffer(context, output) if execute_else_block
          next
        end

        result = Liquid::Utils.to_liquid_value(
          block.evaluate(context),
        )

        if result
          execute_else_block = false
          block.attachment.render_to_output_buffer(context, output)
        end
      end

      output
    end

    private

    def strict2_parse(markup)
      parser = @parse_context.new_parser(markup)
      @left = safe_parse_expression(parser)
      parser.consume(:end_of_string)
    end

    def strict_parse(markup)
      lax_parse(markup)
    end

    def lax_parse(markup)
      if markup =~ Syntax
        @left = parse_expression(Regexp.last_match(1))
      else
        raise SyntaxError, options[:locale].t("errors.syntax.case")
      end
    end

    def record_when_condition(markup)
      body = new_body

      if strict2_mode?
        parse_strict2_when(markup, body)
      else
        parse_lax_when(markup, body)
      end
    end

    def parse_strict2_when(markup, body)
      parser = @parse_context.new_parser(markup)

      loop do
        expr = Condition.parse_expression(parse_context, parser.expression, safe: true)
        block = Condition.new(@left, '==', expr)
        block.attach(body)
        @blocks << block

        break unless parser.id?('or') || parser.consume?(:comma)
      end

      parser.consume(:end_of_string)
    end

    def parse_lax_when(markup, body)
      while markup
        unless markup =~ WhenSyntax
          raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_when")
        end

        markup = Regexp.last_match(2)

        block = Condition.new(@left, '==', Condition.parse_expression(parse_context, Regexp.last_match(1)))
        block.attach(body)
        @blocks << block
      end
    end

    def record_else_condition(markup)
      unless markup.strip.empty?
        raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_else")
      end

      block = ElseCondition.new
      block.attach(new_body)
      @blocks << block
    end

    class ParseTreeVisitor < Liquid::ParseTreeVisitor
      def children
        [@node.left] + @node.blocks
      end
    end
  end
end


================================================
FILE: lib/liquid/tags/comment.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category syntax
  # @liquid_name comment
  # @liquid_summary
  #   Prevents an expression from being rendered or output.
  # @liquid_description
  #   Any text inside `comment` tags won't be output, and any Liquid code will be parsed, but not executed.
  # @liquid_syntax
  #   {% comment %}
  #     content
  #   {% endcomment %}
  # @liquid_syntax_keyword content The content of the comment.
  class Comment < Block
    def render_to_output_buffer(_context, output)
      output
    end

    def unknown_tag(_tag, _markup, _tokens)
    end

    def blank?
      true
    end

    private

    def parse_body(body, tokenizer)
      if parse_context.depth >= MAX_DEPTH
        raise StackLevelError, "Nesting too deep"
      end

      parse_context.depth += 1
      comment_tag_depth = 1

      begin
        # Consume tokens without creating child nodes.
        # The children tag doesn't require to be a valid Liquid except the comment and raw tag.
        # The child comment and raw tag must be closed.
        while (token = tokenizer.send(:shift))
          tag_name = if tokenizer.for_liquid_tag
            next if token.empty? || token.match?(BlockBody::WhitespaceOrNothing)

            tag_name_match = BlockBody::LiquidTagToken.match(token)

            next if tag_name_match.nil?

            tag_name_match[1]
          else
            token =~ BlockBody::FullToken
            Regexp.last_match(2)
          end

          case tag_name
          when "raw"
            parse_raw_tag_body(tokenizer)
          when "comment"
            comment_tag_depth += 1
          when "endcomment"
            comment_tag_depth -= 1
          end

          if comment_tag_depth.zero?
            parse_context.trim_whitespace = (token[-3] == WhitespaceControl) unless tokenizer.for_liquid_tag
            return false
          end
        end

        raise_tag_never_closed(block_name)
      ensure
        parse_context.depth -= 1
      end

      false
    end

    def parse_raw_tag_body(tokenizer)
      while (token = tokenizer.send(:shift))
        return if token =~ BlockBody::FullTokenPossiblyInvalid && "endraw" == Regexp.last_match(2)
      end

      raise_tag_never_closed("raw")
    end
  end
end


================================================
FILE: lib/liquid/tags/continue.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category iteration
  # @liquid_name continue
  # @liquid_summary
  #   Causes a [`for` loop](/docs/api/liquid/tags/for) to skip to the next iteration.
  # @liquid_syntax
  #   {% continue %}
  class Continue < Tag
    INTERRUPT = ContinueInterrupt.new.freeze

    def render_to_output_buffer(context, output)
      context.push_interrupt(INTERRUPT)
      output
    end
  end
end


================================================
FILE: lib/liquid/tags/cycle.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category iteration
  # @liquid_name cycle
  # @liquid_summary
  #   Loops through a group of strings and outputs them one at a time for each iteration of a [`for` loop](/docs/api/liquid/tags/for).
  # @liquid_description
  #   The `cycle` tag must be used inside a `for` loop.
  #
  #   > Tip:
  #   > Use the `cycle` tag to output text in a predictable pattern. For example, to apply odd/even classes to rows in a table.
  # @liquid_syntax
  #   {% cycle string, string, ... %}
  class Cycle < Tag
    SimpleSyntax = /\A#{QuotedFragment}+/o
    NamedSyntax  = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
    UNNAMED_CYCLE_PATTERN = /\w+:0x\h{8}/

    attr_reader :variables

    def initialize(tag_name, markup, options)
      super
      parse_with_selected_parser(markup)
    end

    def named?
      @is_named
    end

    def render_to_output_buffer(context, output)
      context.registers[:cycle] ||= {}

      key       = context.evaluate(@name)
      iteration = context.registers[:cycle][key].to_i

      val = context.evaluate(@variables[iteration])

      if val.is_a?(Array)
        val = val.join
      elsif !val.is_a?(String)
        val = val.to_s
      end

      output << val

      iteration += 1
      iteration = 0 if iteration >= @variables.size

      context.registers[:cycle][key] = iteration
      output
    end

    private

    # cycle [name:] expression(, expression)*
    def strict2_parse(markup)
      p = @parse_context.new_parser(markup)

      @variables = []

      raise SyntaxError, options[:locale].t("errors.syntax.cycle") if p.look(:end_of_string)

      first_expression = safe_parse_expression(p)
      if p.look(:colon)
        # cycle name: expr1, expr2, ...
        @name = first_expression
        @is_named = true
        p.consume(:colon)
        # After the colon, parse the first variable (required for named cycles)
        @variables << maybe_dup_lookup(safe_parse_expression(p))
      else
        # cycle expr1, expr2, ...
        @variables << maybe_dup_lookup(first_expression)
      end

      # Parse remaining comma-separated expressions
      while p.consume?(:comma)
        break if p.look(:end_of_string)

        @variables << maybe_dup_lookup(safe_parse_expression(p))
      end

      p.consume(:end_of_string)

      unless @is_named
        @name = @variables.to_s
        @is_named = !@name.match?(UNNAMED_CYCLE_PATTERN)
      end
    end

    def strict_parse(markup)
      lax_parse(markup)
    end

    def lax_parse(markup)
      case markup
      when NamedSyntax
        @variables = variables_from_string(Regexp.last_match(2))
        @name      = parse_expression(Regexp.last_match(1))
        @is_named = true
      when SimpleSyntax
        @variables = variables_from_string(markup)
        @name      = @variables.to_s
        @is_named = !@name.match?(UNNAMED_CYCLE_PATTERN)
      else
        raise SyntaxError, options[:locale].t("errors.syntax.cycle")
      end
    end

    def variables_from_string(markup)
      markup.split(',').collect do |var|
        var =~ /\s*(#{QuotedFragment})\s*/o
        next unless Regexp.last_match(1)

        var = parse_expression(Regexp.last_match(1))
        maybe_dup_lookup(var)
      end.compact
    end

    # For backwards compatibility, whenever a lookup is used in an unnamed cycle,
    # we make it so that the @variables.to_s produces different strings for cycles
    # called with the same arguments (since @variables.to_s is used as the cycle counter key)
    # This makes it so {% cycle a, b %} and {% cycle a, b %} have independent counters even if a and b share value.
    # This is not true for literal values, {% cycle "a", "b" %} and {% cycle "a", "b" %} share the same counter.
    # I was really scratching my head about this one, but migrating away from this would be more headache
    # than it's worth. So we're keeping this quirk for now.
    def maybe_dup_lookup(var)
      var.is_a?(VariableLookup) ? var.dup : var
    end

    class ParseTreeVisitor < Liquid::ParseTreeVisitor
      def children
        Array(@node.variables)
      end
    end
  end
end


================================================
FILE: lib/liquid/tags/decrement.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category variable
  # @liquid_name decrement
  # @liquid_summary
  #   Creates a new variable, with a default value of -1, that's decreased by 1 with each subsequent call.
  #
  #   > Caution:
  #   > Predefined Liquid objects can be overridden by variables with the same name.
  #   > To make sure that you can access all Liquid objects, make sure that your variable name doesn't match a predefined object's name.
  # @liquid_description
  #   Variables that are declared with `decrement` are unique to the [layout](/themes/architecture/layouts), [template](/themes/architecture/templates),
  #   or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
  #   [snippets](/themes/architecture/snippets) included in the file.
  #
  #   Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/docs/api/liquid/tags/assign)
  #   and [`capture`](/docs/api/liquid/tags/capture). However, `decrement` and [`increment`](/docs/api/liquid/tags/increment) share
  #   variables.
  # @liquid_syntax
  #   {% decrement variable_name %}
  # @liquid_syntax_keyword variable_name The name of the variable being decremented.
  class Decrement < Tag
    attr_reader :variable_name

    def initialize(tag_name, markup, options)
      super
      @variable_name = markup.strip
    end

    def render_to_output_buffer(context, output)
      counter_environment = context.environments.first
      value = counter_environment[@variable_name] || 0
      value -= 1
      counter_environment[@variable_name] = value
      output << value.to_s
      output
    end
  end
end


================================================
FILE: lib/liquid/tags/doc.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category syntax
  # @liquid_name doc
  # @liquid_summary
  #   Documents template elements with annotations.
  # @liquid_description
  #   The `doc` tag allows developers to include documentation within Liquid
  #   templates. Any content inside `doc` tags is not rendered or outputted.
  #   Liquid code inside will be parsed but not executed. This facilitates
  #   tooling support for features like code completion, linting, and inline
  #   documentation.
  #
  #   For detailed documentation syntax and examples, see the
  #   [`LiquidDoc` reference](/docs/storefronts/themes/tools/liquid-doc).
  #
  # @liquid_syntax
  #   {% doc %}
  #     Renders a message.
  #
  #     @param {string} foo - A string value.
  #     @param {string} [bar] - An optional string value.
  #
  #     @example
  #     {% render 'message', foo: 'Hello', bar: 'World' %}
  #   {% enddoc %}
  class Doc < Block
    NO_UNEXPECTED_ARGS = /\A\s*\z/

    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)
        tag_name = token =~ BlockBody::FullTokenPossiblyInvalid && Regexp.last_match(2)

        raise_nested_doc_error if tag_name == @tag_name

        if tag_name == block_delimiter
          parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
          @body << Regexp.last_match(1) if Regexp.last_match(1) != ""
          return
        end
        @body << token unless token.empty?
      end

      raise_tag_never_closed(block_name)
    end

    def render_to_output_buffer(_context, output)
      output
    end

    def blank?
      @body.empty?
    end

    def nodelist
      [@body]
    end

    private

    def ensure_valid_markup(tag_name, markup, parse_context)
      unless NO_UNEXPECTED_ARGS.match?(markup)
        raise SyntaxError, parse_context.locale.t("errors.syntax.block_tag_unexpected_args", tag: tag_name)
      end
    end

    def raise_nested_doc_error
      raise SyntaxError, parse_context.locale.t("errors.syntax.doc_invalid_nested")
    end
  end
end


================================================
FILE: lib/liquid/tags/echo.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category syntax
  # @liquid_name echo
  # @liquid_summary
  #   Outputs an expression.
  # @liquid_description
  #   Using the `echo` tag is the same as wrapping an expression in curly brackets (`{{` and `}}`). However, unlike the curly
  #   bracket method, you can use the `echo` tag inside [`liquid` tags](/docs/api/liquid/tags/liquid).
  #
  #   > Tip:
  #   > You can use [filters](/docs/api/liquid/filters) on expressions inside `echo` tags.
  # @liquid_syntax
  #   {% liquid
  #     echo expression
  #   %}
  # @liquid_syntax_keyword expression The expression to be output.
  class Echo < Tag
    attr_reader :variable

    def initialize(tag_name, markup, parse_context)
      super
      @variable = Variable.new(markup, parse_context)
    end

    def render(context)
      @variable.render_to_output_buffer(context, +'')
    end

    class ParseTreeVisitor < Liquid::ParseTreeVisitor
      def children
        [@node.variable]
      end
    end
  end
end


================================================
FILE: lib/liquid/tags/for.rb
================================================
# frozen_string_literal: true

module Liquid
  # @liquid_public_docs
  # @liquid_type tag
  # @liquid_category iteration
  # @liquid_name for
  # @liquid_summary
  #   Renders an expression for every item in an array.
  # @liquid_description
  #   You can do a maximum of 50 iterations with a `for` loop. If you need to iterate over more than 50 items, then use the
  #   [`paginate` tag](/docs/api/liquid/tags/paginate) to split the items over multiple pages.
  #
  #   > Tip:
  #   > Every `for` loop has an associated [`forloop` object](/docs/api/liquid/objects/forloop) with information about the loop.
  # @liquid_syntax
  #   {% for variable in array %}
  #     expression
  #   {% endfor %}
  # @liquid_syntax_keyword variable The current item in the array.
  # @liquid_syntax_keyword array The array to iterate over.
  # @liquid_syntax_keyword expression The expression to render for each iteration.
  # @liquid_optional_param limit: [number] The number of iterations to perform.
  # @liquid_optional_param offset: [number] The 1-based index to start iterating at.
  # @liquid_optional_param range [untyped] A custom numeric range to iterate over.
  # @liquid_optional_param reversed [untyped] Iterate in reverse order.
  class For < Block
    Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{
Download .txt
gitextract_1on2h7q1/

├── .github/
│   ├── dependabot.yaml
│   └── workflows/
│       ├── cla.yml
│       └── liquid.yml
├── .gitignore
├── .rubocop.yml
├── .rubocop_todo.yml
├── .ruby-version
├── CONTRIBUTING.md
├── Gemfile
├── History.md
├── LICENSE
├── README.md
├── Rakefile
├── bin/
│   └── render
├── example/
│   └── server/
│       ├── example_servlet.rb
│       ├── liquid_servlet.rb
│       ├── server.rb
│       └── templates/
│           ├── index.liquid
│           └── products.liquid
├── lib/
│   ├── liquid/
│   │   ├── block.rb
│   │   ├── block_body.rb
│   │   ├── condition.rb
│   │   ├── const.rb
│   │   ├── context.rb
│   │   ├── deprecations.rb
│   │   ├── document.rb
│   │   ├── drop.rb
│   │   ├── environment.rb
│   │   ├── errors.rb
│   │   ├── expression.rb
│   │   ├── extensions.rb
│   │   ├── file_system.rb
│   │   ├── forloop_drop.rb
│   │   ├── i18n.rb
│   │   ├── interrupts.rb
│   │   ├── lexer.rb
│   │   ├── locales/
│   │   │   └── en.yml
│   │   ├── parse_context.rb
│   │   ├── parse_tree_visitor.rb
│   │   ├── parser.rb
│   │   ├── parser_switching.rb
│   │   ├── partial_cache.rb
│   │   ├── profiler/
│   │   │   └── hooks.rb
│   │   ├── profiler.rb
│   │   ├── range_lookup.rb
│   │   ├── registers.rb
│   │   ├── resource_limits.rb
│   │   ├── standardfilters.rb
│   │   ├── strainer_template.rb
│   │   ├── tablerowloop_drop.rb
│   │   ├── tag/
│   │   │   ├── disableable.rb
│   │   │   └── disabler.rb
│   │   ├── tag.rb
│   │   ├── tags/
│   │   │   ├── assign.rb
│   │   │   ├── break.rb
│   │   │   ├── capture.rb
│   │   │   ├── case.rb
│   │   │   ├── comment.rb
│   │   │   ├── continue.rb
│   │   │   ├── cycle.rb
│   │   │   ├── decrement.rb
│   │   │   ├── doc.rb
│   │   │   ├── echo.rb
│   │   │   ├── for.rb
│   │   │   ├── if.rb
│   │   │   ├── ifchanged.rb
│   │   │   ├── include.rb
│   │   │   ├── increment.rb
│   │   │   ├── inline_comment.rb
│   │   │   ├── raw.rb
│   │   │   ├── render.rb
│   │   │   ├── table_row.rb
│   │   │   └── unless.rb
│   │   ├── tags.rb
│   │   ├── template.rb
│   │   ├── template_factory.rb
│   │   ├── tokenizer.rb
│   │   ├── usage.rb
│   │   ├── utils.rb
│   │   ├── variable.rb
│   │   ├── variable_lookup.rb
│   │   └── version.rb
│   └── liquid.rb
├── liquid.gemspec
├── performance/
│   ├── benchmark.rb
│   ├── memory_profile.rb
│   ├── profile.rb
│   ├── shopify/
│   │   ├── comment_form.rb
│   │   ├── database.rb
│   │   ├── json_filter.rb
│   │   ├── liquid.rb
│   │   ├── money_filter.rb
│   │   ├── paginate.rb
│   │   ├── shop_filter.rb
│   │   ├── tag_filter.rb
│   │   ├── vision.database.yml
│   │   └── weight_filter.rb
│   ├── tests/
│   │   ├── dropify/
│   │   │   ├── article.liquid
│   │   │   ├── blog.liquid
│   │   │   ├── cart.liquid
│   │   │   ├── collection.liquid
│   │   │   ├── index.liquid
│   │   │   ├── page.liquid
│   │   │   ├── product.liquid
│   │   │   └── theme.liquid
│   │   ├── ripen/
│   │   │   ├── article.liquid
│   │   │   ├── blog.liquid
│   │   │   ├── cart.liquid
│   │   │   ├── collection.liquid
│   │   │   ├── index.liquid
│   │   │   ├── page.liquid
│   │   │   ├── product.liquid
│   │   │   └── theme.liquid
│   │   ├── tribble/
│   │   │   ├── 404.liquid
│   │   │   ├── article.liquid
│   │   │   ├── blog.liquid
│   │   │   ├── cart.liquid
│   │   │   ├── collection.liquid
│   │   │   ├── index.liquid
│   │   │   ├── page.liquid
│   │   │   ├── product.liquid
│   │   │   ├── search.liquid
│   │   │   └── theme.liquid
│   │   └── vogue/
│   │       ├── article.liquid
│   │       ├── blog.liquid
│   │       ├── cart.liquid
│   │       ├── collection.liquid
│   │       ├── index.liquid
│   │       ├── page.liquid
│   │       ├── product.liquid
│   │       └── theme.liquid
│   ├── theme_runner.rb
│   └── unit/
│       ├── expression_benchmark.rb
│       └── lexer_benchmark.rb
├── spec/
│   ├── ruby_liquid.rb
│   ├── ruby_liquid_lax.rb
│   ├── ruby_liquid_with_active_support.rb
│   └── ruby_liquid_yjit.rb
└── test/
    ├── fixtures/
    │   └── en_locale.yml
    ├── integration/
    │   ├── assign_test.rb
    │   ├── blank_test.rb
    │   ├── block_test.rb
    │   ├── capture_test.rb
    │   ├── context_test.rb
    │   ├── document_test.rb
    │   ├── drop_test.rb
    │   ├── error_handling_test.rb
    │   ├── expression_test.rb
    │   ├── filter_kwarg_test.rb
    │   ├── filter_test.rb
    │   ├── hash_ordering_test.rb
    │   ├── hash_rendering_test.rb
    │   ├── output_test.rb
    │   ├── parsing_quirks_test.rb
    │   ├── profiler_test.rb
    │   ├── security_test.rb
    │   ├── standard_filter_test.rb
    │   ├── tag/
    │   │   └── disableable_test.rb
    │   ├── tag_test.rb
    │   ├── tags/
    │   │   ├── break_tag_test.rb
    │   │   ├── continue_tag_test.rb
    │   │   ├── cycle_tag_test.rb
    │   │   ├── echo_test.rb
    │   │   ├── for_tag_test.rb
    │   │   ├── if_else_tag_test.rb
    │   │   ├── include_tag_test.rb
    │   │   ├── increment_tag_test.rb
    │   │   ├── inline_comment_test.rb
    │   │   ├── liquid_tag_test.rb
    │   │   ├── raw_tag_test.rb
    │   │   ├── render_tag_test.rb
    │   │   ├── standard_tag_test.rb
    │   │   ├── statements_test.rb
    │   │   ├── table_row_test.rb
    │   │   └── unless_else_tag_test.rb
    │   ├── template_test.rb
    │   ├── trim_mode_test.rb
    │   └── variable_test.rb
    ├── test_helper.rb
    └── unit/
        ├── block_unit_test.rb
        ├── condition_unit_test.rb
        ├── environment_filter_test.rb
        ├── environment_test.rb
        ├── file_system_unit_test.rb
        ├── i18n_unit_test.rb
        ├── lexer_unit_test.rb
        ├── parse_context_unit_test.rb
        ├── parse_tree_visitor_test.rb
        ├── parser_unit_test.rb
        ├── partial_cache_unit_test.rb
        ├── regexp_unit_test.rb
        ├── registers_unit_test.rb
        ├── resource_limits_unit_test.rb
        ├── strainer_template_unit_test.rb
        ├── tag_unit_test.rb
        ├── tags/
        │   ├── case_tag_unit_test.rb
        │   ├── comment_tag_unit_test.rb
        │   ├── doc_tag_unit_test.rb
        │   ├── for_tag_unit_test.rb
        │   └── if_tag_unit_test.rb
        ├── template_factory_unit_test.rb
        ├── template_unit_test.rb
        ├── tokenizer_unit_test.rb
        └── variable_unit_test.rb
Download .txt
SYMBOL INDEX (2031 symbols across 140 files)

FILE: example/server/example_servlet.rb
  type ProductsFilter (line 3) | module ProductsFilter
    function price (line 4) | def price(integer)
    function prettyprint (line 8) | def prettyprint(text)
    function count (line 12) | def count(array)
    function paragraph (line 16) | def paragraph(p)
  class Servlet (line 21) | class Servlet < LiquidServlet
    method index (line 22) | def index
    method products (line 26) | def products
    method products_list (line 32) | def products_list
    method more_products_list (line 40) | def more_products_list
    method description (line 47) | def description

FILE: example/server/liquid_servlet.rb
  class LiquidServlet (line 3) | class LiquidServlet < WEBrick::HTTPServlet::AbstractServlet
    method do_GET (line 4) | def do_GET(req, res)
    method do_POST (line 8) | def do_POST(req, res)
    method handle (line 14) | def handle(_type, req, res)
    method read_template (line 27) | def read_template(filename = @action)

FILE: lib/liquid.rb
  type Liquid (line 26) | module Liquid

FILE: lib/liquid/block.rb
  type Liquid (line 3) | module Liquid
    class Block (line 4) | class Block < Tag
      method initialize (line 7) | def initialize(tag_name, markup, options)
      method parse (line 12) | def parse(tokens)
      method render (line 20) | def render(context)
      method blank? (line 24) | def blank?
      method nodelist (line 28) | def nodelist
      method unknown_tag (line 32) | def unknown_tag(tag_name, _markup, _tokenizer)
      method raise_unknown_tag (line 37) | def self.raise_unknown_tag(tag, block_name, block_delimiter, parse_c...
      method raise_tag_never_closed (line 55) | def raise_tag_never_closed(block_name)
      method block_name (line 59) | def block_name
      method block_delimiter (line 63) | def block_delimiter
      method new_body (line 70) | def new_body
      method parse_body (line 75) | def parse_body(body, tokens)

FILE: lib/liquid/block_body.rb
  type Liquid (line 5) | module Liquid
    class BlockBody (line 6) | class BlockBody
      method initialize (line 17) | def initialize
      method parse (line 22) | def parse(tokenizer, parse_context, &block)
      method freeze (line 34) | def freeze
      method parse_for_liquid_tag (line 39) | def parse_for_liquid_tag(tokenizer, parse_context)
      method unknown_tag_in_liquid_tag (line 71) | def self.unknown_tag_in_liquid_tag(tag, parse_context)
      method raise_missing_tag_terminator (line 76) | def self.raise_missing_tag_terminator(token, parse_context)
      method raise_missing_variable_terminator (line 81) | def self.raise_missing_variable_terminator(token, parse_context)
      method render_node (line 86) | def self.render_node(context, output, node)
      method rescue_render_node (line 94) | def self.rescue_render_node(context, output, line_number, exc, blank...
      method parse_liquid_tag (line 108) | def parse_liquid_tag(markup, parse_context)
      method handle_invalid_tag_token (line 119) | def handle_invalid_tag_token(token, parse_context)
      method parse_for_document (line 127) | def parse_for_document(tokenizer, parse_context, &block)
      method whitespace_handler (line 176) | def whitespace_handler(token, parse_context)
      method blank? (line 190) | def blank?
      method remove_blank_strings (line 209) | def remove_blank_strings
      method render (line 214) | def render(context)
      method render_to_output_buffer (line 218) | def render_to_output_buffer(context, output)
      method render_node (line 244) | def render_node(context, output, node)
      method create_variable (line 248) | def create_variable(token, parse_context)
      method raise_missing_tag_terminator (line 264) | def raise_missing_tag_terminator(token, parse_context)
      method raise_missing_variable_terminator (line 269) | def raise_missing_variable_terminator(token, parse_context)

FILE: lib/liquid/condition.rb
  type Liquid (line 3) | module Liquid
    class Condition (line 11) | class Condition # :nodoc:
      class MethodLiteral (line 33) | class MethodLiteral
        method initialize (line 36) | def initialize(method_name, to_s)
      method operators (line 47) | def self.operators
      method parse_expression (line 51) | def self.parse_expression(parse_context, markup, safe: false)
      method initialize (line 58) | def initialize(left = nil, operator = nil, right = nil)
      method evaluate (line 67) | def evaluate(context = deprecated_default_context)
      method or (line 86) | def or(condition)
      method and (line 91) | def and(condition)
      method attach (line 96) | def attach(attachment)
      method else? (line 100) | def else?
      method inspect (line 104) | def inspect
      method equal_variables (line 114) | def equal_variables(left, right)
      method call_method_literal (line 126) | def call_method_literal(literal, value)
      method liquid_blank? (line 149) | def liquid_blank?(value)
      method liquid_empty? (line 168) | def liquid_empty?(value)
      method interpret_condition (line 177) | def interpret_condition(left, right, op, context)
      method deprecated_default_context (line 199) | def deprecated_default_context
      class ParseTreeVisitor (line 205) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 206) | def children
    class ElseCondition (line 217) | class ElseCondition < Condition
      method else? (line 218) | def else?
      method evaluate (line 222) | def evaluate(_context)

FILE: lib/liquid/const.rb
  type Liquid (line 3) | module Liquid
    type Const (line 4) | module Const

FILE: lib/liquid/context.rb
  type Liquid (line 3) | module Liquid
    class Context (line 16) | class Context
      method build (line 21) | def self.build(environment: Environment.default, environments: {}, o...
      method initialize (line 25) | def initialize(environments = {}, outer_scope = {}, registers = {}, ...
      method warnings (line 63) | def warnings
      method strainer (line 67) | def strainer
      method add_filters (line 75) | def add_filters(filters)
      method apply_global_filter (line 81) | def apply_global_filter(obj)
      method interrupt? (line 86) | def interrupt?
      method push_interrupt (line 91) | def push_interrupt(e)
      method pop_interrupt (line 96) | def pop_interrupt
      method handle_error (line 100) | def handle_error(e, line_number = nil)
      method invoke (line 108) | def invoke(method, *args)
      method push (line 113) | def push(new_scope = {})
      method merge (line 119) | def merge(new_scopes)
      method pop (line 124) | def pop
      method stack (line 137) | def stack(new_scope = {})
      method new_isolated_subcontext (line 146) | def new_isolated_subcontext
      method clear_instance_assigns (line 165) | def clear_instance_assigns
      method []= (line 170) | def []=(key, value)
      method [] (line 182) | def [](expression)
      method key? (line 186) | def key?(key)
      method evaluate (line 190) | def evaluate(object)
      method find_variable (line 195) | def find_variable(key, raise_on_not_found: true)
      method lookup_and_evaluate (line 216) | def lookup_and_evaluate(obj, key, raise_on_not_found: true)
      method with_disabled_tags (line 230) | def with_disabled_tags(tag_names)
      method tag_disabled? (line 241) | def tag_disabled?(tag_name)
      method try_variable_find_in_environments (line 253) | def try_variable_find_in_environments(key, raise_on_not_found:)
      method check_overflow (line 269) | def check_overflow
      method overflow? (line 273) | def overflow?
      method internal_error (line 277) | def internal_error
      method squash_instance_assigns_with_environments (line 284) | def squash_instance_assigns_with_environments

FILE: lib/liquid/deprecations.rb
  type Liquid (line 5) | module Liquid
    class Deprecations (line 6) | class Deprecations
      method warn (line 12) | def warn(name, alternative)

FILE: lib/liquid/document.rb
  type Liquid (line 3) | module Liquid
    class Document (line 4) | class Document
      method parse (line 5) | def self.parse(tokens, parse_context)
      method initialize (line 13) | def initialize(parse_context)
      method nodelist (line 18) | def nodelist
      method parse (line 22) | def parse(tokenizer, parse_context)
      method unknown_tag (line 31) | def unknown_tag(tag, _markup, _tokenizer)
      method render_to_output_buffer (line 40) | def render_to_output_buffer(context, output)
      method render (line 44) | def render(context)
      method new_body (line 50) | def new_body
      method parse_body (line 54) | def parse_body(tokenizer)

FILE: lib/liquid/drop.rb
  type Liquid (line 5) | module Liquid
    class Drop (line 25) | class Drop
      method initialize (line 28) | def initialize
      method liquid_method_missing (line 33) | def liquid_method_missing(method)
      method invoke_drop (line 39) | def invoke_drop(method_or_key)
      method key? (line 47) | def key?(_name)
      method inspect (line 51) | def inspect
      method to_liquid (line 55) | def to_liquid
      method to_s (line 59) | def to_s
      method invokable? (line 66) | def self.invokable?(method_name)
      method invokable_methods (line 70) | def self.invokable_methods

FILE: lib/liquid/environment.rb
  type Liquid (line 3) | module Liquid
    class Environment (line 6) | class Environment
      method build (line 42) | def build(tags: nil, file_system: nil, error_mode: nil, exception_re...
      method default (line 55) | def default
      method dangerously_override (line 65) | def dangerously_override(environment)
      method initialize (line 76) | def initialize
      method register_tag (line 93) | def register_tag(name, klass)
      method register_filter (line 101) | def register_filter(filter)
      method register_filters (line 110) | def register_filters(filters)
      method create_strainer (line 124) | def create_strainer(context, filters = Const::EMPTY_ARRAY)
      method filter_method_names (line 140) | def filter_method_names
      method tag_for_name (line 148) | def tag_for_name(name)
      method freeze (line 152) | def freeze

FILE: lib/liquid/errors.rb
  type Liquid (line 3) | module Liquid
    class Error (line 4) | class Error < ::StandardError
      method to_s (line 9) | def to_s(with_prefix = true)
      method message_prefix (line 24) | def message_prefix

FILE: lib/liquid/expression.rb
  type Liquid (line 3) | module Liquid
    class Expression (line 4) | class Expression
      method safe_parse (line 31) | def safe_parse(parser, ss = StringScanner.new(""), cache = nil)
      method parse (line 35) | def parse(markup, ss = StringScanner.new(""), cache = nil)
      method inner_parse (line 57) | def inner_parse(markup, ss, cache)
      method parse_number (line 74) | def parse_number(markup, ss)

FILE: lib/liquid/extensions.rb
  class String (line 6) | class String # :nodoc:
    method to_liquid (line 7) | def to_liquid
  class Symbol (line 12) | class Symbol # :nodoc:
    method to_liquid (line 13) | def to_liquid
  class Array (line 18) | class Array # :nodoc:
    method to_liquid (line 19) | def to_liquid
  class Hash (line 24) | class Hash # :nodoc:
    method to_liquid (line 25) | def to_liquid
  class Numeric (line 30) | class Numeric # :nodoc:
    method to_liquid (line 31) | def to_liquid
  class Range (line 36) | class Range # :nodoc:
    method to_liquid (line 37) | def to_liquid
  class Time (line 42) | class Time # :nodoc:
    method to_liquid (line 43) | def to_liquid
  class DateTime (line 48) | class DateTime < Date # :nodoc:
    method to_liquid (line 49) | def to_liquid
  class Date (line 54) | class Date # :nodoc:
    method to_liquid (line 55) | def to_liquid
  class TrueClass (line 60) | class TrueClass
    method to_liquid (line 61) | def to_liquid # :nodoc:
  class FalseClass (line 66) | class FalseClass
    method to_liquid (line 67) | def to_liquid # :nodoc:
  class NilClass (line 72) | class NilClass
    method to_liquid (line 73) | def to_liquid # :nodoc:

FILE: lib/liquid/file_system.rb
  type Liquid (line 3) | module Liquid
    class BlankFileSystem (line 17) | class BlankFileSystem
      method read_template_file (line 19) | def read_template_file(_template_path)
    class LocalFileSystem (line 46) | class LocalFileSystem
      method initialize (line 49) | def initialize(root, pattern = "_%s.liquid")
      method read_template_file (line 54) | def read_template_file(template_path)
      method full_path (line 61) | def full_path(template_path)

FILE: lib/liquid/forloop_drop.rb
  type Liquid (line 3) | module Liquid
    class ForloopDrop (line 9) | class ForloopDrop < Drop
      method initialize (line 10) | def initialize(name, length, parentloop)
      method index (line 39) | def index
      method index0 (line 47) | def index0
      method rindex (line 55) | def rindex
      method rindex0 (line 63) | def rindex0
      method first (line 71) | def first
      method last (line 79) | def last
      method increment! (line 85) | def increment!

FILE: lib/liquid/i18n.rb
  type Liquid (line 5) | module Liquid
    class I18n (line 6) | class I18n
      method initialize (line 13) | def initialize(path = DEFAULT_LOCALE)
      method translate (line 17) | def translate(name, vars = {})
      method locale (line 22) | def locale
      method interpolate (line 28) | def interpolate(name, vars)
      method deep_fetch_translation (line 35) | def deep_fetch_translation(name)

FILE: lib/liquid/interrupts.rb
  type Liquid (line 3) | module Liquid
    class Interrupt (line 5) | class Interrupt
      method initialize (line 8) | def initialize(message = nil)
    class BreakInterrupt (line 14) | class BreakInterrupt < Interrupt; end
    class ContinueInterrupt (line 17) | class ContinueInterrupt < Interrupt; end

FILE: lib/liquid/lexer.rb
  type Liquid (line 3) | module Liquid
    class Lexer (line 4) | class Lexer
      method tokenize (line 103) | def tokenize(ss)
      method raise_syntax_error (line 172) | def raise_syntax_error(start_pos, ss)

FILE: lib/liquid/parse_context.rb
  type Liquid (line 3) | module Liquid
    class ParseContext (line 4) | class ParseContext
      method initialize (line 8) | def initialize(options = Const::EMPTY_HASH)
      method [] (line 31) | def [](option_key)
      method new_block_body (line 35) | def new_block_body
      method new_parser (line 39) | def new_parser(input)
      method new_tokenizer (line 44) | def new_tokenizer(source, start_line_number: nil, for_liquid_tag: fa...
      method safe_parse_expression (line 53) | def safe_parse_expression(parser)
      method parse_expression (line 57) | def parse_expression(markup, safe: false)
      method partial= (line 72) | def partial=(value)
      method partial_options (line 79) | def partial_options

FILE: lib/liquid/parse_tree_visitor.rb
  type Liquid (line 3) | module Liquid
    class ParseTreeVisitor (line 4) | class ParseTreeVisitor
      method for (line 5) | def self.for(node, callbacks = Hash.new(proc {}))
      method initialize (line 13) | def initialize(node, callbacks)
      method add_callback_for (line 18) | def add_callback_for(*classes, &block)
      method visit (line 26) | def visit(context = nil)
      method children (line 38) | def children

FILE: lib/liquid/parser.rb
  type Liquid (line 3) | module Liquid
    class Parser (line 4) | class Parser
      method initialize (line 5) | def initialize(input)
      method jump (line 11) | def jump(point)
      method consume (line 15) | def consume(type = nil)
      method consume? (line 27) | def consume?(type)
      method id? (line 35) | def id?(str)
      method look (line 43) | def look(type, ahead = 0)
      method expression (line 49) | def expression
      method argument (line 74) | def argument
      method variable_lookups (line 85) | def variable_lookups

FILE: lib/liquid/parser_switching.rb
  type Liquid (line 3) | module Liquid
    type ParserSwitching (line 4) | module ParserSwitching
      function strict_parse_with_error_mode_fallback (line 13) | def strict_parse_with_error_mode_fallback(markup)
      function parse_with_selected_parser (line 32) | def parse_with_selected_parser(markup)
      function strict2_mode? (line 48) | def strict2_mode?
      function rigid_warn (line 54) | def rigid_warn
      function strict2_parse_with_error_context (line 58) | def strict2_parse_with_error_context(markup)
      function strict_parse_with_error_context (line 66) | def strict_parse_with_error_context(markup)
      function markup_context (line 74) | def markup_context(markup)

FILE: lib/liquid/partial_cache.rb
  type Liquid (line 3) | module Liquid
    class PartialCache (line 4) | class PartialCache
      method load (line 5) | def self.load(template_name, context:, parse_context:)

FILE: lib/liquid/profiler.rb
  type Liquid (line 5) | module Liquid
    class Profiler (line 45) | class Profiler
      class Timing (line 48) | class Timing
        method initialize (line 54) | def initialize(code: nil, template_name: nil, line_number: nil)
        method self_time (line 61) | def self_time
      method initialize (line 75) | def initialize
      method profile (line 81) | def profile(template_name, &block)
      method children (line 98) | def children
      method each (line 107) | def each(&block)
      method [] (line 111) | def [](idx)
      method length (line 115) | def length
      method profile_node (line 119) | def profile_node(template_name, code: nil, line_number: nil)
      method monotonic_time (line 135) | def monotonic_time

FILE: lib/liquid/profiler/hooks.rb
  type Liquid (line 3) | module Liquid
    type BlockBodyProfilingHook (line 4) | module BlockBodyProfilingHook
      function render_node (line 5) | def render_node(context, output, node)
    type DocumentProfilingHook (line 17) | module DocumentProfilingHook
      function render_to_output_buffer (line 18) | def render_to_output_buffer(context, output)
    type ContextProfilingHook (line 25) | module ContextProfilingHook
      function new_isolated_subcontext (line 28) | def new_isolated_subcontext

FILE: lib/liquid/range_lookup.rb
  type Liquid (line 3) | module Liquid
    class RangeLookup (line 4) | class RangeLookup
      method parse (line 5) | def self.parse(start_markup, end_markup, string_scanner, cache = nil)
      method initialize (line 27) | def initialize(start_obj, end_obj)
      method evaluate (line 32) | def evaluate(context)
      method to_integer (line 40) | def to_integer(input)
      class ParseTreeVisitor (line 51) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 52) | def children

FILE: lib/liquid/registers.rb
  type Liquid (line 3) | module Liquid
    class Registers (line 4) | class Registers
      method initialize (line 7) | def initialize(registers = {})
      method []= (line 12) | def []=(key, value)
      method [] (line 16) | def [](key)
      method delete (line 24) | def delete(key)
      method fetch (line 30) | def fetch(key, default = UNDEFINED, &block)
      method key? (line 44) | def key?(key)

FILE: lib/liquid/resource_limits.rb
  type Liquid (line 3) | module Liquid
    class ResourceLimits (line 4) | class ResourceLimits
      method initialize (line 15) | def initialize(limits)
      method increment_render_score (line 26) | def increment_render_score(amount)
      method increment_assign_score (line 33) | def increment_assign_score(amount)
      method increment_write_score (line 41) | def increment_write_score(output)
      method raise_limits_reached (line 52) | def raise_limits_reached
      method reached? (line 57) | def reached?
      method reset (line 61) | def reset
      method with_capture (line 69) | def with_capture

FILE: lib/liquid/standardfilters.rb
  type Liquid (line 6) | module Liquid
    type StandardFilters (line 7) | module StandardFilters
      function try_coerce_encoding (line 32) | def try_coerce_encoding(input, encoding:)
      function size (line 54) | def size(input)
      function downcase (line 65) | def downcase(input)
      function upcase (line 76) | def upcase(input)
      function capitalize (line 87) | def capitalize(input)
      function escape (line 98) | def escape(input)
      function escape_once (line 110) | def escape_once(input)
      function url_encode (line 125) | def url_encode(input)
      function url_decode (line 137) | def url_decode(input)
      function base64_encode (line 153) | def base64_encode(input)
      function base64_decode (line 164) | def base64_decode(input)
      function base64_url_safe_encode (line 178) | def base64_url_safe_encode(input)
      function base64_url_safe_decode (line 189) | def base64_url_safe_decode(input)
      function slice (line 206) | def slice(input, offset, length = nil)
      function truncate (line 236) | def truncate(input, length = 50, truncate_string = "...")
      function truncatewords (line 263) | def truncatewords(input, words = 15, truncate_string = "...")
      function split (line 290) | def split(input, pattern)
      function squish (line 303) | def squish(input)
      function strip (line 316) | def strip(input)
      function lstrip (line 328) | def lstrip(input)
      function rstrip (line 340) | def rstrip(input)
      function strip_html (line 352) | def strip_html(input)
      function strip_newlines (line 367) | def strip_newlines(input)
      function join (line 379) | def join(input, glue = ' ')
      function sort (line 391) | def sort(input, property = nil)
      function sort_natural (line 420) | def sort_natural(input, property = nil)
      function where (line 447) | def where(input, property, target_value = nil)
      function reject (line 460) | def reject(input, property, target_value = nil)
      function has (line 473) | def has(input, property, target_value = nil)
      function find (line 486) | def find(input, property, target_value = nil)
      function find_index (line 499) | def find_index(input, property, target_value = nil)
      function uniq (line 510) | def uniq(input, property = nil)
      function reverse (line 536) | def reverse(input)
      function map (line 548) | def map(input, property)
      function compact (line 570) | def compact(input, property = nil)
      function replace (line 596) | def replace(input, string, replacement = '')
      function replace_first (line 610) | def replace_first(input, string, replacement = '')
      function replace_last (line 624) | def replace_last(input, string, replacement)
      function remove (line 645) | def remove(input, string)
      function remove_first (line 656) | def remove_first(input, string)
      function remove_last (line 667) | def remove_last(input, string)
      function append (line 678) | def append(input, string)
      function concat (line 695) | def concat(input, array)
      function prepend (line 709) | def prepend(input, string)
      function newline_to_br (line 722) | def newline_to_br(input)
      function date (line 767) | def date(input, format)
      function first (line 783) | def first(array)
      function last (line 796) | def last(array)
      function abs (line 809) | def abs(input)
      function plus (line 821) | def plus(input, operand)
      function minus (line 832) | def minus(input, operand)
      function times (line 843) | def times(input, operand)
      function divided_by (line 854) | def divided_by(input, operand)
      function modulo (line 867) | def modulo(input, operand)
      function round (line 880) | def round(input, n = 0)
      function ceil (line 896) | def ceil(input)
      function floor (line 909) | def floor(input)
      function at_least (line 922) | def at_least(input, n)
      function at_most (line 937) | def at_most(input, n)
      function default (line 957) | def default(input, default_value = '', options = {})
      function sum (line 970) | def sum(input, property = nil)
      function filter_array (line 997) | def filter_array(input, property, target_value, default_value = [], ...
      function raise_property_error (line 1016) | def raise_property_error(property)
      function apply_operation (line 1020) | def apply_operation(input, operand, operation)
      function nil_safe_compare (line 1025) | def nil_safe_compare(a, b)
      function nil_safe_casecmp (line 1039) | def nil_safe_casecmp(a, b)
      class InputIterator (line 1049) | class InputIterator
        method initialize (line 1052) | def initialize(input, context)
        method join (line 1065) | def join(glue)
        method concat (line 1080) | def concat(args)
        method reverse (line 1084) | def reverse
        method uniq (line 1088) | def uniq(&block)
        method compact (line 1095) | def compact
        method empty? (line 1099) | def empty?
        method each (line 1104) | def each

FILE: lib/liquid/strainer_template.rb
  type Liquid (line 5) | module Liquid
    class StrainerTemplate (line 11) | class StrainerTemplate
      method initialize (line 12) | def initialize(context)
      method add_filter (line 17) | def add_filter(filter)
      method invokable? (line 30) | def invokable?(method)
      method inherited (line 34) | def inherited(subclass)
      method filter_method_names (line 39) | def filter_method_names
      method filter_methods (line 45) | def filter_methods
      method invoke (line 50) | def invoke(method, *args)

FILE: lib/liquid/tablerowloop_drop.rb
  type Liquid (line 3) | module Liquid
    class TablerowloopDrop (line 9) | class TablerowloopDrop < Drop
      method initialize (line 10) | def initialize(length, cols)
      method index (line 40) | def index
      method index0 (line 48) | def index0
      method col0 (line 56) | def col0
      method rindex (line 64) | def rindex
      method rindex0 (line 72) | def rindex0
      method first (line 80) | def first
      method last (line 88) | def last
      method col_first (line 96) | def col_first
      method col_last (line 104) | def col_last
      method increment! (line 110) | def increment!

FILE: lib/liquid/tag.rb
  type Liquid (line 6) | module Liquid
    class Tag (line 7) | class Tag
      method parse (line 13) | def parse(tag_name, markup, tokenizer, parse_context)
      method disable_tags (line 19) | def disable_tags(*tag_names)
      method disabled_tags (line 29) | def disabled_tags
      method initialize (line 34) | def initialize(tag_name, markup, parse_context)
      method parse (line 41) | def parse(_tokens)
      method raw (line 44) | def raw
      method name (line 48) | def name
      method render (line 52) | def render(_context)
      method render_to_output_buffer (line 59) | def render_to_output_buffer(context, output)
      method blank? (line 65) | def blank?
      method safe_parse_expression (line 71) | def safe_parse_expression(parser)
      method parse_expression (line 75) | def parse_expression(markup, safe: false)

FILE: lib/liquid/tag/disableable.rb
  type Liquid (line 3) | module Liquid
    class Tag (line 4) | class Tag
      type Disableable (line 5) | module Disableable
        function render_to_output_buffer (line 6) | def render_to_output_buffer(context, output)
        function disabled_error (line 14) | def disabled_error(context)

FILE: lib/liquid/tag/disabler.rb
  type Liquid (line 3) | module Liquid
    class Tag (line 4) | class Tag
      type Disabler (line 5) | module Disabler
        function render_to_output_buffer (line 6) | def render_to_output_buffer(context, output)

FILE: lib/liquid/tags.rb
  type Liquid (line 24) | module Liquid
    type Tags (line 25) | module Tags

FILE: lib/liquid/tags/assign.rb
  type Liquid (line 3) | module Liquid
    class Assign (line 20) | class Assign < Tag
      method raise_syntax_error (line 24) | def self.raise_syntax_error(parse_context)
      method initialize (line 30) | def initialize(tag_name, markup, parse_context)
      method render_to_output_buffer (line 40) | def render_to_output_buffer(context, output)
      method blank? (line 47) | def blank?
      method assign_score_of (line 53) | def assign_score_of(val)
      class ParseTreeVisitor (line 73) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 74) | def children

FILE: lib/liquid/tags/break.rb
  type Liquid (line 3) | module Liquid
    class Break (line 21) | class Break < Tag
      method render_to_output_buffer (line 24) | def render_to_output_buffer(context, output)

FILE: lib/liquid/tags/capture.rb
  type Liquid (line 3) | module Liquid
    class Capture (line 22) | class Capture < Block
      method initialize (line 25) | def initialize(tag_name, markup, options)
      method render_to_output_buffer (line 34) | def render_to_output_buffer(context, output)
      method blank? (line 42) | def blank?

FILE: lib/liquid/tags/case.rb
  type Liquid (line 3) | module Liquid
    class Case (line 25) | class Case < Block
      method initialize (line 31) | def initialize(tag_name, markup, options)
      method parse (line 37) | def parse(tokens)
      method nodelist (line 50) | def nodelist
      method unknown_tag (line 54) | def unknown_tag(tag, markup, tokens)
      method render_to_output_buffer (line 65) | def render_to_output_buffer(context, output)
      method strict2_parse (line 89) | def strict2_parse(markup)
      method strict_parse (line 95) | def strict_parse(markup)
      method lax_parse (line 99) | def lax_parse(markup)
      method record_when_condition (line 107) | def record_when_condition(markup)
      method parse_strict2_when (line 117) | def parse_strict2_when(markup, body)
      method parse_lax_when (line 132) | def parse_lax_when(markup, body)
      method record_else_condition (line 146) | def record_else_condition(markup)
      class ParseTreeVisitor (line 156) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 157) | def children

FILE: lib/liquid/tags/comment.rb
  type Liquid (line 3) | module Liquid
    class Comment (line 17) | class Comment < Block
      method render_to_output_buffer (line 18) | def render_to_output_buffer(_context, output)
      method unknown_tag (line 22) | def unknown_tag(_tag, _markup, _tokens)
      method blank? (line 25) | def blank?
      method parse_body (line 31) | def parse_body(body, tokenizer)
      method parse_raw_tag_body (line 80) | def parse_raw_tag_body(tokenizer)

FILE: lib/liquid/tags/continue.rb
  type Liquid (line 3) | module Liquid
    class Continue (line 12) | class Continue < Tag
      method render_to_output_buffer (line 15) | def render_to_output_buffer(context, output)

FILE: lib/liquid/tags/cycle.rb
  type Liquid (line 3) | module Liquid
    class Cycle (line 17) | class Cycle < Tag
      method initialize (line 24) | def initialize(tag_name, markup, options)
      method named? (line 29) | def named?
      method render_to_output_buffer (line 33) | def render_to_output_buffer(context, output)
      method strict2_parse (line 59) | def strict2_parse(markup)
      method strict_parse (line 94) | def strict_parse(markup)
      method lax_parse (line 98) | def lax_parse(markup)
      method variables_from_string (line 113) | def variables_from_string(markup)
      method maybe_dup_lookup (line 130) | def maybe_dup_lookup(var)
      class ParseTreeVisitor (line 134) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 135) | def children

FILE: lib/liquid/tags/decrement.rb
  type Liquid (line 3) | module Liquid
    class Decrement (line 25) | class Decrement < Tag
      method initialize (line 28) | def initialize(tag_name, markup, options)
      method render_to_output_buffer (line 33) | def render_to_output_buffer(context, output)

FILE: lib/liquid/tags/doc.rb
  type Liquid (line 3) | module Liquid
    class Doc (line 30) | class Doc < Block
      method initialize (line 33) | def initialize(tag_name, markup, parse_context)
      method parse (line 38) | def parse(tokens)
      method render_to_output_buffer (line 57) | def render_to_output_buffer(_context, output)
      method blank? (line 61) | def blank?
      method nodelist (line 65) | def nodelist
      method ensure_valid_markup (line 71) | def ensure_valid_markup(tag_name, markup, parse_context)
      method raise_nested_doc_error (line 77) | def raise_nested_doc_error

FILE: lib/liquid/tags/echo.rb
  type Liquid (line 3) | module Liquid
    class Echo (line 21) | class Echo < Tag
      method initialize (line 24) | def initialize(tag_name, markup, parse_context)
      method render (line 29) | def render(context)
      class ParseTreeVisitor (line 33) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 34) | def children

FILE: lib/liquid/tags/for.rb
  type Liquid (line 3) | module Liquid
    class For (line 27) | class For < Block
      method initialize (line 32) | def initialize(tag_name, markup, options)
      method parse (line 40) | def parse(tokens)
      method nodelist (line 52) | def nodelist
      method unknown_tag (line 56) | def unknown_tag(tag, markup, tokens)
      method render_to_output_buffer (line 61) | def render_to_output_buffer(context, output)
      method lax_parse (line 75) | def lax_parse(markup)
      method strict_parse (line 90) | def strict_parse(markup)
      method strict2_parse (line 114) | def strict2_parse(markup)
      method collection_segment (line 118) | def collection_segment(context)
      method render_segment (line 150) | def render_segment(context, output, segment)
      method set_attribute (line 181) | def set_attribute(key, expr, safe: false)
      method render_else (line 194) | def render_else(context, output)
      class ParseTreeVisitor (line 202) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 203) | def children

FILE: lib/liquid/tags/if.rb
  type Liquid (line 3) | module Liquid
    class If (line 16) | class If < Block
      method initialize (line 23) | def initialize(tag_name, markup, options)
      method nodelist (line 29) | def nodelist
      method parse (line 33) | def parse(tokens)
      method unknown_tag (line 45) | def unknown_tag(tag, markup, tokens)
      method render_to_output_buffer (line 53) | def render_to_output_buffer(context, output)
      method strict2_parse (line 69) | def strict2_parse(markup)
      method push_block (line 73) | def push_block(tag, markup)
      method parse_expression (line 84) | def parse_expression(markup, safe: false)
      method lax_parse (line 88) | def lax_parse(markup)
      method strict_parse (line 108) | def strict_parse(markup)
      method parse_binary_comparisons (line 115) | def parse_binary_comparisons(p)
      method parse_comparison (line 126) | def parse_comparison(p)
      class ParseTreeVisitor (line 136) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 137) | def children

FILE: lib/liquid/tags/ifchanged.rb
  type Liquid (line 3) | module Liquid
    class Ifchanged (line 4) | class Ifchanged < Block
      method render_to_output_buffer (line 5) | def render_to_output_buffer(context, output)

FILE: lib/liquid/tags/include.rb
  type Liquid (line 3) | module Liquid
    class Include (line 20) | class Include < Tag
      method initialize (line 28) | def initialize(tag_name, markup, options)
      method parse (line 33) | def parse(_tokens)
      method render_to_output_buffer (line 36) | def render_to_output_buffer(context, output)
      method strict2_parse (line 87) | def strict2_parse(markup)
      method strict_parse (line 107) | def strict_parse(markup)
      method lax_parse (line 111) | def lax_parse(markup)
      class ParseTreeVisitor (line 130) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 131) | def children

FILE: lib/liquid/tags/increment.rb
  type Liquid (line 3) | module Liquid
    class Increment (line 25) | class Increment < Tag
      method initialize (line 28) | def initialize(tag_name, markup, options)
      method render_to_output_buffer (line 33) | def render_to_output_buffer(context, output)

FILE: lib/liquid/tags/inline_comment.rb
  type Liquid (line 3) | module Liquid
    class InlineComment (line 4) | class InlineComment < Tag
      method initialize (line 5) | def initialize(tag_name, markup, options)
      method render_to_output_buffer (line 20) | def render_to_output_buffer(_context, output)
      method blank? (line 24) | def blank?

FILE: lib/liquid/tags/raw.rb
  type Liquid (line 3) | module Liquid
    class Raw (line 15) | class Raw < Block
      method initialize (line 18) | def initialize(tag_name, markup, parse_context)
      method parse (line 24) | def parse(tokens)
      method render_to_output_buffer (line 38) | def render_to_output_buffer(_context, output)
      method nodelist (line 43) | def nodelist
      method blank? (line 47) | def blank?
      method ensure_valid_markup (line 53) | def ensure_valid_markup(tag_name, markup, parse_context)

FILE: lib/liquid/tags/render.rb
  type Liquid (line 3) | module Liquid
    class Render (line 28) | class Render < Tag
      method initialize (line 36) | def initialize(tag_name, markup, options)
      method for_loop? (line 41) | def for_loop?
      method render_to_output_buffer (line 45) | def render_to_output_buffer(context, output)
      method render_tag (line 49) | def render_tag(context, output)
      method strict2_parse (line 88) | def strict2_parse(markup)
      method strict2_template_name (line 110) | def strict2_template_name(p)
      method strict_parse (line 114) | def strict_parse(markup)
      method lax_parse (line 118) | def lax_parse(markup)
      class ParseTreeVisitor (line 136) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 137) | def children

FILE: lib/liquid/tags/table_row.rb
  type Liquid (line 3) | module Liquid
    class TableRow (line 26) | class TableRow < Block
      method initialize (line 32) | def initialize(tag_name, markup, options)
      method strict2_parse (line 37) | def strict2_parse(markup)
      method strict_parse (line 65) | def strict_parse(markup)
      method lax_parse (line 69) | def lax_parse(markup)
      method render_to_output_buffer (line 82) | def render_to_output_buffer(context, output)
      class ParseTreeVisitor (line 123) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 124) | def children
      method to_integer (line 131) | def to_integer(value)

FILE: lib/liquid/tags/unless.rb
  type Liquid (line 5) | module Liquid
    class Unless (line 21) | class Unless < If
      method render_to_output_buffer (line 22) | def render_to_output_buffer(context, output)

FILE: lib/liquid/template.rb
  type Liquid (line 3) | module Liquid
    class Template (line 17) | class Template
      method error_mode= (line 29) | def error_mode=(mode)
      method error_mode (line 34) | def error_mode
      method default_exception_renderer= (line 38) | def default_exception_renderer=(renderer)
      method default_exception_renderer (line 43) | def default_exception_renderer
      method file_system= (line 47) | def file_system=(file_system)
      method file_system (line 52) | def file_system
      method tags (line 56) | def tags
      method register_tag (line 60) | def register_tag(name, klass)
      method register_filter (line 67) | def register_filter(mod)
      method default_resource_limits= (line 72) | def default_resource_limits=(limits)
      method default_resource_limits (line 77) | def default_resource_limits
      method parse (line 84) | def parse(source, options = {})
      method initialize (line 90) | def initialize(environment: Environment.default)
      method parse (line 98) | def parse(source, options = {})
      method registers (line 111) | def registers
      method assigns (line 115) | def assigns
      method instance_assigns (line 119) | def instance_assigns
      method errors (line 123) | def errors
      method render (line 141) | def render(*args)
      method render! (line 200) | def render!(*args)
      method render_to_output_buffer (line 205) | def render_to_output_buffer(context, output)
      method configure_options (line 211) | def configure_options(options)
      method apply_options_to_context (line 230) | def apply_options_to_context(context, options)

FILE: lib/liquid/template_factory.rb
  type Liquid (line 3) | module Liquid
    class TemplateFactory (line 4) | class TemplateFactory
      method for (line 5) | def for(_template_name)

FILE: lib/liquid/tokenizer.rb
  type Liquid (line 5) | module Liquid
    class Tokenizer (line 6) | class Tokenizer
      method initialize (line 17) | def initialize(
      method shift (line 37) | def shift
      method tokenize (line 53) | def tokenize
      method shift_normal (line 64) | def shift_normal
      method next_token (line 72) | def next_token
      method next_text_token (line 95) | def next_text_token
      method next_variable_token (line 114) | def next_variable_token
      method next_tag_token (line 147) | def next_tag_token
      method next_tag_token_with_start (line 156) | def next_tag_token_with_start(start)

FILE: lib/liquid/usage.rb
  type Liquid (line 3) | module Liquid
    type Usage (line 4) | module Usage
      function increment (line 5) | def self.increment(name)

FILE: lib/liquid/utils.rb
  type Liquid (line 3) | module Liquid
    type Utils (line 4) | module Utils
      function slice_collection (line 8) | def self.slice_collection(collection, from, to)
      function slice_collection_using_each (line 16) | def self.slice_collection_using_each(collection, from, to)
      function to_integer (line 41) | def self.to_integer(num)
      function to_number (line 51) | def self.to_number(obj)
      function to_date (line 68) | def self.to_date(obj)
      function to_liquid_value (line 88) | def self.to_liquid_value(obj)
      function to_s (line 96) | def self.to_s(obj, seen = {})
      function inspect (line 116) | def self.inspect(obj, seen = {})
      function array_inspect (line 134) | def self.array_inspect(arr, seen = {})
      function hash_inspect (line 160) | def self.hash_inspect(hash, seen = {})

FILE: lib/liquid/variable.rb
  type Liquid (line 3) | module Liquid
    class Variable (line 14) | class Variable
      method initialize (line 27) | def initialize(markup, parse_context)
      method raw (line 36) | def raw
      method markup_context (line 40) | def markup_context(markup)
      method lax_parse (line 44) | def lax_parse(markup)
      method strict_parse (line 62) | def strict_parse(markup)
      method strict2_parse (line 77) | def strict2_parse(markup)
      method parse_filterargs (line 88) | def parse_filterargs(p)
      method render (line 96) | def render(context)
      method render_to_output_buffer (line 107) | def render_to_output_buffer(context, output)
      method render_obj_to_output (line 113) | def render_obj_to_output(obj, output)
      method disabled? (line 126) | def disabled?(_context)
      method disabled_tags (line 130) | def disabled_tags
      method lax_parse_filter_expressions (line 136) | def lax_parse_filter_expressions(filter_name, unparsed_args)
      method strict2_parse_filter_expressions (line 159) | def strict2_parse_filter_expressions(p)
      method argument (line 177) | def argument(p, positional_arguments, keyword_arguments)
      method end_of_arguments? (line 188) | def end_of_arguments?(p)
      method evaluate_filter_expressions (line 192) | def evaluate_filter_expressions(context, filter_args, filter_kwargs)
      class ParseTreeVisitor (line 204) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 205) | def children

FILE: lib/liquid/variable_lookup.rb
  type Liquid (line 3) | module Liquid
    class VariableLookup (line 4) | class VariableLookup
      method parse (line 9) | def self.parse(markup, string_scanner = StringScanner.new(""), cache...
      method initialize (line 13) | def initialize(markup, string_scanner = StringScanner.new(""), cache...
      method lookup_command? (line 43) | def lookup_command?(lookup_index)
      method evaluate (line 47) | def evaluate(context)
      method == (line 93) | def ==(other)
      method state (line 99) | def state
      class ParseTreeVisitor (line 103) | class ParseTreeVisitor < Liquid::ParseTreeVisitor
        method children (line 104) | def children

FILE: lib/liquid/version.rb
  type Liquid (line 4) | module Liquid

FILE: performance/memory_profile.rb
  class Profiler (line 8) | class Profiler
    method run (line 12) | def self.run
    method initialize (line 17) | def initialize
    method profile (line 23) | def profile(phase, &block)
    method tabulate (line 40) | def tabulate
    method sanitize (line 51) | def sanitize(string)

FILE: performance/shopify/comment_form.rb
  class CommentForm (line 3) | class CommentForm < Liquid::Block
    method initialize (line 6) | def initialize(tag_name, markup, options)
    method render_to_output_buffer (line 17) | def render_to_output_buffer(context, output)
    method wrap_in_form (line 34) | def wrap_in_form(article, input)

FILE: performance/shopify/database.rb
  type Database (line 5) | module Database
    function tables (line 10) | def self.tables

FILE: performance/shopify/json_filter.rb
  type JsonFilter (line 5) | module JsonFilter
    function json (line 6) | def json(object)

FILE: performance/shopify/money_filter.rb
  type MoneyFilter (line 3) | module MoneyFilter
    function money_with_currency (line 4) | def money_with_currency(money)
    function money (line 9) | def money(money)
    function currency (line 16) | def currency

FILE: performance/shopify/paginate.rb
  class Paginate (line 3) | class Paginate < Liquid::Block
    method initialize (line 6) | def initialize(tag_name, markup, options)
    method render_to_output_buffer (line 26) | def render_to_output_buffer(context, output)
    method no_link (line 81) | def no_link(title)
    method link (line 85) | def link(title, page)
    method current_url (line 89) | def current_url

FILE: performance/shopify/shop_filter.rb
  type ShopFilter (line 3) | module ShopFilter
    function asset_url (line 4) | def asset_url(input)
    function global_asset_url (line 8) | def global_asset_url(input)
    function shopify_asset_url (line 12) | def shopify_asset_url(input)
    function script_tag (line 16) | def script_tag(url)
    function stylesheet_tag (line 20) | def stylesheet_tag(url, media = "all")
    function link_to (line 24) | def link_to(link, url, title = "")
    function img_tag (line 28) | def img_tag(url, alt = "")
    function link_to_vendor (line 32) | def link_to_vendor(vendor)
    function link_to_type (line 40) | def link_to_type(type)
    function url_for_vendor (line 48) | def url_for_vendor(vendor_title)
    function url_for_type (line 52) | def url_for_type(type_title)
    function product_img_url (line 56) | def product_img_url(url, style = 'small')
    function default_pagination (line 71) | def default_pagination(paginate)
    function pluralize (line 91) | def pluralize(input, singular, plural)
    function to_handle (line 97) | def to_handle(str)

FILE: performance/shopify/tag_filter.rb
  type TagFilter (line 3) | module TagFilter
    function link_to_tag (line 4) | def link_to_tag(label, tag)
    function highlight_active_tag (line 8) | def highlight_active_tag(tag, css_class = 'active')
    function link_to_add_tag (line 16) | def link_to_add_tag(label, tag)
    function link_to_remove_tag (line 21) | def link_to_remove_tag(label, tag)

FILE: performance/shopify/weight_filter.rb
  type WeightFilter (line 3) | module WeightFilter
    function weight (line 4) | def weight(grams)
    function weight_with_unit (line 8) | def weight_with_unit(grams)

FILE: performance/theme_runner.rb
  class ThemeRunner (line 14) | class ThemeRunner
    class FileSystem (line 15) | class FileSystem
      method initialize (line 16) | def initialize(path)
      method read_template_file (line 21) | def read_template_file(template_path)
    method initialize (line 28) | def initialize
    method compile (line 44) | def compile
    method tokenize (line 52) | def tokenize
    method run (line 65) | def run
    method render (line 72) | def render
    method render_layout (line 89) | def render_layout(template, layout, assigns)
    method compile_and_render (line 94) | def compile_and_render(template, layout, assigns, page_template, templ...
    method compile_all_tests (line 99) | def compile_all_tests
    method compile_test (line 107) | def compile_test(template, layout, assigns, page_template, template_file)
    method each_test (line 120) | def each_test
    method init_template (line 132) | def init_template(page_template, template_file)

FILE: test/integration/assign_test.rb
  class AssignTest (line 5) | class AssignTest < Minitest::Test
    method test_assign_with_hyphen_in_variable_name (line 8) | def test_assign_with_hyphen_in_variable_name
    method test_assigned_variable (line 16) | def test_assigned_variable
    method test_assign_with_filter (line 30) | def test_assign_with_filter
    method test_assign_syntax_error (line 38) | def test_assign_syntax_error
    method test_assign_uses_error_mode (line 42) | def test_assign_uses_error_mode
    method test_expression_with_whitespace_in_square_brackets (line 51) | def test_expression_with_whitespace_in_square_brackets
    method test_assign_score_exceeding_resource_limit (line 56) | def test_assign_score_exceeding_resource_limit
    method test_assign_score_exceeding_limit_from_composite_object (line 67) | def test_assign_score_exceeding_limit_from_composite_object
    method test_assign_score_of_int (line 78) | def test_assign_score_of_int
    method test_assign_score_of_string_counts_bytes (line 82) | def test_assign_score_of_string_counts_bytes
    method test_assign_score_of_array (line 88) | def test_assign_score_of_array
    method test_assign_score_of_hash (line 94) | def test_assign_score_of_hash
    class ObjectWrapperDrop (line 102) | class ObjectWrapperDrop < Liquid::Drop
      method initialize (line 103) | def initialize(obj)
      method value (line 107) | def value
    method assign_score_of (line 112) | def assign_score_of(obj)

FILE: test/integration/blank_test.rb
  class FoobarTag (line 5) | class FoobarTag < Liquid::Tag
    method render_to_output_buffer (line 6) | def render_to_output_buffer(_context, output)
  class BlankTest (line 12) | class BlankTest < Minitest::Test
    method wrap_in_for (line 16) | def wrap_in_for(body)
    method wrap_in_if (line 20) | def wrap_in_if(body)
    method wrap (line 24) | def wrap(body)
    method test_new_tags_are_not_blank_by_default (line 28) | def test_new_tags_are_not_blank_by_default
    method test_loops_are_blank (line 34) | def test_loops_are_blank
    method test_if_else_are_blank (line 38) | def test_if_else_are_blank
    method test_unless_is_blank (line 42) | def test_unless_is_blank
    method test_mark_as_blank_only_during_parsing (line 46) | def test_mark_as_blank_only_during_parsing
    method test_comments_are_blank (line 50) | def test_comments_are_blank
    method test_captures_are_blank (line 54) | def test_captures_are_blank
    method test_nested_blocks_are_blank_but_only_if_all_children_are (line 58) | def test_nested_blocks_are_blank_but_only_if_all_children_are
    method test_assigns_are_blank (line 67) | def test_assigns_are_blank
    method test_whitespace_is_blank (line 71) | def test_whitespace_is_blank
    method test_whitespace_is_not_blank_if_other_stuff_is_present (line 76) | def test_whitespace_is_not_blank_if_other_stuff_is_present
    method test_increment_is_not_blank (line 81) | def test_increment_is_not_blank
    method test_cycle_is_not_blank (line 85) | def test_cycle_is_not_blank
    method test_raw_is_not_blank (line 89) | def test_raw_is_not_blank
    method test_include_is_blank (line 93) | def test_include_is_blank
    method test_case_is_blank (line 111) | def test_case_is_blank

FILE: test/integration/block_test.rb
  class BlockTest (line 5) | class BlockTest < Minitest::Test
    method test_unexpected_end_tag (line 8) | def test_unexpected_end_tag
    method test_with_custom_tag (line 13) | def test_with_custom_tag
    method test_custom_block_tags_have_a_default_render_to_output_buffer_method_for_backwards_compatibility (line 19) | def test_custom_block_tags_have_a_default_render_to_output_buffer_meth...

FILE: test/integration/capture_test.rb
  class CaptureTest (line 5) | class CaptureTest < Minitest::Test
    method test_captures_block_content_in_variable (line 8) | def test_captures_block_content_in_variable
    method test_capture_with_hyphen_in_variable_name (line 12) | def test_capture_with_hyphen_in_variable_name
    method test_capture_to_variable_from_outer_scope_if_existing (line 20) | def test_capture_to_variable_from_outer_scope_if_existing
    method test_assigning_from_capture (line 34) | def test_assigning_from_capture
    method test_increment_assign_score_by_bytes_not_characters (line 47) | def test_increment_assign_score_by_bytes_not_characters

FILE: test/integration/context_test.rb
  class HundredCentes (line 5) | class HundredCentes
    method to_liquid (line 6) | def to_liquid
  class CentsDrop (line 11) | class CentsDrop < Liquid::Drop
    method amount (line 12) | def amount
    method non_zero? (line 16) | def non_zero?
  class ContextSensitiveDrop (line 21) | class ContextSensitiveDrop < Liquid::Drop
    method test (line 22) | def test
  class Category (line 27) | class Category
    method initialize (line 30) | def initialize(name)
    method to_liquid (line 34) | def to_liquid
  class ProductsDrop (line 39) | class ProductsDrop < Liquid::Drop
    method initialize (line 40) | def initialize(products)
    method size (line 44) | def size
    method to_liquid (line 48) | def to_liquid
  class CategoryDrop (line 57) | class CategoryDrop < Liquid::Drop
    method initialize (line 60) | def initialize(category)
  class CounterDrop (line 65) | class CounterDrop < Liquid::Drop
    method count (line 66) | def count
  class ArrayLike (line 72) | class ArrayLike
    method fetch (line 73) | def fetch(index)
    method [] (line 76) | def [](index)
    method to_liquid (line 82) | def to_liquid
  class ContextTest (line 87) | class ContextTest < Minitest::Test
    method setup (line 90) | def setup
    method test_variables (line 94) | def test_variables
    method test_variables_not_existing (line 122) | def test_variables_not_existing
    method test_scoping (line 126) | def test_scoping
    method test_length_query (line 141) | def test_length_query
    method test_hyphenated_variable (line 161) | def test_hyphenated_variable
    method test_add_filter (line 165) | def test_add_filter
    method test_only_intended_filters_make_it_there (line 183) | def test_only_intended_filters_make_it_there
    method test_add_item_in_outer_scope (line 197) | def test_add_item_in_outer_scope
    method test_add_item_in_inner_scope (line 205) | def test_add_item_in_inner_scope
    method test_hierachical_data (line 213) | def test_hierachical_data
    method test_keywords (line 219) | def test_keywords
    method test_digits (line 224) | def test_digits
    method test_strings (line 229) | def test_strings
    method test_merge (line 234) | def test_merge
    method test_array_notation (line 242) | def test_array_notation
    method test_recoursive_array_notation (line 249) | def test_recoursive_array_notation
    method test_hash_to_array_transition (line 257) | def test_hash_to_array_transition
    method test_try_first (line 271) | def test_try_first
    method test_access_hashes_with_hash_notation (line 285) | def test_access_hashes_with_hash_notation
    method test_access_variable_with_hash_notation (line 298) | def test_access_variable_with_hash_notation
    method test_access_hashes_with_hash_access_variables (line 303) | def test_access_hashes_with_hash_access_variables
    method test_hash_notation_only_for_hash_access (line 314) | def test_hash_notation_only_for_hash_access
    method test_first_can_appear_in_middle_of_callchain (line 322) | def test_first_can_appear_in_middle_of_callchain
    method test_cents (line 331) | def test_cents
    method test_nested_cents (line 336) | def test_nested_cents
    method test_cents_through_drop (line 344) | def test_cents_through_drop
    method test_nested_cents_through_drop (line 349) | def test_nested_cents_through_drop
    method test_drop_methods_with_question_marks (line 354) | def test_drop_methods_with_question_marks
    method test_context_from_within_drop (line 359) | def test_context_from_within_drop
    method test_nested_context_from_within_drop (line 364) | def test_nested_context_from_within_drop
    method test_ranges (line 369) | def test_ranges
    method test_cents_through_drop_nestedly (line 378) | def test_cents_through_drop_nestedly
    method test_drop_with_variable_called_only_once (line 386) | def test_drop_with_variable_called_only_once
    method test_drop_with_key_called_only_once (line 394) | def test_drop_with_key_called_only_once
    method test_proc_as_variable (line 402) | def test_proc_as_variable
    method test_lambda_as_variable (line 408) | def test_lambda_as_variable
    method test_nested_lambda_as_variable (line 414) | def test_nested_lambda_as_variable
    method test_array_containing_lambda_as_variable (line 420) | def test_array_containing_lambda_as_variable
    method test_lambda_is_called_once (line 426) | def test_lambda_is_called_once
    method test_nested_lambda_is_called_once (line 439) | def test_nested_lambda_is_called_once
    method test_lambda_in_array_is_called_once (line 454) | def test_lambda_in_array_is_called_once
    method test_access_to_context_from_proc (line 468) | def test_access_to_context_from_proc
    method test_to_liquid_and_context_at_first_level (line 476) | def test_to_liquid_and_context_at_first_level
    method test_interrupt_avoids_object_allocations (line 482) | def test_interrupt_avoids_object_allocations
    method test_context_initialization_with_a_proc_in_environment (line 489) | def test_context_initialization_with_a_proc_in_environment
    method test_apply_global_filter (line 496) | def test_apply_global_filter
    method test_static_environments_are_read_with_lower_priority_than_environments (line 505) | def test_static_environments_are_read_with_lower_priority_than_environ...
    method test_apply_global_filter_when_no_global_filter_exist (line 515) | def test_apply_global_filter_when_no_global_filter_exist
    method test_new_isolated_subcontext_does_not_inherit_variables (line 520) | def test_new_isolated_subcontext_does_not_inherit_variables
    method test_new_isolated_subcontext_inherits_static_environment (line 528) | def test_new_isolated_subcontext_inherits_static_environment
    method test_new_isolated_subcontext_inherits_resource_limits (line 535) | def test_new_isolated_subcontext_inherits_resource_limits
    method test_new_isolated_subcontext_inherits_exception_renderer (line 542) | def test_new_isolated_subcontext_inherits_exception_renderer
    method test_new_isolated_subcontext_does_not_inherit_non_static_registers (line 549) | def test_new_isolated_subcontext_does_not_inherit_non_static_registers
    method test_new_isolated_subcontext_inherits_static_registers (line 559) | def test_new_isolated_subcontext_inherits_static_registers
    method test_new_isolated_subcontext_registers_do_not_pollute_context (line 565) | def test_new_isolated_subcontext_registers_do_not_pollute_context
    method test_new_isolated_subcontext_inherits_filters (line 572) | def test_new_isolated_subcontext_inherits_filters
    method test_disables_tag_specified (line 586) | def test_disables_tag_specified
    method test_disables_nested_tags (line 595) | def test_disables_nested_tags
    method test_override_global_filter (line 615) | def test_override_global_filter
    method test_has_key_will_not_add_an_error_for_missing_keys (line 634) | def test_has_key_will_not_add_an_error_for_missing_keys
    method test_key_lookup_will_raise_for_missing_keys_when_strict_variables_is_enabled (line 642) | def test_key_lookup_will_raise_for_missing_keys_when_strict_variables_...
    method test_has_key_will_not_raise_for_missing_keys_when_strict_variables_is_enabled (line 650) | def test_has_key_will_not_raise_for_missing_keys_when_strict_variables...
    method test_context_always_uses_static_registers (line 657) | def test_context_always_uses_static_registers
    method test_variable_to_liquid_returns_contextual_drop (line 671) | def test_variable_to_liquid_returns_contextual_drop
    method test_new_isolated_context_inherits_parent_environment (line 690) | def test_new_isolated_context_inherits_parent_environment
    method test_newly_built_context_inherits_parent_environment (line 698) | def test_newly_built_context_inherits_parent_environment
    method assert_no_object_allocations (line 707) | def assert_no_object_allocations

FILE: test/integration/document_test.rb
  class DocumentTest (line 5) | class DocumentTest < Minitest::Test
    method test_unexpected_outer_tag (line 8) | def test_unexpected_outer_tag
    method test_unknown_tag (line 13) | def test_unknown_tag

FILE: test/integration/drop_test.rb
  class ContextDrop (line 5) | class ContextDrop < Liquid::Drop
    method scopes (line 6) | def scopes
    method scopes_as_array (line 10) | def scopes_as_array
    method loop_pos (line 14) | def loop_pos
    method liquid_method_missing (line 18) | def liquid_method_missing(method)
  class ProductDrop (line 23) | class ProductDrop < Liquid::Drop
    class TextDrop (line 24) | class TextDrop < Liquid::Drop
      method array (line 25) | def array
      method text (line 29) | def text
    class CatchallDrop (line 34) | class CatchallDrop < Liquid::Drop
      method liquid_method_missing (line 35) | def liquid_method_missing(method)
    method texts (line 40) | def texts
    method catchall (line 44) | def catchall
    method context (line 48) | def context
    method callmenot (line 54) | def callmenot
  class EnumerableDrop (line 59) | class EnumerableDrop < Liquid::Drop
    method liquid_method_missing (line 60) | def liquid_method_missing(method)
    method size (line 64) | def size
    method first (line 68) | def first
    method count (line 72) | def count
    method min (line 76) | def min
    method max (line 80) | def max
    method each (line 84) | def each
  class RealEnumerableDrop (line 91) | class RealEnumerableDrop < Liquid::Drop
    method liquid_method_missing (line 94) | def liquid_method_missing(method)
    method each (line 98) | def each
  class DropsTest (line 105) | class DropsTest < Minitest::Test
    method test_product_drop (line 108) | def test_product_drop
    method test_drop_does_only_respond_to_whitelisted_methods (line 113) | def test_drop_does_only_respond_to_whitelisted_methods
    method test_drops_respond_to_to_liquid (line 122) | def test_drops_respond_to_to_liquid
    method test_text_drop (line 127) | def test_text_drop
    method test_catchall_unknown_method (line 132) | def test_catchall_unknown_method
    method test_catchall_integer_argument_drop (line 137) | def test_catchall_integer_argument_drop
    method test_text_array_drop (line 142) | def test_text_array_drop
    method test_context_drop (line 147) | def test_context_drop
    method test_context_drop_array_with_map (line 152) | def test_context_drop_array_with_map
    method test_nested_context_drop (line 157) | def test_nested_context_drop
    method test_protected (line 162) | def test_protected
    method test_object_methods_not_allowed (line 167) | def test_object_methods_not_allowed
    method test_scope (line 174) | def test_scope
    method test_scope_though_proc (line 180) | def test_scope_though_proc
    method test_scope_with_assigns (line 186) | def test_scope_with_assigns
    method test_scope_from_tags (line 193) | def test_scope_from_tags
    method test_access_context_from_drop (line 199) | def test_access_context_from_drop
    method test_enumerable_drop (line 203) | def test_enumerable_drop
    method test_enumerable_drop_size (line 207) | def test_enumerable_drop_size
    method test_enumerable_drop_will_invoke_liquid_method_missing_for_clashing_method_names (line 211) | def test_enumerable_drop_will_invoke_liquid_method_missing_for_clashin...
    method test_some_enumerable_methods_still_get_invoked (line 220) | def test_some_enumerable_methods_still_get_invoked
    method test_empty_string_value_access (line 238) | def test_empty_string_value_access
    method test_nil_value_access (line 242) | def test_nil_value_access
    method test_default_to_s_on_drops (line 246) | def test_default_to_s_on_drops
    method test_invokable_methods (line 251) | def test_invokable_methods

FILE: test/integration/error_handling_test.rb
  class ErrorHandlingTest (line 5) | class ErrorHandlingTest < Minitest::Test
    method test_templates_parsed_with_line_numbers_renders_them_in_errors (line 8) | def test_templates_parsed_with_line_numbers_renders_them_in_errors
    method test_standard_error (line 41) | def test_standard_error
    method test_syntax (line 49) | def test_syntax
    method test_argument (line 57) | def test_argument
    method test_missing_endtag_parse_time_error (line 65) | def test_missing_endtag_parse_time_error
    method test_unrecognized_operator (line 69) | def test_unrecognized_operator
    method test_lax_unrecognized_operator (line 77) | def test_lax_unrecognized_operator
    method test_with_line_numbers_adds_numbers_to_parser_errors (line 84) | def test_with_line_numbers_adds_numbers_to_parser_errors
    method test_with_line_numbers_adds_numbers_to_parser_errors_with_whitespace_trim (line 95) | def test_with_line_numbers_adds_numbers_to_parser_errors_with_whitespa...
    method test_parsing_warn_with_line_numbers_adds_numbers_to_lexer_errors (line 107) | def test_parsing_warn_with_line_numbers_adds_numbers_to_lexer_errors
    method test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors (line 126) | def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors
    method test_syntax_errors_in_nested_blocks_have_correct_line_number (line 144) | def test_syntax_errors_in_nested_blocks_have_correct_line_number
    method test_strict_error_messages (line 158) | def test_strict_error_messages
    method test_warnings (line 170) | def test_warnings
    method test_warning_line_numbers (line 179) | def test_warning_line_numbers
    method test_exceptions_propagate (line 189) | def test_exceptions_propagate
    method test_default_exception_renderer_with_internal_error (line 196) | def test_default_exception_renderer_with_internal_error
    method test_setting_default_exception_renderer (line 205) | def test_setting_default_exception_renderer
    method test_setting_exception_renderer_on_environment (line 221) | def test_setting_exception_renderer_on_environment
    method test_exception_renderer_exposing_non_liquid_error (line 236) | def test_exception_renderer_exposing_non_liquid_error
    class TestFileSystem (line 252) | class TestFileSystem
      method read_template_file (line 253) | def read_template_file(_template_path)
    method test_included_template_name_with_line_numbers (line 258) | def test_included_template_name_with_line_numbers
    method test_bug_compatible_silencing_of_errors_in_blank_nodes (line 267) | def test_bug_compatible_silencing_of_errors_in_blank_nodes
    method test_syntax_error_is_raised_with_template_name (line 275) | def test_syntax_error_is_raised_with_template_name
    method test_syntax_error_is_raised_with_template_name_from_template_factory (line 294) | def test_syntax_error_is_raised_with_template_name_from_template_factory
    method test_error_is_raised_during_parse_with_template_name (line 316) | def test_error_is_raised_during_parse_with_template_name
    method test_internal_error_is_raised_with_template_name (line 332) | def test_internal_error_is_raised_with_template_name

FILE: test/integration/expression_test.rb
  class ExpressionTest (line 6) | class ExpressionTest < Minitest::Test
    method test_keyword_literals (line 7) | def test_keyword_literals
    method test_string (line 12) | def test_string
    method test_int (line 20) | def test_int
    method test_float (line 26) | def test_float
    method test_range (line 45) | def test_range
    method test_quirky_negative_sign_expression_markup (line 59) | def test_quirky_negative_sign_expression_markup
    method test_expression_cache (line 72) | def test_expression_cache
    method test_expression_cache_with_true_boolean (line 93) | def test_expression_cache_with_true_boolean
    method test_expression_cache_with_lru_redux (line 117) | def test_expression_cache_with_lru_redux
    method test_disable_expression_cache (line 138) | def test_disable_expression_cache
    method test_safe_parse_with_variable_lookup (line 155) | def test_safe_parse_with_variable_lookup
    method test_safe_parse_with_number (line 165) | def test_safe_parse_with_number
    method test_safe_parse_raises_syntax_error_for_invalid_expression (line 173) | def test_safe_parse_raises_syntax_error_for_invalid_expression
    method assert_expression_result (line 186) | def assert_expression_result(expect, markup, **assigns)

FILE: test/integration/filter_kwarg_test.rb
  class FilterKwargTest (line 5) | class FilterKwargTest < Minitest::Test
    type KwargFilter (line 6) | module KwargFilter
      function html_tag (line 7) | def html_tag(_tag, attributes)
    method test_can_parse_data_kwargs (line 16) | def test_can_parse_data_kwargs

FILE: test/integration/filter_test.rb
  type MoneyFilter (line 5) | module MoneyFilter
    function money (line 6) | def money(input)
    function money_with_underscore (line 10) | def money_with_underscore(input)
  type CanadianMoneyFilter (line 15) | module CanadianMoneyFilter
    function money (line 16) | def money(input)
  type SubstituteFilter (line 21) | module SubstituteFilter
    function substitute (line 22) | def substitute(input, params = {})
  class FiltersTest (line 27) | class FiltersTest < Minitest::Test
    type OverrideObjectMethodFilter (line 30) | module OverrideObjectMethodFilter
      function tap (line 31) | def tap(_input)
    method setup (line 36) | def setup
    method test_local_filter (line 40) | def test_local_filter
    method test_underscore_in_filter_name (line 47) | def test_underscore_in_filter_name
    method test_second_filter_overwrites_first (line 53) | def test_second_filter_overwrites_first
    method test_size (line 61) | def test_size
    method test_join (line 65) | def test_join
    method test_sort (line 69) | def test_sort
    method test_sort_natural (line 85) | def test_sort_natural
    method test_compact (line 105) | def test_compact
    method test_strip_html (line 125) | def test_strip_html
    method test_strip_html_ignore_comments_with_html (line 129) | def test_strip_html_ignore_comments_with_html
    method test_capitalize (line 137) | def test_capitalize
    method test_nonexistent_filter_is_ignored (line 141) | def test_nonexistent_filter_is_ignored
    method test_filter_with_keyword_arguments (line 145) | def test_filter_with_keyword_arguments
    method test_override_object_method_in_filter (line 153) | def test_override_object_method_in_filter
    method test_liquid_argument_error (line 160) | def test_liquid_argument_error
  class FiltersInTemplate (line 170) | class FiltersInTemplate < Minitest::Test
    method test_local_global (line 173) | def test_local_global
    method test_local_filter_with_deprecated_syntax (line 181) | def test_local_filter_with_deprecated_syntax
  class TestObject (line 187) | class TestObject < Liquid::Drop
    method initialize (line 189) | def initialize(a)

FILE: test/integration/hash_ordering_test.rb
  class HashOrderingTest (line 5) | class HashOrderingTest < Minitest::Test
    type MoneyFilter (line 6) | module MoneyFilter
      function money (line 7) | def money(input)
    type CanadianMoneyFilter (line 12) | module CanadianMoneyFilter
      function money (line 13) | def money(input)
    method test_global_register_order (line 20) | def test_global_register_order

FILE: test/integration/hash_rendering_test.rb
  class HashRenderingTest (line 5) | class HashRenderingTest < Minitest::Test
    method test_render_empty_hash (line 6) | def test_render_empty_hash
    method test_render_hash_with_string_keys_and_values (line 10) | def test_render_hash_with_string_keys_and_values
    method test_render_hash_with_symbol_keys_and_integer_values (line 14) | def test_render_hash_with_symbol_keys_and_integer_values
    method test_render_nested_hash (line 18) | def test_render_nested_hash
    method test_render_hash_with_array_values (line 22) | def test_render_hash_with_array_values
    method test_render_recursive_hash (line 26) | def test_render_recursive_hash
    method test_hash_with_downcase_filter (line 32) | def test_hash_with_downcase_filter
    method test_hash_with_upcase_filter (line 36) | def test_hash_with_upcase_filter
    method test_hash_with_strip_filter (line 40) | def test_hash_with_strip_filter
    method test_hash_with_escape_filter (line 44) | def test_hash_with_escape_filter
    method test_hash_with_url_encode_filter (line 48) | def test_hash_with_url_encode_filter
    method test_hash_with_strip_html_filter (line 52) | def test_hash_with_strip_html_filter
    method test_hash_with_truncate__20_filter (line 56) | def test_hash_with_truncate__20_filter
    method test_hash_with_replace___key____replaced_key__filter (line 60) | def test_hash_with_replace___key____replaced_key__filter
    method test_hash_with_append____appended_text__filter (line 64) | def test_hash_with_append____appended_text__filter
    method test_hash_with_prepend___prepended_text___filter (line 68) | def test_hash_with_prepend___prepended_text___filter
    method test_render_hash_with_array_values_empty (line 72) | def test_render_hash_with_array_values_empty
    method test_render_hash_with_array_values_hash (line 76) | def test_render_hash_with_array_values_hash
    method test_join_filter_with_hash (line 80) | def test_join_filter_with_hash
    method test_render_hash_with_hash_key (line 86) | def test_render_hash_with_hash_key
    method test_rendering_hash_with_custom_to_s_method_uses_custom_to_s (line 90) | def test_rendering_hash_with_custom_to_s_method_uses_custom_to_s
    method test_rendering_hash_without_custom_to_s_uses_default_inspect (line 94) | def test_rendering_hash_without_custom_to_s_uses_default_inspect

FILE: test/integration/output_test.rb
  type FunnyFilter (line 5) | module FunnyFilter
    function make_funny (line 6) | def make_funny(_input)
    function cite_funny (line 10) | def cite_funny(input)
    function add_smiley (line 14) | def add_smiley(input, smiley = ":-)")
    function add_tag (line 18) | def add_tag(input, tag = "p", id = "foo")
    function paragraph (line 22) | def paragraph(input)
    function link_to (line 26) | def link_to(name, url)
  class OutputTest (line 31) | class OutputTest < Minitest::Test
    method setup (line 34) | def setup
    method test_variable (line 40) | def test_variable
    method test_variable_traversing_with_two_brackets (line 44) | def test_variable_traversing_with_two_brackets
    method test_variable_traversing (line 52) | def test_variable_traversing
    method test_variable_piping (line 57) | def test_variable_piping
    method test_variable_piping_with_input (line 64) | def test_variable_piping_with_input
    method test_variable_piping_with_args (line 71) | def test_variable_piping_with_args
    method test_variable_piping_with_no_args (line 78) | def test_variable_piping_with_no_args
    method test_multiple_variable_piping_with_args (line 85) | def test_multiple_variable_piping_with_args
    method test_variable_piping_with_multiple_args (line 92) | def test_variable_piping_with_multiple_args
    method test_variable_piping_with_variable_args (line 99) | def test_variable_piping_with_variable_args
    method test_multiple_pipings (line 106) | def test_multiple_pipings
    method test_link_to (line 114) | def test_link_to

FILE: test/integration/parsing_quirks_test.rb
  class ParsingQuirksTest (line 5) | class ParsingQuirksTest < Minitest::Test
    method test_parsing_css (line 8) | def test_parsing_css
    method test_raise_on_single_close_bracet (line 13) | def test_raise_on_single_close_bracet
    method test_raise_on_label_and_no_close_bracets (line 19) | def test_raise_on_label_and_no_close_bracets
    method test_raise_on_label_and_no_close_bracets_percent (line 25) | def test_raise_on_label_and_no_close_bracets_percent
    method test_error_on_empty_filter (line 31) | def test_error_on_empty_filter
    method test_meaningless_parens_error (line 44) | def test_meaningless_parens_error
    method test_unexpected_characters_syntax_error (line 53) | def test_unexpected_characters_syntax_error
    method test_no_error_on_lax_empty_filter (line 66) | def test_no_error_on_lax_empty_filter
    method test_meaningless_parens_lax (line 72) | def test_meaningless_parens_lax
    method test_unexpected_characters_silently_eat_logic_lax (line 80) | def test_unexpected_characters_silently_eat_logic_lax
    method test_raise_on_invalid_tag_delimiter (line 89) | def test_raise_on_invalid_tag_delimiter
    method test_unanchored_filter_arguments (line 95) | def test_unanchored_filter_arguments
    method test_invalid_variables_work (line 108) | def test_invalid_variables_work
    method test_extra_dots_in_ranges (line 115) | def test_extra_dots_in_ranges
    method test_blank_variable_markup (line 121) | def test_blank_variable_markup
    method test_lookup_on_var_with_literal_name (line 125) | def test_lookup_on_var_with_literal_name
    method test_contains_in_id (line 131) | def test_contains_in_id
    method test_incomplete_expression (line 135) | def test_incomplete_expression

FILE: test/integration/profiler_test.rb
  class ProfilerTest (line 5) | class ProfilerTest < Minitest::Test
    class TestDrop (line 6) | class TestDrop < Liquid::Drop
      method initialize (line 7) | def initialize(value)
      method to_s (line 12) | def to_s
      method artificial_execution_time (line 22) | def artificial_execution_time
    class ProfilingFileSystem (line 29) | class ProfilingFileSystem
      method read_template_file (line 30) | def read_template_file(template_path)
    method setup (line 35) | def setup
    method test_template_allows_flagging_profiling (line 39) | def test_template_allows_flagging_profiling
    method test_parse_makes_available_simple_profiling (line 46) | def test_parse_makes_available_simple_profiling
    method test_render_ignores_raw_strings_when_profiling (line 56) | def test_render_ignores_raw_strings_when_profiling
    method test_profiling_includes_line_numbers_of_liquid_nodes (line 63) | def test_profiling_includes_line_numbers_of_liquid_nodes
    method test_profiling_includes_line_numbers_of_included_partials (line 74) | def test_profiling_includes_line_numbers_of_included_partials
    method test_profiling_render_tag (line 86) | def test_profiling_render_tag
    method test_profiling_times_the_rendering_of_tokens (line 97) | def test_profiling_times_the_rendering_of_tokens
    method test_profiling_times_the_entire_render (line 105) | def test_profiling_times_the_entire_render
    class SleepTag (line 112) | class SleepTag < Liquid::Tag
      method initialize (line 113) | def initialize(tag_name, markup, parse_context)
      method render_to_output_buffer (line 118) | def render_to_output_buffer(_context, _output)
    method test_profiling_multiple_renders (line 123) | def test_profiling_multiple_renders
    method test_profiling_uses_include_to_mark_children (line 143) | def test_profiling_uses_include_to_mark_children
    method test_profiling_marks_children_with_the_name_of_included_partial (line 151) | def test_profiling_marks_children_with_the_name_of_included_partial
    method test_profiling_supports_multiple_templates (line 161) | def test_profiling_supports_multiple_templates
    method test_profiling_supports_rendering_the_same_partial_multiple_times (line 176) | def test_profiling_supports_rendering_the_same_partial_multiple_times
    method test_can_iterate_over_each_profiling_entry (line 191) | def test_can_iterate_over_each_profiling_entry
    method test_profiling_marks_children_of_if_blocks (line 203) | def test_profiling_marks_children_of_if_blocks
    method test_profiling_marks_children_of_for_blocks (line 211) | def test_profiling_marks_children_of_for_blocks
    method test_profiling_supports_self_time (line 220) | def test_profiling_supports_self_time
    method test_profiling_supports_total_time (line 233) | def test_profiling_supports_total_time

FILE: test/integration/security_test.rb
  type SecurityFilter (line 5) | module SecurityFilter
    function add_one (line 6) | def add_one(input)
  class SecurityTest (line 11) | class SecurityTest < Minitest::Test
    method setup (line 14) | def setup
    method test_no_instance_eval (line 18) | def test_no_instance_eval
    method test_no_existing_instance_eval (line 25) | def test_no_existing_instance_eval
    method test_no_instance_eval_after_mixing_in_new_filter (line 32) | def test_no_instance_eval_after_mixing_in_new_filter
    method test_no_instance_eval_later_in_chain (line 39) | def test_no_instance_eval_later_in_chain
    method test_does_not_permanently_add_filters_to_symbol_table (line 46) | def test_does_not_permanently_add_filters_to_symbol_table
    method test_does_not_add_drop_methods_to_symbol_table (line 65) | def test_does_not_add_drop_methods_to_symbol_table
    method test_max_depth_nested_blocks_does_not_raise_exception (line 76) | def test_max_depth_nested_blocks_does_not_raise_exception
    method test_more_than_max_depth_nested_blocks_raises_exception (line 82) | def test_more_than_max_depth_nested_blocks_raises_exception

FILE: test/integration/standard_filter_test.rb
  class TestThing (line 6) | class TestThing
    method initialize (line 9) | def initialize
    method to_s (line 13) | def to_s
    method [] (line 17) | def [](_whatever)
    method to_liquid (line 21) | def to_liquid
  class TestDrop (line 27) | class TestDrop < Liquid::Drop
    method initialize (line 28) | def initialize(value:)
    method registers (line 34) | def registers
  class TestModel (line 39) | class TestModel
    method initialize (line 40) | def initialize(value:)
    method to_liquid (line 44) | def to_liquid
  class TestEnumerable (line 49) | class TestEnumerable < Liquid::Drop
    method each (line 52) | def each(&block)
  class NumberLikeThing (line 57) | class NumberLikeThing < Liquid::Drop
    method initialize (line 58) | def initialize(amount)
    method to_number (line 62) | def to_number
  class StandardFiltersTest (line 67) | class StandardFiltersTest < Minitest::Test
    method setup (line 73) | def setup
    method test_size (line 77) | def test_size
    method test_downcase (line 83) | def test_downcase
    method test_upcase (line 88) | def test_upcase
    method test_slice (line 93) | def test_slice
    method test_slice_on_arrays (line 118) | def test_slice_on_arrays
    method test_find_on_empty_array (line 136) | def test_find_on_empty_array
    method test_find_index_on_empty_array (line 140) | def test_find_index_on_empty_array
    method test_has_on_empty_array (line 144) | def test_has_on_empty_array
    method test_truncate (line 148) | def test_truncate
    method test_split (line 159) | def test_split
    method test_squish_filter (line 167) | def test_squish_filter
    method test_escape (line 174) | def test_escape
    method test_h (line 181) | def test_h
    method test_escape_once (line 188) | def test_escape_once
    method test_base64_encode (line 192) | def test_base64_encode
    method test_base64_decode (line 197) | def test_base64_decode
    method test_base64_url_safe_encode (line 217) | def test_base64_url_safe_encode
    method test_base64_url_safe_decode (line 225) | def test_base64_url_safe_decode
    method test_url_encode (line 247) | def test_url_encode
    method test_url_decode (line 254) | def test_url_decode
    method test_truncatewords (line 267) | def test_truncatewords
    method test_strip_html (line 284) | def test_strip_html
    method test_join (line 297) | def test_join
    method test_join_calls_to_liquid_on_each_element (line 303) | def test_join_calls_to_liquid_on_each_element
    method test_sort (line 307) | def test_sort
    method test_sort_with_nils (line 312) | def test_sort_with_nils
    method test_sort_when_property_is_sometimes_missing_puts_nils_last (line 317) | def test_sort_when_property_is_sometimes_missing_puts_nils_last
    method test_sort_natural (line 335) | def test_sort_natural
    method test_sort_natural_with_nils (line 340) | def test_sort_natural_with_nils
    method test_sort_natural_when_property_is_sometimes_missing_puts_nils_last (line 345) | def test_sort_natural_when_property_is_sometimes_missing_puts_nils_last
    method test_sort_natural_case_check (line 363) | def test_sort_natural_case_check
    method test_sort_empty_array (line 386) | def test_sort_empty_array
    method test_sort_invalid_property (line 390) | def test_sort_invalid_property
    method test_sort_natural_empty_array (line 402) | def test_sort_natural_empty_array
    method test_sort_natural_invalid_property (line 406) | def test_sort_natural_invalid_property
    method test_legacy_sort_hash (line 418) | def test_legacy_sort_hash
    method test_numerical_vs_lexicographical_sort (line 422) | def test_numerical_vs_lexicographical_sort
    method test_uniq (line 429) | def test_uniq
    method test_uniq_empty_array (line 438) | def test_uniq_empty_array
    method test_uniq_invalid_property (line 442) | def test_uniq_invalid_property
    method test_compact_empty_array (line 454) | def test_compact_empty_array
    method test_compact_invalid_property (line 458) | def test_compact_invalid_property
    method test_reverse (line 470) | def test_reverse
    method test_legacy_reverse_hash (line 474) | def test_legacy_reverse_hash
    method test_map (line 478) | def test_map
    method test_map_doesnt_call_arbitrary_stuff (line 487) | def test_map_doesnt_call_arbitrary_stuff
    method test_map_calls_to_liquid (line 492) | def test_map_calls_to_liquid
    method test_map_calls_context= (line 497) | def test_map_calls_context=
    method test_map_on_hashes (line 507) | def test_map_on_hashes
    method test_legacy_map_on_hashes_with_dynamic_key (line 515) | def test_legacy_map_on_hashes_with_dynamic_key
    method test_sort_calls_to_liquid (line 521) | def test_sort_calls_to_liquid
    method test_map_over_proc (line 527) | def test_map_over_proc
    method test_map_over_drops_returning_procs (line 534) | def test_map_over_drops_returning_procs
    method test_map_works_on_enumerables (line 547) | def test_map_works_on_enumerables
    method test_map_returns_empty_on_2d_input_array (line 552) | def test_map_returns_empty_on_2d_input_array
    method test_map_with_value_property (line 564) | def test_map_with_value_property
    method test_map_returns_input_with_no_property (line 574) | def test_map_returns_input_with_no_property
    method test_sort_works_on_enumerables (line 586) | def test_sort_works_on_enumerables
    method test_first_and_last_call_to_liquid (line 590) | def test_first_and_last_call_to_liquid
    method test_truncate_calls_to_liquid (line 595) | def test_truncate_calls_to_liquid
    method test_date (line 599) | def test_date
    method test_first_last (line 630) | def test_first_last
    method test_first_last_on_strings (line 637) | def test_first_last_on_strings
    method test_first_last_on_unicode_strings (line 653) | def test_first_last_on_unicode_strings
    method test_first_last_on_strings_via_template (line 662) | def test_first_last_on_strings_via_template
    method test_replace (line 671) | def test_replace
    method test_remove (line 688) | def test_remove
    method test_pipes_in_string_arguments (line 699) | def test_pipes_in_string_arguments
    method test_strip (line 703) | def test_strip
    method test_lstrip (line 708) | def test_lstrip
    method test_rstrip (line 713) | def test_rstrip
    method test_strip_newlines (line 718) | def test_strip_newlines
    method test_newlines_to_br (line 723) | def test_newlines_to_br
    method test_plus (line 728) | def test_plus
    method test_minus (line 735) | def test_minus
    method test_abs (line 742) | def test_abs
    method test_times (line 755) | def test_times
    method test_divided_by (line 765) | def test_divided_by
    method test_modulo (line 780) | def test_modulo
    method test_round (line 789) | def test_round
    method test_ceil (line 801) | def test_ceil
    method test_floor (line 811) | def test_floor
    method test_at_most (line 821) | def test_at_most
    method test_at_least (line 832) | def test_at_least
    method test_append (line 843) | def test_append
    method test_concat (line 849) | def test_concat
    method test_prepend (line 859) | def test_prepend
    method test_default (line 865) | def test_default
    method test_default_handle_false (line 877) | def test_default_handle_false
    method test_cannot_access_private_methods (line 889) | def test_cannot_access_private_methods
    method test_date_raises_nothing (line 893) | def test_date_raises_nothing
    method test_reject (line 898) | def test_reject
    method test_reject_with_value (line 912) | def test_reject_with_value
    method test_reject_with_false_value (line 926) | def test_reject_with_false_value
    method test_has (line 940) | def test_has
    method test_has_when_does_not_have_it (line 954) | def test_has_when_does_not_have_it
    method test_has_with_empty_arrays (line 968) | def test_has_with_empty_arrays
    method test_has_with_false_value (line 980) | def test_has_with_false_value
    method test_has_with_false_value_when_does_not_have_it (line 994) | def test_has_with_false_value_when_does_not_have_it
    method test_find_with_value (line 1008) | def test_find_with_value
    method test_find_with_empty_arrays (line 1026) | def test_find_with_empty_arrays
    method test_find_index_with_value (line 1038) | def test_find_index_with_value
    method test_find_index_with_empty_arrays (line 1056) | def test_find_index_with_empty_arrays
    method test_where (line 1068) | def test_where
    method test_where_with_empty_string_is_a_no_op (line 1082) | def test_where_with_empty_string_is_a_no_op
    method test_where_with_nil_is_a_no_op (line 1090) | def test_where_with_nil_is_a_no_op
    method test_where_with_value (line 1099) | def test_where_with_value
    method test_where_with_false_value (line 1113) | def test_where_with_false_value
    method test_where_string_keys (line 1127) | def test_where_string_keys
    method test_where_no_key_set (line 1139) | def test_where_no_key_set
    method test_where_non_array_map_input (line 1156) | def test_where_non_array_map_input
    method test_where_indexable_but_non_map_value (line 1161) | def test_where_indexable_but_non_map_value
    method test_where_non_boolean_value (line 1166) | def test_where_non_boolean_value
    method test_where_array_of_only_unindexable_values (line 1178) | def test_where_array_of_only_unindexable_values
    method test_all_filters_never_raise_non_liquid_exception (line 1183) | def test_all_filters_never_raise_non_liquid_exception
    method test_where_no_target_value (line 1221) | def test_where_no_target_value
    method test_sum_with_all_numbers (line 1232) | def test_sum_with_all_numbers
    method test_sum_with_numeric_strings (line 1241) | def test_sum_with_numeric_strings
    method test_sum_with_nested_arrays (line 1250) | def test_sum_with_nested_arrays
    method test_sum_with_indexable_map_values (line 1259) | def test_sum_with_indexable_map_values
    method test_sum_with_indexable_non_map_values (line 1268) | def test_sum_with_indexable_non_map_values
    method test_sum_with_unindexable_values (line 1277) | def test_sum_with_unindexable_values
    method test_sum_without_property_calls_to_liquid (line 1286) | def test_sum_without_property_calls_to_liquid
    method test_sum_with_property_calls_to_liquid_on_property_values (line 1292) | def test_sum_with_property_calls_to_liquid_on_property_values
    method test_sum_of_floats (line 1298) | def test_sum_of_floats
    method test_sum_of_negative_floats (line 1304) | def test_sum_of_negative_floats
    method test_sum_with_float_strings (line 1310) | def test_sum_with_float_strings
    method test_sum_resulting_in_negative_float (line 1316) | def test_sum_resulting_in_negative_float
    method test_sum_with_floats_and_indexable_map_values (line 1322) | def test_sum_with_floats_and_indexable_map_values
    method test_sum_with_non_string_property (line 1334) | def test_sum_with_non_string_property
    method test_uniq_with_to_liquid_value (line 1345) | def test_uniq_with_to_liquid_value
    method test_uniq_with_to_liquid_value_pick_correct_classes (line 1353) | def test_uniq_with_to_liquid_value_pick_correct_classes
    method with_timezone (line 1363) | def with_timezone(tz)

FILE: test/integration/tag/disableable_test.rb
  class TagDisableableTest (line 5) | class TagDisableableTest < Minitest::Test
    type RenderTagName (line 8) | module RenderTagName
      function render (line 9) | def render(_context)
    class Custom (line 14) | class Custom < Tag
    class Custom2 (line 19) | class Custom2 < Tag
    class DisableCustom (line 24) | class DisableCustom < Block
    class DisableBoth (line 28) | class DisableBoth < Block
    method test_block_tag_disabling_nested_tag (line 32) | def test_block_tag_disabling_nested_tag
    method test_block_tag_disabling_multiple_nested_tags (line 41) | def test_block_tag_disabling_multiple_nested_tags
    method with_disableable_tags (line 52) | def with_disableable_tags

FILE: test/integration/tag_test.rb
  class TagTest (line 5) | class TagTest < Minitest::Test
    method test_custom_tags_have_a_default_render_to_output_buffer_method_for_backwards_compatibility (line 8) | def test_custom_tags_have_a_default_render_to_output_buffer_method_for...

FILE: test/integration/tags/break_tag_test.rb
  class BreakTagTest (line 5) | class BreakTagTest < Minitest::Test
    method test_break_with_no_block (line 10) | def test_break_with_no_block

FILE: test/integration/tags/continue_tag_test.rb
  class ContinueTagTest (line 5) | class ContinueTagTest < Minitest::Test
    method test_continue_with_no_block (line 10) | def test_continue_with_no_block

FILE: test/integration/tags/cycle_tag_test.rb
  class CycleTagTest (line 5) | class CycleTagTest < Minitest::Test
    method test_simple_cycle_inside_for_loop (line 6) | def test_simple_cycle_inside_for_loop
    method test_cycle_with_variables_inside_for_loop (line 16) | def test_cycle_with_variables_inside_for_loop
    method test_cycle_named_groups_string (line 29) | def test_cycle_named_groups_string
    method test_cycle_named_groups_vlookup (line 40) | def test_cycle_named_groups_vlookup
    method test_unnamed_cycle_have_independent_counters_when_used_with_lookups (line 53) | def test_unnamed_cycle_have_independent_counters_when_used_with_lookups
    method test_unnamed_cycle_dependent_counter_when_used_with_literal_values (line 65) | def test_unnamed_cycle_dependent_counter_when_used_with_literal_values
    method test_optional_trailing_comma (line 75) | def test_optional_trailing_comma
    method test_cycle_tag_without_arguments (line 86) | def test_cycle_tag_without_arguments
    method test_cycle_tag_with_error_mode (line 94) | def test_cycle_tag_with_error_mode
    method test_cycle_with_trailing_elements (line 115) | def test_cycle_with_trailing_elements
    method test_cycle_name_with_invalid_expression (line 149) | def test_cycle_name_with_invalid_expression
    method test_cycle_variable_with_invalid_expression (line 166) | def test_cycle_variable_with_invalid_expression

FILE: test/integration/tags/echo_test.rb
  class EchoTest (line 5) | class EchoTest < Minitest::Test
    method test_echo_outputs_its_input (line 8) | def test_echo_outputs_its_input

FILE: test/integration/tags/for_tag_test.rb
  class ThingWithValue (line 5) | class ThingWithValue < Liquid::Drop
    method value (line 6) | def value
  class ForTagTest (line 11) | class ForTagTest < Minitest::Test
    method test_for (line 14) | def test_for
    method test_for_reversed (line 36) | def test_for_reversed
    method test_for_with_range (line 41) | def test_for_with_range
    method test_for_with_variable_range (line 51) | def test_for_with_variable_range
    method test_for_with_hash_value_range (line 55) | def test_for_with_hash_value_range
    method test_for_with_drop_value_range (line 60) | def test_for_with_drop_value_range
    method test_for_with_variable (line 65) | def test_for_with_variable
    method test_for_helpers (line 74) | def test_for_helpers
    method test_for_and_if (line 89) | def test_for_and_if
    method test_for_else (line 98) | def test_for_else
    method test_limiting (line 104) | def test_limiting
    method test_limiting_with_invalid_limit (line 113) | def test_limiting_with_invalid_limit
    method test_limiting_with_invalid_offset (line 127) | def test_limiting_with_invalid_offset
    method test_dynamic_variable_limiting (line 141) | def test_dynamic_variable_limiting
    method test_nested_for (line 149) | def test_nested_for
    method test_offset_only (line 154) | def test_offset_only
    method test_pause_resume (line 159) | def test_pause_resume
    method test_pause_resume_limit (line 178) | def test_pause_resume_limit
    method test_pause_resume_big_limit (line 197) | def test_pause_resume_big_limit
    method test_pause_resume_big_offset (line 216) | def test_pause_resume_big_offset
    method test_for_with_break (line 231) | def test_for_with_break
    method test_for_with_break_after_nested_loop (line 271) | def test_for_with_break_after_nested_loop
    method test_for_with_continue (line 284) | def test_for_with_continue
    method test_for_tag_string (line 327) | def test_for_tag_string
    method test_for_parentloop_references_parent_loop (line 360) | def test_for_parentloop_references_parent_loop
    method test_for_parentloop_nil_when_not_present (line 370) | def test_for_parentloop_nil_when_not_present
    method test_inner_for_over_empty_input (line 380) | def test_inner_for_over_empty_input
    method test_blank_string_not_iterable (line 384) | def test_blank_string_not_iterable
    method test_bad_variable_naming_in_for_loop (line 388) | def test_bad_variable_naming_in_for_loop
    method test_spacing_with_variable_naming_in_for_loop (line 394) | def test_spacing_with_variable_naming_in_for_loop
    class LoaderDrop (line 401) | class LoaderDrop < Liquid::Drop
      method initialize (line 404) | def initialize(data)
      method each (line 408) | def each
      method load_slice (line 413) | def load_slice(from, to)
    method test_iterate_with_each_when_no_limit_applied (line 419) | def test_iterate_with_each_when_no_limit_applied
    method test_iterate_with_load_slice_when_limit_applied (line 429) | def test_iterate_with_load_slice_when_limit_applied
    method test_iterate_with_load_slice_when_limit_and_offset_applied (line 439) | def test_iterate_with_load_slice_when_limit_and_offset_applied
    method test_iterate_with_load_slice_returns_same_results_as_without (line 449) | def test_iterate_with_load_slice_returns_same_results_as_without
    method test_for_cleans_up_registers (line 459) | def test_for_cleans_up_registers

FILE: test/integration/tags/if_else_tag_test.rb
  class IfElseTagTest (line 5) | class IfElseTagTest < Minitest::Test
    method test_if (line 8) | def test_if
    method test_literal_comparisons (line 17) | def test_literal_comparisons
    method test_if_else (line 22) | def test_if_else
    method test_if_boolean (line 28) | def test_if_boolean
    method test_if_or (line 32) | def test_if_or
    method test_if_or_with_operators (line 42) | def test_if_or_with_operators
    method test_comparison_of_strings_containing_and_or_or (line 48) | def test_comparison_of_strings_containing_and_or_or
    method test_comparison_of_expressions_starting_with_and_or_or (line 54) | def test_comparison_of_expressions_starting_with_and_or_or
    method test_if_and (line 68) | def test_if_and
    method test_hash_miss_generates_false (line 74) | def test_hash_miss_generates_false
    method test_if_from_variable (line 78) | def test_if_from_variable
    method test_nested_if (line 111) | def test_nested_if
    method test_comparisons_on_null (line 122) | def test_comparisons_on_null
    method test_else_if (line 134) | def test_else_if
    method test_syntax_error_no_variable (line 142) | def test_syntax_error_no_variable
    method test_syntax_error_no_expression (line 146) | def test_syntax_error_no_expression
    method test_if_with_custom_condition (line 150) | def test_if_with_custom_condition
    method test_operators_are_ignored_unless_isolated (line 160) | def test_operators_are_ignored_unless_isolated
    method test_operators_are_whitelisted (line 172) | def test_operators_are_whitelisted
    method test_multiple_conditions (line 178) | def test_multiple_conditions

FILE: test/integration/tags/include_tag_test.rb
  class TestFileSystem (line 5) | class TestFileSystem
    method read_template_file (line 11) | def read_template_file(template_path)
  class OtherFileSystem (line 16) | class OtherFileSystem
    method read_template_file (line 17) | def read_template_file(_template_path)
  class CountingFileSystem (line 22) | class CountingFileSystem
    method read_template_file (line 24) | def read_template_file(_template_path)
  class CustomInclude (line 31) | class CustomInclude < Liquid::Tag
    method initialize (line 34) | def initialize(tag_name, markup, tokens)
    method parse (line 40) | def parse(tokens)
    method render_to_output_buffer (line 43) | def render_to_output_buffer(_context, output)
  class IncludeTagTest (line 49) | class IncludeTagTest < Minitest::Test
    method test_include_tag_looks_for_file_system_in_registers_first (line 52) | def test_include_tag_looks_for_file_system_in_registers_first
    method test_include_tag_with (line 59) | def test_include_tag_with
    method test_include_tag_with_alias (line 68) | def test_include_tag_with_alias
    method test_include_tag_for_alias (line 77) | def test_include_tag_for_alias
    method test_include_tag_with_default_name (line 86) | def test_include_tag_with_default_name
    method test_include_tag_for (line 95) | def test_include_tag_for
    method test_include_tag_with_local_variables (line 104) | def test_include_tag_with_local_variables
    method test_include_tag_with_multiple_local_variables (line 112) | def test_include_tag_with_multiple_local_variables
    method test_include_tag_with_multiple_local_variables_from_context (line 120) | def test_include_tag_with_multiple_local_variables_from_context
    method test_included_templates_assigns_variables (line 129) | def test_included_templates_assigns_variables
    method test_nested_include_tag (line 137) | def test_nested_include_tag
    method test_nested_include_with_variable (line 149) | def test_nested_include_with_variable
    method test_recursively_included_template_does_not_produce_endless_loop (line 170) | def test_recursively_included_template_does_not_produce_endless_loop
    method test_dynamically_choosen_template (line 184) | def test_dynamically_choosen_template
    method test_strict2_parsing_errors (line 207) | def test_strict2_parsing_errors
    method test_optional_commas (line 226) | def test_optional_commas
    method test_include_tag_caches_second_read_of_same_partial (line 233) | def test_include_tag_caches_second_read_of_same_partial
    method test_include_tag_doesnt_cache_partials_across_renders (line 243) | def test_include_tag_doesnt_cache_partials_across_renders
    method test_include_tag_within_if_statement (line 258) | def test_include_tag_within_if_statement
    method test_custom_include_tag (line 266) | def test_custom_include_tag
    method test_custom_include_tag_within_if_statement (line 279) | def test_custom_include_tag_within_if_statement
    method test_does_not_add_error_in_strict_mode_for_missing_variable (line 292) | def test_does_not_add_error_in_strict_mode_for_missing_variable
    method test_passing_options_to_included_templates (line 300) | def test_passing_options_to_included_templates
    method test_render_raise_argument_error_when_template_is_undefined (line 317) | def test_render_raise_argument_error_when_template_is_undefined
    method test_render_raise_argument_error_when_template_is_not_a_string (line 331) | def test_render_raise_argument_error_when_template_is_not_a_string
    method test_including_via_variable_value (line 339) | def test_including_via_variable_value
    method test_including_with_strict_variables (line 363) | def test_including_with_strict_variables
    method test_break_through_include (line 374) | def test_break_through_include
    method test_render_tag_renders_error_with_template_name (line 383) | def test_render_tag_renders_error_with_template_name
    method test_render_tag_renders_error_with_template_name_from_template_factory (line 393) | def test_render_tag_renders_error_with_template_name_from_template_fac...
    method test_include_template_with_invalid_expression (line 404) | def test_include_template_with_invalid_expression
    method test_include_with_invalid_expression (line 417) | def test_include_with_invalid_expression
    method test_include_attribute_with_invalid_expression (line 430) | def test_include_attribute_with_invalid_expression

FILE: test/integration/tags/increment_tag_test.rb
  class IncrementTagTest (line 5) | class IncrementTagTest < Minitest::Test
    method test_inc (line 8) | def test_inc
    method test_dec (line 19) | def test_dec

FILE: test/integration/tags/inline_comment_test.rb
  class InlineCommentTest (line 5) | class InlineCommentTest < Minitest::Test
    method test_inline_comment_returns_nothing (line 8) | def test_inline_comment_returns_nothing
    method test_inline_comment_does_not_require_a_space_after_the_pound_sign (line 15) | def test_inline_comment_does_not_require_a_space_after_the_pound_sign
    method test_liquid_inline_comment_returns_nothing (line 19) | def test_liquid_inline_comment_returns_nothing
    method test_inline_comment_can_be_written_on_multiple_lines (line 35) | def test_inline_comment_can_be_written_on_multiple_lines
    method test_inline_comment_multiple_pound_signs (line 47) | def test_inline_comment_multiple_pound_signs
    method test_inline_comments_require_the_pound_sign_on_every_new_line (line 57) | def test_inline_comments_require_the_pound_sign_on_every_new_line
    method test_inline_comment_does_not_support_nested_tags (line 66) | def test_inline_comment_does_not_support_nested_tags

FILE: test/integration/tags/liquid_tag_test.rb
  class LiquidTagTest (line 5) | class LiquidTagTest < Minitest::Test
    method test_liquid_tag (line 8) | def test_liquid_tag
    method test_liquid_tag_errors (line 48) | def test_liquid_tag_errors
    method test_line_number_is_correct_after_a_blank_token (line 79) | def test_line_number_is_correct_after_a_blank_token
    method test_nested_liquid_tag (line 84) | def test_nested_liquid_tag
    method test_cannot_open_blocks_living_past_a_liquid_tag (line 94) | def test_cannot_open_blocks_living_past_a_liquid_tag
    method test_cannot_close_blocks_created_before_a_liquid_tag (line 103) | def test_cannot_close_blocks_created_before_a_liquid_tag
    method test_liquid_tag_in_raw (line 111) | def test_liquid_tag_in_raw
    method test_nested_liquid_tags (line 117) | def test_nested_liquid_tags
    method test_nested_liquid_tags_on_same_line (line 128) | def test_nested_liquid_tags_on_same_line
    method test_nested_liquid_liquid_is_not_skipped_if_used_in_non_tag_position (line 134) | def test_nested_liquid_liquid_is_not_skipped_if_used_in_non_tag_position
    method test_next_liquid_with_unclosed_if_tag (line 140) | def test_next_liquid_with_unclosed_if_tag

FILE: test/integration/tags/raw_tag_test.rb
  class RawTagTest (line 5) | class RawTagTest < Minitest::Test
    method test_tag_in_raw (line 8) | def test_tag_in_raw
    method test_output_in_raw (line 15) | def test_output_in_raw
    method test_open_tag_in_raw (line 22) | def test_open_tag_in_raw
    method test_invalid_raw (line 34) | def test_invalid_raw

FILE: test/integration/tags/render_tag_test.rb
  class RenderTagTest (line 5) | class RenderTagTest < Minitest::Test
    method test_render_with_no_arguments (line 8) | def test_render_with_no_arguments
    method test_render_tag_looks_for_file_system_in_registers_first (line 16) | def test_render_tag_looks_for_file_system_in_registers_first
    method test_render_passes_named_arguments_into_inner_scope (line 24) | def test_render_passes_named_arguments_into_inner_scope
    method test_render_accepts_literals_as_arguments (line 33) | def test_render_accepts_literals_as_arguments
    method test_render_accepts_multiple_named_arguments (line 41) | def test_render_accepts_multiple_named_arguments
    method test_render_does_not_inherit_parent_scope_variables (line 49) | def test_render_does_not_inherit_parent_scope_variables
    method test_render_does_not_inherit_variable_with_same_name_as_snippet (line 57) | def test_render_does_not_inherit_variable_with_same_name_as_snippet
    method test_render_does_not_mutate_parent_scope (line 65) | def test_render_does_not_mutate_parent_scope
    method test_nested_render_tag (line 73) | def test_nested_render_tag
    method test_recursively_rendered_template_does_not_produce_endless_loop (line 84) | def test_recursively_rendered_template_does_not_produce_endless_loop
    method test_sub_contexts_count_towards_the_same_recursion_limit (line 94) | def test_sub_contexts_count_towards_the_same_recursion_limit
    method test_dynamically_choosen_templates_are_not_allowed (line 104) | def test_dynamically_choosen_templates_are_not_allowed
    method test_strict2_parsing_errors (line 108) | def test_strict2_parsing_errors
    method test_optional_commas (line 127) | def test_optional_commas
    method test_render_tag_caches_second_read_of_same_partial (line 134) | def test_render_tag_caches_second_read_of_same_partial
    method test_render_tag_doesnt_cache_partials_across_renders (line 144) | def test_render_tag_doesnt_cache_partials_across_renders
    method test_render_tag_within_if_statement (line 160) | def test_render_tag_within_if_statement
    method test_break_through_render (line 168) | def test_break_through_render
    method test_increment_is_isolated_between_renders (line 174) | def test_increment_is_isolated_between_renders
    method test_decrement_is_isolated_between_renders (line 182) | def test_decrement_is_isolated_between_renders
    method test_includes_will_not_render_inside_render_tag (line 190) | def test_includes_will_not_render_inside_render_tag
    method test_includes_will_not_render_inside_nested_sibling_tags (line 202) | def test_includes_will_not_render_inside_nested_sibling_tags
    method test_render_tag_with (line 216) | def test_render_tag_with
    method test_render_tag_with_alias (line 228) | def test_render_tag_with_alias
    method test_render_tag_for_alias (line 240) | def test_render_tag_for_alias
    method test_render_tag_for (line 252) | def test_render_tag_for
    method test_render_tag_forloop (line 264) | def test_render_tag_forloop
    method test_render_tag_for_drop (line 275) | def test_render_tag_for_drop
    method test_render_tag_with_drop (line 286) | def test_render_tag_with_drop
    method test_render_tag_renders_error_with_template_name (line 297) | def test_render_tag_renders_error_with_template_name
    method test_render_tag_renders_error_with_template_name_from_template_factory (line 307) | def test_render_tag_renders_error_with_template_name_from_template_fac...
    method test_render_with_invalid_expression (line 318) | def test_render_with_invalid_expression
    method test_render_attribute_with_invalid_expression (line 331) | def test_render_attribute_with_invalid_expression

FILE: test/integration/tags/standard_tag_test.rb
  class StandardTagTest (line 5) | class StandardTagTest < Minitest::Test
    method test_no_transform (line 8) | def test_no_transform
    method test_has_a_block_which_does_nothing (line 24) | def test_has_a_block_which_does_nothing
    method test_hyphenated_assign (line 59) | def test_hyphenated_assign
    method test_assign_with_colon_and_spaces (line 64) | def test_assign_with_colon_and_spaces
    method test_capture (line 69) | def test_capture
    method test_capture_detects_bad_syntax (line 78) | def test_capture_detects_bad_syntax
    method test_case (line 88) | def test_case
    method test_case_with_else (line 125) | def test_case_with_else
    method test_case_on_size (line 148) | def test_case_on_size
    method test_case_on_size_with_else (line 157) | def test_case_on_size_with_else
    method test_case_on_length_with_else (line 195) | def test_case_on_length_with_else
    method test_assign_from_case (line 221) | def test_assign_from_case
    method test_case_when_or (line 231) | def test_case_when_or
    method test_case_when_comma (line 246) | def test_case_when_comma
    method test_case_when_comma_and_blank_body (line 261) | def test_case_when_comma_and_blank_body
    method test_assign (line 266) | def test_assign
    method test_assign_unassigned (line 270) | def test_assign_unassigned
    method test_assign_an_empty_string (line 275) | def test_assign_an_empty_string
    method test_assign_is_global (line 279) | def test_assign_is_global
    method test_case_detects_bad_syntax (line 283) | def test_case_detects_bad_syntax
    method test_cycle (line 293) | def test_cycle
    method test_multiple_cycles (line 306) | def test_multiple_cycles
    method test_multiple_named_cycles (line 313) | def test_multiple_named_cycles
    method test_multiple_named_cycles_with_names_from_context (line 320) | def test_multiple_named_cycles_with_names_from_context
    method test_size_of_array (line 329) | def test_size_of_array
    method test_size_of_hash (line 334) | def test_size_of_hash
    method test_illegal_symbols (line 339) | def test_illegal_symbols
    method test_ifchanged (line 346) | def test_ifchanged
    method test_multiline_tag (line 354) | def test_multiline_tag

FILE: test/integration/tags/statements_test.rb
  class StatementsTest (line 5) | class StatementsTest < Minitest::Test
    method test_true_eql_true (line 8) | def test_true_eql_true
    method test_true_not_eql_true (line 13) | def test_true_not_eql_true
    method test_true_lq_true (line 18) | def test_true_lq_true
    method test_one_lq_zero (line 23) | def test_one_lq_zero
    method test_zero_lq_one (line 28) | def test_zero_lq_one
    method test_zero_lq_or_equal_one (line 33) | def test_zero_lq_or_equal_one
    method test_zero_lq_or_equal_one_involving_nil (line 38) | def test_zero_lq_or_equal_one_involving_nil
    method test_zero_lqq_or_equal_one (line 46) | def test_zero_lqq_or_equal_one
    method test_strings (line 51) | def test_strings
    method test_strings_not_equal (line 56) | def test_strings_not_equal
    method test_var_strings_equal (line 61) | def test_var_strings_equal
    method test_var_strings_are_not_equal (line 66) | def test_var_strings_are_not_equal
    method test_var_and_long_string_are_equal (line 71) | def test_var_and_long_string_are_equal
    method test_var_and_long_string_are_equal_backwards (line 76) | def test_var_and_long_string_are_equal_backwards
    method test_is_collection_empty (line 88) | def test_is_collection_empty
    method test_is_not_collection_empty (line 93) | def test_is_not_collection_empty
    method test_nil (line 98) | def test_nil
    method test_not_nil (line 106) | def test_not_nil

FILE: test/integration/tags/table_row_test.rb
  class TableRowTest (line 5) | class TableRowTest < Minitest::Test
    class ArrayDrop (line 8) | class ArrayDrop < Liquid::Drop
      method initialize (line 11) | def initialize(array)
      method each (line 15) | def each(&block)
    method test_table_row (line 20) | def test_table_row
    method test_table_row_with_different_cols (line 34) | def test_table_row_with_different_cols
    method test_table_col_counter (line 42) | def test_table_col_counter
    method test_quoted_fragment (line 50) | def test_quoted_fragment
    method test_enumerable_drop (line 63) | def test_enumerable_drop
    method test_offset_and_limit (line 71) | def test_offset_and_limit
    method test_blank_string_not_iterable (line 79) | def test_blank_string_not_iterable
    method test_cols_nil_constant_same_as_evaluated_nil_expression (line 87) | def test_cols_nil_constant_same_as_evaluated_nil_expression
    method test_nil_limit_is_treated_as_zero (line 105) | def test_nil_limit_is_treated_as_zero
    method test_nil_offset_is_treated_as_zero (line 121) | def test_nil_offset_is_treated_as_zero
    method test_tablerow_loop_drop_attributes (line 139) | def test_tablerow_loop_drop_attributes
    method test_table_row_renders_correct_error_message_for_invalid_parameters (line 191) | def test_table_row_renders_correct_error_message_for_invalid_parameters
    method test_table_row_handles_interrupts (line 214) | def test_table_row_handles_interrupts
    method test_table_row_does_not_leak_interrupts (line 226) | def test_table_row_does_not_leak_interrupts
    method test_tablerow_with_cols_attribute_in_strict2_mode (line 262) | def test_tablerow_with_cols_attribute_in_strict2_mode
    method test_tablerow_with_limit_attribute_in_strict2_mode (line 278) | def test_tablerow_with_limit_attribute_in_strict2_mode
    method test_tablerow_with_offset_attribute_in_strict2_mode (line 293) | def test_tablerow_with_offset_attribute_in_strict2_mode
    method test_tablerow_with_range_attribute_in_strict2_mode (line 308) | def test_tablerow_with_range_attribute_in_strict2_mode
    method test_tablerow_with_multiple_attributes_in_strict2_mode (line 323) | def test_tablerow_with_multiple_attributes_in_strict2_mode
    method test_tablerow_with_variable_collection_in_strict2_mode (line 339) | def test_tablerow_with_variable_collection_in_strict2_mode
    method test_tablerow_with_dotted_access_in_strict2_mode (line 355) | def test_tablerow_with_dotted_access_in_strict2_mode
    method test_tablerow_with_bracketed_access_in_strict2_mode (line 371) | def test_tablerow_with_bracketed_access_in_strict2_mode
    method test_tablerow_without_attributes_in_strict2_mode (line 386) | def test_tablerow_without_attributes_in_strict2_mode
    method test_tablerow_without_in_keyword_in_strict2_mode (line 401) | def test_tablerow_without_in_keyword_in_strict2_mode
    method test_tablerow_with_multiple_invalid_attributes_reports_first_in_strict2_mode (line 410) | def test_tablerow_with_multiple_invalid_attributes_reports_first_in_st...
    method test_tablerow_with_empty_collection_in_strict2_mode (line 419) | def test_tablerow_with_empty_collection_in_strict2_mode
    method test_tablerow_with_invalid_attribute_strict_vs_strict2 (line 434) | def test_tablerow_with_invalid_attribute_strict_vs_strict2
    method test_tablerow_with_invalid_expression_strict_vs_strict2 (line 452) | def test_tablerow_with_invalid_expression_strict_vs_strict2

FILE: test/integration/tags/unless_else_tag_test.rb
  class UnlessElseTagTest (line 5) | class UnlessElseTagTest < Minitest::Test
    method test_unless (line 8) | def test_unless
    method test_unless_else (line 17) | def test_unless_else
    method test_unless_in_loop (line 23) | def test_unless_in_loop
    method test_unless_else_in_loop (line 27) | def test_unless_else_in_loop

FILE: test/integration/template_test.rb
  class TemplateContextDrop (line 6) | class TemplateContextDrop < Liquid::Drop
    method liquid_method_missing (line 7) | def liquid_method_missing(method)
    method foo (line 11) | def foo
    method baz (line 15) | def baz
  class SomethingWithLength (line 20) | class SomethingWithLength < Liquid::Drop
    method length (line 21) | def length
  class ErroneousDrop (line 26) | class ErroneousDrop < Liquid::Drop
    method bad_method (line 27) | def bad_method
  class DropWithUndefinedMethod (line 32) | class DropWithUndefinedMethod < Liquid::Drop
    method foo (line 33) | def foo
  class TemplateTest (line 38) | class TemplateTest < Minitest::Test
    method test_instance_assigns_persist_on_same_template_object_between_parses (line 41) | def test_instance_assigns_persist_on_same_template_object_between_parses
    method test_warnings_is_not_exponential_time (line 47) | def test_warnings_is_not_exponential_time
    method test_instance_assigns_persist_on_same_template_parsing_between_renders (line 57) | def test_instance_assigns_persist_on_same_template_parsing_between_ren...
    method test_custom_assigns_do_not_persist_on_same_template (line 63) | def test_custom_assigns_do_not_persist_on_same_template
    method test_custom_assigns_squash_instance_assigns (line 69) | def test_custom_assigns_squash_instance_assigns
    method test_persistent_assigns_squash_instance_assigns (line 75) | def test_persistent_assigns_squash_instance_assigns
    method test_lambda_is_called_once_from_persistent_assigns_over_multiple_parses_and_renders (line 82) | def test_lambda_is_called_once_from_persistent_assigns_over_multiple_p...
    method test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders (line 94) | def test_lambda_is_called_once_from_custom_assigns_over_multiple_parse...
    method test_resource_limits_works_with_custom_length_method (line 108) | def test_resource_limits_works_with_custom_length_method
    method test_resource_limits_render_length (line 114) | def test_resource_limits_render_length
    method test_resource_limits_render_score (line 124) | def test_resource_limits_render_score
    method test_resource_limits_aborts_rendering_after_first_error (line 140) | def test_resource_limits_aborts_rendering_after_first_error
    method test_resource_limits_hash_in_template_gets_updated_even_if_no_limits_are_set (line 147) | def test_resource_limits_hash_in_template_gets_updated_even_if_no_limi...
    method test_render_length_persists_between_blocks (line 154) | def test_render_length_persists_between_blocks
    method test_render_length_uses_number_of_bytes_not_characters (line 174) | def test_render_length_uses_number_of_bytes_not_characters
    method test_cumulative_render_score_limit_across_render_tags (line 182) | def test_cumulative_render_score_limit_across_render_tags
    method test_cumulative_render_score_limit_raises_on_render_bang (line 207) | def test_cumulative_render_score_limit_raises_on_render_bang
    method test_cumulative_assign_score_limit_across_include_tags (line 222) | def test_cumulative_assign_score_limit_across_include_tags
    method test_cumulative_render_score_tracks_across_partials_without_limit (line 246) | def test_cumulative_render_score_tracks_across_partials_without_limit
    method test_default_resource_limits_unaffected_by_render_with_context (line 262) | def test_default_resource_limits_unaffected_by_render_with_context
    method test_can_use_drop_as_context (line 270) | def test_can_use_drop_as_context
    method test_render_bang_force_rethrow_errors_on_passed_context (line 279) | def test_render_bang_force_rethrow_errors_on_passed_context
    method test_exception_renderer_that_returns_string (line 289) | def test_exception_renderer_that_returns_string
    method test_exception_renderer_that_raises (line 302) | def test_exception_renderer_that_raises
    method test_global_filter_option_on_render (line 313) | def test_global_filter_option_on_render
    method test_global_filter_option_when_native_filters_exist (line 320) | def test_global_filter_option_when_native_filters_exist
    method test_undefined_variables (line 327) | def test_undefined_variables
    method test_nil_value_does_not_raise (line 341) | def test_nil_value_does_not_raise
    method test_undefined_variables_raise (line 349) | def test_undefined_variables_raise
    method test_undefined_drop_methods (line 357) | def test_undefined_drop_methods
    method test_undefined_drop_methods_raise (line 367) | def test_undefined_drop_methods_raise
    method test_undefined_filters (line 376) | def test_undefined_filters
    method test_undefined_filters_raise (line 391) | def test_undefined_filters_raise
    method test_using_range_literal_works_as_expected (line 399) | def test_using_range_literal_works_as_expected
    method test_source_string_subclass (line 407) | def test_source_string_subclass
    method test_raises_error_with_invalid_utf8 (line 421) | def test_raises_error_with_invalid_utf8
    method test_allows_non_string_values_as_source (line 433) | def test_allows_non_string_values_as_source

FILE: test/integration/trim_mode_test.rb
  class TrimModeTest (line 5) | class TrimModeTest < Minitest::Test
    method test_standard_output (line 9) | def test_standard_output
    method test_variable_output_with_multiple_blank_lines (line 27) | def test_variable_output_with_multiple_blank_lines
    method test_tag_output_with_multiple_blank_lines (line 47) | def test_tag_output_with_multiple_blank_lines
    method test_standard_tags (line 70) | def test_standard_tags
    method test_no_trim_output (line 112) | def test_no_trim_output
    method test_no_trim_tags (line 119) | def test_no_trim_tags
    method test_single_line_outer_tag (line 129) | def test_single_line_outer_tag
    method test_single_line_inner_tag (line 139) | def test_single_line_inner_tag
    method test_single_line_post_tag (line 149) | def test_single_line_post_tag
    method test_single_line_pre_tag (line 159) | def test_single_line_pre_tag
    method test_pre_trim_output (line 169) | def test_pre_trim_output
    method test_pre_trim_tags (line 186) | def test_pre_trim_tags
    method test_post_trim_output (line 223) | def test_post_trim_output
    method test_post_trim_tags (line 240) | def test_post_trim_tags
    method test_pre_and_post_trim_tags (line 277) | def test_pre_and_post_trim_tags
    method test_post_and_pre_trim_tags (line 313) | def test_post_and_pre_trim_tags
    method test_trim_output (line 352) | def test_trim_output
    method test_trim_tags (line 368) | def test_trim_tags
    method test_whitespace_trim_output (line 402) | def test_whitespace_trim_output
    method test_whitespace_trim_tags (line 419) | def test_whitespace_trim_tags
    method test_complex_trim_output (line 453) | def test_complex_trim_output
    method test_complex_trim (line 483) | def test_complex_trim
    method test_right_trim_followed_by_tag (line 501) | def test_right_trim_followed_by_tag
    method test_raw_output (line 505) | def test_raw_output
    method test_pre_trim_blank_preceding_text (line 532) | def test_pre_trim_blank_preceding_text
    method test_bug_compatible_pre_trim (line 538) | def test_bug_compatible_pre_trim
    method test_trim_blank (line 555) | def test_trim_blank

FILE: test/integration/variable_test.rb
  class VariableTest (line 6) | class VariableTest < Minitest::Test
    method test_simple_variable (line 9) | def test_simple_variable
    method test_variable_render_calls_to_liquid (line 14) | def test_variable_render_calls_to_liquid
    method test_variable_lookup_calls_to_liquid_value (line 18) | def test_variable_lookup_calls_to_liquid_value
    method test_if_tag_calls_to_liquid_value (line 26) | def test_if_tag_calls_to_liquid_value
    method test_unless_tag_calls_to_liquid_value (line 42) | def test_unless_tag_calls_to_liquid_value
    method test_case_tag_calls_to_liquid_value (line 47) | def test_case_tag_calls_to_liquid_value
    method test_simple_with_whitespaces (line 51) | def test_simple_with_whitespaces
    method test_expression_with_whitespace_in_square_brackets (line 56) | def test_expression_with_whitespace_in_square_brackets
    method test_ignore_unknown (line 61) | def test_ignore_unknown
    method test_using_blank_as_variable_name (line 65) | def test_using_blank_as_variable_name
    method test_using_empty_as_variable_name (line 69) | def test_using_empty_as_variable_name
    method test_hash_scoping (line 73) | def test_hash_scoping
    method test_false_renders_as_false (line 78) | def test_false_renders_as_false
    method test_nil_renders_as_empty_string (line 83) | def test_nil_renders_as_empty_string
    method test_preset_assigns (line 88) | def test_preset_assigns
    method test_reuse_parsed_template (line 94) | def test_reuse_parsed_template
    method test_assigns_not_polluted_from_template (line 104) | def test_assigns_not_polluted_from_template
    method test_hash_with_default_proc (line 113) | def test_hash_with_default_proc
    method test_multiline_variable (line 125) | def test_multiline_variable
    method test_render_symbol (line 129) | def test_render_symbol
    method test_nested_array (line 133) | def test_nested_array
    method test_dynamic_find_var (line 137) | def test_dynamic_find_var
    method test_raw_value_variable (line 141) | def test_raw_value_variable
    method test_dynamic_find_var_with_drop (line 145) | def test_dynamic_find_var_with_drop
    method test_double_nested_variable_lookup (line 167) | def test_double_nested_variable_lookup
    method test_variable_lookup_should_not_hang_with_invalid_syntax (line 179) | def test_variable_lookup_should_not_hang_with_invalid_syntax
    method test_filter_with_single_trailing_comma (line 213) | def test_filter_with_single_trailing_comma
    method test_multiple_filters_with_trailing_commas (line 226) | def test_multiple_filters_with_trailing_commas
    method test_filter_with_colon_but_no_arguments (line 239) | def test_filter_with_colon_but_no_arguments
    method test_filter_chain_with_colon_no_args (line 252) | def test_filter_chain_with_colon_no_args
    method test_combining_trailing_comma_and_empty_args (line 265) | def test_combining_trailing_comma_and_empty_args

FILE: test/test_helper.rb
  type Minitest (line 25) | module Minitest
    class Test (line 26) | class Test
      method fixture (line 27) | def fixture(name)
    type Assertions (line 32) | module Assertions
      function assert_template_result (line 35) | def assert_template_result(
      function assert_match_syntax_error (line 49) | def assert_match_syntax_error(match, template, error_mode: nil)
      function assert_syntax_error (line 56) | def assert_syntax_error(template, error_mode: nil)
      function assert_usage_increment (line 60) | def assert_usage_increment(name, times: 1)
      function with_global_filter (line 77) | def with_global_filter(*globals, &blk)
      function with_error_modes (line 85) | def with_error_modes(*modes)
      function with_custom_tag (line 95) | def with_custom_tag(tag_name, tag_class, &block)
  class ThingWithToLiquid (line 104) | class ThingWithToLiquid
    method to_liquid (line 105) | def to_liquid
  class SettingsDrop (line 110) | class SettingsDrop < Liquid::Drop
    method initialize (line 111) | def initialize(settings)
    method liquid_method_missing (line 116) | def liquid_method_missing(key)
  class IntegerDrop (line 121) | class IntegerDrop < Liquid::Drop
    method initialize (line 122) | def initialize(value)
    method to_s (line 127) | def to_s
    method to_liquid_value (line 131) | def to_liquid_value
  class BooleanDrop (line 136) | class BooleanDrop < Liquid::Drop
    method initialize (line 137) | def initialize(value)
    method to_liquid_value (line 142) | def to_liquid_value
    method to_s (line 146) | def to_s
  class StringDrop (line 151) | class StringDrop < Liquid::Drop
    method initialize (line 154) | def initialize(value)
    method to_liquid_value (line 159) | def to_liquid_value
    method to_s (line 163) | def to_s
    method to_str (line 167) | def to_str
    method inspect (line 171) | def inspect
    method <=> (line 175) | def <=>(other)
  class ErrorDrop (line 180) | class ErrorDrop < Liquid::Drop
    method standard_error (line 181) | def standard_error
    method argument_error (line 185) | def argument_error
    method syntax_error (line 189) | def syntax_error
    method runtime_error (line 193) | def runtime_error
    method exception (line 197) | def exception
  class CustomToLiquidDrop (line 202) | class CustomToLiquidDrop < Liquid::Drop
    method initialize (line 203) | def initialize(value)
    method to_liquid (line 208) | def to_liquid
  class HashWithCustomToS (line 213) | class HashWithCustomToS < Hash
    method to_s (line 214) | def to_s
  class HashWithoutCustomToS (line 219) | class HashWithoutCustomToS < Hash
  class StubFileSystem (line 222) | class StubFileSystem
    method initialize (line 225) | def initialize(values)
    method read_template_file (line 230) | def read_template_file(template_path)
  class StubTemplateFactory (line 236) | class StubTemplateFactory
    method initialize (line 239) | def initialize
    method for (line 243) | def for(template_name)

FILE: test/unit/block_unit_test.rb
  class BlockUnitTest (line 5) | class BlockUnitTest < Minitest::Test
    method test_blankspace (line 8) | def test_blankspace
    method test_variable_beginning (line 13) | def test_variable_beginning
    method test_variable_end (line 20) | def test_variable_end
    method test_variable_middle (line 27) | def test_variable_middle
    method test_variable_with_multibyte_character (line 35) | def test_variable_with_multibyte_character
    method test_variable_many_embedded_fragments (line 41) | def test_variable_many_embedded_fragments
    method test_comment_tag_with_block (line 50) | def test_comment_tag_with_block
    method test_doc_tag_with_block (line 56) | def test_doc_tag_with_block
    method block_types (line 64) | def block_types(nodelist)

FILE: test/unit/condition_unit_test.rb
  class ConditionUnitTest (line 5) | class ConditionUnitTest < Minitest::Test
    method setup (line 8) | def setup
    method test_basic_condition (line 12) | def test_basic_condition
    method test_default_operators_evalute_true (line 17) | def test_default_operators_evalute_true
    method test_default_operators_evalute_false (line 34) | def test_default_operators_evalute_false
    method test_contains_works_on_strings (line 46) | def test_contains_works_on_strings
    method test_contains_binary_encoding_compatibility_with_utf8 (line 58) | def test_contains_binary_encoding_compatibility_with_utf8
    method test_invalid_comparation_operator (line 63) | def test_invalid_comparation_operator
    method test_comparation_of_int_and_str (line 67) | def test_comparation_of_int_and_str
    method test_hash_compare_backwards_compatibility (line 74) | def test_hash_compare_backwards_compatibility
    method test_contains_works_on_arrays (line 82) | def test_contains_works_on_arrays
    method test_contains_returns_false_for_nil_operands (line 97) | def test_contains_returns_false_for_nil_operands
    method test_contains_return_false_on_wrong_data_type (line 103) | def test_contains_return_false_on_wrong_data_type
    method test_contains_with_string_left_operand_coerces_right_operand_to_string (line 107) | def test_contains_with_string_left_operand_coerces_right_operand_to_st...
    method test_or_condition (line 112) | def test_or_condition
    method test_and_condition (line 125) | def test_and_condition
    method test_should_allow_custom_proc_operator (line 139) | def test_should_allow_custom_proc_operator
    method test_left_or_right_may_contain_operators (line 148) | def test_left_or_right_may_contain_operators
    method test_default_context_is_deprecated (line 155) | def test_default_context_is_deprecated
    method test_parse_expression_in_strict_mode (line 169) | def test_parse_expression_in_strict_mode
    method test_parse_expression_in_strict2_mode_raises_internal_error (line 179) | def test_parse_expression_in_strict2_mode_raises_internal_error
    method test_parse_expression_with_safe_true_in_strict2_mode (line 190) | def test_parse_expression_with_safe_true_in_strict2_mode
    method test_blank_with_whitespace_string (line 213) | def test_blank_with_whitespace_string
    method test_blank_with_empty_string (line 222) | def test_blank_with_empty_string
    method test_blank_with_empty_array (line 231) | def test_blank_with_empty_array
    method test_blank_with_empty_hash (line 240) | def test_blank_with_empty_hash
    method test_blank_with_nil (line 249) | def test_blank_with_nil
    method test_blank_with_false (line 258) | def test_blank_with_false
    method test_not_blank_with_true (line 267) | def test_not_blank_with_true
    method test_not_blank_with_number (line 276) | def test_not_blank_with_number
    method test_not_blank_with_string_content (line 285) | def test_not_blank_with_string_content
    method test_not_blank_with_non_empty_array (line 294) | def test_not_blank_with_non_empty_array
    method test_not_blank_with_non_empty_hash (line 303) | def test_not_blank_with_non_empty_hash
    method test_empty_with_empty_string (line 319) | def test_empty_with_empty_string
    method test_empty_with_whitespace_string_not_empty (line 328) | def test_empty_with_whitespace_string_not_empty
    method test_empty_with_empty_array (line 338) | def test_empty_with_empty_array
    method test_empty_with_empty_hash (line 347) | def test_empty_with_empty_hash
    method test_nil_is_not_empty (line 356) | def test_nil_is_not_empty
    method assert_evaluates_true (line 368) | def assert_evaluates_true(left, op, right)
    method assert_evaluates_false (line 375) | def assert_evaluates_false(left, op, right)
    method assert_evaluates_argument_error (line 382) | def assert_evaluates_argument_error(left, op, right)

FILE: test/unit/environment_filter_test.rb
  class EnvironmentFilterTest (line 5) | class EnvironmentFilterTest < Minitest::Test
    type AccessScopeFilters (line 8) | module AccessScopeFilters
      function public_filter (line 9) | def public_filter
      function private_filter (line 13) | def private_filter
    type LateAddedFilter (line 19) | module LateAddedFilter
      function late_added_filter (line 20) | def late_added_filter(_input)
    method setup (line 25) | def setup
    method test_strainer (line 33) | def test_strainer
    method test_strainer_raises_argument_error (line 39) | def test_strainer_raises_argument_error
    method test_strainer_argument_error_contains_backtrace (line 46) | def test_strainer_argument_error_contains_backtrace
    method test_strainer_only_invokes_public_filter_methods (line 62) | def test_strainer_only_invokes_public_filter_methods
    method test_strainer_returns_nil_if_no_filter_method_found (line 71) | def test_strainer_returns_nil_if_no_filter_method_found
    method test_strainer_returns_first_argument_if_no_method_and_arguments_given (line 77) | def test_strainer_returns_first_argument_if_no_method_and_arguments_given
    method test_strainer_only_allows_methods_defined_in_filters (line 82) | def test_strainer_only_allows_methods_defined_in_filters
    method test_strainer_uses_a_class_cache_to_avoid_method_cache_invalidation (line 89) | def test_strainer_uses_a_class_cache_to_avoid_method_cache_invalidation
    method test_add_global_filter_clears_cache (line 101) | def test_add_global_filter_clears_cache

FILE: test/unit/environment_test.rb
  class EnvironmentTest (line 5) | class EnvironmentTest < Minitest::Test
    class UnsubscribeFooter (line 8) | class UnsubscribeFooter < Liquid::Tag
      method render (line 9) | def render(_context)
    method test_custom_tag (line 14) | def test_custom_tag

FILE: test/unit/file_system_unit_test.rb
  class FileSystemUnitTest (line 5) | class FileSystemUnitTest < Minitest::Test
    method test_default (line 8) | def test_default
    method test_local (line 14) | def test_local
    method test_custom_template_filename_patterns (line 32) | def test_custom_template_filename_patterns

FILE: test/unit/i18n_unit_test.rb
  class I18nUnitTest (line 5) | class I18nUnitTest < Minitest::Test
    method setup (line 8) | def setup
    method test_simple_translate_string (line 12) | def test_simple_translate_string
    method test_nested_translate_string (line 16) | def test_nested_translate_string
    method test_single_string_interpolation (line 20) | def test_single_string_interpolation
    method test_raises_unknown_translation (line 30) | def test_raises_unknown_translation
    method test_sets_default_path_to_en (line 36) | def test_sets_default_path_to_en

FILE: test/unit/lexer_unit_test.rb
  class LexerUnitTest (line 5) | class LexerUnitTest < Minitest::Test
    method test_strings (line 8) | def test_strings
    method test_integer (line 15) | def test_integer
    method test_float (line 22) | def test_float
    method test_comparison (line 29) | def test_comparison
    method test_comparison_without_whitespace (line 36) | def test_comparison_without_whitespace
    method test_comparison_with_negative_number (line 43) | def test_comparison_with_negative_number
    method test_raise_for_invalid_comparison (line 50) | def test_raise_for_invalid_comparison
    method test_specials (line 64) | def test_specials
    method test_fancy_identifiers (line 76) | def test_fancy_identifiers
    method test_whitespace (line 82) | def test_whitespace
    method test_unexpected_character (line 89) | def test_unexpected_character
    method test_negative_numbers (line 95) | def test_negative_numbers
    method test_greater_than_two_digits (line 102) | def test_greater_than_two_digits
    method test_error_with_utf8_character (line 109) | def test_error_with_utf8_character
    method test_contains_as_attribute_name (line 120) | def test_contains_as_attribute_name
    method test_tokenize_incomplete_expression (line 127) | def test_tokenize_incomplete_expression
    method test_error_with_invalid_utf8 (line 134) | def test_error_with_invalid_utf8
    method tokenize (line 146) | def tokenize(input)

FILE: test/unit/parse_context_unit_test.rb
  class ParseContextUnitTest (line 5) | class ParseContextUnitTest < Minitest::Test
    method test_safe_parse_expression_with_variable_lookup (line 8) | def test_safe_parse_expression_with_variable_lookup
    method test_safe_parse_expression_raises_syntax_error_for_invalid_expression (line 24) | def test_safe_parse_expression_raises_syntax_error_for_invalid_expression
    method test_parse_expression_with_variable_lookup (line 40) | def test_parse_expression_with_variable_lookup
    method test_parse_expression_with_safe_true (line 54) | def test_parse_expression_with_safe_true
    method test_parse_expression_with_empty_string (line 68) | def test_parse_expression_with_empty_string
    method test_parse_expression_with_empty_string_and_safe_true (line 79) | def test_parse_expression_with_empty_string_and_safe_true
    method test_safe_parse_expression_advances_parser_pointer (line 87) | def test_safe_parse_expression_advances_parser_pointer
    method test_parse_expression_with_whitespace_in_strict2_mode (line 105) | def test_parse_expression_with_whitespace_in_strict2_mode
    method strict_parse_context (line 112) | def strict_parse_context
    method strict2_parse_context (line 118) | def strict2_parse_context

FILE: test/unit/parse_tree_visitor_test.rb
  class ParseTreeVisitorTest (line 5) | class ParseTreeVisitorTest < Minitest::Test
    method test_variable (line 8) | def test_variable
    method test_varible_with_filter (line 15) | def test_varible_with_filter
    method test_dynamic_variable (line 22) | def test_dynamic_variable
    method test_echo (line 29) | def test_echo
    method test_if_condition (line 36) | def test_if_condition
    method test_complex_if_condition (line 43) | def test_complex_if_condition
    method test_if_body (line 50) | def test_if_body
    method test_unless_condition (line 57) | def test_unless_condition
    method test_complex_unless_condition (line 64) | def test_complex_unless_condition
    method test_unless_body (line 71) | def test_unless_body
    method test_elsif_condition (line 78) | def test_elsif_condition
    method test_complex_elsif_condition (line 85) | def test_complex_elsif_condition
    method test_elsif_body (line 92) | def test_elsif_body
    method test_else_body (line 99) | def test_else_body
    method test_case_left (line 106) | def test_case_left
    method test_case_condition (line 113) | def test_case_condition
    method test_case_when_body (line 120) | def test_case_when_body
    method test_case_else_body (line 127) | def test_case_else_body
    method test_for_in (line 134) | def test_for_in
    method test_for_limit (line 141) | def test_for_limit
    method test_for_offset (line 148) | def test_for_offset
    method test_for_body (line 155) | def test_for_body
    method test_for_range (line 162) | def test_for_range
    method test_tablerow_in (line 169) | def test_tablerow_in
    method test_tablerow_limit (line 176) | def test_tablerow_limit
    method test_tablerow_offset (line 183) | def test_tablerow_offset
    method test_tablerow_body (line 190) | def test_tablerow_body
    method test_cycle (line 197) | def test_cycle
    method test_assign (line 204) | def test_assign
    method test_capture (line 211) | def test_capture
    method test_include (line 218) | def test_include
    method test_include_with (line 225) | def test_include_with
    method test_include_for (line 232) | def test_include_for
    method test_render_with (line 239) | def test_render_with
    method test_render_for (line 246) | def test_render_for
    method test_preserve_tree_structure (line 253) | def test_preserve_tree_structure
    method traversal (line 266) | def traversal(template)
    method visit (line 272) | def visit(template)

FILE: test/unit/parser_unit_test.rb
  class ParserUnitTest (line 5) | class ParserUnitTest < Minitest::Test
    method test_consume (line 8) | def test_consume
    method test_jump (line 15) | def test_jump
    method test_consume? (line 21) | def test_consume?
    method test_id? (line 29) | def test_id?
    method test_look (line 38) | def test_look
    method test_expressions (line 48) | def test_expressions
    method test_ranges (line 61) | def test_ranges
    method test_arguments (line 69) | def test_arguments
    method test_invalid_expression (line 78) | def test_invalid_expression
    method new_parser (line 87) | def new_parser(str)

FILE: test/unit/partial_cache_unit_test.rb
  class PartialCacheUnitTest (line 5) | class PartialCacheUnitTest < Minitest::Test
    method test_uses_the_file_system_register_if_present (line 6) | def test_uses_the_file_system_register_if_present
    method test_reads_from_the_file_system_only_once_per_file (line 22) | def test_reads_from_the_file_system_only_once_per_file
    method test_cache_state_is_stored_per_context (line 39) | def test_cache_state_is_stored_per_context
    method test_cache_is_not_broken_when_a_different_parse_context_is_used (line 72) | def test_cache_is_not_broken_when_a_different_parse_context_is_used
    method test_uses_default_template_factory_when_no_template_factory_found_in_register (line 94) | def test_uses_default_template_factory_when_no_template_factory_found_...
    method test_uses_template_factory_register_if_present (line 110) | def test_uses_template_factory_register_if_present
    method test_cache_state_is_shared_for_subcontexts (line 129) | def test_cache_state_is_shared_for_subcontexts
    method test_uses_template_name_from_template_factory (line 160) | def test_uses_template_name_from_template_factory
    method test_includes_error_mode_into_template_cache (line 178) | def test_includes_error_mode_into_template_cache

FILE: test/unit/regexp_unit_test.rb
  class RegexpUnitTest (line 6) | class RegexpUnitTest < Minitest::Test
    method test_empty (line 9) | def test_empty
    method test_quote (line 13) | def test_quote
    method test_words (line 17) | def test_words
    method test_tags (line 21) | def test_tags
    method test_double_quoted_words (line 27) | def test_double_quoted_words
    method test_single_quoted_words (line 31) | def test_single_quoted_words
    method test_quoted_words_in_the_middle (line 35) | def test_quoted_words_in_the_middle
    method test_variable_parser (line 39) | def test_variable_parser
    method test_variable_parser_with_large_input (line 49) | def test_variable_parser_with_large_input

FILE: test/unit/registers_unit_test.rb
  class RegistersUnitTest (line 5) | class RegistersUnitTest < Minitest::Test
    method test_set (line 8) | def test_set
    method test_get_missing_key (line 18) | def test_get_missing_key
    method test_delete (line 24) | def test_delete
    method test_fetch (line 39) | def test_fetch
    method test_key (line 63) | def test_key
    method test_static_register_can_be_frozen (line 74) | def test_static_register_can_be_frozen
    method test_new_static_retains_static (line 96) | def test_new_static_retains_static
    method test_multiple_instances_are_unique (line 120) | def test_multiple_instances_are_unique
    method test_initialization_reused_static_same_memory_object (line 140) | def test_initialization_reused_static_same_memory_object

FILE: test/unit/resource_limits_unit_test.rb
  class ResourceLimitsUnitTest (line 5) | class ResourceLimitsUnitTest < Minitest::Test
    method test_cumulative_scores_initialize_to_zero (line 6) | def test_cumulative_scores_initialize_to_zero
    method test_cumulative_limits_default_to_nil (line 12) | def test_cumulative_limits_default_to_nil
    method test_cumulative_limits_configurable_via_hash (line 18) | def test_cumulative_limits_configurable_via_hash
    method test_cumulative_limits_configurable_via_accessor (line 27) | def test_cumulative_limits_configurable_via_accessor
    method test_cumulative_scores_survive_reset (line 33) | def test_cumulative_scores_survive_reset
    method test_cumulative_scores_accumulate_across_resets (line 46) | def test_cumulative_scores_accumulate_across_resets
    method test_cumulative_render_score_limit_raises (line 58) | def test_cumulative_render_score_limit_raises
    method test_cumulative_assign_score_limit_raises (line 71) | def test_cumulative_assign_score_limit_raises
    method test_per_template_limits_still_work_with_cumulative (line 82) | def test_per_template_limits_still_work_with_cumulative

FILE: test/unit/strainer_template_unit_test.rb
  class StrainerTemplateUnitTest (line 5) | class StrainerTemplateUnitTest < Minitest::Test
    method test_add_filter_when_wrong_filter_class (line 8) | def test_add_filter_when_wrong_filter_class
    type PrivateMethodOverrideFilter (line 19) | module PrivateMethodOverrideFilter
      function public_filter (line 22) | def public_filter
    method test_add_filter_raises_when_module_privately_overrides_registered_public_methods (line 27) | def test_add_filter_raises_when_module_privately_overrides_registered_...
    type ProtectedMethodOverrideFilter (line 38) | module ProtectedMethodOverrideFilter
      function public_filter (line 41) | def public_filter
    method test_add_filter_raises_when_module_overrides_registered_public_method_as_protected (line 46) | def test_add_filter_raises_when_module_overrides_registered_public_met...
    type PublicMethodOverrideFilter (line 57) | module PublicMethodOverrideFilter
      function public_filter (line 58) | def public_filter
    method test_add_filter_does_not_raise_when_module_overrides_previously_registered_method (line 63) | def test_add_filter_does_not_raise_when_module_overrides_previously_re...
    method test_add_filter_does_not_include_already_included_module (line 72) | def test_add_filter_does_not_include_already_included_module

FILE: test/unit/tag_unit_test.rb
  class TagUnitTest (line 5) | class TagUnitTest < Minitest::Test
    method test_tag (line 8) | def test_tag
    method test_return_raw_text_of_tag (line 14) | def test_return_raw_text_of_tag
    method test_tag_name_should_return_name_of_the_tag (line 19) | def test_tag_name_should_return_name_of_the_tag
    class CustomTag (line 24) | class CustomTag < Liquid::Tag
      method render (line 25) | def render(_context); end
    method test_tag_render_to_output_buffer_nil_value (line 28) | def test_tag_render_to_output_buffer_nil_value
    method new_tokenizer (line 35) | def new_tokenizer

FILE: test/unit/tags/case_tag_unit_test.rb
  class CaseTagUnitTest (line 5) | class CaseTagUnitTest < Minitest::Test
    method test_case_nodelist (line 8) | def test_case_nodelist
    method test_case_with_trailing_element (line 13) | def test_case_with_trailing_element
    method test_case_when_with_trailing_element (line 34) | def test_case_when_with_trailing_element
    method test_case_when_with_comma (line 55) | def test_case_when_with_comma
    method test_case_when_with_or (line 70) | def test_case_when_with_or
    method test_case_when_empty (line 85) | def test_case_when_empty
    method test_case_with_invalid_expression (line 105) | def test_case_with_invalid_expression
    method test_case_when_with_invalid_expression (line 127) | def test_case_when_with_invalid_expression

FILE: test/unit/tags/comment_tag_unit_test.rb
  class CommentTagUnitTest (line 5) | class CommentTagUnitTest < Minitest::Test
    method test_comment_inside_liquid_tag (line 6) | def test_comment_inside_liquid_tag
    method test_does_not_parse_nodes_inside_a_comment (line 19) | def test_does_not_parse_nodes_inside_a_comment
    method test_allows_unclosed_tags (line 34) | def test_allows_unclosed_tags
    method test_open_tags_in_comment (line 42) | def test_open_tags_in_comment
    method test_child_comment_tags_need_to_be_closed (line 75) | def test_child_comment_tags_need_to_be_closed
    method test_child_raw_tags_need_to_be_closed (line 95) | def test_child_raw_tags_need_to_be_closed
    method test_error_line_number_is_correct (line 114) | def test_error_line_number_is_correct
    method test_comment_tag_delimiter_with_extra_strings (line 131) | def test_comment_tag_delimiter_with_extra_strings
    method test_nested_comment_tag_with_extra_strings (line 145) | def test_nested_comment_tag_with_extra_strings
    method test_ignores_delimiter_with_extra_strings (line 159) | def test_ignores_delimiter_with_extra_strings
    method test_delimiter_can_have_extra_strings (line 172) | def test_delimiter_can_have_extra_strings
    method test_with_whitespace_control (line 180) | def test_with_whitespace_control
    method test_dont_override_liquid_tag_whitespace_control (line 191) | def test_dont_override_liquid_tag_whitespace_control

FILE: test/unit/tags/doc_tag_unit_test.rb
  class DocTagUnitTest (line 5) | class DocTagUnitTest < Minitest::Test
    method test_doc_tag (line 6) | def test_doc_tag
    method test_doc_tag_body_content (line 23) | def test_doc_tag_body_content
    method test_doc_tag_does_not_support_extra_arguments (line 38) | def test_doc_tag_does_not_support_extra_arguments
    method test_doc_tag_must_support_valid_tags (line 54) | def test_doc_tag_must_support_valid_tags
    method test_doc_tag_ignores_liquid_nodes (line 60) | def test_doc_tag_ignores_liquid_nodes
    method test_doc_tag_ignores_unclosed_liquid_tags (line 77) | def test_doc_tag_ignores_unclosed_liquid_tags
    method test_doc_tag_does_not_allow_nested_docs (line 87) | def test_doc_tag_does_not_allow_nested_docs
    method test_doc_tag_ignores_nested_raw_tags (line 105) | def test_doc_tag_ignores_nested_raw_tags
    method test_doc_tag_ignores_unclosed_assign (line 115) | def test_doc_tag_ignores_unclosed_assign
    method test_doc_tag_ignores_malformed_syntax (line 125) | def test_doc_tag_ignores_malformed_syntax
    method test_doc_tag_captures_token_before_enddoc (line 134) | def test_doc_tag_captures_token_before_enddoc
    method test_doc_tag_preserves_error_line_numbers (line 148) | def test_doc_tag_preserves_error_line_numbers
    method test_doc_tag_whitespace_control (line 164) | def test_doc_tag_whitespace_control
    method test_doc_tag_delimiter_handling (line 175) | def test_doc_tag_delimiter_handling
    method test_doc_tag_visitor (line 190) | def test_doc_tag_visitor
    method test_doc_tag_blank_with_empty_content (line 199) | def test_doc_tag_blank_with_empty_content
    method test_doc_tag_blank_with_content (line 213) | def test_doc_tag_blank_with_content
    method test_doc_tag_blank_with_whitespace_only (line 227) | def test_doc_tag_blank_with_whitespace_only
    method test_doc_tag_nodelist_returns_array_with_body (line 241) | def test_doc_tag_nodelist_returns_array_with_body
    method test_doc_tag_nodelist_with_empty_content (line 258) | def test_doc_tag_nodelist_with_empty_content
    method traversal (line 275) | def traversal(template)
    method visit (line 284) | def visit(template)

FILE: test/unit/tags/for_tag_unit_test.rb
  class ForTagUnitTest (line 5) | class ForTagUnitTest < Minitest::Test
    method test_for_nodelist (line 6) | def test_for_nodelist
    method test_for_else_nodelist (line 11) | def test_for_else_nodelist

FILE: test/unit/tags/if_tag_unit_test.rb
  class IfTagUnitTest (line 5) | class IfTagUnitTest < Minitest::Test
    method test_if_nodelist (line 6) | def test_if_nodelist

FILE: test/unit/template_factory_unit_test.rb
  class TemplateFactoryUnitTest (line 5) | class TemplateFactoryUnitTest < Minitest::Test
    method test_for_returns_liquid_template_instance (line 8) | def test_for_returns_liquid_template_instance

FILE: test/unit/template_unit_test.rb
  class TemplateUnitTest (line 5) | class TemplateUnitTest < Minitest::Test
    method test_sets_default_localization_in_document (line 8) | def test_sets_default_localization_in_document
    method test_sets_default_localization_in_context_with_quick_initialization (line 14) | def test_sets_default_localization_in_context_with_quick_initialization
    class FakeTag (line 23) | class FakeTag; end
    method test_tags_can_be_looped_over (line 25) | def test_tags_can_be_looped_over
    class TemplateSubclass (line 32) | class TemplateSubclass < Liquid::Template
    method test_template_inheritance (line 35) | def test_template_inheritance
    method test_invalid_utf8 (line 39) | def test_invalid_utf8

FILE: test/unit/tokenizer_unit_test.rb
  class TokenizerTest (line 5) | class TokenizerTest < Minitest::Test
    method test_tokenize_strings (line 6) | def test_tokenize_strings
    method test_tokenize_variables (line 12) | def test_tokenize_variables
    method test_tokenize_blocks (line 19) | def test_tokenize_blocks
    method test_calculate_line_numbers_per_token_with_profiling (line 27) | def test_calculate_line_numbers_per_token_with_profiling
    method test_tokenize_with_nil_source_returns_empty_array (line 34) | def test_tokenize_with_nil_source_returns_empty_array
    method test_incomplete_curly_braces (line 38) | def test_incomplete_curly_braces
    method test_unmatching_start_and_end (line 44) | def test_unmatching_start_and_end
    method new_tokenizer (line 53) | def new_tokenizer(source, parse_context: Liquid::ParseContext.new, sta...
    method tokenize (line 57) | def tokenize(source)
    method tokenize_line_numbers (line 67) | def tokenize_line_numbers(source)

FILE: test/unit/variable_unit_test.rb
  class VariableUnitTest (line 5) | class VariableUnitTest < Minitest::Test
    method test_variable (line 8) | def test_variable
    method test_filters (line 13) | def test_filters
    method test_filter_with_date_parameter (line 55) | def test_filter_with_date_parameter
    method test_filters_without_whitespace (line 61) | def test_filters_without_whitespace
    method test_symbol (line 75) | def test_symbol
    method test_string_to_filter (line 81) | def test_string_to_filter
    method test_string_single_quoted (line 87) | def test_string_single_quoted
    method test_string_double_quoted (line 92) | def test_string_double_quoted
    method test_integer (line 97) | def test_integer
    method test_float (line 102) | def test_float
    method test_dashes (line 107) | def test_dashes
    method test_string_with_special_chars (line 118) | def test_string_with_special_chars
    method test_string_dot (line 123) | def test_string_dot
    method test_filter_with_keyword_arguments (line 128) | def test_filter_with_keyword_arguments
    method test_lax_filter_argument_parsing (line 134) | def test_lax_filter_argument_parsing
    method test_strict_filter_argument_parsing (line 156) | def test_strict_filter_argument_parsing
    method test_strict2_filter_argument_parsing (line 164) | def test_strict2_filter_argument_parsing
    method test_output_raw_source_of_variable (line 200) | def test_output_raw_source_of_variable
    method test_variable_lookup_interface (line 205) | def test_variable_lookup_interface
    method create_variable (line 213) | def create_variable(markup, options = {})
Condensed preview — 204 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (784K chars).
[
  {
    "path": ".github/dependabot.yaml",
    "chars": 114,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: \"/\"\n    schedule:\n      interval: weekly\n"
  },
  {
    "path": ".github/workflows/cla.yml",
    "chars": 593,
    "preview": "name: Contributor License Agreement (CLA)\n\non:\n  pull_request_target:\n    types: [opened, synchronize]\n  issue_comment:\n"
  },
  {
    "path": ".github/workflows/liquid.yml",
    "chars": 2509,
    "preview": "name: Liquid\non: [push]\n\nenv:\n  BUNDLE_JOBS: 4\n  BUNDLE_RETRY: 3\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    strategy:"
  },
  {
    "path": ".gitignore",
    "chars": 69,
    "preview": "*~\n*.gem\n*.swp\npkg\n*.rbc\n.rvmrc\n.bundle\n.byebug_history\nGemfile.lock\n"
  },
  {
    "path": ".rubocop.yml",
    "chars": 508,
    "preview": "inherit_gem:\n  rubocop-shopify: rubocop.yml\n\ninherit_from:\n  - .rubocop_todo.yml\n\nrequire: rubocop-performance\n\nPerforma"
  },
  {
    "path": ".rubocop_todo.yml",
    "chars": 6337,
    "preview": "# This configuration was generated by\n# `rubocop --auto-gen-config`\n# on 2022-05-18 19:25:47 UTC using RuboCop version 1"
  },
  {
    "path": ".ruby-version",
    "chars": 6,
    "preview": "3.4.1\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1556,
    "preview": "# How to contribute\n\n## Things we will merge\n\n* Bugfixes\n* Performance improvements\n* Features that are likely to be use"
  },
  {
    "path": "Gemfile",
    "chars": 732,
    "preview": "# frozen_string_literal: true\n\nsource 'https://rubygems.org'\ngit_source(:github) do |repo_name|\n  \"https://github.com/#{"
  },
  {
    "path": "History.md",
    "chars": 21535,
    "preview": "# Liquid Change Log\n\n## 5.11.0\n* Revert the Inline Snippets tag (#2001), treat its inclusion in the latest Liquid releas"
  },
  {
    "path": "LICENSE",
    "chars": 1063,
    "preview": "Copyright (c) 2005, 2006 Tobias Luetke\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of "
  },
  {
    "path": "README.md",
    "chars": 7783,
    "preview": "[![Build status](https://github.com/Shopify/liquid/actions/workflows/liquid.yml/badge.svg)](https://github.com/Shopify/l"
  },
  {
    "path": "Rakefile",
    "chars": 3765,
    "preview": "# frozen_string_literal: true\n\nrequire 'rake'\nrequire 'rake/testtask'\n$LOAD_PATH.unshift(File.expand_path(\"../lib\", __FI"
  },
  {
    "path": "bin/render",
    "chars": 909,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nrequire 'bundler/setup'\nrequire 'liquid'\n\nclass VirtualFileSystem\n  d"
  },
  {
    "path": "example/server/example_servlet.rb",
    "chars": 1493,
    "preview": "# frozen_string_literal: true\n\nmodule ProductsFilter\n  def price(integer)\n    format(\"$%.2d USD\", integer / 100.0)\n  end"
  },
  {
    "path": "example/server/liquid_servlet.rb",
    "chars": 716,
    "preview": "# frozen_string_literal: true\n\nclass LiquidServlet < WEBrick::HTTPServlet::AbstractServlet\n  def do_GET(req, res)\n    ha"
  },
  {
    "path": "example/server/server.rb",
    "chars": 325,
    "preview": "# frozen_string_literal: true\n\nrequire 'webrick'\nrequire 'rexml/document'\n\nrequire_relative '../../lib/liquid'\nrequire_r"
  },
  {
    "path": "example/server/templates/index.liquid",
    "chars": 107,
    "preview": "<p>Hello world!</p>\n\n<p>It is {{date}}</p>\n\n\n<p>Check out the <a href=\"/products\">Products</a> screen </p>\n"
  },
  {
    "path": "example/server/templates/products.liquid",
    "chars": 1431,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n  \"http://www.w3.org/TR/"
  },
  {
    "path": "lib/liquid/block.rb",
    "chars": 2332,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class Block < Tag\n    MAX_DEPTH = 100\n\n    def initialize(tag_name, marku"
  },
  {
    "path": "lib/liquid/block_body.rb",
    "chars": 9172,
    "preview": "# frozen_string_literal: true\n\nrequire 'English'\n\nmodule Liquid\n  class BlockBody\n    LiquidTagToken      = /\\A\\s*(#{Tag"
  },
  {
    "path": "lib/liquid/condition.rb",
    "chars": 6014,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # Container for liquid nodes which conveniently wraps decision making log"
  },
  {
    "path": "lib/liquid/const.rb",
    "chars": 125,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  module Const\n    EMPTY_HASH = {}.freeze\n    EMPTY_ARRAY = [].freeze\n  end"
  },
  {
    "path": "lib/liquid/context.rb",
    "chars": 9478,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # Context keeps the variable stack and resolves variables, as well as key"
  },
  {
    "path": "lib/liquid/deprecations.rb",
    "chars": 469,
    "preview": "# frozen_string_literal: true\n\nrequire \"set\"\n\nmodule Liquid\n  class Deprecations\n    class << self\n      attr_accessor :"
  },
  {
    "path": "lib/liquid/document.rb",
    "chars": 1448,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class Document\n    def self.parse(tokens, parse_context)\n      doc = new("
  },
  {
    "path": "lib/liquid/drop.rb",
    "chars": 2260,
    "preview": "# frozen_string_literal: true\n\nrequire 'set'\n\nmodule Liquid\n  # A drop in liquid is a class which allows you to export D"
  },
  {
    "path": "lib/liquid/environment.rb",
    "chars": 5354,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # The Environment is the container for all configuration options of Liqui"
  },
  {
    "path": "lib/liquid/errors.rb",
    "chars": 1445,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class Error < ::StandardError\n    attr_accessor :line_number\n    attr_acc"
  },
  {
    "path": "lib/liquid/expression.rb",
    "chars": 3598,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class Expression\n    LITERALS = {\n      nil => nil,\n      'nil' => nil,\n "
  },
  {
    "path": "lib/liquid/extensions.rb",
    "chars": 780,
    "preview": "# frozen_string_literal: true\n\nrequire 'time'\nrequire 'date'\n\nclass String # :nodoc:\n  def to_liquid\n    self\n  end\nend\n"
  },
  {
    "path": "lib/liquid/file_system.rb",
    "chars": 2806,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # A Liquid file system is a way to let your templates retrieve other temp"
  },
  {
    "path": "lib/liquid/forloop_drop.rb",
    "chars": 2123,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type object\n  # @liquid_name forloop\n  "
  },
  {
    "path": "lib/liquid/i18n.rb",
    "chars": 979,
    "preview": "# frozen_string_literal: true\n\nrequire 'yaml'\n\nmodule Liquid\n  class I18n\n    DEFAULT_LOCALE = File.join(File.expand_pat"
  },
  {
    "path": "lib/liquid/interrupts.rb",
    "chars": 479,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # An interrupt is any command that breaks processing of a block (ex: a fo"
  },
  {
    "path": "lib/liquid/lexer.rb",
    "chars": 5702,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class Lexer\n    CLOSE_ROUND = [:close_round, \")\"].freeze\n    CLOSE_SQUARE"
  },
  {
    "path": "lib/liquid/locales/en.yml",
    "chars": 2451,
    "preview": "---\n  errors:\n    syntax:\n      tag_unexpected_args: \"Syntax Error in '%{tag}' - Valid syntax: %{tag}\"\n      block_tag_u"
  },
  {
    "path": "lib/liquid/parse_context.rb",
    "chars": 2819,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class ParseContext\n    attr_accessor :locale, :line_number, :trim_whitesp"
  },
  {
    "path": "lib/liquid/parse_tree_visitor.rb",
    "chars": 1036,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class ParseTreeVisitor\n    def self.for(node, callbacks = Hash.new(proc {"
  },
  {
    "path": "lib/liquid/parser.rb",
    "chars": 2299,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class Parser\n    def initialize(input)\n      ss = input.is_a?(StringScann"
  },
  {
    "path": "lib/liquid/parser_switching.rb",
    "chars": 2155,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  module ParserSwitching\n    # Do not use this.\n    #\n    # It's basically "
  },
  {
    "path": "lib/liquid/partial_cache.rb",
    "chars": 925,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class PartialCache\n    def self.load(template_name, context:, parse_conte"
  },
  {
    "path": "lib/liquid/profiler/hooks.rb",
    "chars": 859,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  module BlockBodyProfilingHook\n    def render_node(context, output, node)\n"
  },
  {
    "path": "lib/liquid/profiler.rb",
    "chars": 4060,
    "preview": "# frozen_string_literal: true\n\nrequire 'liquid/profiler/hooks'\n\nmodule Liquid\n  # Profiler enables support for profiling"
  },
  {
    "path": "lib/liquid/range_lookup.rb",
    "chars": 1477,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class RangeLookup\n    def self.parse(start_markup, end_markup, string_sca"
  },
  {
    "path": "lib/liquid/registers.rb",
    "chars": 965,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class Registers\n    attr_reader :static\n\n    def initialize(registers = {"
  },
  {
    "path": "lib/liquid/resource_limits.rb",
    "chars": 2709,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class ResourceLimits\n    attr_accessor :render_length_limit,\n      :rende"
  },
  {
    "path": "lib/liquid/standardfilters.rb",
    "chars": 34720,
    "preview": "# frozen_string_literal: true\n\nrequire 'cgi'\nrequire 'base64'\nrequire 'bigdecimal'\nmodule Liquid\n  module StandardFilter"
  },
  {
    "path": "lib/liquid/strainer_template.rb",
    "chars": 1767,
    "preview": "# frozen_string_literal: true\n\nrequire 'set'\n\nmodule Liquid\n  # StrainerTemplate is the computed class for the filters s"
  },
  {
    "path": "lib/liquid/tablerowloop_drop.rb",
    "chars": 2763,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type object\n  # @liquid_name tablerowlo"
  },
  {
    "path": "lib/liquid/tag/disableable.rb",
    "chars": 606,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class Tag\n    module Disableable\n      def render_to_output_buffer(contex"
  },
  {
    "path": "lib/liquid/tag/disabler.rb",
    "chars": 248,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class Tag\n    module Disabler\n      def render_to_output_buffer(context, "
  },
  {
    "path": "lib/liquid/tag.rb",
    "chars": 1717,
    "preview": "# frozen_string_literal: true\n\nrequire 'liquid/tag/disabler'\nrequire 'liquid/tag/disableable'\n\nmodule Liquid\n  class Tag"
  },
  {
    "path": "lib/liquid/tags/assign.rb",
    "chars": 2237,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category variable\n"
  },
  {
    "path": "lib/liquid/tags/break.rb",
    "chars": 668,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # Break tag to be used to break out of a for loop.\n  #\n  # == Basic Usage"
  },
  {
    "path": "lib/liquid/tags/capture.rb",
    "chars": 1339,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category variable\n"
  },
  {
    "path": "lib/liquid/tags/case.rb",
    "chars": 4382,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category condition"
  },
  {
    "path": "lib/liquid/tags/comment.rb",
    "chars": 2325,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category syntax\n  "
  },
  {
    "path": "lib/liquid/tags/continue.rb",
    "chars": 482,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category iteration"
  },
  {
    "path": "lib/liquid/tags/cycle.rb",
    "chars": 4205,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category iteration"
  },
  {
    "path": "lib/liquid/tags/decrement.rb",
    "chars": 1761,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category variable\n"
  },
  {
    "path": "lib/liquid/tags/doc.rb",
    "chars": 2238,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category syntax\n  "
  },
  {
    "path": "lib/liquid/tags/echo.rb",
    "chars": 1071,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category syntax\n  "
  },
  {
    "path": "lib/liquid/tags/for.rb",
    "chars": 5958,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category iteration"
  },
  {
    "path": "lib/liquid/tags/if.rb",
    "chars": 3904,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category condition"
  },
  {
    "path": "lib/liquid/tags/ifchanged.rb",
    "chars": 362,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class Ifchanged < Block\n    def render_to_output_buffer(context, output)\n"
  },
  {
    "path": "lib/liquid/tags/include.rb",
    "chars": 4071,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category theme\n  #"
  },
  {
    "path": "lib/liquid/tags/increment.rb",
    "chars": 1748,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category variable\n"
  },
  {
    "path": "lib/liquid/tags/inline_comment.rb",
    "chars": 841,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class InlineComment < Tag\n    def initialize(tag_name, markup, options)\n "
  },
  {
    "path": "lib/liquid/tags/raw.rb",
    "chars": 1432,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category syntax\n  "
  },
  {
    "path": "lib/liquid/tags/render.rb",
    "chars": 5186,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category theme\n  #"
  },
  {
    "path": "lib/liquid/tags/table_row.rb",
    "chars": 4265,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @liquid_category iteration"
  },
  {
    "path": "lib/liquid/tags/unless.rb",
    "chars": 1368,
    "preview": "# frozen_string_literal: true\n\nrequire_relative 'if'\n\nmodule Liquid\n  # @liquid_public_docs\n  # @liquid_type tag\n  # @li"
  },
  {
    "path": "lib/liquid/tags.rb",
    "chars": 1247,
    "preview": "# frozen_string_literal: true\n\nrequire_relative \"tags/table_row\"\nrequire_relative \"tags/echo\"\nrequire_relative \"tags/if\""
  },
  {
    "path": "lib/liquid/template.rb",
    "chars": 7659,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # Templates are central to liquid.\n  # Interpreting templates is a two st"
  },
  {
    "path": "lib/liquid/template_factory.rb",
    "chars": 142,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class TemplateFactory\n    def for(_template_name)\n      Liquid::Template."
  },
  {
    "path": "lib/liquid/tokenizer.rb",
    "chars": 3296,
    "preview": "# frozen_string_literal: true\n\nrequire \"strscan\"\n\nmodule Liquid\n  class Tokenizer\n    attr_reader :line_number, :for_liq"
  },
  {
    "path": "lib/liquid/usage.rb",
    "chars": 107,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  module Usage\n    def self.increment(name)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/liquid/utils.rb",
    "chars": 4321,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  module Utils\n    DECIMAL_REGEX = /\\A-?\\d+\\.\\d+\\z/\n    UNIX_TIMESTAMP_REGE"
  },
  {
    "path": "lib/liquid/variable.rb",
    "chars": 5994,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  # Holds variables. Variables are only loaded \"just in time\"\n  # and are n"
  },
  {
    "path": "lib/liquid/variable_lookup.rb",
    "chars": 3406,
    "preview": "# frozen_string_literal: true\n\nmodule Liquid\n  class VariableLookup\n    COMMAND_METHODS = ['size', 'first', 'last'].free"
  },
  {
    "path": "lib/liquid/version.rb",
    "chars": 88,
    "preview": "# encoding: utf-8\n# frozen_string_literal: true\n\nmodule Liquid\n  VERSION = \"5.12.0\"\nend\n"
  },
  {
    "path": "lib/liquid.rb",
    "chars": 3445,
    "preview": "# frozen_string_literal: true\n\n# Copyright (c) 2005 Tobias Luetke\n#\n# Permission is hereby granted, free of charge, to a"
  },
  {
    "path": "liquid.gemspec",
    "chars": 1099,
    "preview": "# encoding: utf-8\n# frozen_string_literal: true\n\nlib = File.expand_path('../lib/', __FILE__)\n$LOAD_PATH.unshift(lib) unl"
  },
  {
    "path": "performance/benchmark.rb",
    "chars": 757,
    "preview": "# frozen_string_literal: true\n\nrequire 'benchmark/ips'\nrequire_relative 'theme_runner'\n\nRubyVM::YJIT.enable if defined?("
  },
  {
    "path": "performance/memory_profile.rb",
    "chars": 1606,
    "preview": "# frozen_string_literal: true\n\nrequire 'benchmark/ips'\nrequire 'memory_profiler'\nrequire 'terminal-table'\nrequire_relati"
  },
  {
    "path": "performance/profile.rb",
    "chars": 716,
    "preview": "# frozen_string_literal: true\n\nrequire 'stackprof'\nrequire_relative 'theme_runner'\n\nLiquid::Template.error_mode = ARGV.f"
  },
  {
    "path": "performance/shopify/comment_form.rb",
    "chars": 1049,
    "preview": "# frozen_string_literal: true\n\nclass CommentForm < Liquid::Block\n  Syntax = /(#{Liquid::VariableSignature}+)/\n\n  def ini"
  },
  {
    "path": "performance/shopify/database.rb",
    "chars": 1745,
    "preview": "# frozen_string_literal: true\n\nrequire 'yaml'\n\nmodule Database\n  DATABASE_FILE_PATH = \"#{__dir__}/vision.database.yml\"\n\n"
  },
  {
    "path": "performance/shopify/json_filter.rb",
    "chars": 154,
    "preview": "# frozen_string_literal: true\n\nrequire 'json'\n\nmodule JsonFilter\n  def json(object)\n    JSON.dump(object.reject { |k, _v"
  },
  {
    "path": "performance/shopify/liquid.rb",
    "chars": 731,
    "preview": "# frozen_string_literal: true\n\n$LOAD_PATH.unshift(__dir__ + '/../../lib')\nrequire_relative '../../lib/liquid'\n\nrequire_r"
  },
  {
    "path": "performance/shopify/money_filter.rb",
    "chars": 310,
    "preview": "# frozen_string_literal: true\n\nmodule MoneyFilter\n  def money_with_currency(money)\n    return '' if money.nil?\n    forma"
  },
  {
    "path": "performance/shopify/paginate.rb",
    "chars": 2557,
    "preview": "# frozen_string_literal: true\n\nclass Paginate < Liquid::Block\n  Syntax = /(#{Liquid::QuotedFragment})\\s*(by\\s*(\\d+))?/\n\n"
  },
  {
    "path": "performance/shopify/shop_filter.rb",
    "chars": 2854,
    "preview": "# frozen_string_literal: true\n\nmodule ShopFilter\n  def asset_url(input)\n    \"/files/1/[shop_id]/[shop_id]/assets/#{input"
  },
  {
    "path": "performance/shopify/tag_filter.rb",
    "chars": 779,
    "preview": "# frozen_string_literal: true\n\nmodule TagFilter\n  def link_to_tag(label, tag)\n    \"<a title=\\\"Show tag #{tag}\\\" href=\\\"/"
  },
  {
    "path": "performance/shopify/vision.database.yml",
    "chars": 29695,
    "preview": "# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n#   Variants\n# =-=-=-"
  },
  {
    "path": "performance/shopify/weight_filter.rb",
    "chars": 177,
    "preview": "# frozen_string_literal: true\n\nmodule WeightFilter\n  def weight(grams)\n    format(\"%.2f\", grams / 1000)\n  end\n\n  def wei"
  },
  {
    "path": "performance/tests/dropify/article.liquid",
    "chars": 2757,
    "preview": "<div class=\"article\">\n  <h2 class=\"article-title\">{{ article.title }}</h2>\n  <p class=\"article-details\">posted <span cla"
  },
  {
    "path": "performance/tests/dropify/blog.liquid",
    "chars": 821,
    "preview": "<div id=\"page\">\n  <h2>{{page.title}}</h2>\n\n  {% paginate blog.articles by 20 %}\n\n    {% for article in blog.articles  %}"
  },
  {
    "path": "performance/tests/dropify/cart.liquid",
    "chars": 2362,
    "preview": "<script type=\"text/javascript\">\n  function remove_item(id) {\n      document.getElementById('updates_'+id).value = 0;\n   "
  },
  {
    "path": "performance/tests/dropify/collection.liquid",
    "chars": 768,
    "preview": "{% paginate collection.products by 20 %}\n\n<ul id=\"product-collection\">\n    {% for product in collection.products %}\n    "
  },
  {
    "path": "performance/tests/dropify/index.liquid",
    "chars": 1784,
    "preview": "<div id=\"frontproducts\"><div id=\"frontproducts-top\"><div id=\"frontproducts-bottom\">\n<h2 style=\"display: none;\">Featured "
  },
  {
    "path": "performance/tests/dropify/page.liquid",
    "chars": 113,
    "preview": "<div id=\"page\">\n  <h2>{{page.title}}</h2>\n\n  <div class=\"article textile\">\n    {{page.content}}\n  </div>\n\n</div>\n"
  },
  {
    "path": "performance/tests/dropify/product.liquid",
    "chars": 2707,
    "preview": "<div id=\"productpage\">\n\n  <div id=\"productimages\"><div id=\"productimages-top\"><div id=\"productimages-bottom\">\n    {% for"
  },
  {
    "path": "performance/tests/dropify/theme.liquid",
    "chars": 3903,
    "preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dt"
  },
  {
    "path": "performance/tests/ripen/article.liquid",
    "chars": 2691,
    "preview": "<div class=\"article\">\n  <h2 class=\"article-title\">{{ article.title }}</h2>\n  <p class=\"article-details\">posted <span cla"
  },
  {
    "path": "performance/tests/ripen/blog.liquid",
    "chars": 437,
    "preview": "<div id=\"blog-page\">\n  <h2 class=\"heading-shaded\">{{page.title}}</h2>\n   {% for article in blog.articles  %}\n      <h4>\n"
  },
  {
    "path": "performance/tests/ripen/cart.liquid",
    "chars": 2077,
    "preview": "<script type=\"text/javascript\">\n  function remove_item(id) {\n      document.getElementById('updates_'+id).value = 0;\n   "
  },
  {
    "path": "performance/tests/ripen/collection.liquid",
    "chars": 985,
    "preview": "<div id=\"collection-page\">\n\n{% if collection.description %}\n  <div id=\"collection-description\" class=\"textile\">{{ collec"
  },
  {
    "path": "performance/tests/ripen/index.liquid",
    "chars": 1347,
    "preview": "<div id=\"home-page\">\n  <h3 class=\"heading-shaded\">Featured products...</h3>\n  <div class=\"featured-prod-row clearfix\">\n "
  },
  {
    "path": "performance/tests/ripen/page.liquid",
    "chars": 98,
    "preview": "<div id=\"single-page\">\n<h2 class=\"heading-shaded\">{{page.title}}</h2>\n  {{ page.content }}\n</div>\n"
  },
  {
    "path": "performance/tests/ripen/product.liquid",
    "chars": 2815,
    "preview": "<div id=\"product-page\">\n  <h2 class=\"heading-shaded\">{{ product.title }}</h2>\n  <div id=\"product-details\">\n  <div id=\"pr"
  },
  {
    "path": "performance/tests/ripen/theme.liquid",
    "chars": 2551,
    "preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmln"
  },
  {
    "path": "performance/tests/tribble/404.liquid",
    "chars": 2126,
    "preview": "  <div id=\"page\" class=\"innerpage clearfix\">\n\n    <div id=\"text-page\">\n      <div class=\"entry\">\n        <h1>Oh no!</h1>"
  },
  {
    "path": "performance/tests/tribble/article.liquid",
    "chars": 3259,
    "preview": "\n  <div id=\"page\" class=\"innerpage clearfix\">\n    <div id=\"text-page\">\n\n          <div class=\"entry\">\n            <h1><s"
  },
  {
    "path": "performance/tests/tribble/blog.liquid",
    "chars": 1133,
    "preview": "  <div id=\"page\" class=\"innerpage clearfix\">\n    <div id=\"text-page\">\n      <h1>Post from our blog...</h1>\n      {% pagi"
  },
  {
    "path": "performance/tests/tribble/cart.liquid",
    "chars": 4986,
    "preview": "<script type=\"text/javascript\">\n  function remove_item(id) {\n      document.getElementById('updates_'+id).value = 0;\n   "
  },
  {
    "path": "performance/tests/tribble/collection.liquid",
    "chars": 2507,
    "preview": "  <div id=\"page\" class=\"innerpage clearfix\">\n    <h1>{{ collection.title }}</h1>\n    {% if collection.description.size >"
  },
  {
    "path": "performance/tests/tribble/index.liquid",
    "chars": 3130,
    "preview": "  <div id=\"gwrap\">\n    <div id=\"gbox\">\n      <h1>Three Great Reasons You Should Shop With Us...</h1>\n      <ul>\n        "
  },
  {
    "path": "performance/tests/tribble/page.liquid",
    "chars": 1980,
    "preview": "  <div id=\"page\" class=\"innerpage clearfix\">\n\n    <div id=\"text-page\">\n      <div class=\"entry\">\n        <h1>{{page.titl"
  },
  {
    "path": "performance/tests/tribble/product.liquid",
    "chars": 3636,
    "preview": "<div id=\"page\" class=\"innerpage clearfix\">\n  <h1>{{ collection.title }} {{ product.title }}</h1>\n\n\n  <p class=\"latest-ne"
  },
  {
    "path": "performance/tests/tribble/search.liquid",
    "chars": 1290,
    "preview": "\n\n\n\n    <div id=\"page\" class=\"innerpage clearfix\">\n    <h1>Search Results</h1>\n    {% if search.performed %}\n\n     {% pa"
  },
  {
    "path": "performance/tests/tribble/theme.liquid",
    "chars": 2960,
    "preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmln"
  },
  {
    "path": "performance/tests/vogue/article.liquid",
    "chars": 2459,
    "preview": "<div class=\"article\">\n  <h3 class=\"article-head-title\">{{ article.title }}</h3>\n  <p> posted {{ article.created_at | dat"
  },
  {
    "path": "performance/tests/vogue/blog.liquid",
    "chars": 782,
    "preview": "<div id=\"shop-id-label_about\">\n<h3 class=\"article-head-title\">{{page.title}}</h3>\n\n{% paginate blog.articles by 20 %}\n\n "
  },
  {
    "path": "performance/tests/vogue/cart.liquid",
    "chars": 3061,
    "preview": "<h1>Shopping Cart</h1>\n{% if cart.item_count == 0 %}\n  <p><strong>Your shopping basket is empty.</strong> Perhaps a feat"
  },
  {
    "path": "performance/tests/vogue/collection.liquid",
    "chars": 1081,
    "preview": "{% paginate collection.products by 12 %}{% if collection.products.size == 0 %}\n  <strong>No products found in this colle"
  },
  {
    "path": "performance/tests/vogue/index.liquid",
    "chars": 1217,
    "preview": "  <div id=\"about-excerpt\">\n    {% assign article = pages.frontpage %}\n    {% if article.content != \"\" %}\n      <h2>{{ ar"
  },
  {
    "path": "performance/tests/vogue/page.liquid",
    "chars": 50,
    "preview": "  <h1>{{ page.title }}</h1>\n  {{ page.content }}\n\n"
  },
  {
    "path": "performance/tests/vogue/product.liquid",
    "chars": 2595,
    "preview": "<div id=\"product-left\">\n  {% for image in product.images %}{% if forloop.first %}<div id=\"product-image\">\n    <a href=\"{"
  },
  {
    "path": "performance/tests/vogue/theme.liquid",
    "chars": 4876,
    "preview": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
  },
  {
    "path": "performance/theme_runner.rb",
    "chars": 4713,
    "preview": "# frozen_string_literal: true\n\n# This profiler run simulates Shopify.\n# We are looking in the tests directory for liquid"
  },
  {
    "path": "performance/unit/expression_benchmark.rb",
    "chars": 1551,
    "preview": "# frozen_string_literal: true\n\nrequire \"benchmark/ips\"\n\n# benchmark liquid lexing\n\nrequire 'liquid'\n\nRubyVM::YJIT.enable"
  },
  {
    "path": "performance/unit/lexer_benchmark.rb",
    "chars": 654,
    "preview": "# frozen_string_literal: true\n\nrequire \"benchmark/ips\"\n\n# benchmark liquid lexing\n\nrequire 'liquid'\n\nRubyVM::YJIT.enable"
  },
  {
    "path": "spec/ruby_liquid.rb",
    "chars": 1107,
    "preview": "# frozen_string_literal: true\n\n# Liquid Spec Adapter for Shopify/liquid (Ruby reference implementation)\n#\n# Run with: bu"
  },
  {
    "path": "spec/ruby_liquid_lax.rb",
    "chars": 977,
    "preview": "# frozen_string_literal: true\n\n# Liquid Spec Adapter for Shopify/liquid with lax parsing mode\n#\n# Run with: bundle exec "
  },
  {
    "path": "spec/ruby_liquid_with_active_support.rb",
    "chars": 1202,
    "preview": "# frozen_string_literal: true\n\n# Liquid Spec Adapter for Shopify/liquid with ActiveSupport loaded\n#\n# Run with: bundle e"
  },
  {
    "path": "spec/ruby_liquid_yjit.rb",
    "chars": 1155,
    "preview": "# frozen_string_literal: true\n\n# Liquid Spec Adapter for Shopify/liquid with YJIT + strict mode + ActiveSupport\n#\n# Run "
  },
  {
    "path": "test/fixtures/en_locale.yml",
    "chars": 253,
    "preview": "---\n  simple: \"less is more\"\n  whatever: \"something %{something}\"\n  errors:\n    i18n:\n      undefined_interpolation: \"un"
  },
  {
    "path": "test/integration/assign_test.rb",
    "chars": 3208,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass AssignTest < Minitest::Test\n  include Liquid\n\n  def test_ass"
  },
  {
    "path": "test/integration/blank_test.rb",
    "chars": 3416,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass FoobarTag < Liquid::Tag\n  def render_to_output_buffer(_conte"
  },
  {
    "path": "test/integration/block_test.rb",
    "chars": 1514,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass BlockTest < Minitest::Test\n  include Liquid\n\n  def test_unex"
  },
  {
    "path": "test/integration/capture_test.rb",
    "chars": 1530,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass CaptureTest < Minitest::Test\n  include Liquid\n\n  def test_ca"
  },
  {
    "path": "test/integration/context_test.rb",
    "chars": 20908,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass HundredCentes\n  def to_liquid\n    100\n  end\nend\n\nclass Cents"
  },
  {
    "path": "test/integration/document_test.rb",
    "chars": 419,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass DocumentTest < Minitest::Test\n  include Liquid\n\n  def test_u"
  },
  {
    "path": "test/integration/drop_test.rb",
    "chars": 10112,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ContextDrop < Liquid::Drop\n  def scopes\n    @context.scopes."
  },
  {
    "path": "test/integration/error_handling_test.rb",
    "chars": 11167,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ErrorHandlingTest < Minitest::Test\n  include Liquid\n\n  def t"
  },
  {
    "path": "test/integration/expression_test.rb",
    "chars": 5186,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\nrequire 'lru_redux'\n\nclass ExpressionTest < Minitest::Test\n  def te"
  },
  {
    "path": "test/integration/filter_kwarg_test.rb",
    "chars": 544,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass FilterKwargTest < Minitest::Test\n  module KwargFilter\n    de"
  },
  {
    "path": "test/integration/filter_test.rb",
    "chars": 5675,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nmodule MoneyFilter\n  def money(input)\n    format(' %d$ ', input)\n "
  },
  {
    "path": "test/integration/hash_ordering_test.rb",
    "chars": 494,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass HashOrderingTest < Minitest::Test\n  module MoneyFilter\n    d"
  },
  {
    "path": "test/integration/hash_rendering_test.rb",
    "chars": 4809,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass HashRenderingTest < Minitest::Test\n  def test_render_empty_h"
  },
  {
    "path": "test/integration/output_test.rb",
    "chars": 3307,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nmodule FunnyFilter\n  def make_funny(_input)\n    'LOL'\n  end\n\n  def"
  },
  {
    "path": "test/integration/parsing_quirks_test.rb",
    "chars": 5064,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ParsingQuirksTest < Minitest::Test\n  include Liquid\n\n  def t"
  },
  {
    "path": "test/integration/profiler_test.rb",
    "chars": 6962,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ProfilerTest < Minitest::Test\n  class TestDrop < Liquid::Dro"
  },
  {
    "path": "test/integration/security_test.rb",
    "chars": 2669,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nmodule SecurityFilter\n  def add_one(input)\n    \"#{input} + 1\"\n  en"
  },
  {
    "path": "test/integration/standard_filter_test.rb",
    "chars": 47070,
    "preview": "# encoding: utf-8\n# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass TestThing\n  attr_reader :foo\n\n  def initia"
  },
  {
    "path": "test/integration/tag/disableable_test.rb",
    "chars": 1422,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass TagDisableableTest < Minitest::Test\n  include Liquid\n\n  modu"
  },
  {
    "path": "test/integration/tag_test.rb",
    "chars": 1098,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass TagTest < Minitest::Test\n  include Liquid\n\n  def test_custom"
  },
  {
    "path": "test/integration/tags/break_tag_test.rb",
    "chars": 380,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass BreakTagTest < Minitest::Test\n  include Liquid\n\n  # tests th"
  },
  {
    "path": "test/integration/tags/continue_tag_test.rb",
    "chars": 365,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ContinueTagTest < Minitest::Test\n  include Liquid\n\n  # tests"
  },
  {
    "path": "test/integration/tags/cycle_tag_test.rb",
    "chars": 5381,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass CycleTagTest < Minitest::Test\n  def test_simple_cycle_inside"
  },
  {
    "path": "test/integration/tags/echo_test.rb",
    "chars": 276,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass EchoTest < Minitest::Test\n  include Liquid\n\n  def test_echo_"
  },
  {
    "path": "test/integration/tags/for_tag_test.rb",
    "chars": 15967,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ThingWithValue < Liquid::Drop\n  def value\n    3\n  end\nend\n\nc"
  },
  {
    "path": "test/integration/tags/if_else_tag_test.rb",
    "chars": 9528,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass IfElseTagTest < Minitest::Test\n  include Liquid\n\n  def test_"
  },
  {
    "path": "test/integration/tags/include_tag_test.rb",
    "chars": 14323,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass TestFileSystem\n  PARTIALS = {\n    \"nested_template\" => \"{% i"
  },
  {
    "path": "test/integration/tags/increment_tag_test.rb",
    "chars": 986,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass IncrementTagTest < Minitest::Test\n  include Liquid\n\n  def te"
  },
  {
    "path": "test/integration/tags/inline_comment_test.rb",
    "chars": 2110,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass InlineCommentTest < Minitest::Test\n  include Liquid\n\n  def t"
  },
  {
    "path": "test/integration/tags/liquid_tag_test.rb",
    "chars": 3731,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass LiquidTagTest < Minitest::Test\n  include Liquid\n\n  def test_"
  },
  {
    "path": "test/integration/tags/raw_tag_test.rb",
    "chars": 1843,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass RawTagTest < Minitest::Test\n  include Liquid\n\n  def test_tag"
  },
  {
    "path": "test/integration/tags/render_tag_test.rb",
    "chars": 10703,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass RenderTagTest < Minitest::Test\n  include Liquid\n\n  def test_"
  },
  {
    "path": "test/integration/tags/standard_tag_test.rb",
    "chars": 13558,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass StandardTagTest < Minitest::Test\n  include Liquid\n\n  def tes"
  },
  {
    "path": "test/integration/tags/statements_test.rb",
    "chars": 3702,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass StatementsTest < Minitest::Test\n  include Liquid\n\n  def test"
  },
  {
    "path": "test/integration/tags/table_row_test.rb",
    "chars": 14541,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass TableRowTest < Minitest::Test\n  include Liquid\n\n  class Arra"
  },
  {
    "path": "test/integration/tags/unless_else_tag_test.rb",
    "chars": 1279,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass UnlessElseTagTest < Minitest::Test\n  include Liquid\n\n  def t"
  },
  {
    "path": "test/integration/template_test.rb",
    "chars": 15321,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\nrequire 'timeout'\n\nclass TemplateContextDrop < Liquid::Drop\n  def l"
  },
  {
    "path": "test/integration/trim_mode_test.rb",
    "chars": 11558,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass TrimModeTest < Minitest::Test\n  include Liquid\n\n  # Make sur"
  },
  {
    "path": "test/integration/variable_test.rb",
    "chars": 9530,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\nrequire 'timeout'\n\nclass VariableTest < Minitest::Test\n  include Li"
  },
  {
    "path": "test/test_helper.rb",
    "chars": 5403,
    "preview": "#!/usr/bin/env ruby\n# frozen_string_literal: true\n\nENV[\"MT_NO_EXPECTATIONS\"] = \"1\"\nrequire 'minitest/autorun'\n\n$LOAD_PAT"
  },
  {
    "path": "test/unit/block_unit_test.rb",
    "chars": 2127,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass BlockUnitTest < Minitest::Test\n  include Liquid\n\n  def test_"
  },
  {
    "path": "test/unit/condition_unit_test.rb",
    "chars": 14230,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ConditionUnitTest < Minitest::Test\n  include Liquid\n\n  def s"
  },
  {
    "path": "test/unit/environment_filter_test.rb",
    "chars": 3354,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass EnvironmentFilterTest < Minitest::Test\n  include Liquid\n\n  m"
  },
  {
    "path": "test/unit/environment_test.rb",
    "chars": 675,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass EnvironmentTest < Minitest::Test\n  include Liquid\n\n  class U"
  },
  {
    "path": "test/unit/file_system_unit_test.rb",
    "chars": 1105,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass FileSystemUnitTest < Minitest::Test\n  include Liquid\n\n  def "
  },
  {
    "path": "test/unit/i18n_unit_test.rb",
    "chars": 965,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass I18nUnitTest < Minitest::Test\n  include Liquid\n\n  def setup\n"
  },
  {
    "path": "test/unit/lexer_unit_test.rb",
    "chars": 3597,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass LexerUnitTest < Minitest::Test\n  include Liquid\n\n  def test_"
  },
  {
    "path": "test/unit/parse_context_unit_test.rb",
    "chars": 4131,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ParseContextUnitTest < Minitest::Test\n  include Liquid\n\n  de"
  },
  {
    "path": "test/unit/parse_tree_visitor_test.rb",
    "chars": 5052,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ParseTreeVisitorTest < Minitest::Test\n  include Liquid\n\n  de"
  },
  {
    "path": "test/unit/parser_unit_test.rb",
    "chars": 2404,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ParserUnitTest < Minitest::Test\n  include Liquid\n\n  def test"
  },
  {
    "path": "test/unit/partial_cache_unit_test.rb",
    "chars": 5401,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass PartialCacheUnitTest < Minitest::Test\n  def test_uses_the_fi"
  },
  {
    "path": "test/unit/regexp_unit_test.rb",
    "chars": 2226,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\nrequire 'timeout'\n\nclass RegexpUnitTest < Minitest::Test\n  include "
  },
  {
    "path": "test/unit/registers_unit_test.rb",
    "chars": 4335,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass RegistersUnitTest < Minitest::Test\n  include Liquid\n\n  def t"
  },
  {
    "path": "test/unit/resource_limits_unit_test.rb",
    "chars": 2689,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ResourceLimitsUnitTest < Minitest::Test\n  def test_cumulativ"
  },
  {
    "path": "test/unit/strainer_template_unit_test.rb",
    "chars": 2388,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass StrainerTemplateUnitTest < Minitest::Test\n  include Liquid\n\n"
  },
  {
    "path": "test/unit/tag_unit_test.rb",
    "chars": 1075,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass TagUnitTest < Minitest::Test\n  include Liquid\n\n  def test_ta"
  },
  {
    "path": "test/unit/tags/case_tag_unit_test.rb",
    "chars": 3701,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass CaseTagUnitTest < Minitest::Test\n  include Liquid\n\n  def tes"
  },
  {
    "path": "test/unit/tags/comment_tag_unit_test.rb",
    "chars": 4898,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass CommentTagUnitTest < Minitest::Test\n  def test_comment_insid"
  },
  {
    "path": "test/unit/tags/doc_tag_unit_test.rb",
    "chars": 7302,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass DocTagUnitTest < Minitest::Test\n  def test_doc_tag\n    templ"
  },
  {
    "path": "test/unit/tags/for_tag_unit_test.rb",
    "chars": 516,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ForTagUnitTest < Minitest::Test\n  def test_for_nodelist\n    "
  },
  {
    "path": "test/unit/tags/if_tag_unit_test.rb",
    "chars": 299,
    "preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass IfTagUnitTest < Minitest::Test\n  def test_if_nodelist\n    te"
  }
]

// ... and 4 more files (download for full content)

About this extraction

This page contains the full source code of the Shopify/liquid GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 204 files (720.9 KB), approximately 203.7k tokens, and a symbol index with 2031 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!