mirror of
https://github.com/kemko/liquid.git
synced 2026-01-02 08:15:41 +03:00
Compare commits
1 Commits
usage-trac
...
for-loop-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a61903da54 |
2
.github/probots.yml
vendored
2
.github/probots.yml
vendored
@@ -1,2 +0,0 @@
|
|||||||
enabled:
|
|
||||||
- cla
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,4 +7,3 @@ pkg
|
|||||||
.ruby-version
|
.ruby-version
|
||||||
Gemfile.lock
|
Gemfile.lock
|
||||||
.bundle
|
.bundle
|
||||||
.byebug_history
|
|
||||||
|
|||||||
43
.rubocop.yml
43
.rubocop.yml
@@ -1,20 +1,14 @@
|
|||||||
inherit_from:
|
inherit_from: ./.rubocop_todo.yml
|
||||||
- .rubocop_todo.yml
|
|
||||||
- ./.rubocop_todo.yml
|
|
||||||
|
|
||||||
require: rubocop-performance
|
|
||||||
|
|
||||||
Performance:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'performance/shopify/*'
|
- 'performance/shopify/*'
|
||||||
- 'vendor/bundle/**/*'
|
|
||||||
- 'pkg/**'
|
- 'pkg/**'
|
||||||
|
|
||||||
Metrics/BlockNesting:
|
Metrics/BlockNesting:
|
||||||
Max: 3
|
Max: 3
|
||||||
|
Exclude:
|
||||||
|
- 'lib/liquid/block_body.rb'
|
||||||
|
|
||||||
Metrics/ModuleLength:
|
Metrics/ModuleLength:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
@@ -37,8 +31,8 @@ Lint/ParenthesesAsGroupedExpression:
|
|||||||
Lint/UnusedBlockArgument:
|
Lint/UnusedBlockArgument:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Layout/EndAlignment:
|
Lint/EndAlignment:
|
||||||
EnforcedStyleAlignWith: variable
|
AlignWith: variable
|
||||||
|
|
||||||
Lint/UnusedMethodArgument:
|
Lint/UnusedMethodArgument:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
@@ -67,10 +61,10 @@ Style/BracesAroundHashParameters:
|
|||||||
Style/NumericLiterals:
|
Style/NumericLiterals:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Layout/SpaceInsideArrayLiteralBrackets:
|
Style/SpaceInsideBrackets:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Layout/SpaceBeforeBlockBraces:
|
Style/SpaceBeforeBlockBraces:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/Documentation:
|
Style/Documentation:
|
||||||
@@ -79,19 +73,19 @@ Style/Documentation:
|
|||||||
Style/ClassAndModuleChildren:
|
Style/ClassAndModuleChildren:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/TrailingCommaInArrayLiteral:
|
Style/TrailingComma:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/TrailingCommaInHashLiteral:
|
Style/IndentHash:
|
||||||
Enabled: false
|
EnforcedStyle: consistent
|
||||||
|
|
||||||
Style/FormatString:
|
Style/FormatString:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Layout/AlignParameters:
|
Style/AlignParameters:
|
||||||
EnforcedStyle: with_fixed_indentation
|
EnforcedStyle: with_fixed_indentation
|
||||||
|
|
||||||
Layout/MultilineOperationIndentation:
|
Style/MultilineOperationIndentation:
|
||||||
EnforcedStyle: indented
|
EnforcedStyle: indented
|
||||||
|
|
||||||
Style/IfUnlessModifier:
|
Style/IfUnlessModifier:
|
||||||
@@ -100,7 +94,7 @@ Style/IfUnlessModifier:
|
|||||||
Style/RaiseArgs:
|
Style/RaiseArgs:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/PreferredHashMethods:
|
Style/DeprecatedHashMethods:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/RegexpLiteral:
|
Style/RegexpLiteral:
|
||||||
@@ -109,10 +103,13 @@ Style/RegexpLiteral:
|
|||||||
Style/SymbolLiteral:
|
Style/SymbolLiteral:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Naming/ConstantName:
|
Performance/Count:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Layout/CaseIndentation:
|
Style/ConstantName:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/CaseIndentation:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/ClassVars:
|
Style/ClassVars:
|
||||||
@@ -126,7 +123,3 @@ Style/TrivialAccessors:
|
|||||||
|
|
||||||
Style/WordArray:
|
Style/WordArray:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Naming/MethodName:
|
|
||||||
Exclude:
|
|
||||||
- 'example/server/liquid_servlet.rb'
|
|
||||||
|
|||||||
@@ -1,345 +1,72 @@
|
|||||||
# This configuration was generated by
|
# This configuration was generated by `rubocop --auto-gen-config`
|
||||||
# `rubocop --auto-gen-config`
|
# on 2015-06-08 18:16:16 +0000 using RuboCop version 0.32.0.
|
||||||
# on 2019-08-29 00:43:36 +1000 using RuboCop version 0.74.0.
|
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
# versions of RuboCop, may require this file to be generated again.
|
# versions of RuboCop, may require this file to be generated again.
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: TreatCommentsAsGroupSeparators, Include.
|
|
||||||
# Include: **/*.gemspec
|
|
||||||
Gemspec/OrderedDependencies:
|
|
||||||
Exclude:
|
|
||||||
- 'liquid.gemspec'
|
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
# Configuration parameters: Include.
|
|
||||||
# Include: **/*.gemspec,
|
|
||||||
Gemspec/RequiredRubyVersion:
|
|
||||||
Exclude:
|
|
||||||
- 'liquid.gemspec'
|
|
||||||
|
|
||||||
# Offense count: 124
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
||||||
# SupportedStyles: with_first_argument, with_fixed_indentation
|
|
||||||
Layout/AlignArguments:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 7
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
|
|
||||||
# SupportedHashRocketStyles: key, separator, table
|
|
||||||
# SupportedColonStyles: key, separator, table
|
|
||||||
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
|
|
||||||
Layout/AlignHash:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/condition.rb'
|
|
||||||
- 'lib/liquid/expression.rb'
|
|
||||||
- 'test/unit/context_unit_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 6
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Layout/ClosingHeredocIndentation:
|
|
||||||
Exclude:
|
|
||||||
- 'test/integration/tags/for_tag_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 27
|
|
||||||
# Cop supports --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/standardfilters.rb'
|
|
||||||
- 'lib/liquid/strainer.rb'
|
|
||||||
- 'lib/liquid/tags/for.rb'
|
|
||||||
- 'lib/liquid/tags/if.rb'
|
|
||||||
- 'lib/liquid/tags/include.rb'
|
|
||||||
- 'lib/liquid/utils.rb'
|
|
||||||
- 'lib/liquid/variable.rb'
|
|
||||||
- 'lib/liquid/variable_lookup.rb'
|
|
||||||
|
|
||||||
# Offense count: 5
|
# Offense count: 5
|
||||||
# Cop supports --auto-correct.
|
Lint/NestedMethodDefinition:
|
||||||
# Configuration parameters: EnforcedStyle.
|
Enabled: false
|
||||||
# SupportedStyles: squiggly, active_support, powerpack, unindent
|
|
||||||
Layout/IndentHeredoc:
|
|
||||||
Exclude:
|
|
||||||
- 'test/integration/tags/for_tag_test.rb'
|
|
||||||
- 'test/integration/trim_mode_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 6
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: EnforcedStyle.
|
|
||||||
# SupportedStyles: symmetrical, new_line, same_line
|
|
||||||
Layout/MultilineMethodCallBraceLayout:
|
|
||||||
Exclude:
|
|
||||||
- 'test/integration/error_handling_test.rb'
|
|
||||||
- 'test/unit/strainer_unit_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: AllowForAlignment.
|
|
||||||
Layout/SpaceAroundOperators:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/condition.rb'
|
|
||||||
|
|
||||||
# Offense count: 2
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: EnforcedStyle.
|
|
||||||
# SupportedStyles: runtime_error, standard_error
|
|
||||||
Lint/InheritException:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/interrupts.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
# Configuration parameters: CheckForMethodsWithNoSideEffects.
|
|
||||||
Lint/Void:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/parse_context.rb'
|
|
||||||
|
|
||||||
# Offense count: 53
|
# Offense count: 53
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 56
|
Max: 58
|
||||||
|
|
||||||
# Offense count: 12
|
# Offense count: 12
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
|
Max: 15
|
||||||
|
|
||||||
|
# Offense count: 553
|
||||||
|
# Configuration parameters: AllowURI, URISchemes.
|
||||||
|
Metrics/LineLength:
|
||||||
|
Max: 294
|
||||||
|
|
||||||
|
# Offense count: 77
|
||||||
|
# Configuration parameters: CountComments.
|
||||||
|
Metrics/MethodLength:
|
||||||
|
Max: 46
|
||||||
|
|
||||||
|
# Offense count: 6
|
||||||
|
Metrics/PerceivedComplexity:
|
||||||
Max: 13
|
Max: 13
|
||||||
|
|
||||||
# Offense count: 118
|
|
||||||
# Configuration parameters: CountComments, ExcludedMethods.
|
|
||||||
Metrics/MethodLength:
|
|
||||||
Max: 38
|
|
||||||
|
|
||||||
# Offense count: 9
|
|
||||||
Metrics/PerceivedComplexity:
|
|
||||||
Max: 11
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
Style/AccessorMethodName:
|
||||||
# Configuration parameters: PreferredName.
|
|
||||||
Naming/RescuedExceptionsVariableName:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/context.rb'
|
|
||||||
|
|
||||||
# Offense count: 20
|
|
||||||
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
|
|
||||||
# AllowedNames: io, id, to, by, on, in, at, ip, db
|
|
||||||
Naming/UncommunicativeMethodParamName:
|
|
||||||
Exclude:
|
|
||||||
- 'example/server/example_servlet.rb'
|
|
||||||
- 'lib/liquid/condition.rb'
|
|
||||||
- 'lib/liquid/context.rb'
|
|
||||||
- 'lib/liquid/standardfilters.rb'
|
|
||||||
- 'lib/liquid/tags/if.rb'
|
|
||||||
- 'lib/liquid/variable.rb'
|
|
||||||
- 'test/integration/filter_test.rb'
|
|
||||||
- 'test/integration/standard_filter_test.rb'
|
|
||||||
- 'test/integration/template_test.rb'
|
|
||||||
- 'test/unit/condition_unit_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 5
|
|
||||||
# Configuration parameters: EnforcedStyle.
|
|
||||||
# SupportedStyles: inline, group
|
|
||||||
Style/AccessModifierDeclarations:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/block_body.rb'
|
|
||||||
- 'lib/liquid/tag.rb'
|
|
||||||
- 'lib/liquid/tags/include.rb'
|
|
||||||
- 'test/unit/strainer_unit_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 10
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: EnforcedStyle.
|
|
||||||
# SupportedStyles: prefer_alias, prefer_alias_method
|
|
||||||
Style/Alias:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/drop.rb'
|
|
||||||
- 'lib/liquid/i18n.rb'
|
|
||||||
- 'lib/liquid/profiler/hooks.rb'
|
|
||||||
- 'lib/liquid/standardfilters.rb'
|
|
||||||
- 'lib/liquid/tag.rb'
|
|
||||||
- 'lib/liquid/tags/include.rb'
|
|
||||||
- 'lib/liquid/variable.rb'
|
|
||||||
|
|
||||||
# Offense count: 22
|
|
||||||
Style/CommentedKeyword:
|
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
|
Style/ClosingParenthesisIndentation:
|
||||||
# SupportedStyles: assign_to_condition, assign_inside_condition
|
Enabled: false
|
||||||
Style/ConditionalAssignment:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/errors.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Style/EmptyCaseCondition:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/lexer.rb'
|
|
||||||
|
|
||||||
# Offense count: 5
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: EnforcedStyle.
|
|
||||||
# SupportedStyles: compact, expanded
|
|
||||||
Style/EmptyMethod:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/tag.rb'
|
|
||||||
- 'lib/liquid/tags/comment.rb'
|
|
||||||
- 'lib/liquid/tags/include.rb'
|
|
||||||
- 'test/integration/tags/include_tag_test.rb'
|
|
||||||
- 'test/unit/context_unit_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 3
|
# Offense count: 3
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Style/Encoding:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/version.rb'
|
|
||||||
- 'liquid.gemspec'
|
|
||||||
- 'test/integration/standard_filter_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 2
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Style/ExpandPathArguments:
|
|
||||||
Exclude:
|
|
||||||
- 'Rakefile'
|
|
||||||
- 'liquid.gemspec'
|
|
||||||
|
|
||||||
# Offense count: 7
|
|
||||||
# Configuration parameters: EnforcedStyle.
|
|
||||||
# SupportedStyles: annotated, template, unannotated
|
|
||||||
Style/FormatStringToken:
|
|
||||||
Exclude:
|
|
||||||
- 'test/integration/filter_test.rb'
|
|
||||||
- 'test/integration/hash_ordering_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 106
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: EnforcedStyle.
|
|
||||||
# SupportedStyles: always, never
|
|
||||||
Style/FrozenStringLiteralComment:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 14
|
|
||||||
# Configuration parameters: MinBodyLength.
|
# Configuration parameters: MinBodyLength.
|
||||||
Style/GuardClause:
|
Style/GuardClause:
|
||||||
Exclude:
|
Enabled: false
|
||||||
- 'lib/liquid/condition.rb'
|
|
||||||
- 'lib/liquid/lexer.rb'
|
|
||||||
- 'lib/liquid/strainer.rb'
|
|
||||||
- 'lib/liquid/tags/assign.rb'
|
|
||||||
- 'lib/liquid/tags/capture.rb'
|
|
||||||
- 'lib/liquid/tags/case.rb'
|
|
||||||
- 'lib/liquid/tags/for.rb'
|
|
||||||
- 'lib/liquid/tags/include.rb'
|
|
||||||
- 'lib/liquid/tags/raw.rb'
|
|
||||||
- 'lib/liquid/tags/table_row.rb'
|
|
||||||
- 'lib/liquid/variable.rb'
|
|
||||||
- 'test/unit/tokenizer_unit_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 53
|
# Offense count: 4
|
||||||
# Cop supports --auto-correct.
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||||
# Configuration parameters: EnforcedStyle.
|
Style/MethodName:
|
||||||
# SupportedStyles: literals, strict
|
|
||||||
Style/MutableConstant:
|
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
Style/MultilineBlockChain:
|
||||||
# Configuration parameters: EnforcedStyle, MinBodyLength.
|
Enabled: false
|
||||||
# SupportedStyles: skip_modifier_ifs, always
|
|
||||||
|
# Offense count: 2
|
||||||
|
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
|
||||||
Style/Next:
|
Style/Next:
|
||||||
Exclude:
|
Enabled: false
|
||||||
- 'lib/liquid/tags/for.rb'
|
|
||||||
|
|
||||||
# Offense count: 13
|
# Offense count: 7
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods.
|
|
||||||
# SupportedStyles: predicate, comparison
|
|
||||||
Style/NumericPredicate:
|
|
||||||
Exclude:
|
|
||||||
- 'spec/**/*'
|
|
||||||
- 'lib/liquid/context.rb'
|
|
||||||
- 'lib/liquid/forloop_drop.rb'
|
|
||||||
- 'lib/liquid/standardfilters.rb'
|
|
||||||
- 'lib/liquid/tablerowloop_drop.rb'
|
|
||||||
- 'test/integration/standard_filter_test.rb'
|
|
||||||
- 'test/integration/template_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 14
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: PreferredDelimiters.
|
|
||||||
Style/PercentLiteralDelimiters:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/tags/if.rb'
|
|
||||||
- 'liquid.gemspec'
|
|
||||||
- 'test/integration/assign_test.rb'
|
|
||||||
- 'test/integration/standard_filter_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Style/RedundantSelf:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/strainer.rb'
|
|
||||||
|
|
||||||
# Offense count: 3
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, Whitelist.
|
|
||||||
# Whitelist: present?, blank?, presence, try, try!
|
|
||||||
Style/SafeNavigation:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/drop.rb'
|
|
||||||
- 'lib/liquid/strainer.rb'
|
|
||||||
- 'lib/liquid/tokenizer.rb'
|
|
||||||
|
|
||||||
# Offense count: 9
|
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: AllowAsExpressionSeparator.
|
# Configuration parameters: AllowAsExpressionSeparator.
|
||||||
Style/Semicolon:
|
Style/Semicolon:
|
||||||
Exclude:
|
Enabled: false
|
||||||
- 'test/integration/error_handling_test.rb'
|
|
||||||
- 'test/integration/template_test.rb'
|
|
||||||
- 'test/unit/context_unit_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 7
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: MinSize.
|
|
||||||
# SupportedStyles: percent, brackets
|
|
||||||
Style/SymbolArray:
|
|
||||||
EnforcedStyle: brackets
|
|
||||||
|
|
||||||
# Offense count: 2
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
|
|
||||||
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
|
|
||||||
Style/TernaryParentheses:
|
|
||||||
Exclude:
|
|
||||||
- 'lib/liquid/context.rb'
|
|
||||||
- 'lib/liquid/utils.rb'
|
|
||||||
|
|
||||||
# Offense count: 2
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Style/UnneededPercentQ:
|
|
||||||
Exclude:
|
|
||||||
- 'test/integration/error_handling_test.rb'
|
|
||||||
|
|
||||||
# Offense count: 1
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
# Configuration parameters: MaxLineLength.
|
||||||
Style/WhileUntilModifier:
|
Style/WhileUntilModifier:
|
||||||
Exclude:
|
Enabled: false
|
||||||
- 'lib/liquid/tags/case.rb'
|
|
||||||
|
|
||||||
# Offense count: 665
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
|
||||||
# URISchemes: http, https
|
|
||||||
Metrics/LineLength:
|
|
||||||
Max: 294
|
|
||||||
|
|||||||
33
.travis.yml
33
.travis.yml
@@ -1,30 +1,29 @@
|
|||||||
language: ruby
|
language: ruby
|
||||||
cache: bundler
|
|
||||||
|
|
||||||
rvm:
|
rvm:
|
||||||
- 2.4
|
- 2.1
|
||||||
- 2.5
|
- 2.2
|
||||||
- &latest_ruby 2.6
|
- 2.3.3
|
||||||
- 2.7
|
|
||||||
- ruby-head
|
- ruby-head
|
||||||
- jruby-head
|
- jruby-head
|
||||||
- truffleruby
|
# - rbx-2
|
||||||
|
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- libgmp3-dev
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
|
||||||
- rvm: *latest_ruby
|
|
||||||
script: bundle exec rake memory_profile:run
|
|
||||||
name: Profiling Memory Usage
|
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- rvm: ruby-head
|
|
||||||
- rvm: jruby-head
|
- rvm: jruby-head
|
||||||
- rvm: truffleruby
|
|
||||||
|
|
||||||
branches:
|
install:
|
||||||
only:
|
- gem install rainbow -v 2.2.1
|
||||||
- master
|
- bundle install
|
||||||
- gh-pages
|
|
||||||
- /.*-stable/
|
script: "bundle exec rake"
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
disable: true
|
disable: true
|
||||||
|
|||||||
18
Gemfile
18
Gemfile
@@ -1,25 +1,17 @@
|
|||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
git_source(:github) do |repo_name|
|
|
||||||
"https://github.com/#{repo_name}.git"
|
|
||||||
end
|
|
||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
|
gem 'stackprof', platforms: :mri
|
||||||
|
|
||||||
group :benchmark, :test do
|
group :benchmark, :test do
|
||||||
gem 'benchmark-ips'
|
gem 'benchmark-ips'
|
||||||
gem 'memory_profiler'
|
|
||||||
gem 'terminal-table'
|
|
||||||
|
|
||||||
install_if -> { RUBY_PLATFORM !~ /mingw|mswin|java/ && RUBY_ENGINE != 'truffleruby' } do
|
|
||||||
gem 'stackprof'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'rubocop', '~> 0.74.0', require: false
|
gem 'rubocop', '0.34.2'
|
||||||
gem 'rubocop-performance', require: false
|
|
||||||
|
|
||||||
platform :mri, :truffleruby do
|
platform :mri do
|
||||||
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'liquid-tag'
|
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'bd53db95de3d44d631e7c5a267c3d934e66107dd'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
53
History.md
53
History.md
@@ -1,58 +1,5 @@
|
|||||||
# Liquid Change Log
|
# Liquid Change Log
|
||||||
|
|
||||||
## 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"
|
## 4.0.0 / 2016-12-14 / branch "4-0-stable"
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ Liquid is a template engine which was written with very specific requirements:
|
|||||||
|
|
||||||
## How to use Liquid
|
## 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.
|
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.
|
For standard use you can just pass it the content of a file and call render with a parameters hash.
|
||||||
|
|
||||||
|
|||||||
19
Rakefile
19
Rakefile
@@ -3,7 +3,7 @@ require 'rake/testtask'
|
|||||||
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
||||||
require "liquid/version"
|
require "liquid/version"
|
||||||
|
|
||||||
task default: [:test, :rubocop]
|
task default: [:rubocop, :test]
|
||||||
|
|
||||||
desc 'run test suite with default parser'
|
desc 'run test suite with default parser'
|
||||||
Rake::TestTask.new(:base_test) do |t|
|
Rake::TestTask.new(:base_test) do |t|
|
||||||
@@ -19,10 +19,8 @@ task :warn_test do
|
|||||||
end
|
end
|
||||||
|
|
||||||
task :rubocop do
|
task :rubocop do
|
||||||
if RUBY_ENGINE == 'ruby'
|
require 'rubocop/rake_task'
|
||||||
require 'rubocop/rake_task'
|
RuboCop::RakeTask.new
|
||||||
RuboCop::RakeTask.new
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'runs test suite with both strict and lax parsers'
|
desc 'runs test suite with both strict and lax parsers'
|
||||||
@@ -34,8 +32,8 @@ task :test do
|
|||||||
Rake::Task['base_test'].reenable
|
Rake::Task['base_test'].reenable
|
||||||
Rake::Task['base_test'].invoke
|
Rake::Task['base_test'].invoke
|
||||||
|
|
||||||
if RUBY_ENGINE == 'ruby' || RUBY_ENGINE == 'truffleruby'
|
if RUBY_ENGINE == 'ruby'
|
||||||
ENV['LIQUID_C'] = '1'
|
ENV['LIQUID-C'] = '1'
|
||||||
|
|
||||||
ENV['LIQUID_PARSER_MODE'] = 'lax'
|
ENV['LIQUID_PARSER_MODE'] = 'lax'
|
||||||
Rake::Task['base_test'].reenable
|
Rake::Task['base_test'].reenable
|
||||||
@@ -87,13 +85,6 @@ namespace :profile do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
namespace :memory_profile do
|
|
||||||
desc "Run memory profiler"
|
|
||||||
task :run do
|
|
||||||
ruby "./performance/memory_profile.rb"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc "Run example"
|
desc "Run example"
|
||||||
task :example do
|
task :example do
|
||||||
ruby "-w -d -Ilib example/server/server.rb"
|
ruby "-w -d -Ilib example/server/server.rb"
|
||||||
|
|||||||
3
circle.yml
Normal file
3
circle.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
machine:
|
||||||
|
ruby:
|
||||||
|
version: ruby-2.1
|
||||||
@@ -45,7 +45,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
require "liquid/version"
|
require "liquid/version"
|
||||||
require 'liquid/parse_tree_visitor'
|
|
||||||
require 'liquid/lexer'
|
require 'liquid/lexer'
|
||||||
require 'liquid/parser'
|
require 'liquid/parser'
|
||||||
require 'liquid/i18n'
|
require 'liquid/i18n'
|
||||||
@@ -74,13 +73,7 @@ require 'liquid/condition'
|
|||||||
require 'liquid/utils'
|
require 'liquid/utils'
|
||||||
require 'liquid/tokenizer'
|
require 'liquid/tokenizer'
|
||||||
require 'liquid/parse_context'
|
require 'liquid/parse_context'
|
||||||
require 'liquid/partial_cache'
|
|
||||||
require 'liquid/usage'
|
|
||||||
|
|
||||||
# Load all the tags of the standard library
|
# Load all the tags of the standard library
|
||||||
#
|
#
|
||||||
Dir["#{__dir__}/liquid/tags/*.rb"].each { |f| require f }
|
Dir["#{__dir__}/liquid/tags/*.rb"].each { |f| require f }
|
||||||
|
|
||||||
# Load all usage tracking
|
|
||||||
#
|
|
||||||
Dir["#{__dir__}/liquid/usages/*.rb"].each { |f| require f }
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# For backwards compatibility
|
|
||||||
def render(context)
|
def render(context)
|
||||||
@body.render(context)
|
@body.render(context)
|
||||||
end
|
end
|
||||||
@@ -27,12 +26,12 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def unknown_tag(tag, _params, _tokens)
|
def unknown_tag(tag, _params, _tokens)
|
||||||
if tag == 'else'.freeze
|
case tag
|
||||||
|
when 'else'.freeze
|
||||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unexpected_else".freeze,
|
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unexpected_else".freeze,
|
||||||
block_name: block_name))
|
block_name: block_name))
|
||||||
elsif tag.start_with?('end'.freeze)
|
when 'end'.freeze
|
||||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.invalid_delimiter".freeze,
|
raise SyntaxError.new(parse_context.locale.t("errors.syntax.invalid_delimiter".freeze,
|
||||||
tag: tag,
|
|
||||||
block_name: block_name,
|
block_name: block_name,
|
||||||
block_delimiter: block_delimiter))
|
block_delimiter: block_delimiter))
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class BlockBody
|
class BlockBody
|
||||||
LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o
|
FullToken = /\A#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
||||||
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
|
||||||
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
||||||
WhitespaceOrNothing = /\A\s*\z/
|
|
||||||
TAGSTART = "{%".freeze
|
TAGSTART = "{%".freeze
|
||||||
VARSTART = "{{".freeze
|
VARSTART = "{{".freeze
|
||||||
|
|
||||||
@@ -14,83 +12,41 @@ module Liquid
|
|||||||
@blank = true
|
@blank = true
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse(tokenizer, parse_context, &block)
|
def parse(tokenizer, parse_context)
|
||||||
parse_context.line_number = tokenizer.line_number
|
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
|
|
||||||
|
|
||||||
private def parse_for_liquid_tag(tokenizer, parse_context)
|
|
||||||
while token = tokenizer.shift
|
while token = tokenizer.shift
|
||||||
unless token.empty? || token =~ WhitespaceOrNothing
|
unless token.empty?
|
||||||
unless token =~ LiquidTagToken
|
case
|
||||||
# line isn't empty but didn't match tag syntax, yield and let the
|
when token.start_with?(TAGSTART)
|
||||||
# caller raise a syntax error
|
whitespace_handler(token, parse_context)
|
||||||
return yield token, token
|
if token =~ FullToken
|
||||||
|
tag_name = $1
|
||||||
|
markup = $2
|
||||||
|
# fetch the tag from registered blocks
|
||||||
|
if tag = registered_tags[tag_name]
|
||||||
|
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
|
||||||
|
@blank &&= new_tag.blank?
|
||||||
|
@nodelist << new_tag
|
||||||
|
else
|
||||||
|
# end parsing if we reach an unknown tag and let the caller decide
|
||||||
|
# determine how to proceed
|
||||||
|
return yield tag_name, markup
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise_missing_tag_terminator(token, parse_context)
|
||||||
|
end
|
||||||
|
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 =~ /\A\s*\z/)
|
||||||
end
|
end
|
||||||
tag_name = $1
|
|
||||||
markup = $2
|
|
||||||
unless tag = registered_tags[tag_name]
|
|
||||||
# end parsing if we reach an unknown tag and let the caller decide
|
|
||||||
# determine how to proceed
|
|
||||||
return yield tag_name, markup
|
|
||||||
end
|
|
||||||
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
|
|
||||||
@blank &&= new_tag.blank?
|
|
||||||
@nodelist << new_tag
|
|
||||||
end
|
|
||||||
parse_context.line_number = tokenizer.line_number
|
|
||||||
end
|
|
||||||
|
|
||||||
yield nil, nil
|
|
||||||
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
|
|
||||||
raise_missing_tag_terminator(token, parse_context)
|
|
||||||
end
|
|
||||||
tag_name = $2
|
|
||||||
markup = $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 += $1.count("\n".freeze) + $3.count("\n".freeze)
|
|
||||||
end
|
|
||||||
|
|
||||||
if tag_name == 'liquid'.freeze
|
|
||||||
liquid_tag_tokenizer = Tokenizer.new(markup, line_number: parse_context.line_number, for_liquid_tag: true)
|
|
||||||
next parse_for_liquid_tag(liquid_tag_tokenizer, parse_context, &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
unless tag = registered_tags[tag_name]
|
|
||||||
# end parsing if we reach an unknown tag and let the caller decide
|
|
||||||
# determine how to proceed
|
|
||||||
return yield tag_name, markup
|
|
||||||
end
|
|
||||||
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
|
|
||||||
@blank &&= new_tag.blank?
|
|
||||||
@nodelist << new_tag
|
|
||||||
when token.start_with?(VARSTART)
|
|
||||||
whitespace_handler(token, parse_context)
|
|
||||||
@nodelist << create_variable(token, parse_context)
|
|
||||||
@blank = false
|
|
||||||
else
|
|
||||||
if parse_context.trim_whitespace
|
|
||||||
token.lstrip!
|
|
||||||
end
|
|
||||||
parse_context.trim_whitespace = false
|
|
||||||
@nodelist << token
|
|
||||||
@blank &&= !!(token =~ WhitespaceOrNothing)
|
|
||||||
end
|
end
|
||||||
parse_context.line_number = tokenizer.line_number
|
parse_context.line_number = tokenizer.line_number
|
||||||
end
|
end
|
||||||
@@ -113,57 +69,52 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
render_to_output_buffer(context, '')
|
output = []
|
||||||
end
|
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
context.resource_limits.render_score += @nodelist.length
|
context.resource_limits.render_score += @nodelist.length
|
||||||
|
|
||||||
idx = 0
|
@nodelist.each do |token|
|
||||||
while node = @nodelist[idx]
|
# Break out if we have any unhanded interrupts.
|
||||||
previous_output_size = output.bytesize
|
break if context.interrupt?
|
||||||
|
|
||||||
case node
|
begin
|
||||||
when String
|
|
||||||
output << node
|
|
||||||
when Variable
|
|
||||||
render_node(context, output, node)
|
|
||||||
when Block
|
|
||||||
render_node(context, node.blank? ? '' : output, node)
|
|
||||||
break if context.interrupt? # might have happened in a for-block
|
|
||||||
when Continue, Break
|
|
||||||
# If we get an Interrupt that means the block must stop processing. An
|
# If we get an Interrupt that means the block must stop processing. An
|
||||||
# Interrupt is any command that stops block execution such as {% break %}
|
# Interrupt is any command that stops block execution such as {% break %}
|
||||||
# or {% continue %}
|
# or {% continue %}
|
||||||
context.push_interrupt(node.interrupt)
|
if token.is_a?(Continue) || token.is_a?(Break)
|
||||||
break
|
context.push_interrupt(token.interrupt)
|
||||||
else # Other non-Block tags
|
break
|
||||||
render_node(context, output, node)
|
end
|
||||||
break if context.interrupt? # might have happened through an include
|
|
||||||
end
|
|
||||||
idx += 1
|
|
||||||
|
|
||||||
raise_if_resource_limits_reached(context, output.bytesize - previous_output_size)
|
node_output = render_node(token, context)
|
||||||
|
|
||||||
|
unless token.is_a?(Block) && token.blank?
|
||||||
|
output << node_output
|
||||||
|
end
|
||||||
|
rescue MemoryError => e
|
||||||
|
raise e
|
||||||
|
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
|
||||||
|
context.handle_error(e, token.line_number)
|
||||||
|
output << nil
|
||||||
|
rescue ::StandardError => e
|
||||||
|
line_number = token.is_a?(String) ? nil : token.line_number
|
||||||
|
output << context.handle_error(e, line_number)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
output.join
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def render_node(context, output, node)
|
def render_node(node, context)
|
||||||
node.render_to_output_buffer(context, output)
|
node_output = node.is_a?(String) ? node : node.render(context)
|
||||||
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
|
node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s
|
||||||
context.handle_error(e, node.line_number)
|
|
||||||
rescue ::StandardError => e
|
|
||||||
line_number = node.is_a?(String) ? nil : node.line_number
|
|
||||||
output << context.handle_error(e, line_number)
|
|
||||||
end
|
|
||||||
|
|
||||||
def raise_if_resource_limits_reached(context, length)
|
context.resource_limits.render_length += node_output.length
|
||||||
context.resource_limits.render_length += length
|
if context.resource_limits.reached?
|
||||||
return unless context.resource_limits.reached?
|
raise MemoryError.new("Memory limits exceeded".freeze)
|
||||||
raise MemoryError.new("Memory limits exceeded".freeze)
|
end
|
||||||
|
node_output
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_variable(token, parse_context)
|
def create_variable(token, parse_context)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ module Liquid
|
|||||||
@@operators
|
@@operators
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :attachment, :child_condition
|
attr_reader :attachment
|
||||||
attr_accessor :left, :operator, :right
|
attr_accessor :left, :operator, :right
|
||||||
|
|
||||||
def initialize(left = nil, operator = nil, right = nil)
|
def initialize(left = nil, operator = nil, right = nil)
|
||||||
@@ -83,7 +83,7 @@ module Liquid
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
attr_reader :child_relation
|
attr_reader :child_relation, :child_condition
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
@@ -128,15 +128,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
||||||
def children
|
|
||||||
[
|
|
||||||
@node.left, @node.right,
|
|
||||||
@node.child_condition, @node.attachment
|
|
||||||
].compact
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class ElseCondition < Condition
|
class ElseCondition < Condition
|
||||||
|
|||||||
@@ -12,25 +12,17 @@ module Liquid
|
|||||||
#
|
#
|
||||||
# context['bob'] #=> nil class Context
|
# context['bob'] #=> nil class Context
|
||||||
class Context
|
class Context
|
||||||
attr_reader :scopes, :errors, :registers, :environments, :resource_limits, :static_registers, :static_environments
|
attr_reader :scopes, :errors, :registers, :environments, :resource_limits
|
||||||
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
||||||
|
|
||||||
# rubocop:disable Metrics/ParameterLists
|
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil)
|
||||||
def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_registers: {}, static_environments: {})
|
@environments = [environments].flatten
|
||||||
new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_registers, static_environments)
|
@scopes = [(outer_scope || {})]
|
||||||
end
|
@registers = registers
|
||||||
|
@errors = []
|
||||||
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_registers = {}, static_environments = {})
|
@partial = false
|
||||||
@environments = [environments].flatten
|
@strict_variables = false
|
||||||
@static_environments = [static_environments].flatten.map(&:freeze).freeze
|
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
|
||||||
@scopes = [(outer_scope || {})]
|
|
||||||
@registers = registers
|
|
||||||
@static_registers = static_registers.freeze
|
|
||||||
@errors = []
|
|
||||||
@partial = false
|
|
||||||
@strict_variables = false
|
|
||||||
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
|
|
||||||
@base_scope_depth = 0
|
|
||||||
squash_instance_assigns_with_environments
|
squash_instance_assigns_with_environments
|
||||||
|
|
||||||
@this_stack_used = false
|
@this_stack_used = false
|
||||||
@@ -44,7 +36,6 @@ module Liquid
|
|||||||
@filters = []
|
@filters = []
|
||||||
@global_filter = nil
|
@global_filter = nil
|
||||||
end
|
end
|
||||||
# rubocop:enable Metrics/ParameterLists
|
|
||||||
|
|
||||||
def warnings
|
def warnings
|
||||||
@warnings ||= []
|
@warnings ||= []
|
||||||
@@ -98,7 +89,7 @@ module Liquid
|
|||||||
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
|
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
|
||||||
def push(new_scope = {})
|
def push(new_scope = {})
|
||||||
@scopes.unshift(new_scope)
|
@scopes.unshift(new_scope)
|
||||||
check_overflow
|
raise StackLevelError, "Nesting too deep".freeze if @scopes.length > Block::MAX_DEPTH
|
||||||
end
|
end
|
||||||
|
|
||||||
# Merge a hash of variables in the current local scope
|
# Merge a hash of variables in the current local scope
|
||||||
@@ -135,25 +126,6 @@ module Liquid
|
|||||||
@this_stack_used = old_stack_used
|
@this_stack_used = old_stack_used
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates a new context inheriting resource limits, filters, environment etc.,
|
|
||||||
# but with an isolated scope.
|
|
||||||
def new_isolated_subcontext
|
|
||||||
check_overflow
|
|
||||||
|
|
||||||
Context.build(
|
|
||||||
resource_limits: resource_limits,
|
|
||||||
static_environments: static_environments,
|
|
||||||
static_registers: static_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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def clear_instance_assigns
|
def clear_instance_assigns
|
||||||
@scopes[0] = {}
|
@scopes[0] = {}
|
||||||
end
|
end
|
||||||
@@ -192,13 +164,23 @@ module Liquid
|
|||||||
# This was changed from find() to find_index() because this is a very hot
|
# 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
|
# path and find_index() is optimized in MRI to reduce object allocation
|
||||||
index = @scopes.find_index { |s| s.key?(key) }
|
index = @scopes.find_index { |s| s.key?(key) }
|
||||||
|
scope = @scopes[index] if index
|
||||||
|
|
||||||
variable = if index
|
variable = nil
|
||||||
lookup_and_evaluate(@scopes[index], key, raise_on_not_found: raise_on_not_found)
|
|
||||||
else
|
if scope.nil?
|
||||||
try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found)
|
@environments.each do |e|
|
||||||
|
variable = lookup_and_evaluate(e, key, raise_on_not_found: raise_on_not_found)
|
||||||
|
unless variable.nil?
|
||||||
|
scope = e
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope ||= @environments.last || @scopes.last
|
||||||
|
variable ||= lookup_and_evaluate(scope, key, raise_on_not_found: raise_on_not_found)
|
||||||
|
|
||||||
variable = variable.to_liquid
|
variable = variable.to_liquid
|
||||||
variable.context = self if variable.respond_to?(:context=)
|
variable.context = self if variable.respond_to?(:context=)
|
||||||
|
|
||||||
@@ -219,38 +201,8 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
attr_writer :base_scope_depth, :warnings, :errors, :strainer, :filters
|
|
||||||
|
|
||||||
private
|
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".freeze if overflow?
|
|
||||||
end
|
|
||||||
|
|
||||||
def overflow?
|
|
||||||
base_scope_depth + @scopes.length > Block::MAX_DEPTH
|
|
||||||
end
|
|
||||||
|
|
||||||
def internal_error
|
def internal_error
|
||||||
# raise and catch to set backtrace and cause on exception
|
# raise and catch to set backtrace and cause on exception
|
||||||
raise Liquid::InternalError, 'internal'
|
raise Liquid::InternalError, 'internal'
|
||||||
|
|||||||
@@ -19,26 +19,22 @@ module Liquid
|
|||||||
'false'.freeze => false,
|
'false'.freeze => false,
|
||||||
'blank'.freeze => MethodLiteral.new(:blank?, '').freeze,
|
'blank'.freeze => MethodLiteral.new(:blank?, '').freeze,
|
||||||
'empty'.freeze => MethodLiteral.new(:empty?, '').freeze
|
'empty'.freeze => MethodLiteral.new(:empty?, '').freeze
|
||||||
}.freeze
|
}
|
||||||
|
|
||||||
SINGLE_QUOTED_STRING = /\A'(.*)'\z/m
|
|
||||||
DOUBLE_QUOTED_STRING = /\A"(.*)"\z/m
|
|
||||||
INTEGERS_REGEX = /\A(-?\d+)\z/
|
|
||||||
FLOATS_REGEX = /\A(-?\d[\d\.]+)\z/
|
|
||||||
RANGES_REGEX = /\A\((\S+)\.\.(\S+)\)\z/
|
|
||||||
|
|
||||||
def self.parse(markup)
|
def self.parse(markup)
|
||||||
if LITERALS.key?(markup)
|
if LITERALS.key?(markup)
|
||||||
LITERALS[markup]
|
LITERALS[markup]
|
||||||
else
|
else
|
||||||
case markup
|
case markup
|
||||||
when SINGLE_QUOTED_STRING, DOUBLE_QUOTED_STRING
|
when /\A'(.*)'\z/m # Single quoted strings
|
||||||
$1
|
$1
|
||||||
when INTEGERS_REGEX
|
when /\A"(.*)"\z/m # Double quoted strings
|
||||||
|
$1
|
||||||
|
when /\A(-?\d+)\z/ # Integer and floats
|
||||||
$1.to_i
|
$1.to_i
|
||||||
when RANGES_REGEX
|
when /\A\((\S+)\.\.(\S+)\)\z/ # Ranges
|
||||||
RangeLookup.parse($1, $2)
|
RangeLookup.parse($1, $2)
|
||||||
when FLOATS_REGEX
|
when /\A(-?\d[\d\.]+)\z/ # Floats
|
||||||
$1.to_f
|
$1.to_f
|
||||||
else
|
else
|
||||||
VariableLookup.parse(markup)
|
VariableLookup.parse(markup)
|
||||||
|
|||||||
@@ -7,12 +7,6 @@ class String # :nodoc:
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Symbol # :nodoc:
|
|
||||||
def to_liquid
|
|
||||||
to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Array # :nodoc:
|
class Array # :nodoc:
|
||||||
def to_liquid
|
def to_liquid
|
||||||
self
|
self
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ module Liquid
|
|||||||
def interpolate(name, vars)
|
def interpolate(name, vars)
|
||||||
name.gsub(/%\{(\w+)\}/) do
|
name.gsub(/%\{(\w+)\}/) do
|
||||||
# raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym]
|
# raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym]
|
||||||
(vars[$1.to_sym]).to_s
|
"#{vars[$1.to_sym]}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -12,14 +12,13 @@ module Liquid
|
|||||||
')'.freeze => :close_round,
|
')'.freeze => :close_round,
|
||||||
'?'.freeze => :question,
|
'?'.freeze => :question,
|
||||||
'-'.freeze => :dash
|
'-'.freeze => :dash
|
||||||
}.freeze
|
}
|
||||||
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
||||||
SINGLE_STRING_LITERAL = /'[^\']*'/
|
SINGLE_STRING_LITERAL = /'[^\']*'/
|
||||||
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
||||||
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
||||||
DOTDOT = /\.\./
|
DOTDOT = /\.\./
|
||||||
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
|
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
|
||||||
WHITESPACE_OR_NOTHING = /\s*/
|
|
||||||
|
|
||||||
def initialize(input)
|
def initialize(input)
|
||||||
@ss = StringScanner.new(input)
|
@ss = StringScanner.new(input)
|
||||||
@@ -29,7 +28,7 @@ module Liquid
|
|||||||
@output = []
|
@output = []
|
||||||
|
|
||||||
until @ss.eos?
|
until @ss.eos?
|
||||||
@ss.skip(WHITESPACE_OR_NOTHING)
|
@ss.skip(/\s*/)
|
||||||
break if @ss.eos?
|
break if @ss.eos?
|
||||||
tok = case
|
tok = case
|
||||||
when t = @ss.scan(COMPARISON_OPERATOR) then [:comparison, t]
|
when t = @ss.scan(COMPARISON_OPERATOR) then [:comparison, t]
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
if: "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
if: "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
||||||
include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"
|
include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"
|
||||||
unknown_tag: "Unknown tag '%{tag}'"
|
unknown_tag: "Unknown tag '%{tag}'"
|
||||||
invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
invalid_delimiter: "'end' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
||||||
unexpected_else: "%{block_name} tag does not expect 'else' tag"
|
unexpected_else: "%{block_name} tag does not expect 'else' tag"
|
||||||
unexpected_outer_tag: "Unexpected outer '%{tag}' tag"
|
unexpected_outer_tag: "Unexpected outer '%{tag}' tag"
|
||||||
tag_termination: "Tag '%{token}' was not properly terminated with regexp: %{tag_end}"
|
tag_termination: "Tag '%{token}' was not properly terminated with regexp: %{tag_end}"
|
||||||
@@ -22,6 +22,5 @@
|
|||||||
tag_never_closed: "'%{block_name}' tag was never closed"
|
tag_never_closed: "'%{block_name}' tag was never closed"
|
||||||
meta_syntax_error: "Liquid syntax error: #{e.message}"
|
meta_syntax_error: "Liquid syntax error: #{e.message}"
|
||||||
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
||||||
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
|
||||||
argument:
|
argument:
|
||||||
include: "Argument error in tag 'include' - Illegal template name"
|
include: "Argument error in tag 'include' - Illegal template name"
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Liquid
|
|
||||||
class ParseTreeVisitor
|
|
||||||
def self.for(node, callbacks = Hash.new(proc {}))
|
|
||||||
if defined?(node.class::ParseTreeVisitor)
|
|
||||||
node.class::ParseTreeVisitor
|
|
||||||
else
|
|
||||||
self
|
|
||||||
end.new(node, callbacks)
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(node, callbacks)
|
|
||||||
@node = node
|
|
||||||
@callbacks = callbacks
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_callback_for(*classes, &block)
|
|
||||||
callback = block
|
|
||||||
callback = ->(node, _) { yield node } if block.arity.abs == 1
|
|
||||||
callback = ->(_, _) { yield } if block.arity.zero?
|
|
||||||
classes.each { |klass| @callbacks[klass] = callback }
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
def visit(context = nil)
|
|
||||||
children.map do |node|
|
|
||||||
item, new_context = @callbacks[node.class].call(node, context)
|
|
||||||
[
|
|
||||||
item,
|
|
||||||
ParseTreeVisitor.for(node, @callbacks).visit(new_context || context)
|
|
||||||
]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def children
|
|
||||||
@node.respond_to?(:nodelist) ? Array(@node.nodelist) : []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -44,14 +44,11 @@ module Liquid
|
|||||||
tok[0] == type
|
tok[0] == type
|
||||||
end
|
end
|
||||||
|
|
||||||
SINGLE_TOKEN_EXPRESSION_TYPES = [:string, :number].freeze
|
|
||||||
private_constant :SINGLE_TOKEN_EXPRESSION_TYPES
|
|
||||||
|
|
||||||
def expression
|
def expression
|
||||||
token = @tokens[@p]
|
token = @tokens[@p]
|
||||||
if token[0] == :id
|
if token[0] == :id
|
||||||
variable_signature
|
variable_signature
|
||||||
elsif SINGLE_TOKEN_EXPRESSION_TYPES.include? token[0]
|
elsif [:string, :number].include? token[0]
|
||||||
consume
|
consume
|
||||||
elsif token.first == :open_round
|
elsif token.first == :open_round
|
||||||
consume
|
consume
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
module Liquid
|
|
||||||
class PartialCache
|
|
||||||
def self.load(template_name, context:, parse_context:)
|
|
||||||
cached_partials = (context.registers[:cached_partials] ||= {})
|
|
||||||
cached = cached_partials[template_name]
|
|
||||||
return cached if cached
|
|
||||||
|
|
||||||
file_system = (context.registers[:file_system] ||= Liquid::Template.file_system)
|
|
||||||
source = file_system.read_template_file(template_name)
|
|
||||||
parse_context.partial = true
|
|
||||||
|
|
||||||
partial = Liquid::Template.parse(source, parse_context)
|
|
||||||
cached_partials[template_name] = partial
|
|
||||||
ensure
|
|
||||||
parse_context.partial = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class BlockBody
|
class BlockBody
|
||||||
def render_node_with_profiling(context, output, node)
|
def render_node_with_profiling(node, context)
|
||||||
Profiler.profile_node_render(node) do
|
Profiler.profile_node_render(node) do
|
||||||
render_node_without_profiling(context, output, node)
|
render_node_without_profiling(node, context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -11,13 +11,13 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
class Include < Tag
|
class Include < Tag
|
||||||
def render_to_output_buffer_with_profiling(context, output)
|
def render_with_profiling(context)
|
||||||
Profiler.profile_children(context.evaluate(@template_name_expr).to_s) do
|
Profiler.profile_children(context.evaluate(@template_name_expr).to_s) do
|
||||||
render_to_output_buffer_without_profiling(context, output)
|
render_without_profiling(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
alias_method :render_to_output_buffer_without_profiling, :render_to_output_buffer
|
alias_method :render_without_profiling, :render
|
||||||
alias_method :render_to_output_buffer, :render_to_output_buffer_with_profiling
|
alias_method :render, :render_with_profiling
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,14 +9,8 @@ module Liquid
|
|||||||
'<'.freeze => '<'.freeze,
|
'<'.freeze => '<'.freeze,
|
||||||
'"'.freeze => '"'.freeze,
|
'"'.freeze => '"'.freeze,
|
||||||
"'".freeze => '''.freeze
|
"'".freeze => '''.freeze
|
||||||
}.freeze
|
}
|
||||||
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
||||||
STRIP_HTML_BLOCKS = Regexp.union(
|
|
||||||
/<script.*?<\/script>/m,
|
|
||||||
/<!--.*?-->/m,
|
|
||||||
/<style.*?<\/style>/m
|
|
||||||
)
|
|
||||||
STRIP_HTML_TAGS = /<.*?>/m
|
|
||||||
|
|
||||||
# Return the size of an array or of an string
|
# Return the size of an array or of an string
|
||||||
def size(input)
|
def size(input)
|
||||||
@@ -52,12 +46,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def url_decode(input)
|
def url_decode(input)
|
||||||
return if input.nil?
|
CGI.unescape(input.to_s) unless input.nil?
|
||||||
|
|
||||||
result = CGI.unescape(input.to_s)
|
|
||||||
raise Liquid::ArgumentError, "invalid byte sequence in #{result.encoding}" unless result.valid_encoding?
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def slice(input, offset, length = nil)
|
def slice(input, offset, length = nil)
|
||||||
@@ -79,7 +68,7 @@ module Liquid
|
|||||||
truncate_string_str = truncate_string.to_s
|
truncate_string_str = truncate_string.to_s
|
||||||
l = length - truncate_string_str.length
|
l = length - truncate_string_str.length
|
||||||
l = 0 if l < 0
|
l = 0 if l < 0
|
||||||
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
|
input_str.length > length ? input_str[0...l] + truncate_string_str : input_str
|
||||||
end
|
end
|
||||||
|
|
||||||
def truncatewords(input, words = 15, truncate_string = "...".freeze)
|
def truncatewords(input, words = 15, truncate_string = "...".freeze)
|
||||||
@@ -88,7 +77,7 @@ module Liquid
|
|||||||
words = Utils.to_integer(words)
|
words = Utils.to_integer(words)
|
||||||
l = words - 1
|
l = words - 1
|
||||||
l = 0 if l < 0
|
l = 0 if l < 0
|
||||||
wordlist.length > l ? wordlist[0..l].join(" ".freeze).concat(truncate_string.to_s) : input
|
wordlist.length > l ? wordlist[0..l].join(" ".freeze) + truncate_string.to_s : input
|
||||||
end
|
end
|
||||||
|
|
||||||
# Split input string into an array of substrings separated by given pattern.
|
# Split input string into an array of substrings separated by given pattern.
|
||||||
@@ -114,9 +103,7 @@ module Liquid
|
|||||||
|
|
||||||
def strip_html(input)
|
def strip_html(input)
|
||||||
empty = ''.freeze
|
empty = ''.freeze
|
||||||
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
input.to_s.gsub(/<script.*?<\/script>/m, empty).gsub(/<!--.*?-->/m, empty).gsub(/<style.*?<\/style>/m, empty).gsub(/<.*?>/m, empty)
|
||||||
result.gsub!(STRIP_HTML_TAGS, empty)
|
|
||||||
result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove all newlines from the string
|
# Remove all newlines from the string
|
||||||
@@ -133,18 +120,19 @@ module Liquid
|
|||||||
# provide optional property with which to sort an array of hashes or drops
|
# provide optional property with which to sort an array of hashes or drops
|
||||||
def sort(input, property = nil)
|
def sort(input, property = nil)
|
||||||
ary = InputIterator.new(input)
|
ary = InputIterator.new(input)
|
||||||
|
|
||||||
return [] if ary.empty?
|
|
||||||
|
|
||||||
if property.nil?
|
if property.nil?
|
||||||
|
ary.sort
|
||||||
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
||||||
|
[]
|
||||||
|
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
|
||||||
ary.sort do |a, b|
|
ary.sort do |a, b|
|
||||||
nil_safe_compare(a, b)
|
a = a[property]
|
||||||
end
|
b = b[property]
|
||||||
elsif ary.all? { |el| el.respond_to?(:[]) }
|
if a && b
|
||||||
begin
|
a <=> b
|
||||||
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
|
else
|
||||||
rescue TypeError
|
a ? -1 : 1
|
||||||
raise_property_error(property)
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -154,40 +142,12 @@ module Liquid
|
|||||||
def sort_natural(input, property = nil)
|
def sort_natural(input, property = nil)
|
||||||
ary = InputIterator.new(input)
|
ary = InputIterator.new(input)
|
||||||
|
|
||||||
return [] if ary.empty?
|
|
||||||
|
|
||||||
if property.nil?
|
if property.nil?
|
||||||
ary.sort do |a, b|
|
ary.sort { |a, b| a.casecmp(b) }
|
||||||
nil_safe_casecmp(a, b)
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
||||||
end
|
|
||||||
elsif ary.all? { |el| el.respond_to?(:[]) }
|
|
||||||
begin
|
|
||||||
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
|
|
||||||
rescue TypeError
|
|
||||||
raise_property_error(property)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Filter the elements of an array to those with a certain property value.
|
|
||||||
# By default the target is any truthy value.
|
|
||||||
def where(input, property, target_value = nil)
|
|
||||||
ary = InputIterator.new(input)
|
|
||||||
|
|
||||||
if ary.empty?
|
|
||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[]) && target_value.nil?
|
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
|
||||||
begin
|
ary.sort { |a, b| a[property].casecmp(b[property]) }
|
||||||
ary.select { |item| item[property] }
|
|
||||||
rescue TypeError
|
|
||||||
raise_property_error(property)
|
|
||||||
end
|
|
||||||
elsif ary.first.respond_to?(:[])
|
|
||||||
begin
|
|
||||||
ary.select { |item| item[property] == target_value }
|
|
||||||
rescue TypeError
|
|
||||||
raise_property_error(property)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -201,11 +161,7 @@ module Liquid
|
|||||||
elsif ary.empty? # The next two cases assume a non-empty array.
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[])
|
elsif ary.first.respond_to?(:[])
|
||||||
begin
|
ary.uniq{ |a| a[property] }
|
||||||
ary.uniq { |a| a[property] }
|
|
||||||
rescue TypeError
|
|
||||||
raise_property_error(property)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -227,8 +183,6 @@ module Liquid
|
|||||||
r.is_a?(Proc) ? r.call : r
|
r.is_a?(Proc) ? r.call : r
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue TypeError
|
|
||||||
raise_property_error(property)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove nils within an array
|
# Remove nils within an array
|
||||||
@@ -241,11 +195,7 @@ module Liquid
|
|||||||
elsif ary.empty? # The next two cases assume a non-empty array.
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[])
|
elsif ary.first.respond_to?(:[])
|
||||||
begin
|
ary.reject{ |a| a[property].nil? }
|
||||||
ary.reject { |a| a[property].nil? }
|
|
||||||
rescue TypeError
|
|
||||||
raise_property_error(property)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -403,22 +353,6 @@ module Liquid
|
|||||||
raise Liquid::FloatDomainError, e.message
|
raise Liquid::FloatDomainError, e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
def at_least(input, n)
|
|
||||||
min_value = Utils.to_number(n)
|
|
||||||
|
|
||||||
result = Utils.to_number(input)
|
|
||||||
result = min_value if min_value > result
|
|
||||||
result.is_a?(BigDecimal) ? result.to_f : result
|
|
||||||
end
|
|
||||||
|
|
||||||
def at_most(input, n)
|
|
||||||
max_value = Utils.to_number(n)
|
|
||||||
|
|
||||||
result = Utils.to_number(input)
|
|
||||||
result = max_value if max_value < result
|
|
||||||
result.is_a?(BigDecimal) ? result.to_f : result
|
|
||||||
end
|
|
||||||
|
|
||||||
def default(input, default_value = ''.freeze)
|
def default(input, default_value = ''.freeze)
|
||||||
if !input || input.respond_to?(:empty?) && input.empty?
|
if !input || input.respond_to?(:empty?) && input.empty?
|
||||||
default_value
|
default_value
|
||||||
@@ -429,31 +363,11 @@ module Liquid
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def raise_property_error(property)
|
|
||||||
raise Liquid::ArgumentError.new("cannot select the property '#{property}'")
|
|
||||||
end
|
|
||||||
|
|
||||||
def apply_operation(input, operand, operation)
|
def apply_operation(input, operand, operation)
|
||||||
result = Utils.to_number(input).send(operation, Utils.to_number(operand))
|
result = Utils.to_number(input).send(operation, Utils.to_number(operand))
|
||||||
result.is_a?(BigDecimal) ? result.to_f : result
|
result.is_a?(BigDecimal) ? result.to_f : result
|
||||||
end
|
end
|
||||||
|
|
||||||
def nil_safe_compare(a, b)
|
|
||||||
if !a.nil? && !b.nil?
|
|
||||||
a <=> b
|
|
||||||
else
|
|
||||||
a.nil? ? 1 : -1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def nil_safe_casecmp(a, b)
|
|
||||||
if !a.nil? && !b.nil?
|
|
||||||
a.to_s.casecmp(b.to_s)
|
|
||||||
else
|
|
||||||
a.nil? ? 1 : -1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class InputIterator
|
class InputIterator
|
||||||
include Enumerable
|
include Enumerable
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ module Liquid
|
|||||||
include ParserSwitching
|
include ParserSwitching
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def parse(tag_name, markup, tokenizer, parse_context)
|
def parse(tag_name, markup, tokenizer, options)
|
||||||
tag = new(tag_name, markup, parse_context)
|
tag = new(tag_name, markup, options)
|
||||||
tag.parse(tokenizer)
|
tag.parse(tokenizer)
|
||||||
tag
|
tag
|
||||||
end
|
end
|
||||||
@@ -36,14 +36,6 @@ module Liquid
|
|||||||
''.freeze
|
''.freeze
|
||||||
end
|
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)
|
|
||||||
output << render(context)
|
|
||||||
output
|
|
||||||
end
|
|
||||||
|
|
||||||
def blank?
|
def blank?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,27 +10,21 @@ module Liquid
|
|||||||
class Assign < Tag
|
class Assign < Tag
|
||||||
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
|
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
|
||||||
|
|
||||||
def self.syntax_error_translation_key
|
|
||||||
"errors.syntax.assign".freeze
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :to, :from
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
if markup =~ Syntax
|
if markup =~ Syntax
|
||||||
@to = $1
|
@to = $1
|
||||||
@from = Variable.new($2, options)
|
@from = Variable.new($2, options)
|
||||||
else
|
else
|
||||||
raise SyntaxError.new(options[:locale].t(self.class.syntax_error_translation_key))
|
raise SyntaxError.new options[:locale].t("errors.syntax.assign".freeze)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
val = @from.render(context)
|
val = @from.render(context)
|
||||||
context.scopes.last[@to] = val
|
context.scopes.last[@to] = val
|
||||||
context.resource_limits.assign_score += assign_score_of(val)
|
context.resource_limits.assign_score += assign_score_of(val)
|
||||||
output
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def blank?
|
def blank?
|
||||||
@@ -41,7 +35,7 @@ module Liquid
|
|||||||
|
|
||||||
def assign_score_of(val)
|
def assign_score_of(val)
|
||||||
if val.instance_of?(String)
|
if val.instance_of?(String)
|
||||||
val.bytesize
|
val.length
|
||||||
elsif val.instance_of?(Array) || val.instance_of?(Hash)
|
elsif val.instance_of?(Array) || val.instance_of?(Hash)
|
||||||
sum = 1
|
sum = 1
|
||||||
# Uses #each to avoid extra allocations.
|
# Uses #each to avoid extra allocations.
|
||||||
@@ -51,12 +45,6 @@ module Liquid
|
|||||||
1
|
1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
||||||
def children
|
|
||||||
[@node.from]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.register_tag('assign'.freeze, Assign)
|
Template.register_tag('assign'.freeze, Assign)
|
||||||
|
|||||||
@@ -22,12 +22,11 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
previous_output_size = output.bytesize
|
output = super
|
||||||
super
|
|
||||||
context.scopes.last[@to] = output
|
context.scopes.last[@to] = output
|
||||||
context.resource_limits.assign_score += (output.bytesize - previous_output_size)
|
context.resource_limits.assign_score += output.length
|
||||||
output
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def blank?
|
def blank?
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ module Liquid
|
|||||||
Syntax = /(#{QuotedFragment})/o
|
Syntax = /(#{QuotedFragment})/o
|
||||||
WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om
|
WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om
|
||||||
|
|
||||||
attr_reader :blocks, :left
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
@blocks = []
|
@blocks = []
|
||||||
@@ -38,21 +36,21 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
context.stack do
|
context.stack do
|
||||||
execute_else_block = true
|
execute_else_block = true
|
||||||
|
|
||||||
|
output = ''
|
||||||
@blocks.each do |block|
|
@blocks.each do |block|
|
||||||
if block.else?
|
if block.else?
|
||||||
block.attachment.render_to_output_buffer(context, output) if execute_else_block
|
return block.attachment.render(context) if execute_else_block
|
||||||
elsif block.evaluate(context)
|
elsif block.evaluate(context)
|
||||||
execute_else_block = false
|
execute_else_block = false
|
||||||
block.attachment.render_to_output_buffer(context, output)
|
output << block.attachment.render(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
output
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -82,12 +80,6 @@ module Liquid
|
|||||||
block.attach(BlockBody.new)
|
block.attach(BlockBody.new)
|
||||||
@blocks << block
|
@blocks << block
|
||||||
end
|
end
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
||||||
def children
|
|
||||||
[@node.left] + @node.blocks
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.register_tag('case'.freeze, Case)
|
Template.register_tag('case'.freeze, Case)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class Comment < Block
|
class Comment < Block
|
||||||
def render_to_output_buffer(_context, output)
|
def render(_context)
|
||||||
output
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def unknown_tag(_tag, _markup, _tokens)
|
def unknown_tag(_tag, _markup, _tokens)
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ module Liquid
|
|||||||
SimpleSyntax = /\A#{QuotedFragment}+/o
|
SimpleSyntax = /\A#{QuotedFragment}+/o
|
||||||
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
|
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
|
||||||
|
|
||||||
attr_reader :variables
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
case markup
|
case markup
|
||||||
@@ -31,29 +29,18 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
context.registers[:cycle] ||= {}
|
context.registers[:cycle] ||= Hash.new(0)
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
key = context.evaluate(@name)
|
key = context.evaluate(@name)
|
||||||
iteration = context.registers[:cycle][key].to_i
|
iteration = context.registers[:cycle][key]
|
||||||
|
result = context.evaluate(@variables[iteration])
|
||||||
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 += 1
|
||||||
iteration = 0 if iteration >= @variables.size
|
iteration = 0 if iteration >= @variables.size
|
||||||
context.registers[:cycle][key] = iteration
|
context.registers[:cycle][key] = iteration
|
||||||
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -64,12 +51,6 @@ module Liquid
|
|||||||
$1 ? Expression.parse($1) : nil
|
$1 ? Expression.parse($1) : nil
|
||||||
end.compact
|
end.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
||||||
def children
|
|
||||||
Array(@node.variables)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.register_tag('cycle', Cycle)
|
Template.register_tag('cycle', Cycle)
|
||||||
|
|||||||
@@ -23,12 +23,11 @@ module Liquid
|
|||||||
@variable = markup.strip
|
@variable = markup.strip
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
value = context.environments.first[@variable] ||= 0
|
value = context.environments.first[@variable] ||= 0
|
||||||
value -= 1
|
value -= 1
|
||||||
context.environments.first[@variable] = value
|
context.environments.first[@variable] = value
|
||||||
output << value.to_s
|
value.to_s
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
module Liquid
|
|
||||||
# Echo outputs an expression
|
|
||||||
#
|
|
||||||
# {% echo monkey %}
|
|
||||||
# {% echo user.name %}
|
|
||||||
#
|
|
||||||
# This is identical to variable output syntax, like {{ foo }}, but works
|
|
||||||
# inside {% liquid %} tags. The full syntax is supported, including filters:
|
|
||||||
#
|
|
||||||
# {% echo user | link %}
|
|
||||||
#
|
|
||||||
class Echo < Tag
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
Template.register_tag('echo'.freeze, Echo)
|
|
||||||
end
|
|
||||||
@@ -46,7 +46,8 @@ module Liquid
|
|||||||
class For < Block
|
class For < Block
|
||||||
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
||||||
|
|
||||||
attr_reader :collection_name, :variable_name, :limit, :from
|
attr_reader :collection_name
|
||||||
|
attr_reader :variable_name
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
@@ -70,16 +71,14 @@ module Liquid
|
|||||||
@else_block = BlockBody.new
|
@else_block = BlockBody.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
segment = collection_segment(context)
|
segment = collection_segment(context)
|
||||||
|
|
||||||
if segment.empty?
|
if segment.empty?
|
||||||
render_else(context, output)
|
render_else(context)
|
||||||
else
|
else
|
||||||
render_segment(context, output, segment)
|
render_segment(context, segment)
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
@@ -121,28 +120,19 @@ module Liquid
|
|||||||
private
|
private
|
||||||
|
|
||||||
def collection_segment(context)
|
def collection_segment(context)
|
||||||
offsets = context.registers[:for] ||= {}
|
offsets = context.registers[:for] ||= Hash.new(0)
|
||||||
|
|
||||||
from = if @from == :continue
|
from = if @from == :continue
|
||||||
offsets[@name].to_i
|
offsets[@name].to_i
|
||||||
else
|
else
|
||||||
from_value = context.evaluate(@from)
|
context.evaluate(@from).to_i
|
||||||
if from_value.nil?
|
|
||||||
0
|
|
||||||
else
|
|
||||||
Utils.to_integer(from_value)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
collection = context.evaluate(@collection_name)
|
collection = context.evaluate(@collection_name)
|
||||||
collection = collection.to_a if collection.is_a?(Range)
|
collection = collection.step(1).to_a if collection.is_a?(Range)
|
||||||
|
|
||||||
limit_value = context.evaluate(@limit)
|
limit = context.evaluate(@limit)
|
||||||
to = if limit_value.nil?
|
to = limit ? limit.to_i + from : nil
|
||||||
nil
|
|
||||||
else
|
|
||||||
Utils.to_integer(limit_value) + from
|
|
||||||
end
|
|
||||||
|
|
||||||
segment = Utils.slice_collection(collection, from, to)
|
segment = Utils.slice_collection(collection, from, to)
|
||||||
segment.reverse! if @reversed
|
segment.reverse! if @reversed
|
||||||
@@ -152,10 +142,12 @@ module Liquid
|
|||||||
segment
|
segment
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_segment(context, output, segment)
|
def render_segment(context, segment)
|
||||||
for_stack = context.registers[:for_stack] ||= []
|
for_stack = context.registers[:for_stack] ||= []
|
||||||
length = segment.length
|
length = segment.length
|
||||||
|
|
||||||
|
result = ''
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
|
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
|
||||||
|
|
||||||
@@ -164,9 +156,9 @@ module Liquid
|
|||||||
begin
|
begin
|
||||||
context['forloop'.freeze] = loop_vars
|
context['forloop'.freeze] = loop_vars
|
||||||
|
|
||||||
segment.each do |item|
|
segment.each_with_index do |item, index|
|
||||||
context[@variable_name] = item
|
context[@variable_name] = item
|
||||||
@for_block.render_to_output_buffer(context, output)
|
result << @for_block.render(context)
|
||||||
loop_vars.send(:increment!)
|
loop_vars.send(:increment!)
|
||||||
|
|
||||||
# Handle any interrupts if they exist.
|
# Handle any interrupts if they exist.
|
||||||
@@ -181,7 +173,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_attribute(key, expr)
|
def set_attribute(key, expr)
|
||||||
@@ -197,18 +189,8 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_else(context, output)
|
def render_else(context)
|
||||||
if @else_block
|
@else_block ? @else_block.render(context) : ''.freeze
|
||||||
@else_block.render_to_output_buffer(context, output)
|
|
||||||
else
|
|
||||||
output
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
||||||
def children
|
|
||||||
(super + [@node.limit, @node.from, @node.collection_name]).compact
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ module Liquid
|
|||||||
class If < Block
|
class If < Block
|
||||||
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
||||||
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
|
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
|
||||||
BOOLEAN_OPERATORS = %w(and or).freeze
|
BOOLEAN_OPERATORS = %w(and or)
|
||||||
|
|
||||||
attr_reader :blocks
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
@@ -22,15 +20,15 @@ module Liquid
|
|||||||
push_block('if'.freeze, markup)
|
push_block('if'.freeze, markup)
|
||||||
end
|
end
|
||||||
|
|
||||||
def nodelist
|
|
||||||
@blocks.map(&:attachment)
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse(tokens)
|
def parse(tokens)
|
||||||
while parse_body(@blocks.last.attachment, tokens)
|
while parse_body(@blocks.last.attachment, tokens)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def nodelist
|
||||||
|
@blocks.map(&:attachment)
|
||||||
|
end
|
||||||
|
|
||||||
def unknown_tag(tag, markup, tokens)
|
def unknown_tag(tag, markup, tokens)
|
||||||
if ['elsif'.freeze, 'else'.freeze].include?(tag)
|
if ['elsif'.freeze, 'else'.freeze].include?(tag)
|
||||||
push_block(tag, markup)
|
push_block(tag, markup)
|
||||||
@@ -39,16 +37,15 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
context.stack do
|
context.stack do
|
||||||
@blocks.each do |block|
|
@blocks.each do |block|
|
||||||
if block.evaluate(context)
|
if block.evaluate(context)
|
||||||
return block.attachment.render_to_output_buffer(context, output)
|
return block.attachment.render(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -111,12 +108,6 @@ module Liquid
|
|||||||
Condition.new(a)
|
Condition.new(a)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
||||||
def children
|
|
||||||
@node.blocks
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.register_tag('if'.freeze, If)
|
Template.register_tag('if'.freeze, If)
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class Ifchanged < Block
|
class Ifchanged < Block
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
context.stack do
|
context.stack do
|
||||||
block_output = ''
|
output = super
|
||||||
super(context, block_output)
|
|
||||||
|
|
||||||
if block_output != context.registers[:ifchanged]
|
if output != context.registers[:ifchanged]
|
||||||
context.registers[:ifchanged] = block_output
|
context.registers[:ifchanged] = output
|
||||||
output << block_output
|
output
|
||||||
|
else
|
||||||
|
''.freeze
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ module Liquid
|
|||||||
class Include < Tag
|
class Include < Tag
|
||||||
Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o
|
Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o
|
||||||
|
|
||||||
attr_reader :template_name_expr, :variable_name_expr, :attributes
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
|
|
||||||
@@ -42,16 +40,11 @@ module Liquid
|
|||||||
def parse(_tokens)
|
def parse(_tokens)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
template_name = context.evaluate(@template_name_expr)
|
template_name = context.evaluate(@template_name_expr)
|
||||||
raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
|
raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
|
||||||
|
|
||||||
partial = PartialCache.load(
|
partial = load_cached_partial(template_name, context)
|
||||||
template_name,
|
|
||||||
context: context,
|
|
||||||
parse_context: parse_context
|
|
||||||
)
|
|
||||||
|
|
||||||
context_variable_name = template_name.split('/'.freeze).last
|
context_variable_name = template_name.split('/'.freeze).last
|
||||||
|
|
||||||
variable = if @variable_name_expr
|
variable = if @variable_name_expr
|
||||||
@@ -71,33 +64,48 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
if variable.is_a?(Array)
|
if variable.is_a?(Array)
|
||||||
variable.each do |var|
|
variable.collect do |var|
|
||||||
context[context_variable_name] = var
|
context[context_variable_name] = var
|
||||||
partial.render_to_output_buffer(context, output)
|
partial.render(context)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
context[context_variable_name] = variable
|
context[context_variable_name] = variable
|
||||||
partial.render_to_output_buffer(context, output)
|
partial.render(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
context.template_name = old_template_name
|
context.template_name = old_template_name
|
||||||
context.partial = old_partial
|
context.partial = old_partial
|
||||||
end
|
end
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
alias_method :parse_context, :options
|
alias_method :parse_context, :options
|
||||||
private :parse_context
|
private :parse_context
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
def load_cached_partial(template_name, context)
|
||||||
def children
|
cached_partials = context.registers[:cached_partials] || {}
|
||||||
[
|
|
||||||
@node.template_name_expr,
|
if cached = cached_partials[template_name]
|
||||||
@node.variable_name_expr
|
return cached
|
||||||
] + @node.attributes.values
|
|
||||||
end
|
end
|
||||||
|
source = read_template_from_file_system(context)
|
||||||
|
begin
|
||||||
|
parse_context.partial = true
|
||||||
|
partial = Liquid::Template.parse(source, parse_context)
|
||||||
|
ensure
|
||||||
|
parse_context.partial = false
|
||||||
|
end
|
||||||
|
cached_partials[template_name] = partial
|
||||||
|
context.registers[:cached_partials] = cached_partials
|
||||||
|
partial
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_template_from_file_system(context)
|
||||||
|
file_system = context.registers[:file_system] || Liquid::Template.file_system
|
||||||
|
|
||||||
|
file_system.read_template_file(context.evaluate(@template_name_expr))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,10 @@ module Liquid
|
|||||||
@variable = markup.strip
|
@variable = markup.strip
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
value = context.environments.first[@variable] ||= 0
|
value = context.environments.first[@variable] ||= 0
|
||||||
context.environments.first[@variable] = value + 1
|
context.environments.first[@variable] = value + 1
|
||||||
|
value.to_s
|
||||||
output << value.to_s
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ module Liquid
|
|||||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
|
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(_context, output)
|
def render(_context)
|
||||||
output << @body
|
@body
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def nodelist
|
def nodelist
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
module Liquid
|
|
||||||
class Render < Tag
|
|
||||||
Syntax = /(#{QuotedString})#{QuotedFragment}*/o
|
|
||||||
|
|
||||||
attr_reader :template_name_expr, :attributes
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
|
||||||
super
|
|
||||||
|
|
||||||
raise SyntaxError.new(options[:locale].t("errors.syntax.render".freeze)) unless markup =~ Syntax
|
|
||||||
|
|
||||||
template_name = $1
|
|
||||||
|
|
||||||
@template_name_expr = Expression.parse(template_name)
|
|
||||||
|
|
||||||
@attributes = {}
|
|
||||||
markup.scan(TagAttributes) do |key, value|
|
|
||||||
@attributes[key] = Expression.parse(value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
# Though we evaluate this here we will only ever parse it as a string literal.
|
|
||||||
template_name = context.evaluate(@template_name_expr)
|
|
||||||
raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
|
|
||||||
|
|
||||||
partial = PartialCache.load(
|
|
||||||
template_name,
|
|
||||||
context: context,
|
|
||||||
parse_context: parse_context
|
|
||||||
)
|
|
||||||
|
|
||||||
inner_context = context.new_isolated_subcontext
|
|
||||||
inner_context.template_name = template_name
|
|
||||||
inner_context.partial = true
|
|
||||||
@attributes.each do |key, value|
|
|
||||||
inner_context[key] = context.evaluate(value)
|
|
||||||
end
|
|
||||||
partial.render_to_output_buffer(inner_context, output)
|
|
||||||
|
|
||||||
output
|
|
||||||
end
|
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
||||||
def children
|
|
||||||
[
|
|
||||||
@node.template_name_expr,
|
|
||||||
] + @node.attributes.values
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Template.register_tag('render'.freeze, Render)
|
|
||||||
end
|
|
||||||
@@ -2,8 +2,6 @@ module Liquid
|
|||||||
class TableRow < Block
|
class TableRow < Block
|
||||||
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
|
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
|
||||||
|
|
||||||
attr_reader :variable_name, :collection_name, :attributes
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
if markup =~ Syntax
|
if markup =~ Syntax
|
||||||
@@ -18,7 +16,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
collection = context.evaluate(@collection_name) or return ''.freeze
|
collection = context.evaluate(@collection_name) or return ''.freeze
|
||||||
|
|
||||||
from = @attributes.key?('offset'.freeze) ? context.evaluate(@attributes['offset'.freeze]).to_i : 0
|
from = @attributes.key?('offset'.freeze) ? context.evaluate(@attributes['offset'.freeze]).to_i : 0
|
||||||
@@ -30,34 +28,25 @@ module Liquid
|
|||||||
|
|
||||||
cols = context.evaluate(@attributes['cols'.freeze]).to_i
|
cols = context.evaluate(@attributes['cols'.freeze]).to_i
|
||||||
|
|
||||||
output << "<tr class=\"row1\">\n"
|
result = "<tr class=\"row1\">\n"
|
||||||
context.stack do
|
context.stack do
|
||||||
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
|
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
|
||||||
context['tablerowloop'.freeze] = tablerowloop
|
context['tablerowloop'.freeze] = tablerowloop
|
||||||
|
|
||||||
collection.each do |item|
|
collection.each_with_index do |item, index|
|
||||||
context[@variable_name] = item
|
context[@variable_name] = item
|
||||||
|
|
||||||
output << "<td class=\"col#{tablerowloop.col}\">"
|
result << "<td class=\"col#{tablerowloop.col}\">" << super << '</td>'
|
||||||
super
|
|
||||||
output << '</td>'
|
|
||||||
|
|
||||||
if tablerowloop.col_last && !tablerowloop.last
|
if tablerowloop.col_last && !tablerowloop.last
|
||||||
output << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
|
result << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
|
||||||
end
|
end
|
||||||
|
|
||||||
tablerowloop.send(:increment!)
|
tablerowloop.send(:increment!)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
result << "</tr>\n"
|
||||||
output << "</tr>\n"
|
result
|
||||||
output
|
|
||||||
end
|
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
||||||
def children
|
|
||||||
super + @node.attributes.values + [@node.collection_name]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -6,23 +6,23 @@ module Liquid
|
|||||||
# {% unless x < 0 %} x is greater than zero {% endunless %}
|
# {% unless x < 0 %} x is greater than zero {% endunless %}
|
||||||
#
|
#
|
||||||
class Unless < If
|
class Unless < If
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
context.stack do
|
context.stack do
|
||||||
# First condition is interpreted backwards ( if not )
|
# First condition is interpreted backwards ( if not )
|
||||||
first_block = @blocks.first
|
first_block = @blocks.first
|
||||||
unless first_block.evaluate(context)
|
unless first_block.evaluate(context)
|
||||||
return first_block.attachment.render_to_output_buffer(context, output)
|
return first_block.attachment.render(context)
|
||||||
end
|
end
|
||||||
|
|
||||||
# After the first condition unless works just like if
|
# After the first condition unless works just like if
|
||||||
@blocks[1..-1].each do |block|
|
@blocks[1..-1].each do |block|
|
||||||
if block.evaluate(context)
|
if block.evaluate(context)
|
||||||
return block.attachment.render_to_output_buffer(context, output)
|
return block.attachment.render(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
output
|
''.freeze
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ module Liquid
|
|||||||
private
|
private
|
||||||
|
|
||||||
def lookup_class(name)
|
def lookup_class(name)
|
||||||
Object.const_get(name)
|
name.split("::").reject(&:empty?).reduce(Object) { |scope, const| scope.const_get(const) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -187,12 +187,9 @@ module Liquid
|
|||||||
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
|
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
|
||||||
end
|
end
|
||||||
|
|
||||||
output = nil
|
|
||||||
|
|
||||||
case args.last
|
case args.last
|
||||||
when Hash
|
when Hash
|
||||||
options = args.pop
|
options = args.pop
|
||||||
output = options[:output] if options[:output]
|
|
||||||
|
|
||||||
registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
|
registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
|
||||||
|
|
||||||
@@ -207,9 +204,10 @@ module Liquid
|
|||||||
begin
|
begin
|
||||||
# render the nodelist.
|
# render the nodelist.
|
||||||
# for performance reasons we get an array back here. join will make a string out of it.
|
# for performance reasons we get an array back here. join will make a string out of it.
|
||||||
with_profiling(context) do
|
result = with_profiling(context) do
|
||||||
@root.render_to_output_buffer(context, output || '')
|
@root.render(context)
|
||||||
end
|
end
|
||||||
|
result.respond_to?(:join) ? result.join : result
|
||||||
rescue Liquid::MemoryError => e
|
rescue Liquid::MemoryError => e
|
||||||
context.handle_error(e)
|
context.handle_error(e)
|
||||||
ensure
|
ensure
|
||||||
@@ -222,10 +220,6 @@ module Liquid
|
|||||||
render(*args)
|
render(*args)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
render(context, output: output)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def tokenize(source)
|
def tokenize(source)
|
||||||
|
|||||||
@@ -1,31 +1,25 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class Tokenizer
|
class Tokenizer
|
||||||
attr_reader :line_number, :for_liquid_tag
|
attr_reader :line_number
|
||||||
|
|
||||||
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
|
def initialize(source, line_numbers = false)
|
||||||
@source = source
|
@source = source
|
||||||
@line_number = line_number || (line_numbers ? 1 : nil)
|
@line_number = line_numbers ? 1 : nil
|
||||||
@for_liquid_tag = for_liquid_tag
|
|
||||||
@tokens = tokenize
|
@tokens = tokenize
|
||||||
end
|
end
|
||||||
|
|
||||||
def shift
|
def shift
|
||||||
token = @tokens.shift or return
|
token = @tokens.shift
|
||||||
|
@line_number += token.count("\n") if @line_number && token
|
||||||
if @line_number
|
|
||||||
@line_number += @for_liquid_tag ? 1 : token.count("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
token
|
token
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def tokenize
|
def tokenize
|
||||||
|
@source = @source.source if @source.respond_to?(:source)
|
||||||
return [] if @source.to_s.empty?
|
return [] if @source.to_s.empty?
|
||||||
|
|
||||||
return @source.split("\n") if @for_liquid_tag
|
|
||||||
|
|
||||||
tokens = @source.split(TemplateParser)
|
tokens = @source.split(TemplateParser)
|
||||||
|
|
||||||
# removes the rogue empty element at the beginning of the array
|
# removes the rogue empty element at the beginning of the array
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
module Liquid
|
|
||||||
# Usage is used to store
|
|
||||||
class Usage
|
|
||||||
@messages = {}
|
|
||||||
class << self
|
|
||||||
def enable
|
|
||||||
Liquid::Context.send(:alias_method, :try_variable_find_in_environments, :try_variable_find_in_environments_usage)
|
|
||||||
end
|
|
||||||
|
|
||||||
def disable
|
|
||||||
Liquid::Context.send(:alias_method, :try_variable_find_in_environments, :try_variable_find_in_environments_original)
|
|
||||||
end
|
|
||||||
|
|
||||||
def track(message)
|
|
||||||
@messages[message] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
def results
|
|
||||||
@messages
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
module Liquid
|
|
||||||
class Context
|
|
||||||
alias try_variable_find_in_environments_original try_variable_find_in_environments
|
|
||||||
|
|
||||||
def try_variable_find_in_environments_usage(key, raise_on_not_found:)
|
|
||||||
Usage.track("Using try_variable_find_in_environment")
|
|
||||||
@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
|
|
||||||
|
|
||||||
Usage.track("try_variable_find_in_environment reports Nil but responds to key") if environment.key?(key)
|
|
||||||
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
|
|
||||||
|
|
||||||
Usage.track("try_variable_find_in_environment reports Nil but responds to key") if environment.key?(key)
|
|
||||||
end
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Liquid::Usage.track("Usage is enabled")
|
|
||||||
@@ -46,11 +46,11 @@ module Liquid
|
|||||||
def self.to_number(obj)
|
def self.to_number(obj)
|
||||||
case obj
|
case obj
|
||||||
when Float
|
when Float
|
||||||
BigDecimal(obj.to_s)
|
BigDecimal.new(obj.to_s)
|
||||||
when Numeric
|
when Numeric
|
||||||
obj
|
obj
|
||||||
when String
|
when String
|
||||||
(obj.strip =~ /\A-?\d+\.\d+\z/) ? BigDecimal(obj) : obj.to_i
|
(obj.strip =~ /\A-?\d+\.\d+\z/) ? BigDecimal.new(obj) : obj.to_i
|
||||||
else
|
else
|
||||||
if obj.respond_to?(:to_number)
|
if obj.respond_to?(:to_number)
|
||||||
obj.to_number
|
obj.to_number
|
||||||
|
|||||||
@@ -10,16 +10,10 @@ module Liquid
|
|||||||
# {{ user | link }}
|
# {{ user | link }}
|
||||||
#
|
#
|
||||||
class Variable
|
class Variable
|
||||||
FilterMarkupRegex = /#{FilterSeparator}\s*(.*)/om
|
|
||||||
FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
|
FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
|
||||||
FilterArgsRegex = /(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o
|
|
||||||
JustTagAttributes = /\A#{TagAttributes}\z/o
|
|
||||||
MarkupWithQuotedFragment = /(#{QuotedFragment})(.*)/om
|
|
||||||
|
|
||||||
attr_accessor :filters, :name, :line_number
|
attr_accessor :filters, :name, :line_number
|
||||||
attr_reader :parse_context
|
attr_reader :parse_context
|
||||||
alias_method :options, :parse_context
|
alias_method :options, :parse_context
|
||||||
|
|
||||||
include ParserSwitching
|
include ParserSwitching
|
||||||
|
|
||||||
def initialize(markup, parse_context)
|
def initialize(markup, parse_context)
|
||||||
@@ -41,17 +35,17 @@ module Liquid
|
|||||||
|
|
||||||
def lax_parse(markup)
|
def lax_parse(markup)
|
||||||
@filters = []
|
@filters = []
|
||||||
return unless markup =~ MarkupWithQuotedFragment
|
return unless markup =~ /(#{QuotedFragment})(.*)/om
|
||||||
|
|
||||||
name_markup = $1
|
name_markup = $1
|
||||||
filter_markup = $2
|
filter_markup = $2
|
||||||
@name = Expression.parse(name_markup)
|
@name = Expression.parse(name_markup)
|
||||||
if filter_markup =~ FilterMarkupRegex
|
if filter_markup =~ /#{FilterSeparator}\s*(.*)/om
|
||||||
filters = $1.scan(FilterParser)
|
filters = $1.scan(FilterParser)
|
||||||
filters.each do |f|
|
filters.each do |f|
|
||||||
next unless f =~ /\w+/
|
next unless f =~ /\w+/
|
||||||
filtername = Regexp.last_match(0)
|
filtername = Regexp.last_match(0)
|
||||||
filterargs = f.scan(FilterArgsRegex).flatten
|
filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o).flatten
|
||||||
@filters << parse_filter_expressions(filtername, filterargs)
|
@filters << parse_filter_expressions(filtername, filterargs)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -85,38 +79,26 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
obj = context.apply_global_filter(obj)
|
obj = context.apply_global_filter(obj)
|
||||||
|
|
||||||
taint_check(context, obj)
|
taint_check(context, obj)
|
||||||
|
|
||||||
obj
|
obj
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
|
||||||
obj = render(context)
|
|
||||||
|
|
||||||
if obj.is_a?(Array)
|
|
||||||
output << obj.join
|
|
||||||
elsif obj.nil?
|
|
||||||
else
|
|
||||||
output << obj.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
output
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def parse_filter_expressions(filter_name, unparsed_args)
|
def parse_filter_expressions(filter_name, unparsed_args)
|
||||||
filter_args = []
|
filter_args = []
|
||||||
keyword_args = nil
|
keyword_args = {}
|
||||||
unparsed_args.each do |a|
|
unparsed_args.each do |a|
|
||||||
if matches = a.match(JustTagAttributes)
|
if matches = a.match(/\A#{TagAttributes}\z/o)
|
||||||
keyword_args ||= {}
|
|
||||||
keyword_args[matches[1]] = Expression.parse(matches[2])
|
keyword_args[matches[1]] = Expression.parse(matches[2])
|
||||||
else
|
else
|
||||||
filter_args << Expression.parse(a)
|
filter_args << Expression.parse(a)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
result = [filter_name, filter_args]
|
result = [filter_name, filter_args]
|
||||||
result << keyword_args if keyword_args
|
result << keyword_args unless keyword_args.empty?
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -150,11 +132,5 @@ module Liquid
|
|||||||
raise error
|
raise error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
||||||
def children
|
|
||||||
[@node.name] + @node.filters.flatten
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class VariableLookup
|
class VariableLookup
|
||||||
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
|
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
|
||||||
COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze].freeze
|
COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze]
|
||||||
|
|
||||||
attr_reader :name, :lookups
|
attr_reader :name, :lookups
|
||||||
|
|
||||||
@@ -78,11 +78,5 @@ module Liquid
|
|||||||
def state
|
def state
|
||||||
[@name, @lookups, @command_flags]
|
[@name, @lookups, @command_flags]
|
||||||
end
|
end
|
||||||
|
|
||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
|
||||||
def children
|
|
||||||
@node.lookups
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
VERSION = "4.0.3".freeze
|
VERSION = "4.0.0"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
lib = File.expand_path('../lib/', __FILE__)
|
lib = File.expand_path('../lib/', __FILE__)
|
||||||
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
|
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ Gem::Specification.new do |s|
|
|||||||
s.license = "MIT"
|
s.license = "MIT"
|
||||||
# s.description = "A secure, non-evaling end user template engine with aesthetic markup."
|
# s.description = "A secure, non-evaling end user template engine with aesthetic markup."
|
||||||
|
|
||||||
s.required_ruby_version = ">= 2.4.0"
|
s.required_ruby_version = ">= 2.1.0"
|
||||||
s.required_rubygems_version = ">= 1.3.7"
|
s.required_rubygems_version = ">= 1.3.7"
|
||||||
|
|
||||||
s.test_files = Dir.glob("{test}/**/*")
|
s.test_files = Dir.glob("{test}/**/*")
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'benchmark/ips'
|
|
||||||
require 'memory_profiler'
|
|
||||||
require 'terminal-table'
|
|
||||||
require_relative 'theme_runner'
|
|
||||||
|
|
||||||
class Profiler
|
|
||||||
LOG_LABEL = "Profiling: ".rjust(14).freeze
|
|
||||||
REPORTS_DIR = File.expand_path('.memprof', __dir__).freeze
|
|
||||||
|
|
||||||
def self.run
|
|
||||||
puts
|
|
||||||
yield new
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@allocated = []
|
|
||||||
@retained = []
|
|
||||||
@headings = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def profile(phase, &block)
|
|
||||||
print LOG_LABEL
|
|
||||||
print "#{phase}.. ".ljust(10)
|
|
||||||
report = MemoryProfiler.report(&block)
|
|
||||||
puts 'Done.'
|
|
||||||
@headings << phase.capitalize
|
|
||||||
@allocated << "#{report.scale_bytes(report.total_allocated_memsize)} (#{report.total_allocated} objects)"
|
|
||||||
@retained << "#{report.scale_bytes(report.total_retained_memsize)} (#{report.total_retained} objects)"
|
|
||||||
|
|
||||||
return if ENV['CI']
|
|
||||||
|
|
||||||
require 'fileutils'
|
|
||||||
report_file = File.join(REPORTS_DIR, "#{sanitize(phase)}.txt")
|
|
||||||
FileUtils.mkdir_p(REPORTS_DIR)
|
|
||||||
report.pretty_print(to_file: report_file, scale_bytes: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def tabulate
|
|
||||||
table = Terminal::Table.new(headings: @headings.unshift('Phase')) do |t|
|
|
||||||
t << @allocated.unshift('Total allocated')
|
|
||||||
t << @retained.unshift('Total retained')
|
|
||||||
end
|
|
||||||
|
|
||||||
puts
|
|
||||||
puts table
|
|
||||||
puts "\nDetailed report(s) saved to #{REPORTS_DIR}/" unless ENV['CI']
|
|
||||||
end
|
|
||||||
|
|
||||||
def sanitize(string)
|
|
||||||
string.downcase.gsub(/[\W]/, '-').squeeze('-')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
|
|
||||||
|
|
||||||
runner = ThemeRunner.new
|
|
||||||
Profiler.run do |x|
|
|
||||||
x.profile('parse') { runner.compile }
|
|
||||||
x.profile('render') { runner.render }
|
|
||||||
x.tabulate
|
|
||||||
end
|
|
||||||
@@ -12,7 +12,7 @@ class CommentForm < Liquid::Block
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
article = context[@variable_name]
|
article = context[@variable_name]
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
@@ -23,9 +23,7 @@ class CommentForm < Liquid::Block
|
|||||||
'email' => context['comment.email'],
|
'email' => context['comment.email'],
|
||||||
'body' => context['comment.body']
|
'body' => context['comment.body']
|
||||||
}
|
}
|
||||||
|
wrap_in_form(article, render_all(@nodelist, context))
|
||||||
output << wrap_in_form(article, render_all(@nodelist, context, output))
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class Paginate < Liquid::Block
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
@context = context
|
@context = context
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class FoobarTag < Liquid::Tag
|
class FoobarTag < Liquid::Tag
|
||||||
def render_to_output_buffer(context, output)
|
def render(*args)
|
||||||
output << ' '
|
" "
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Liquid::Template.register_tag('foobar', FoobarTag)
|
||||||
end
|
end
|
||||||
|
|
||||||
class BlankTestFileSystem
|
class BlankTestFileSystem
|
||||||
@@ -30,9 +31,7 @@ class BlankTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_new_tags_are_not_blank_by_default
|
def test_new_tags_are_not_blank_by_default
|
||||||
with_custom_tag('foobar', FoobarTag) do
|
assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
|
||||||
assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_loops_are_blank
|
def test_loops_are_blank
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class BlockTest < Minitest::Test
|
|
||||||
include Liquid
|
|
||||||
|
|
||||||
def test_unexpected_end_tag
|
|
||||||
exc = assert_raises(SyntaxError) do
|
|
||||||
Template.parse("{% if true %}{% endunless %}")
|
|
||||||
end
|
|
||||||
assert_equal exc.message, "Liquid syntax error: 'endunless' is not a valid delimiter for if tags. use endif"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -123,7 +123,7 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
',
|
',
|
||||||
error_mode: :warn,
|
error_mode: :warn,
|
||||||
line_numbers: true
|
line_numbers: true
|
||||||
)
|
)
|
||||||
|
|
||||||
assert_equal ['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
|
assert_equal ['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
|
||||||
template.warnings.map(&:message)
|
template.warnings.map(&:message)
|
||||||
@@ -140,7 +140,7 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
',
|
',
|
||||||
error_mode: :strict,
|
error_mode: :strict,
|
||||||
line_numbers: true
|
line_numbers: true
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal 'Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message
|
assert_equal 'Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message
|
||||||
@@ -158,7 +158,7 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
bla
|
bla
|
||||||
',
|
',
|
||||||
line_numbers: true
|
line_numbers: true
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal "Liquid syntax error (line 5): Unknown tag 'foo'", err.message
|
assert_equal "Liquid syntax error (line 5): Unknown tag 'foo'", err.message
|
||||||
|
|||||||
@@ -1,247 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class ParseTreeVisitorTest < Minitest::Test
|
|
||||||
include Liquid
|
|
||||||
|
|
||||||
def test_variable
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({{ test }}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_varible_with_filter
|
|
||||||
assert_equal(
|
|
||||||
["test", "infilter"],
|
|
||||||
visit(%({{ test | split: infilter }}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_dynamic_variable
|
|
||||||
assert_equal(
|
|
||||||
["test", "inlookup"],
|
|
||||||
visit(%({{ test[inlookup] }}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_if_condition
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% if test %}{% endif %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_complex_if_condition
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% if 1 == 1 and 2 == test %}{% endif %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_if_body
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% if 1 == 1 %}{{ test }}{% endif %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unless_condition
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% unless test %}{% endunless %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_complex_unless_condition
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% unless 1 == 1 and 2 == test %}{% endunless %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_unless_body
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% unless 1 == 1 %}{{ test }}{% endunless %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_elsif_condition
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% if 1 == 1 %}{% elsif test %}{% endif %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_complex_elsif_condition
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% if 1 == 1 %}{% elsif 1 == 1 and 2 == test %}{% endif %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_elsif_body
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% if 1 == 1 %}{% elsif 2 == 2 %}{{ test }}{% endif %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_else_body
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% if 1 == 1 %}{% else %}{{ test }}{% endif %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_case_left
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% case test %}{% endcase %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_case_condition
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% case 1 %}{% when test %}{% endcase %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_case_when_body
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% case 1 %}{% when 2 %}{{ test }}{% endcase %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_case_else_body
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% case 1 %}{% else %}{{ test }}{% endcase %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_for_in
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% for x in test %}{% endfor %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_for_limit
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% for x in (1..5) limit: test %}{% endfor %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_for_offset
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% for x in (1..5) offset: test %}{% endfor %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_for_body
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% for x in (1..5) %}{{ test }}{% endfor %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_tablerow_in
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% tablerow x in test %}{% endtablerow %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_tablerow_limit
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% tablerow x in (1..5) limit: test %}{% endtablerow %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_tablerow_offset
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% tablerow x in (1..5) offset: test %}{% endtablerow %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_tablerow_body
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% tablerow x in (1..5) %}{{ test }}{% endtablerow %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_cycle
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% cycle test %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_assign
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% assign x = test %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_capture
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% capture x %}{{ test }}{% endcapture %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_include
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% include test %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_include_with
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% include "hai" with test %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_include_for
|
|
||||||
assert_equal(
|
|
||||||
["test"],
|
|
||||||
visit(%({% include "hai" for test %}))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_preserve_tree_structure
|
|
||||||
assert_equal(
|
|
||||||
[[nil, [
|
|
||||||
[nil, [[nil, [["other", []]]]]],
|
|
||||||
["test", []],
|
|
||||||
["xs", []]
|
|
||||||
]]],
|
|
||||||
traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def traversal(template)
|
|
||||||
ParseTreeVisitor
|
|
||||||
.for(Template.parse(template).root)
|
|
||||||
.add_callback_for(VariableLookup, &:name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def visit(template)
|
|
||||||
traversal(template).visit.flatten.compact
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -99,7 +99,7 @@ class ParsingQuirksTest < Minitest::Test
|
|||||||
# After the messed up quotes a filter without parameters (reverse) should work
|
# After the messed up quotes a filter without parameters (reverse) should work
|
||||||
# but one with parameters (remove) shouldn't be detected.
|
# but one with parameters (remove) shouldn't be detected.
|
||||||
assert_template_result('here', "{{ 'hi there' | split:\"t\"\" | reverse | first}}")
|
assert_template_result('here', "{{ 'hi there' | split:\"t\"\" | reverse | first}}")
|
||||||
assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}")
|
assert_template_result('hi ', "{{ 'hi there' | split:\"t\"\" | remove:\"i\" | first}}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -158,10 +158,6 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal '1', @filters.url_decode(1)
|
assert_equal '1', @filters.url_decode(1)
|
||||||
assert_equal '2001-02-03', @filters.url_decode(Date.new(2001, 2, 3))
|
assert_equal '2001-02-03', @filters.url_decode(Date.new(2001, 2, 3))
|
||||||
assert_nil @filters.url_decode(nil)
|
assert_nil @filters.url_decode(nil)
|
||||||
exception = assert_raises Liquid::ArgumentError do
|
|
||||||
@filters.url_decode('%ff')
|
|
||||||
end
|
|
||||||
assert_equal 'Liquid error: invalid byte sequence in UTF-8', exception.message
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_truncatewords
|
def test_truncatewords
|
||||||
@@ -181,9 +177,6 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal 'test', @filters.strip_html("<div\nclass='multiline'>test</div>")
|
assert_equal 'test', @filters.strip_html("<div\nclass='multiline'>test</div>")
|
||||||
assert_equal 'test', @filters.strip_html("<!-- foo bar \n test -->test")
|
assert_equal 'test', @filters.strip_html("<!-- foo bar \n test -->test")
|
||||||
assert_equal '', @filters.strip_html(nil)
|
assert_equal '', @filters.strip_html(nil)
|
||||||
|
|
||||||
# Quirk of the existing implementation
|
|
||||||
assert_equal 'foo;', @filters.strip_html("<<<script </script>script>foo;</script>")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_join
|
def test_join
|
||||||
@@ -197,11 +190,6 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
|
assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], @filters.sort([{ "a" => 4 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sort_with_nils
|
|
||||||
assert_equal [1, 2, 3, 4, nil], @filters.sort([nil, 4, 3, 2, 1])
|
|
||||||
assert_equal [{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }, {}], @filters.sort([{ "a" => 4 }, { "a" => 3 }, {}, { "a" => 1 }, { "a" => 2 }], "a")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sort_when_property_is_sometimes_missing_puts_nils_last
|
def test_sort_when_property_is_sometimes_missing_puts_nils_last
|
||||||
input = [
|
input = [
|
||||||
{ "price" => 4, "handle" => "alpha" },
|
{ "price" => 4, "handle" => "alpha" },
|
||||||
@@ -220,89 +208,14 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal expectation, @filters.sort(input, "price")
|
assert_equal expectation, @filters.sort(input, "price")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sort_natural
|
|
||||||
assert_equal ["a", "B", "c", "D"], @filters.sort_natural(["c", "D", "a", "B"])
|
|
||||||
assert_equal [{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, { "a" => "a" }, { "a" => "B" }], "a")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sort_natural_with_nils
|
|
||||||
assert_equal ["a", "B", "c", "D", nil], @filters.sort_natural([nil, "c", "D", "a", "B"])
|
|
||||||
assert_equal [{ "a" => "a" }, { "a" => "B" }, { "a" => "c" }, { "a" => "D" }, {}], @filters.sort_natural([{ "a" => "D" }, { "a" => "c" }, {}, { "a" => "a" }, { "a" => "B" }], "a")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sort_natural_when_property_is_sometimes_missing_puts_nils_last
|
|
||||||
input = [
|
|
||||||
{ "price" => "4", "handle" => "alpha" },
|
|
||||||
{ "handle" => "beta" },
|
|
||||||
{ "price" => "1", "handle" => "gamma" },
|
|
||||||
{ "handle" => "delta" },
|
|
||||||
{ "price" => 2, "handle" => "epsilon" }
|
|
||||||
]
|
|
||||||
expectation = [
|
|
||||||
{ "price" => "1", "handle" => "gamma" },
|
|
||||||
{ "price" => 2, "handle" => "epsilon" },
|
|
||||||
{ "price" => "4", "handle" => "alpha" },
|
|
||||||
{ "handle" => "delta" },
|
|
||||||
{ "handle" => "beta" }
|
|
||||||
]
|
|
||||||
assert_equal expectation, @filters.sort_natural(input, "price")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sort_natural_case_check
|
|
||||||
input = [
|
|
||||||
{ "key" => "X" },
|
|
||||||
{ "key" => "Y" },
|
|
||||||
{ "key" => "Z" },
|
|
||||||
{ "fake" => "t" },
|
|
||||||
{ "key" => "a" },
|
|
||||||
{ "key" => "b" },
|
|
||||||
{ "key" => "c" }
|
|
||||||
]
|
|
||||||
expectation = [
|
|
||||||
{ "key" => "a" },
|
|
||||||
{ "key" => "b" },
|
|
||||||
{ "key" => "c" },
|
|
||||||
{ "key" => "X" },
|
|
||||||
{ "key" => "Y" },
|
|
||||||
{ "key" => "Z" },
|
|
||||||
{ "fake" => "t" }
|
|
||||||
]
|
|
||||||
assert_equal expectation, @filters.sort_natural(input, "key")
|
|
||||||
assert_equal ["a", "b", "c", "X", "Y", "Z"], @filters.sort_natural(["X", "Y", "Z", "a", "b", "c"])
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sort_empty_array
|
def test_sort_empty_array
|
||||||
assert_equal [], @filters.sort([], "a")
|
assert_equal [], @filters.sort([], "a")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sort_invalid_property
|
|
||||||
foo = [
|
|
||||||
[1],
|
|
||||||
[2],
|
|
||||||
[3]
|
|
||||||
]
|
|
||||||
|
|
||||||
assert_raises Liquid::ArgumentError do
|
|
||||||
@filters.sort(foo, "bar")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sort_natural_empty_array
|
def test_sort_natural_empty_array
|
||||||
assert_equal [], @filters.sort_natural([], "a")
|
assert_equal [], @filters.sort_natural([], "a")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sort_natural_invalid_property
|
|
||||||
foo = [
|
|
||||||
[1],
|
|
||||||
[2],
|
|
||||||
[3]
|
|
||||||
]
|
|
||||||
|
|
||||||
assert_raises Liquid::ArgumentError do
|
|
||||||
@filters.sort_natural(foo, "bar")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_legacy_sort_hash
|
def test_legacy_sort_hash
|
||||||
assert_equal [{ a: 1, b: 2 }], @filters.sort({ a: 1, b: 2 })
|
assert_equal [{ a: 1, b: 2 }], @filters.sort({ a: 1, b: 2 })
|
||||||
end
|
end
|
||||||
@@ -326,34 +239,10 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal [], @filters.uniq([], "a")
|
assert_equal [], @filters.uniq([], "a")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_uniq_invalid_property
|
|
||||||
foo = [
|
|
||||||
[1],
|
|
||||||
[2],
|
|
||||||
[3]
|
|
||||||
]
|
|
||||||
|
|
||||||
assert_raises Liquid::ArgumentError do
|
|
||||||
@filters.uniq(foo, "bar")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_compact_empty_array
|
def test_compact_empty_array
|
||||||
assert_equal [], @filters.compact([], "a")
|
assert_equal [], @filters.compact([], "a")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_compact_invalid_property
|
|
||||||
foo = [
|
|
||||||
[1],
|
|
||||||
[2],
|
|
||||||
[3]
|
|
||||||
]
|
|
||||||
|
|
||||||
assert_raises Liquid::ArgumentError do
|
|
||||||
@filters.compact(foo, "bar")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_reverse
|
def test_reverse
|
||||||
assert_equal [4, 3, 2, 1], @filters.reverse([1, 2, 3, 4])
|
assert_equal [4, 3, 2, 1], @filters.reverse([1, 2, 3, 4])
|
||||||
end
|
end
|
||||||
@@ -419,29 +308,6 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new
|
assert_template_result "123", '{{ foo | map: "foo" }}', "foo" => TestEnumerable.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_map_returns_empty_on_2d_input_array
|
|
||||||
foo = [
|
|
||||||
[1],
|
|
||||||
[2],
|
|
||||||
[3]
|
|
||||||
]
|
|
||||||
|
|
||||||
assert_raises Liquid::ArgumentError do
|
|
||||||
@filters.map(foo, "bar")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_map_returns_empty_with_no_property
|
|
||||||
foo = [
|
|
||||||
[1],
|
|
||||||
[2],
|
|
||||||
[3]
|
|
||||||
]
|
|
||||||
assert_raises Liquid::ArgumentError do
|
|
||||||
@filters.map(foo, nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sort_works_on_enumerables
|
def test_sort_works_on_enumerables
|
||||||
assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new
|
assert_template_result "213", '{{ foo | sort: "bar" | map: "foo" }}', "foo" => TestEnumerable.new
|
||||||
end
|
end
|
||||||
@@ -472,9 +338,9 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
|
assert_equal '07/05/2006', @filters.date("2006-07-05 10:00:00", "%m/%d/%Y")
|
||||||
|
|
||||||
assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
|
assert_equal "07/16/2004", @filters.date("Fri Jul 16 01:00:00 2004", "%m/%d/%Y")
|
||||||
assert_equal Date.today.year.to_s, @filters.date('now', '%Y')
|
assert_equal "#{Date.today.year}", @filters.date('now', '%Y')
|
||||||
assert_equal Date.today.year.to_s, @filters.date('today', '%Y')
|
assert_equal "#{Date.today.year}", @filters.date('today', '%Y')
|
||||||
assert_equal Date.today.year.to_s, @filters.date('Today', '%Y')
|
assert_equal "#{Date.today.year}", @filters.date('Today', '%Y')
|
||||||
|
|
||||||
assert_nil @filters.date(nil, "%B")
|
assert_nil @filters.date(nil, "%B")
|
||||||
|
|
||||||
@@ -630,28 +496,6 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_template_result "5", "{{ price | floor }}", 'price' => NumberLikeThing.new(5.4)
|
assert_template_result "5", "{{ price | floor }}", 'price' => NumberLikeThing.new(5.4)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_at_most
|
|
||||||
assert_template_result "4", "{{ 5 | at_most:4 }}"
|
|
||||||
assert_template_result "5", "{{ 5 | at_most:5 }}"
|
|
||||||
assert_template_result "5", "{{ 5 | at_most:6 }}"
|
|
||||||
|
|
||||||
assert_template_result "4.5", "{{ 4.5 | at_most:5 }}"
|
|
||||||
assert_template_result "5", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(6)
|
|
||||||
assert_template_result "4", "{{ width | at_most:5 }}", 'width' => NumberLikeThing.new(4)
|
|
||||||
assert_template_result "4", "{{ 5 | at_most: width }}", 'width' => NumberLikeThing.new(4)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_at_least
|
|
||||||
assert_template_result "5", "{{ 5 | at_least:4 }}"
|
|
||||||
assert_template_result "5", "{{ 5 | at_least:5 }}"
|
|
||||||
assert_template_result "6", "{{ 5 | at_least:6 }}"
|
|
||||||
|
|
||||||
assert_template_result "5", "{{ 4.5 | at_least:5 }}"
|
|
||||||
assert_template_result "6", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(6)
|
|
||||||
assert_template_result "5", "{{ width | at_least:5 }}", 'width' => NumberLikeThing.new(4)
|
|
||||||
assert_template_result "6", "{{ 5 | at_least: width }}", 'width' => NumberLikeThing.new(6)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_append
|
def test_append
|
||||||
assigns = { 'a' => 'bc', 'b' => 'd' }
|
assigns = { 'a' => 'bc', 'b' => 'd' }
|
||||||
assert_template_result('bcd', "{{ a | append: 'd'}}", assigns)
|
assert_template_result('bcd', "{{ a | append: 'd'}}", assigns)
|
||||||
@@ -692,78 +536,6 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_template_result('abc', "{{ 'abc' | date: '%D' }}")
|
assert_template_result('abc', "{{ 'abc' | date: '%D' }}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_where
|
|
||||||
input = [
|
|
||||||
{ "handle" => "alpha", "ok" => true },
|
|
||||||
{ "handle" => "beta", "ok" => false },
|
|
||||||
{ "handle" => "gamma", "ok" => false },
|
|
||||||
{ "handle" => "delta", "ok" => true }
|
|
||||||
]
|
|
||||||
|
|
||||||
expectation = [
|
|
||||||
{ "handle" => "alpha", "ok" => true },
|
|
||||||
{ "handle" => "delta", "ok" => true }
|
|
||||||
]
|
|
||||||
|
|
||||||
assert_equal expectation, @filters.where(input, "ok", true)
|
|
||||||
assert_equal expectation, @filters.where(input, "ok")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_where_no_key_set
|
|
||||||
input = [
|
|
||||||
{ "handle" => "alpha", "ok" => true },
|
|
||||||
{ "handle" => "beta" },
|
|
||||||
{ "handle" => "gamma" },
|
|
||||||
{ "handle" => "delta", "ok" => true }
|
|
||||||
]
|
|
||||||
|
|
||||||
expectation = [
|
|
||||||
{ "handle" => "alpha", "ok" => true },
|
|
||||||
{ "handle" => "delta", "ok" => true }
|
|
||||||
]
|
|
||||||
|
|
||||||
assert_equal expectation, @filters.where(input, "ok", true)
|
|
||||||
assert_equal expectation, @filters.where(input, "ok")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_where_non_array_map_input
|
|
||||||
assert_equal [{ "a" => "ok" }], @filters.where({ "a" => "ok" }, "a", "ok")
|
|
||||||
assert_equal [], @filters.where({ "a" => "not ok" }, "a", "ok")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_where_indexable_but_non_map_value
|
|
||||||
assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok", true) }
|
|
||||||
assert_raises(Liquid::ArgumentError) { @filters.where(1, "ok") }
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_where_non_boolean_value
|
|
||||||
input = [
|
|
||||||
{ "message" => "Bonjour!", "language" => "French" },
|
|
||||||
{ "message" => "Hello!", "language" => "English" },
|
|
||||||
{ "message" => "Hallo!", "language" => "German" }
|
|
||||||
]
|
|
||||||
|
|
||||||
assert_equal [{ "message" => "Bonjour!", "language" => "French" }], @filters.where(input, "language", "French")
|
|
||||||
assert_equal [{ "message" => "Hallo!", "language" => "German" }], @filters.where(input, "language", "German")
|
|
||||||
assert_equal [{ "message" => "Hello!", "language" => "English" }], @filters.where(input, "language", "English")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_where_array_of_only_unindexable_values
|
|
||||||
assert_nil @filters.where([nil], "ok", true)
|
|
||||||
assert_nil @filters.where([nil], "ok")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_where_no_target_value
|
|
||||||
input = [
|
|
||||||
{ "foo" => false },
|
|
||||||
{ "foo" => true },
|
|
||||||
{ "foo" => "for sure" },
|
|
||||||
{ "bar" => true }
|
|
||||||
]
|
|
||||||
|
|
||||||
assert_equal [{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo")
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def with_timezone(tz)
|
def with_timezone(tz)
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class EchoTest < Minitest::Test
|
|
||||||
include Liquid
|
|
||||||
|
|
||||||
def test_echo_outputs_its_input
|
|
||||||
assert_template_result('BAR', <<~LIQUID, { 'variable-name' => 'bar' })
|
|
||||||
{%- echo variable-name | upcase -%}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -48,6 +48,10 @@ HERE
|
|||||||
|
|
||||||
def test_for_with_variable_range
|
def test_for_with_variable_range
|
||||||
assert_template_result(' 1 2 3 ', '{%for item in (1..foobar) %} {{item}} {%endfor%}', "foobar" => 3)
|
assert_template_result(' 1 2 3 ', '{%for item in (1..foobar) %} {{item}} {%endfor%}', "foobar" => 3)
|
||||||
|
assert_template_result(' 1.0 2.0 3.0 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1..3.0))
|
||||||
|
assert_template_result(' 1.0 2.0 3.0 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1.0..3))
|
||||||
|
assert_template_result(' 1.0 2.0 3.0 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1.0..3.0))
|
||||||
|
assert_template_result(' 1.5 2.5 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1.5..3))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_with_hash_value_range
|
def test_for_with_hash_value_range
|
||||||
@@ -103,34 +107,6 @@ HERE
|
|||||||
assert_template_result('3456', '{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}', assigns)
|
assert_template_result('3456', '{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}', assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_limiting_with_invalid_limit
|
|
||||||
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
|
||||||
template = <<-MKUP
|
|
||||||
{% for i in array limit: true offset: 1 %}
|
|
||||||
{{ i }}
|
|
||||||
{% endfor %}
|
|
||||||
MKUP
|
|
||||||
|
|
||||||
exception = assert_raises(Liquid::ArgumentError) do
|
|
||||||
Template.parse(template).render!(assigns)
|
|
||||||
end
|
|
||||||
assert_equal("Liquid error: invalid integer", exception.message)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_limiting_with_invalid_offset
|
|
||||||
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
|
||||||
template = <<-MKUP
|
|
||||||
{% for i in array limit: 1 offset: true %}
|
|
||||||
{{ i }}
|
|
||||||
{% endfor %}
|
|
||||||
MKUP
|
|
||||||
|
|
||||||
exception = assert_raises(Liquid::ArgumentError) do
|
|
||||||
Template.parse(template).render!(assigns)
|
|
||||||
end
|
|
||||||
assert_equal("Liquid error: invalid integer", exception.message)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_dynamic_variable_limiting
|
def test_dynamic_variable_limiting
|
||||||
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
assigns = { 'array' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] }
|
||||||
assigns['limit'] = 2
|
assigns['limit'] = 2
|
||||||
@@ -187,7 +163,7 @@ HERE
|
|||||||
assert_template_result(expected, markup, assigns)
|
assert_template_result(expected, markup, assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_pause_resume_big_limit
|
def test_pause_resume_BIG_limit
|
||||||
assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
|
assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
|
||||||
markup = <<-MKUP
|
markup = <<-MKUP
|
||||||
{%for i in array.items limit:3 %}{{i}}{%endfor%}
|
{%for i in array.items limit:3 %}{{i}}{%endfor%}
|
||||||
@@ -206,7 +182,7 @@ HERE
|
|||||||
assert_template_result(expected, markup, assigns)
|
assert_template_result(expected, markup, assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_pause_resume_big_offset
|
def test_pause_resume_BIG_offset
|
||||||
assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
|
assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] } }
|
||||||
markup = '{%for i in array.items limit:3 %}{{i}}{%endfor%}
|
markup = '{%for i in array.items limit:3 %}{{i}}{%endfor%}
|
||||||
next
|
next
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ class TestFileSystem
|
|||||||
when 'assignments'
|
when 'assignments'
|
||||||
"{% assign foo = 'bar' %}"
|
"{% assign foo = 'bar' %}"
|
||||||
|
|
||||||
when 'break'
|
|
||||||
"{% break %}"
|
|
||||||
|
|
||||||
else
|
else
|
||||||
template_path
|
template_path
|
||||||
end
|
end
|
||||||
@@ -66,9 +63,8 @@ class CustomInclude < Liquid::Tag
|
|||||||
def parse(tokens)
|
def parse(tokens)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render(context)
|
||||||
output << @template_name[1..-2]
|
@template_name[1..-2]
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -246,9 +242,4 @@ class IncludeTagTest < Minitest::Test
|
|||||||
|
|
||||||
assert_equal [], template.errors
|
assert_equal [], template.errors
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_break_through_include
|
|
||||||
assert_template_result "1", "{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}"
|
|
||||||
assert_template_result "1", "{% for i in (1..3) %}{{ i }}{% include 'break' %}{{ i }}{% endfor %}"
|
|
||||||
end
|
|
||||||
end # IncludeTagTest
|
end # IncludeTagTest
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class LiquidTagTest < Minitest::Test
|
|
||||||
include Liquid
|
|
||||||
|
|
||||||
def test_liquid_tag
|
|
||||||
assert_template_result('1 2 3', <<~LIQUID, 'array' => [1, 2, 3])
|
|
||||||
{%- liquid
|
|
||||||
echo array | join: " "
|
|
||||||
-%}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
assert_template_result('1 2 3', <<~LIQUID, 'array' => [1, 2, 3])
|
|
||||||
{%- liquid
|
|
||||||
for value in array
|
|
||||||
echo value
|
|
||||||
unless forloop.last
|
|
||||||
echo " "
|
|
||||||
endunless
|
|
||||||
endfor
|
|
||||||
-%}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
assert_template_result('4 8 12 6', <<~LIQUID, 'array' => [1, 2, 3])
|
|
||||||
{%- liquid
|
|
||||||
for value in array
|
|
||||||
assign double_value = value | times: 2
|
|
||||||
echo double_value | times: 2
|
|
||||||
unless forloop.last
|
|
||||||
echo " "
|
|
||||||
endunless
|
|
||||||
endfor
|
|
||||||
|
|
||||||
echo " "
|
|
||||||
echo double_value
|
|
||||||
-%}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
assert_template_result('abc', <<~LIQUID)
|
|
||||||
{%- liquid echo "a" -%}
|
|
||||||
b
|
|
||||||
{%- liquid echo "c" -%}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_liquid_tag_errors
|
|
||||||
assert_match_syntax_error("syntax error (line 1): Unknown tag 'error'", <<~LIQUID)
|
|
||||||
{%- liquid error no such tag -%}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
assert_match_syntax_error("syntax error (line 7): Unknown tag 'error'", <<~LIQUID)
|
|
||||||
{{ test }}
|
|
||||||
|
|
||||||
{%-
|
|
||||||
liquid
|
|
||||||
for value in array
|
|
||||||
|
|
||||||
error no such tag
|
|
||||||
endfor
|
|
||||||
-%}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
assert_match_syntax_error("syntax error (line 2): Unknown tag '!!! the guards are vigilant'", <<~LIQUID)
|
|
||||||
{%- liquid
|
|
||||||
!!! the guards are vigilant
|
|
||||||
-%}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
assert_match_syntax_error("syntax error (line 4): 'for' tag was never closed", <<~LIQUID)
|
|
||||||
{%- liquid
|
|
||||||
for value in array
|
|
||||||
echo 'forgot to close the for tag'
|
|
||||||
-%}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_line_number_is_correct_after_a_blank_token
|
|
||||||
assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n\n error %}")
|
|
||||||
assert_match_syntax_error("syntax error (line 3): Unknown tag 'error'", "{% liquid echo ''\n \n error %}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_cannot_open_blocks_living_past_a_liquid_tag
|
|
||||||
assert_match_syntax_error("syntax error (line 3): 'if' tag was never closed", <<~LIQUID)
|
|
||||||
{%- liquid
|
|
||||||
if true
|
|
||||||
-%}
|
|
||||||
{%- endif -%}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_quirk_can_close_blocks_created_before_a_liquid_tag
|
|
||||||
assert_template_result("42", <<~LIQUID)
|
|
||||||
{%- if true -%}
|
|
||||||
42
|
|
||||||
{%- liquid endif -%}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_liquid_tag_in_raw
|
|
||||||
assert_template_result("{% liquid echo 'test' %}\n", <<~LIQUID)
|
|
||||||
{% raw %}{% liquid echo 'test' %}{% endraw %}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class RenderTagTest < Minitest::Test
|
|
||||||
include Liquid
|
|
||||||
|
|
||||||
def test_render_with_no_arguments
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('source' => 'rendered content')
|
|
||||||
assert_template_result 'rendered content', '{% render "source" %}'
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_tag_looks_for_file_system_in_registers_first
|
|
||||||
file_system = StubFileSystem.new('pick_a_source' => 'from register file system')
|
|
||||||
assert_equal 'from register file system',
|
|
||||||
Template.parse('{% render "pick_a_source" %}').render!({}, registers: { file_system: file_system })
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_passes_named_arguments_into_inner_scope
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('product' => '{{ inner_product.title }}')
|
|
||||||
assert_template_result 'My Product', '{% render "product", inner_product: outer_product %}',
|
|
||||||
'outer_product' => { 'title' => 'My Product' }
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_accepts_literals_as_arguments
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ price }}')
|
|
||||||
assert_template_result '123', '{% render "snippet", price: 123 %}'
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_accepts_multiple_named_arguments
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ one }} {{ two }}')
|
|
||||||
assert_template_result '1 2', '{% render "snippet", one: 1, two: 2 %}'
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_does_not_inherit_parent_scope_variables
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ outer_variable }}')
|
|
||||||
assert_template_result '', '{% assign outer_variable = "should not be visible" %}{% render "snippet" %}'
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_does_not_inherit_variable_with_same_name_as_snippet
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ snippet }}')
|
|
||||||
assert_template_result '', "{% assign snippet = 'should not be visible' %}{% render 'snippet' %}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_sets_the_correct_template_name_for_errors
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ unsafe }}')
|
|
||||||
|
|
||||||
with_taint_mode :error do
|
|
||||||
template = Liquid::Template.parse('{% render "snippet", unsafe: unsafe %}')
|
|
||||||
context = Context.new('unsafe' => (+'unsafe').tap(&:taint))
|
|
||||||
template.render(context)
|
|
||||||
|
|
||||||
assert_equal [Liquid::TaintedError], template.errors.map(&:class)
|
|
||||||
assert_equal 'snippet', template.errors.first.template_name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_sets_the_correct_template_name_for_warnings
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ unsafe }}')
|
|
||||||
|
|
||||||
with_taint_mode :warn do
|
|
||||||
template = Liquid::Template.parse('{% render "snippet", unsafe: unsafe %}')
|
|
||||||
context = Context.new('unsafe' => (+'unsafe').tap(&:taint))
|
|
||||||
template.render(context)
|
|
||||||
|
|
||||||
assert_equal [Liquid::TaintedError], context.warnings.map(&:class)
|
|
||||||
assert_equal 'snippet', context.warnings.first.template_name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_does_not_mutate_parent_scope
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{% assign inner = 1 %}')
|
|
||||||
assert_template_result '', "{% render 'snippet' %}{{ inner }}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_nested_render_tag
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new(
|
|
||||||
'one' => "one {% render 'two' %}",
|
|
||||||
'two' => 'two'
|
|
||||||
)
|
|
||||||
assert_template_result 'one two', "{% render 'one' %}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_recursively_rendered_template_does_not_produce_endless_loop
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('loop' => '{% render "loop" %}')
|
|
||||||
|
|
||||||
assert_raises Liquid::StackLevelError do
|
|
||||||
Template.parse('{% render "loop" %}').render!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_includes_and_renders_count_towards_the_same_recursion_limit
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new(
|
|
||||||
'loop_render' => '{% render "loop_include" %}',
|
|
||||||
'loop_include' => '{% include "loop_render" %}'
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_raises Liquid::StackLevelError do
|
|
||||||
Template.parse('{% render "loop_include" %}').render!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_dynamically_choosen_templates_are_not_allowed
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => 'should not be rendered')
|
|
||||||
|
|
||||||
assert_raises Liquid::SyntaxError do
|
|
||||||
Liquid::Template.parse("{% assign name = 'snippet' %}{% render name %}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_include_tag_caches_second_read_of_same_partial
|
|
||||||
file_system = StubFileSystem.new('snippet' => 'echo')
|
|
||||||
assert_equal 'echoecho',
|
|
||||||
Template.parse('{% render "snippet" %}{% render "snippet" %}')
|
|
||||||
.render!({}, registers: { file_system: file_system })
|
|
||||||
assert_equal 1, file_system.file_read_count
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_tag_doesnt_cache_partials_across_renders
|
|
||||||
file_system = StubFileSystem.new('snippet' => 'my message')
|
|
||||||
|
|
||||||
assert_equal 'my message',
|
|
||||||
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system })
|
|
||||||
assert_equal 1, file_system.file_read_count
|
|
||||||
|
|
||||||
assert_equal 'my message',
|
|
||||||
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system })
|
|
||||||
assert_equal 2, file_system.file_read_count
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_render_tag_within_if_statement
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => 'my message')
|
|
||||||
assert_template_result 'my message', '{% if true %}{% render "snippet" %}{% endif %}'
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_break_through_render
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('break' => '{% break %}')
|
|
||||||
assert_template_result '1', '{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}'
|
|
||||||
assert_template_result '112233', '{% for i in (1..3) %}{{ i }}{% render "break" %}{{ i }}{% endfor %}'
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_increment_is_isolated_between_renders
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('incr' => '{% increment %}')
|
|
||||||
assert_template_result '010', '{% increment %}{% increment %}{% render "incr" %}'
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_decrement_is_isolated_between_renders
|
|
||||||
Liquid::Template.file_system = StubFileSystem.new('decr' => '{% decrement %}')
|
|
||||||
assert_template_result '-1-2-1', '{% decrement %}{% decrement %}{% render "decr" %}'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -139,16 +139,6 @@ class TemplateTest < Minitest::Test
|
|||||||
refute_nil t.resource_limits.assign_score
|
refute_nil t.resource_limits.assign_score
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_resource_limits_assign_score_counts_bytes_not_characters
|
|
||||||
t = Template.parse("{% assign foo = 'すごい' %}")
|
|
||||||
t.render
|
|
||||||
assert_equal 9, t.resource_limits.assign_score
|
|
||||||
|
|
||||||
t = Template.parse("{% capture foo %}すごい{% endcapture %}")
|
|
||||||
t.render
|
|
||||||
assert_equal 9, t.resource_limits.assign_score
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_resource_limits_assign_score_nested
|
def test_resource_limits_assign_score_nested
|
||||||
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
|
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
|
||||||
|
|
||||||
@@ -197,14 +187,6 @@ class TemplateTest < Minitest::Test
|
|||||||
assert_equal "ababab", t.render
|
assert_equal "ababab", t.render
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_length_uses_number_of_bytes_not_characters
|
|
||||||
t = Template.parse("{% if true %}すごい{% endif %}")
|
|
||||||
t.resource_limits.render_length_limit = 10
|
|
||||||
assert_equal "Liquid error: Memory limits exceeded", t.render
|
|
||||||
t.resource_limits.render_length_limit = 18
|
|
||||||
assert_equal "すごい", t.render
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_default_resource_limits_unaffected_by_render_with_context
|
def test_default_resource_limits_unaffected_by_render_with_context
|
||||||
context = Context.new
|
context = Context.new
|
||||||
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}")
|
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}")
|
||||||
@@ -279,15 +261,6 @@ class TemplateTest < Minitest::Test
|
|||||||
assert_equal 'Liquid error: undefined variable d', t.errors[2].message
|
assert_equal 'Liquid error: undefined variable d', t.errors[2].message
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_nil_value_does_not_raise
|
|
||||||
Liquid::Template.error_mode = :strict
|
|
||||||
t = Template.parse("some{{x}}thing")
|
|
||||||
result = t.render!({ 'x' => nil }, strict_variables: true)
|
|
||||||
|
|
||||||
assert_equal 0, t.errors.count
|
|
||||||
assert_equal 'something', result
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_undefined_variables_raise
|
def test_undefined_variables_raise
|
||||||
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
||||||
|
|
||||||
|
|||||||
@@ -496,10 +496,6 @@ class TrimModeTest < Minitest::Test
|
|||||||
assert_template_result(expected, text)
|
assert_template_result(expected, text)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_right_trim_followed_by_tag
|
|
||||||
assert_template_result('ab c', '{{ "a" -}}{{ "b" }} c')
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_raw_output
|
def test_raw_output
|
||||||
whitespace = ' '
|
whitespace = ' '
|
||||||
text = <<-END_TEMPLATE
|
text = <<-END_TEMPLATE
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class TryVariablesUsageTest < Minitest::Test
|
|
||||||
include Liquid
|
|
||||||
|
|
||||||
def test_test_usages
|
|
||||||
Usage.enable
|
|
||||||
template = Template.parse(%({{test}}))
|
|
||||||
assert_equal 'worked', template.render!('test' => 'worked')
|
|
||||||
assert_equal 'worked wonderfully', template.render!('test' => 'worked wonderfully')
|
|
||||||
assert_equal true, Usage.results["Using try_variable_find_in_environment"]
|
|
||||||
Usage.disable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class UsageEnabledUsageTest < Minitest::Test
|
|
||||||
include Liquid
|
|
||||||
|
|
||||||
def test_live_usages
|
|
||||||
assert_equal true, Usage.results["Usage is enabled"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -89,8 +89,4 @@ class VariableTest < Minitest::Test
|
|||||||
def test_multiline_variable
|
def test_multiline_variable
|
||||||
assert_equal 'worked', Template.parse("{{\ntest\n}}").render!('test' => 'worked')
|
assert_equal 'worked', Template.parse("{{\ntest\n}}").render!('test' => 'worked')
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_symbol
|
|
||||||
assert_template_result 'bar', '{{ foo }}', 'foo' => :bar
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
29
test/test_helper.rb
Executable file → Normal file
29
test/test_helper.rb
Executable file → Normal file
@@ -14,7 +14,7 @@ if env_mode = ENV['LIQUID_PARSER_MODE']
|
|||||||
end
|
end
|
||||||
Liquid::Template.error_mode = mode
|
Liquid::Template.error_mode = mode
|
||||||
|
|
||||||
if ENV['LIQUID_C'] == '1'
|
if ENV['LIQUID-C'] == '1'
|
||||||
puts "-- LIQUID C"
|
puts "-- LIQUID C"
|
||||||
require 'liquid/c'
|
require 'liquid/c'
|
||||||
end
|
end
|
||||||
@@ -37,18 +37,18 @@ module Minitest
|
|||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
def assert_template_result(expected, template, assigns = {}, message = nil)
|
def assert_template_result(expected, template, assigns = {}, message = nil)
|
||||||
assert_equal expected, Template.parse(template, line_numbers: true).render!(assigns), message
|
assert_equal expected, Template.parse(template).render!(assigns), message
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_template_result_matches(expected, template, assigns = {}, message = nil)
|
def assert_template_result_matches(expected, template, assigns = {}, message = nil)
|
||||||
return assert_template_result(expected, template, assigns, message) unless expected.is_a? Regexp
|
return assert_template_result(expected, template, assigns, message) unless expected.is_a? Regexp
|
||||||
|
|
||||||
assert_match expected, Template.parse(template, line_numbers: true).render!(assigns), message
|
assert_match expected, Template.parse(template).render!(assigns), message
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_match_syntax_error(match, template, assigns = {})
|
def assert_match_syntax_error(match, template, assigns = {})
|
||||||
exception = assert_raises(Liquid::SyntaxError) do
|
exception = assert_raises(Liquid::SyntaxError) do
|
||||||
Template.parse(template, line_numbers: true).render(assigns)
|
Template.parse(template).render(assigns)
|
||||||
end
|
end
|
||||||
assert_match match, exception.message
|
assert_match match, exception.message
|
||||||
end
|
end
|
||||||
@@ -84,13 +84,6 @@ module Minitest
|
|||||||
ensure
|
ensure
|
||||||
Liquid::Template.error_mode = old_mode
|
Liquid::Template.error_mode = old_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_custom_tag(tag_name, tag_class)
|
|
||||||
Liquid::Template.register_tag(tag_name, tag_class)
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
Liquid::Template.tags.delete(tag_name)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -121,17 +114,3 @@ class ErrorDrop < Liquid::Drop
|
|||||||
raise Exception, 'exception'
|
raise Exception, 'exception'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class StubFileSystem
|
|
||||||
attr_reader :file_read_count
|
|
||||||
|
|
||||||
def initialize(values)
|
|
||||||
@file_read_count = 0
|
|
||||||
@values = values
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_template_file(template_path)
|
|
||||||
@file_read_count += 1
|
|
||||||
@values.fetch(template_path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -44,47 +44,10 @@ class BlockUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_with_custom_tag
|
def test_with_custom_tag
|
||||||
with_custom_tag('testtag', Block) do
|
Liquid::Template.register_tag("testtag", Block)
|
||||||
assert Liquid::Template.parse("{% testtag %} {% endtesttag %}")
|
assert Liquid::Template.parse("{% testtag %} {% endtesttag %}")
|
||||||
end
|
ensure
|
||||||
end
|
Liquid::Template.tags.delete('testtag')
|
||||||
|
|
||||||
def test_custom_block_tags_have_a_default_render_to_output_buffer_method_for_backwards_compatibility
|
|
||||||
klass1 = Class.new(Block) do
|
|
||||||
def render(*)
|
|
||||||
'hello'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
with_custom_tag('blabla', klass1) do
|
|
||||||
template = Liquid::Template.parse("{% blabla %} bla {% endblabla %}")
|
|
||||||
|
|
||||||
assert_equal 'hello', template.render
|
|
||||||
|
|
||||||
buf = ''
|
|
||||||
output = template.render({}, output: buf)
|
|
||||||
assert_equal 'hello', output
|
|
||||||
assert_equal 'hello', buf
|
|
||||||
assert_equal buf.object_id, output.object_id
|
|
||||||
end
|
|
||||||
|
|
||||||
klass2 = Class.new(klass1) do
|
|
||||||
def render(*)
|
|
||||||
'foo' + super + 'bar'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
with_custom_tag('blabla', klass2) do
|
|
||||||
template = Liquid::Template.parse("{% blabla %} foo {% endblabla %}")
|
|
||||||
|
|
||||||
assert_equal 'foohellobar', template.render
|
|
||||||
|
|
||||||
buf = ''
|
|
||||||
output = template.render({}, output: buf)
|
|
||||||
assert_equal 'foohellobar', output
|
|
||||||
assert_equal 'foohellobar', buf
|
|
||||||
assert_equal buf.object_id, output.object_id
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
assert_evaluates_true 1, '<=', 1
|
assert_evaluates_true 1, '<=', 1
|
||||||
# negative numbers
|
# negative numbers
|
||||||
assert_evaluates_true 1, '>', -1
|
assert_evaluates_true 1, '>', -1
|
||||||
assert_evaluates_true -1, '<', 1
|
assert_evaluates_true (-1), '<', 1
|
||||||
assert_evaluates_true 1.0, '>', -1.0
|
assert_evaluates_true 1.0, '>', -1.0
|
||||||
assert_evaluates_true -1.0, '<', 1.0
|
assert_evaluates_true (-1.0), '<', 1.0
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_default_operators_evalute_false
|
def test_default_operators_evalute_false
|
||||||
|
|||||||
@@ -468,79 +468,11 @@ class ContextUnitTest < Minitest::Test
|
|||||||
assert_equal 'hi filtered', context.apply_global_filter('hi')
|
assert_equal 'hi filtered', context.apply_global_filter('hi')
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_static_environments_are_read_with_lower_priority_than_environments
|
|
||||||
context = Context.build(
|
|
||||||
static_environments: { 'shadowed' => 'static', 'unshadowed' => 'static' },
|
|
||||||
environments: { 'shadowed' => 'dynamic' }
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_equal 'dynamic', context['shadowed']
|
|
||||||
assert_equal 'static', context['unshadowed']
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_apply_global_filter_when_no_global_filter_exist
|
def test_apply_global_filter_when_no_global_filter_exist
|
||||||
context = Context.new
|
context = Context.new
|
||||||
assert_equal 'hi', context.apply_global_filter('hi')
|
assert_equal 'hi', context.apply_global_filter('hi')
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_new_isolated_subcontext_does_not_inherit_variables
|
|
||||||
super_context = Context.new
|
|
||||||
super_context['my_variable'] = 'some value'
|
|
||||||
subcontext = super_context.new_isolated_subcontext
|
|
||||||
|
|
||||||
assert_nil subcontext['my_variable']
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_new_isolated_subcontext_inherits_static_environment
|
|
||||||
super_context = Context.build(static_environments: { 'my_environment_value' => 'my value' })
|
|
||||||
subcontext = super_context.new_isolated_subcontext
|
|
||||||
|
|
||||||
assert_equal 'my value', subcontext['my_environment_value']
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_new_isolated_subcontext_inherits_resource_limits
|
|
||||||
resource_limits = ResourceLimits.new({})
|
|
||||||
super_context = Context.new({}, {}, {}, false, resource_limits)
|
|
||||||
subcontext = super_context.new_isolated_subcontext
|
|
||||||
assert_equal resource_limits, subcontext.resource_limits
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_new_isolated_subcontext_inherits_exception_renderer
|
|
||||||
super_context = Context.new
|
|
||||||
super_context.exception_renderer = ->(_e) { 'my exception message' }
|
|
||||||
subcontext = super_context.new_isolated_subcontext
|
|
||||||
assert_equal 'my exception message', subcontext.handle_error(Liquid::Error.new)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_new_isolated_subcontext_does_not_inherit_non_static_registers
|
|
||||||
registers = {
|
|
||||||
my_register: :my_value
|
|
||||||
}
|
|
||||||
super_context = Context.new({}, {}, registers)
|
|
||||||
subcontext = super_context.new_isolated_subcontext
|
|
||||||
assert_nil subcontext.registers[:my_register]
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_new_isolated_subcontext_inherits_static_registers
|
|
||||||
super_context = Context.build(static_registers: { my_register: :my_value })
|
|
||||||
subcontext = super_context.new_isolated_subcontext
|
|
||||||
assert_equal :my_value, subcontext.static_registers[:my_register]
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_new_isolated_subcontext_inherits_filters
|
|
||||||
my_filter = Module.new do
|
|
||||||
def my_filter(*)
|
|
||||||
'my filter result'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
super_context = Context.new
|
|
||||||
super_context.add_filters([my_filter])
|
|
||||||
subcontext = super_context.new_isolated_subcontext
|
|
||||||
template = Template.parse('{{ 123 | my_filter }}')
|
|
||||||
assert_equal 'my filter result', template.render(subcontext)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def assert_no_object_allocations
|
def assert_no_object_allocations
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class PartialCacheUnitTest < Minitest::Test
|
|
||||||
def test_uses_the_file_system_register_if_present
|
|
||||||
context = Liquid::Context.build(
|
|
||||||
registers: {
|
|
||||||
file_system: StubFileSystem.new('my_partial' => 'my partial body')
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
partial = Liquid::PartialCache.load(
|
|
||||||
'my_partial',
|
|
||||||
context: context,
|
|
||||||
parse_context: Liquid::ParseContext.new
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_equal 'my partial body', partial.render
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_reads_from_the_file_system_only_once_per_file
|
|
||||||
file_system = StubFileSystem.new('my_partial' => 'some partial body')
|
|
||||||
context = Liquid::Context.build(
|
|
||||||
registers: { file_system: file_system }
|
|
||||||
)
|
|
||||||
|
|
||||||
2.times do
|
|
||||||
Liquid::PartialCache.load(
|
|
||||||
'my_partial',
|
|
||||||
context: context,
|
|
||||||
parse_context: Liquid::ParseContext.new
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_equal 1, file_system.file_read_count
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_cache_state_is_stored_per_context
|
|
||||||
parse_context = Liquid::ParseContext.new
|
|
||||||
shared_file_system = StubFileSystem.new(
|
|
||||||
'my_partial' => 'my shared value'
|
|
||||||
)
|
|
||||||
context_one = Liquid::Context.build(
|
|
||||||
registers: {
|
|
||||||
file_system: shared_file_system
|
|
||||||
}
|
|
||||||
)
|
|
||||||
context_two = Liquid::Context.build(
|
|
||||||
registers: {
|
|
||||||
file_system: shared_file_system
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
2.times do
|
|
||||||
Liquid::PartialCache.load(
|
|
||||||
'my_partial',
|
|
||||||
context: context_one,
|
|
||||||
parse_context: parse_context
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
Liquid::PartialCache.load(
|
|
||||||
'my_partial',
|
|
||||||
context: context_two,
|
|
||||||
parse_context: parse_context
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_equal 2, shared_file_system.file_read_count
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_cache_is_not_broken_when_a_different_parse_context_is_used
|
|
||||||
file_system = StubFileSystem.new('my_partial' => 'some partial body')
|
|
||||||
context = Liquid::Context.build(
|
|
||||||
registers: { file_system: file_system }
|
|
||||||
)
|
|
||||||
|
|
||||||
Liquid::PartialCache.load(
|
|
||||||
'my_partial',
|
|
||||||
context: context,
|
|
||||||
parse_context: Liquid::ParseContext.new(my_key: 'value one')
|
|
||||||
)
|
|
||||||
Liquid::PartialCache.load(
|
|
||||||
'my_partial',
|
|
||||||
context: context,
|
|
||||||
parse_context: Liquid::ParseContext.new(my_key: 'value two')
|
|
||||||
)
|
|
||||||
|
|
||||||
# Technically what we care about is that the file was parsed twice,
|
|
||||||
# but measuring file reads is an OK proxy for this.
|
|
||||||
assert_equal 1, file_system.file_read_count
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -18,42 +18,4 @@ class TagUnitTest < Minitest::Test
|
|||||||
tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
||||||
assert_equal 'some_tag', tag.tag_name
|
assert_equal 'some_tag', tag.tag_name
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_custom_tags_have_a_default_render_to_output_buffer_method_for_backwards_compatibility
|
|
||||||
klass1 = Class.new(Tag) do
|
|
||||||
def render(*)
|
|
||||||
'hello'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
with_custom_tag('blabla', klass1) do
|
|
||||||
template = Liquid::Template.parse("{% blabla %}")
|
|
||||||
|
|
||||||
assert_equal 'hello', template.render
|
|
||||||
|
|
||||||
buf = ''
|
|
||||||
output = template.render({}, output: buf)
|
|
||||||
assert_equal 'hello', output
|
|
||||||
assert_equal 'hello', buf
|
|
||||||
assert_equal buf.object_id, output.object_id
|
|
||||||
end
|
|
||||||
|
|
||||||
klass2 = Class.new(klass1) do
|
|
||||||
def render(*)
|
|
||||||
'foo' + super + 'bar'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
with_custom_tag('blabla', klass2) do
|
|
||||||
template = Liquid::Template.parse("{% blabla %}")
|
|
||||||
|
|
||||||
assert_equal 'foohellobar', template.render
|
|
||||||
|
|
||||||
buf = ''
|
|
||||||
output = template.render({}, output: buf)
|
|
||||||
assert_equal 'foohellobar', output
|
|
||||||
assert_equal 'foohellobar', buf
|
|
||||||
assert_equal buf.object_id, output.object_id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user