mirror of
https://github.com/kemko/liquid.git
synced 2026-01-01 15:55:40 +03:00
Compare commits
53 Commits
liquid-usa
...
improve_me
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9be96b58d | ||
|
|
dbaef5e79b | ||
|
|
48a155a213 | ||
|
|
c69a9a77c6 | ||
|
|
ef79fa3898 | ||
|
|
f7ad602bfc | ||
|
|
ffd6049ba2 | ||
|
|
b3ad54c0c2 | ||
|
|
67eca3f58d | ||
|
|
0847bf560f | ||
|
|
8074565c3e | ||
|
|
24e81267b9 | ||
|
|
c0ffee3ff9 | ||
|
|
c0ffeeef26 | ||
|
|
22dbf90b7d | ||
|
|
40c68c9c83 | ||
|
|
b7f0f158ab | ||
|
|
d8f31046a9 | ||
|
|
6c6382ed69 | ||
|
|
53ba1372f9 | ||
|
|
57c9cf64eb | ||
|
|
e83b1e4159 | ||
|
|
3784020a8d | ||
|
|
1223444738 | ||
|
|
2bfeed2b00 | ||
|
|
04b800d768 | ||
|
|
f1d62978ef | ||
|
|
ffadc64f28 | ||
|
|
5302f40342 | ||
|
|
b0f8c2c03e | ||
|
|
37e40673ff | ||
|
|
fefee4c675 | ||
|
|
1aa7d3d2ba | ||
|
|
0db9c56f34 | ||
|
|
f4d134cd5c | ||
|
|
b667bcb48b | ||
|
|
2c14e0b2ba | ||
|
|
ca207ed93f | ||
|
|
ef13343591 | ||
|
|
adb40c41b7 | ||
|
|
d8403af515 | ||
|
|
0d26f05bb8 | ||
|
|
1dcad34b06 | ||
|
|
9a42c8c8b2 | ||
|
|
1fcef2133f | ||
|
|
d7514b1305 | ||
|
|
c0ffee5919 | ||
|
|
724d02e9b3 | ||
|
|
a5b387cdd4 | ||
|
|
1f90a37b63 | ||
|
|
799da202df | ||
|
|
dafbb4ae90 | ||
|
|
2324564743 |
@@ -1,3 +1,5 @@
|
||||
# Recommended rubocop version: ~> 0.78.0
|
||||
|
||||
AllCops:
|
||||
Exclude:
|
||||
- 'db/schema.rb'
|
||||
@@ -20,7 +22,7 @@ Style/Alias:
|
||||
- prefer_alias
|
||||
- prefer_alias_method
|
||||
|
||||
Layout/AlignHash:
|
||||
Layout/HashAlignment:
|
||||
EnforcedHashRocketStyle: key
|
||||
EnforcedColonStyle: key
|
||||
EnforcedLastArgumentHashStyle: ignore_implicit
|
||||
@@ -30,7 +32,7 @@ Layout/AlignHash:
|
||||
- ignore_implicit
|
||||
- ignore_explicit
|
||||
|
||||
Layout/AlignParameters:
|
||||
Layout/ParameterAlignment:
|
||||
EnforcedStyle: with_fixed_indentation
|
||||
SupportedStyles:
|
||||
- with_first_parameter
|
||||
@@ -172,7 +174,7 @@ Naming/FileName:
|
||||
Regex:
|
||||
IgnoreExecutableScripts: true
|
||||
|
||||
Layout/IndentFirstArgument:
|
||||
Layout/FirstArgumentIndentation:
|
||||
EnforcedStyle: consistent
|
||||
SupportedStyles:
|
||||
- consistent
|
||||
@@ -225,7 +227,7 @@ Layout/IndentationConsistency:
|
||||
Layout/IndentationWidth:
|
||||
Width: 2
|
||||
|
||||
Layout/IndentFirstArrayElement:
|
||||
Layout/FirstArrayElementIndentation:
|
||||
EnforcedStyle: consistent
|
||||
SupportedStyles:
|
||||
- special_inside_parentheses
|
||||
@@ -233,10 +235,10 @@ Layout/IndentFirstArrayElement:
|
||||
- align_brackets
|
||||
IndentationWidth:
|
||||
|
||||
Layout/IndentAssignment:
|
||||
Layout/AssignmentIndentation:
|
||||
IndentationWidth:
|
||||
|
||||
Layout/IndentFirstHashElement:
|
||||
Layout/FirstHashElementIndentation:
|
||||
EnforcedStyle: consistent
|
||||
SupportedStyles:
|
||||
- special_inside_parentheses
|
||||
@@ -340,9 +342,9 @@ Style/PercentQLiterals:
|
||||
Naming/PredicateName:
|
||||
NamePrefix:
|
||||
- is_
|
||||
NamePrefixBlacklist:
|
||||
ForbiddenPrefixes:
|
||||
- is_
|
||||
NameWhitelist:
|
||||
AllowedMethods:
|
||||
- is_a?
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
@@ -467,7 +469,7 @@ Style/TernaryParentheses:
|
||||
- require_no_parentheses
|
||||
AllowSafeAssignment: true
|
||||
|
||||
Layout/TrailingBlankLines:
|
||||
Layout/TrailingEmptyLines:
|
||||
EnforcedStyle: final_newline
|
||||
SupportedStyles:
|
||||
- final_newline
|
||||
@@ -478,7 +480,7 @@ Style/TrivialAccessors:
|
||||
AllowPredicates: true
|
||||
AllowDSLWriters: false
|
||||
IgnoreClassMethods: false
|
||||
Whitelist:
|
||||
AllowedMethods:
|
||||
- to_ary
|
||||
- to_a
|
||||
- to_c
|
||||
@@ -509,7 +511,7 @@ Style/WhileUntilModifier:
|
||||
Metrics/BlockNesting:
|
||||
Max: 3
|
||||
|
||||
Metrics/LineLength:
|
||||
Layout/LineLength:
|
||||
Max: 120
|
||||
AllowHeredoc: true
|
||||
AllowURI: true
|
||||
@@ -561,7 +563,7 @@ Lint/UnusedMethodArgument:
|
||||
Naming/AccessorMethodName:
|
||||
Enabled: true
|
||||
|
||||
Layout/AlignArray:
|
||||
Layout/ArrayAlignment:
|
||||
Enabled: true
|
||||
|
||||
Style/ArrayJoin:
|
||||
@@ -819,13 +821,13 @@ Layout/TrailingWhitespace:
|
||||
Style/UnlessElse:
|
||||
Enabled: true
|
||||
|
||||
Style/UnneededCapitalW:
|
||||
Style/RedundantCapitalW:
|
||||
Enabled: true
|
||||
|
||||
Style/UnneededInterpolation:
|
||||
Style/RedundantInterpolation:
|
||||
Enabled: true
|
||||
|
||||
Style/UnneededPercentQ:
|
||||
Style/RedundantPercentQ:
|
||||
Enabled: true
|
||||
|
||||
Style/VariableInterpolation:
|
||||
@@ -840,7 +842,7 @@ Style/WhileUntilDo:
|
||||
Style/ZeroLengthPredicate:
|
||||
Enabled: true
|
||||
|
||||
Layout/IndentHeredoc:
|
||||
Layout/HeredocIndentation:
|
||||
EnforcedStyle: squiggly
|
||||
|
||||
Lint/AmbiguousOperator:
|
||||
@@ -864,7 +866,7 @@ Lint/DeprecatedClassMethods:
|
||||
Lint/DuplicateMethods:
|
||||
Enabled: true
|
||||
|
||||
Lint/DuplicatedKey:
|
||||
Lint/DuplicateHashKey:
|
||||
Enabled: true
|
||||
|
||||
Lint/EachWithObjectArgument:
|
||||
@@ -891,8 +893,8 @@ Lint/FloatOutOfRange:
|
||||
Lint/FormatParameterMismatch:
|
||||
Enabled: true
|
||||
|
||||
Lint/HandleExceptions:
|
||||
Enabled: true
|
||||
Lint/SuppressedException:
|
||||
AllowComments: true
|
||||
|
||||
Lint/ImplicitStringConcatenation:
|
||||
Description: Checks for adjacent string literals on the same line, which could
|
||||
@@ -947,7 +949,7 @@ Lint/ShadowedException:
|
||||
Lint/ShadowingOuterLocalVariable:
|
||||
Enabled: true
|
||||
|
||||
Lint/StringConversionInInterpolation:
|
||||
Lint/RedundantStringCoercion:
|
||||
Enabled: true
|
||||
|
||||
Lint/UnderscorePrefixedVariableName:
|
||||
@@ -956,13 +958,13 @@ Lint/UnderscorePrefixedVariableName:
|
||||
Lint/UnifiedInteger:
|
||||
Enabled: true
|
||||
|
||||
Lint/UnneededCopDisableDirective:
|
||||
Lint/RedundantCopDisableDirective:
|
||||
Enabled: true
|
||||
|
||||
Lint/UnneededCopEnableDirective:
|
||||
Lint/RedundantCopEnableDirective:
|
||||
Enabled: true
|
||||
|
||||
Lint/UnneededSplatExpansion:
|
||||
Lint/RedundantSplatExpansion:
|
||||
Enabled: true
|
||||
|
||||
Lint/UnreachableCode:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
inherit_from:
|
||||
- https://shopify.github.io/ruby-style-guide/rubocop.yml
|
||||
- 'https://shopify.github.io/ruby-style-guide/rubocop.yml'
|
||||
- .rubocop_todo.yml
|
||||
|
||||
require: rubocop-performance
|
||||
@@ -8,9 +8,10 @@ Performance:
|
||||
Enabled: true
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.4
|
||||
Exclude:
|
||||
- 'vendor/bundle/**/*'
|
||||
|
||||
|
||||
Naming/MethodName:
|
||||
Exclude:
|
||||
- 'example/server/liquid_servlet.rb'
|
||||
- 'example/server/liquid_servlet.rb'
|
||||
|
||||
@@ -1,107 +1,11 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config`
|
||||
# on 2019-08-29 12:16:25 +1000 using RuboCop version 0.74.0.
|
||||
# on 2019-09-11 06:34:25 +1000 using RuboCop version 0.74.0.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
||||
|
||||
# Offense count: 13
|
||||
# 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'
|
||||
- 'performance/shopify/comment_form.rb'
|
||||
- 'performance/shopify/database.rb'
|
||||
- 'performance/shopify/paginate.rb'
|
||||
- 'test/unit/context_unit_test.rb'
|
||||
|
||||
# Offense count: 3
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
|
||||
Layout/ExtraSpacing:
|
||||
Exclude:
|
||||
- 'performance/shopify/paginate.rb'
|
||||
|
||||
# Offense count: 5
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# 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: 4
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment.
|
||||
Layout/SpaceAroundOperators:
|
||||
Exclude:
|
||||
- 'lib/liquid/condition.rb'
|
||||
- 'performance/shopify/paginate.rb'
|
||||
|
||||
# Offense count: 9
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
|
||||
# SupportedStyles: space, no_space
|
||||
# SupportedStylesForEmptyBraces: space, no_space
|
||||
Layout/SpaceBeforeBlockBraces:
|
||||
Exclude:
|
||||
- 'example/server/server.rb'
|
||||
- 'lib/liquid/variable.rb'
|
||||
- 'test/integration/drop_test.rb'
|
||||
- 'test/integration/standard_filter_test.rb'
|
||||
- 'test/integration/tags/if_else_tag_test.rb'
|
||||
|
||||
# Offense count: 19
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets.
|
||||
# SupportedStyles: space, no_space, compact
|
||||
# SupportedStylesForEmptyBrackets: space, no_space
|
||||
Layout/SpaceInsideArrayLiteralBrackets:
|
||||
Exclude:
|
||||
- 'test/integration/drop_test.rb'
|
||||
- 'test/integration/standard_filter_test.rb'
|
||||
- 'test/integration/tags/for_tag_test.rb'
|
||||
- 'test/integration/tags/include_tag_test.rb'
|
||||
- 'test/integration/tags/standard_tag_test.rb'
|
||||
- 'test/unit/context_unit_test.rb'
|
||||
|
||||
# Offense count: 2
|
||||
Lint/AmbiguousOperator:
|
||||
Exclude:
|
||||
- 'test/unit/condition_unit_test.rb'
|
||||
|
||||
# Offense count: 16
|
||||
# Configuration parameters: AllowSafeAssignment.
|
||||
Lint/AssignmentInCondition:
|
||||
Exclude:
|
||||
- 'lib/liquid/block_body.rb'
|
||||
- 'lib/liquid/lexer.rb'
|
||||
- 'lib/liquid/standardfilters.rb'
|
||||
- 'lib/liquid/tags/for.rb'
|
||||
- 'lib/liquid/tags/if.rb'
|
||||
- 'lib/liquid/tags/include.rb'
|
||||
- 'lib/liquid/tags/raw.rb'
|
||||
- 'lib/liquid/variable.rb'
|
||||
- 'performance/profile.rb'
|
||||
- 'test/test_helper.rb'
|
||||
- 'test/unit/tokenizer_unit_test.rb'
|
||||
|
||||
# Offense count: 2
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
@@ -110,52 +14,11 @@ Lint/InheritException:
|
||||
Exclude:
|
||||
- 'lib/liquid/interrupts.rb'
|
||||
|
||||
# Offense count: 10
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
|
||||
Lint/UnusedBlockArgument:
|
||||
Exclude:
|
||||
- 'lib/liquid/condition.rb'
|
||||
- 'lib/liquid/context.rb'
|
||||
- 'lib/liquid/document.rb'
|
||||
- 'lib/liquid/parse_context.rb'
|
||||
- 'lib/liquid/template.rb'
|
||||
- 'performance/shopify/json_filter.rb'
|
||||
- 'test/integration/filter_test.rb'
|
||||
- 'test/integration/render_profiling_test.rb'
|
||||
- 'test/integration/variable_test.rb'
|
||||
- 'test/unit/condition_unit_test.rb'
|
||||
|
||||
# Offense count: 12
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
|
||||
Lint/UnusedMethodArgument:
|
||||
Exclude:
|
||||
- 'example/server/liquid_servlet.rb'
|
||||
- 'test/integration/blank_test.rb'
|
||||
- 'test/integration/error_handling_test.rb'
|
||||
- 'test/integration/filter_test.rb'
|
||||
- 'test/integration/output_test.rb'
|
||||
- 'test/integration/standard_filter_test.rb'
|
||||
- 'test/integration/tags/include_tag_test.rb'
|
||||
- 'test/unit/strainer_unit_test.rb'
|
||||
|
||||
# Offense count: 2
|
||||
Lint/UselessAssignment:
|
||||
Exclude:
|
||||
- 'performance/shopify/database.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Configuration parameters: CheckForMethodsWithNoSideEffects.
|
||||
Lint/Void:
|
||||
Exclude:
|
||||
- 'lib/liquid/parse_context.rb'
|
||||
|
||||
# Offense count: 95
|
||||
# Offense count: 98
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
||||
# URISchemes: http, https
|
||||
Metrics/LineLength:
|
||||
Layout/LineLength:
|
||||
Max: 294
|
||||
|
||||
# Offense count: 44
|
||||
@@ -177,235 +40,9 @@ Naming/ConstantName:
|
||||
- 'performance/shopify/paginate.rb'
|
||||
- 'test/integration/tags/include_tag_test.rb'
|
||||
|
||||
# Offense count: 2
|
||||
# Configuration parameters: .
|
||||
# SupportedStyles: snake_case, camelCase
|
||||
Naming/MethodName:
|
||||
EnforcedStyle: snake_case
|
||||
|
||||
# Offense count: 3
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: always, conditionals
|
||||
Style/AndOr:
|
||||
Exclude:
|
||||
- 'lib/liquid/i18n.rb'
|
||||
- 'lib/liquid/tags/table_row.rb'
|
||||
- 'lib/liquid/tokenizer.rb'
|
||||
|
||||
# Offense count: 40
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: braces, no_braces, context_dependent
|
||||
Style/BracesAroundHashParameters:
|
||||
Exclude:
|
||||
- 'test/integration/error_handling_test.rb'
|
||||
- 'test/integration/filter_test.rb'
|
||||
- 'test/integration/render_profiling_test.rb'
|
||||
- 'test/integration/standard_filter_test.rb'
|
||||
- 'test/integration/tags/echo_test.rb'
|
||||
- 'test/integration/tags/increment_tag_test.rb'
|
||||
- 'test/integration/tags/standard_tag_test.rb'
|
||||
- 'test/integration/template_test.rb'
|
||||
- 'test/unit/condition_unit_test.rb'
|
||||
- 'test/unit/context_unit_test.rb'
|
||||
|
||||
# Offense count: 5
|
||||
Style/ClassVars:
|
||||
Exclude:
|
||||
- 'lib/liquid/condition.rb'
|
||||
- 'lib/liquid/strainer.rb'
|
||||
- 'lib/liquid/template.rb'
|
||||
|
||||
# Offense count: 2
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
|
||||
# SupportedStyles: assign_to_condition, assign_inside_condition
|
||||
Style/ConditionalAssignment:
|
||||
Exclude:
|
||||
- 'lib/liquid/errors.rb'
|
||||
- 'performance/shopify/shop_filter.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Configuration parameters: AllowCoercion.
|
||||
Style/DateTime:
|
||||
Exclude:
|
||||
- 'test/unit/context_unit_test.rb'
|
||||
|
||||
# Offense count: 2
|
||||
# Cop supports --auto-correct.
|
||||
Style/EachWithObject:
|
||||
Exclude:
|
||||
- 'performance/shopify/database.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Cop supports --auto-correct.
|
||||
Style/EmptyCaseCondition:
|
||||
Exclude:
|
||||
- 'lib/liquid/lexer.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: each, for
|
||||
Style/For:
|
||||
Exclude:
|
||||
- 'performance/shopify/shop_filter.rb'
|
||||
|
||||
# Offense count: 9
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: format, sprintf, percent
|
||||
Style/FormatString:
|
||||
Exclude:
|
||||
- 'example/server/example_servlet.rb'
|
||||
- 'performance/shopify/money_filter.rb'
|
||||
- 'performance/shopify/weight_filter.rb'
|
||||
- 'test/integration/filter_test.rb'
|
||||
- 'test/integration/hash_ordering_test.rb'
|
||||
|
||||
# Offense count: 115
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: always, never
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 30
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: IgnoreMacros, IgnoredMethods, IncludedMacros, AllowParenthesesInMultilineCall, AllowParenthesesInChaining, AllowParenthesesInCamelCaseMethod, EnforcedStyle.
|
||||
# SupportedStyles: require_parentheses, omit_parentheses
|
||||
Style/MethodCallWithArgsParentheses:
|
||||
Exclude:
|
||||
- 'Gemfile'
|
||||
- 'Rakefile'
|
||||
- 'lib/liquid/block_body.rb'
|
||||
- 'lib/liquid/parser.rb'
|
||||
- 'lib/liquid/tags/for.rb'
|
||||
- 'liquid.gemspec'
|
||||
- 'performance/shopify/database.rb'
|
||||
- 'performance/shopify/liquid.rb'
|
||||
- 'test/test_helper.rb'
|
||||
- 'test/unit/condition_unit_test.rb'
|
||||
- 'test/unit/tags/if_tag_unit_test.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, MinBodyLength.
|
||||
# SupportedStyles: skip_modifier_ifs, always
|
||||
Style/Next:
|
||||
Exclude:
|
||||
- 'lib/liquid/tags/for.rb'
|
||||
|
||||
# Offense count: 52
|
||||
# Cop supports --auto-correct.
|
||||
Style/PerlBackrefs:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 33
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: compact, exploded
|
||||
Style/RaiseArgs:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 1
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantSelf:
|
||||
Exclude:
|
||||
- 'lib/liquid/strainer.rb'
|
||||
|
||||
# Offense count: 5
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
|
||||
# SupportedStyles: slashes, percent_r, mixed
|
||||
Style/RegexpLiteral:
|
||||
Exclude:
|
||||
- 'lib/liquid/file_system.rb'
|
||||
- 'lib/liquid/standardfilters.rb'
|
||||
- 'performance/shopify/shop_filter.rb'
|
||||
- 'test/unit/condition_unit_test.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: 10
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowAsExpressionSeparator.
|
||||
Style/Semicolon:
|
||||
Exclude:
|
||||
- 'performance/shopify/database.rb'
|
||||
- 'test/integration/error_handling_test.rb'
|
||||
- 'test/integration/template_test.rb'
|
||||
- 'test/unit/context_unit_test.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: use_perl_names, use_english_names
|
||||
Style/SpecialGlobalVars:
|
||||
Exclude:
|
||||
- 'performance/shopify/liquid.rb'
|
||||
|
||||
# Offense count: 2
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: single_quotes, double_quotes
|
||||
Style/StringLiteralsInInterpolation:
|
||||
Exclude:
|
||||
- 'performance/shopify/tag_filter.rb'
|
||||
|
||||
# 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: 21
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyleForMultiline.
|
||||
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
|
||||
Style/TrailingCommaInArrayLiteral:
|
||||
Exclude:
|
||||
- 'lib/liquid/parse_tree_visitor.rb'
|
||||
- 'lib/liquid/tags/include.rb'
|
||||
- 'test/integration/parse_tree_visitor_test.rb'
|
||||
- 'test/integration/standard_filter_test.rb'
|
||||
|
||||
# Offense count: 9
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyleForMultiline.
|
||||
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
|
||||
Style/TrailingCommaInHashLiteral:
|
||||
Exclude:
|
||||
- 'lib/liquid/condition.rb'
|
||||
- 'lib/liquid/lexer.rb'
|
||||
- 'lib/liquid/standardfilters.rb'
|
||||
- 'performance/shopify/comment_form.rb'
|
||||
- 'performance/shopify/database.rb'
|
||||
- 'performance/shopify/paginate.rb'
|
||||
- 'performance/theme_runner.rb'
|
||||
- 'test/integration/output_test.rb'
|
||||
- 'test/unit/context_unit_test.rb'
|
||||
|
||||
# Offense count: 2
|
||||
# Cop supports --auto-correct.
|
||||
Style/UnneededPercentQ:
|
||||
Exclude:
|
||||
- 'test/integration/error_handling_test.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# Cop supports --auto-correct.
|
||||
Style/WhileUntilModifier:
|
||||
Exclude:
|
||||
- 'lib/liquid/tags/case.rb'
|
||||
- 'lib/liquid/template.rb'
|
||||
10
.travis.yml
10
.travis.yml
@@ -4,21 +4,15 @@ cache: bundler
|
||||
rvm:
|
||||
- 2.4
|
||||
- 2.5
|
||||
- &latest_ruby 2.6
|
||||
- 2.7
|
||||
- 2.6
|
||||
- &latest_ruby 2.7
|
||||
- ruby-head
|
||||
- jruby-head
|
||||
- truffleruby
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- rvm: *latest_ruby
|
||||
script: bundle exec rake memory_profile:run
|
||||
name: Profiling Memory Usage
|
||||
allow_failures:
|
||||
- rvm: ruby-head
|
||||
- rvm: jruby-head
|
||||
- rvm: truffleruby
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
||||
4
Gemfile
4
Gemfile
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
git_source(:github) do |repo_name|
|
||||
"https://github.com/#{repo_name}.git"
|
||||
@@ -16,7 +18,7 @@ group :benchmark, :test do
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'rubocop', '~> 0.74.0', require: false
|
||||
gem 'rubocop', '~> 0.78.0', require: false
|
||||
gem 'rubocop-performance', require: false
|
||||
|
||||
platform :mri, :truffleruby do
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Liquid Change Log
|
||||
|
||||
### Unreleased
|
||||
|
||||
* Split Strainer class as a factory and a template (#1208) [Thierry Joyal]
|
||||
* Remove handling of a nil context in the Strainer class (#1218) [Thierry Joyal]
|
||||
* Change `Liquid::MemoryError` message to be more specific about which limit was reached. (#1206) [Alan Tan]
|
||||
|
||||
## 4.0.3 / 2019-03-12
|
||||
|
||||
### Fixed
|
||||
|
||||
18
Rakefile
18
Rakefile
@@ -1,18 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
||||
$LOAD_PATH.unshift(File.expand_path("../lib", __FILE__))
|
||||
require "liquid/version"
|
||||
|
||||
task default: [:test, :rubocop]
|
||||
task(default: [:test, :rubocop])
|
||||
|
||||
desc 'run test suite with default parser'
|
||||
desc('run test suite with default parser')
|
||||
Rake::TestTask.new(:base_test) do |t|
|
||||
t.libs << '.' << 'lib' << 'test'
|
||||
t.test_files = FileList['test/{integration,unit}/**/*_test.rb']
|
||||
t.verbose = false
|
||||
t.verbose = false
|
||||
end
|
||||
|
||||
desc 'run test suite with warn error mode'
|
||||
desc('run test suite with warn error mode')
|
||||
task :warn_test do
|
||||
ENV['LIQUID_PARSER_MODE'] = 'warn'
|
||||
Rake::Task['base_test'].invoke
|
||||
@@ -25,7 +27,7 @@ task :rubocop do
|
||||
end
|
||||
end
|
||||
|
||||
desc 'runs test suite with both strict and lax parsers'
|
||||
desc('runs test suite with both strict and lax parsers')
|
||||
task :test do
|
||||
ENV['LIQUID_PARSER_MODE'] = 'lax'
|
||||
Rake::Task['base_test'].invoke
|
||||
@@ -47,7 +49,7 @@ task :test do
|
||||
end
|
||||
end
|
||||
|
||||
task gem: :build
|
||||
task(gem: :build)
|
||||
task :build do
|
||||
system "gem build liquid.gemspec"
|
||||
end
|
||||
@@ -94,7 +96,7 @@ namespace :memory_profile do
|
||||
end
|
||||
end
|
||||
|
||||
desc "Run example"
|
||||
desc("Run example")
|
||||
task :example do
|
||||
ruby "-w -d -Ilib example/server/server.rb"
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ProductsFilter
|
||||
def price(integer)
|
||||
sprintf("$%.2d USD", integer / 100.0)
|
||||
format("$%.2d USD", integer / 100.0)
|
||||
end
|
||||
|
||||
def prettyprint(text)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class LiquidServlet < WEBrick::HTTPServlet::AbstractServlet
|
||||
def do_GET(req, res)
|
||||
handle(:get, req, res)
|
||||
@@ -9,17 +11,17 @@ class LiquidServlet < WEBrick::HTTPServlet::AbstractServlet
|
||||
|
||||
private
|
||||
|
||||
def handle(type, req, res)
|
||||
@request = req
|
||||
def handle(_type, req, res)
|
||||
@request = req
|
||||
@response = res
|
||||
|
||||
@request.path_info =~ /(\w+)\z/
|
||||
@action = $1 || 'index'
|
||||
@action = Regexp.last_match(1) || 'index'
|
||||
@assigns = send(@action) if respond_to?(@action)
|
||||
|
||||
@response['Content-Type'] = "text/html"
|
||||
@response.status = 200
|
||||
@response.body = Liquid::Template.parse(read_template).render(@assigns, filters: [ProductsFilter])
|
||||
@response.body = Liquid::Template.parse(read_template).render(@assigns, filters: [ProductsFilter])
|
||||
end
|
||||
|
||||
def read_template(filename = @action)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'webrick'
|
||||
require 'rexml/document'
|
||||
|
||||
@@ -8,5 +10,5 @@ require_relative 'example_servlet'
|
||||
# Setup webrick
|
||||
server = WEBrick::HTTPServer.new(Port: ARGV[1] || 3000)
|
||||
server.mount('/', Servlet)
|
||||
trap("INT"){ server.shutdown }
|
||||
trap("INT") { server.shutdown }
|
||||
server.start
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Copyright (c) 2005 Tobias Luetke
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
@@ -21,10 +23,10 @@
|
||||
|
||||
module Liquid
|
||||
FilterSeparator = /\|/
|
||||
ArgumentSeparator = ','.freeze
|
||||
FilterArgumentSeparator = ':'.freeze
|
||||
VariableAttributeSeparator = '.'.freeze
|
||||
WhitespaceControl = '-'.freeze
|
||||
ArgumentSeparator = ','
|
||||
FilterArgumentSeparator = ':'
|
||||
VariableAttributeSeparator = '.'
|
||||
WhitespaceControl = '-'
|
||||
TagStart = /\{\%/
|
||||
TagEnd = /\%\}/
|
||||
VariableSignature = /\(?[\w\-\.\[\]]\)?/
|
||||
@@ -55,7 +57,8 @@ require 'liquid/forloop_drop'
|
||||
require 'liquid/extensions'
|
||||
require 'liquid/errors'
|
||||
require 'liquid/interrupts'
|
||||
require 'liquid/strainer'
|
||||
require 'liquid/strainer_factory'
|
||||
require 'liquid/strainer_template'
|
||||
require 'liquid/expression'
|
||||
require 'liquid/context'
|
||||
require 'liquid/parser_switching'
|
||||
@@ -76,7 +79,11 @@ require 'liquid/tokenizer'
|
||||
require 'liquid/parse_context'
|
||||
require 'liquid/partial_cache'
|
||||
require 'liquid/usage'
|
||||
require 'liquid/register'
|
||||
require 'liquid/static_registers'
|
||||
require 'liquid/template_factory'
|
||||
|
||||
# Load all the tags of the standard library
|
||||
#
|
||||
Dir["#{__dir__}/liquid/tags/*.rb"].each { |f| require f }
|
||||
Dir["#{__dir__}/liquid/registers/*.rb"].each { |f| require f }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Block < Tag
|
||||
MAX_DEPTH = 100
|
||||
@@ -27,16 +29,16 @@ module Liquid
|
||||
end
|
||||
|
||||
def unknown_tag(tag, _params, _tokens)
|
||||
if tag == 'else'.freeze
|
||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unexpected_else".freeze,
|
||||
block_name: block_name))
|
||||
elsif tag.start_with?('end'.freeze)
|
||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.invalid_delimiter".freeze,
|
||||
if tag == 'else'
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_else",
|
||||
block_name: block_name)
|
||||
elsif tag.start_with?('end')
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.invalid_delimiter",
|
||||
tag: tag,
|
||||
block_name: block_name,
|
||||
block_delimiter: block_delimiter))
|
||||
block_delimiter: block_delimiter)
|
||||
else
|
||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unknown_tag".freeze, tag: tag))
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -52,7 +54,7 @@ module Liquid
|
||||
|
||||
def parse_body(body, tokens)
|
||||
if parse_context.depth >= MAX_DEPTH
|
||||
raise StackLevelError, "Nesting too deep".freeze
|
||||
raise StackLevelError, "Nesting too deep"
|
||||
end
|
||||
parse_context.depth += 1
|
||||
begin
|
||||
@@ -61,7 +63,7 @@ module Liquid
|
||||
|
||||
return false if end_tag_name == block_delimiter
|
||||
unless end_tag_name
|
||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_never_closed", block_name: block_name)
|
||||
end
|
||||
|
||||
# this tag is not registered with the system
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'English'
|
||||
|
||||
module Liquid
|
||||
class BlockBody
|
||||
LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o
|
||||
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
||||
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
||||
LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o
|
||||
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
||||
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
||||
WhitespaceOrNothing = /\A\s*\z/
|
||||
TAGSTART = "{%".freeze
|
||||
VARSTART = "{{".freeze
|
||||
TAGSTART = "{%"
|
||||
VARSTART = "{{"
|
||||
|
||||
attr_reader :nodelist
|
||||
|
||||
def initialize
|
||||
@nodelist = []
|
||||
@blank = true
|
||||
@blank = true
|
||||
end
|
||||
|
||||
def parse(tokenizer, parse_context, &block)
|
||||
@@ -25,16 +29,16 @@ module Liquid
|
||||
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 =~ LiquidTagToken
|
||||
# line isn't empty but didn't match tag syntax, yield and let the
|
||||
# caller raise a syntax error
|
||||
return yield token, token
|
||||
end
|
||||
tag_name = $1
|
||||
markup = $2
|
||||
unless tag = registered_tags[tag_name]
|
||||
tag_name = Regexp.last_match(1)
|
||||
markup = Regexp.last_match(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
|
||||
@@ -49,8 +53,23 @@ module Liquid
|
||||
yield nil, nil
|
||||
end
|
||||
|
||||
# @api private
|
||||
def self.unknown_tag_in_liquid_tag(end_tag_name, end_tag_markup)
|
||||
yield end_tag_name, end_tag_markup
|
||||
ensure
|
||||
Usage.increment("liquid_tag_contains_outer_tag") unless $ERROR_INFO.is_a?(SyntaxError)
|
||||
end
|
||||
|
||||
private def parse_liquid_tag(markup, parse_context, &block)
|
||||
liquid_tag_tokenizer = Tokenizer.new(markup, line_number: parse_context.line_number, for_liquid_tag: true)
|
||||
parse_for_liquid_tag(liquid_tag_tokenizer, parse_context) do |end_tag_name, end_tag_markup|
|
||||
next unless end_tag_name
|
||||
self.class.unknown_tag_in_liquid_tag(end_tag_name, end_tag_markup, &block)
|
||||
end
|
||||
end
|
||||
|
||||
private def parse_for_document(tokenizer, parse_context, &block)
|
||||
while token = tokenizer.shift
|
||||
while (token = tokenizer.shift)
|
||||
next if token.empty?
|
||||
case
|
||||
when token.start_with?(TAGSTART)
|
||||
@@ -58,21 +77,21 @@ module Liquid
|
||||
unless token =~ FullToken
|
||||
raise_missing_tag_terminator(token, parse_context)
|
||||
end
|
||||
tag_name = $2
|
||||
markup = $4
|
||||
tag_name = Regexp.last_match(2)
|
||||
markup = Regexp.last_match(4)
|
||||
|
||||
if parse_context.line_number
|
||||
# newlines inside the tag should increase the line number,
|
||||
# particularly important for multiline {% liquid %} tags
|
||||
parse_context.line_number += $1.count("\n".freeze) + $3.count("\n".freeze)
|
||||
parse_context.line_number += Regexp.last_match(1).count("\n") + Regexp.last_match(3).count("\n")
|
||||
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)
|
||||
if tag_name == 'liquid'
|
||||
parse_liquid_tag(markup, parse_context, &block)
|
||||
next
|
||||
end
|
||||
|
||||
unless tag = registered_tags[tag_name]
|
||||
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
|
||||
@@ -101,7 +120,7 @@ module Liquid
|
||||
def whitespace_handler(token, parse_context)
|
||||
if token[2] == WhitespaceControl
|
||||
previous_token = @nodelist.last
|
||||
if previous_token.is_a? String
|
||||
if previous_token.is_a?(String)
|
||||
previous_token.rstrip!
|
||||
end
|
||||
end
|
||||
@@ -113,14 +132,14 @@ module Liquid
|
||||
end
|
||||
|
||||
def render(context)
|
||||
render_to_output_buffer(context, '')
|
||||
render_to_output_buffer(context, +'')
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
context.resource_limits.render_score += @nodelist.length
|
||||
|
||||
idx = 0
|
||||
while node = @nodelist[idx]
|
||||
while (node = @nodelist[idx])
|
||||
previous_output_size = output.bytesize
|
||||
|
||||
case node
|
||||
@@ -129,7 +148,7 @@ module Liquid
|
||||
when Variable
|
||||
render_node(context, output, node)
|
||||
when Block
|
||||
render_node(context, node.blank? ? '' : output, node)
|
||||
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
|
||||
@@ -152,7 +171,13 @@ module Liquid
|
||||
private
|
||||
|
||||
def render_node(context, output, node)
|
||||
node.render_to_output_buffer(context, output)
|
||||
if node.disabled?(context)
|
||||
output << node.disabled_error_message
|
||||
return
|
||||
end
|
||||
disable_tags(context, node.disabled_tags) do
|
||||
node.render_to_output_buffer(context, output)
|
||||
end
|
||||
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
|
||||
context.handle_error(e, node.line_number)
|
||||
rescue ::StandardError => e
|
||||
@@ -160,10 +185,25 @@ module Liquid
|
||||
output << context.handle_error(e, line_number)
|
||||
end
|
||||
|
||||
def disable_tags(context, tags, &block)
|
||||
return yield if tags.empty?
|
||||
context.registers[:disabled_tags].disable(tags, &block)
|
||||
end
|
||||
|
||||
def raise_if_resource_limits_reached(context, length)
|
||||
context.resource_limits.render_length += length
|
||||
return unless context.resource_limits.reached?
|
||||
raise MemoryError.new("Memory limits exceeded".freeze)
|
||||
|
||||
error_message =
|
||||
if context.resource_limits.render_length_reached?
|
||||
MemoryError::RENDER_LENGTH_ERROR_MESSAGE
|
||||
elsif context.resource_limits.render_score_reached?
|
||||
MemoryError::RENDER_SCORE_ERROR_MESSAGE
|
||||
elsif context.resource_limits.assign_score_reached?
|
||||
MemoryError::ASSIGN_SCORE_ERROR_MESSAGE
|
||||
end
|
||||
|
||||
return unless error_message
|
||||
raise MemoryError, error_message
|
||||
end
|
||||
|
||||
def create_variable(token, parse_context)
|
||||
@@ -175,11 +215,11 @@ module Liquid
|
||||
end
|
||||
|
||||
def raise_missing_tag_terminator(token, parse_context)
|
||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_termination".freeze, token: token, tag_end: TagEnd.inspect))
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_termination", token: token, tag_end: TagEnd.inspect)
|
||||
end
|
||||
|
||||
def raise_missing_variable_terminator(token, parse_context)
|
||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.variable_termination".freeze, token: token, tag_end: VariableEnd.inspect))
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.variable_termination", token: token, tag_end: VariableEnd.inspect)
|
||||
end
|
||||
|
||||
def registered_tags
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Container for liquid nodes which conveniently wraps decision making logic
|
||||
#
|
||||
@@ -8,21 +10,21 @@ module Liquid
|
||||
#
|
||||
class Condition #:nodoc:
|
||||
@@operators = {
|
||||
'=='.freeze => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
|
||||
'!='.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
||||
'<>'.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
||||
'<'.freeze => :<,
|
||||
'>'.freeze => :>,
|
||||
'>='.freeze => :>=,
|
||||
'<='.freeze => :<=,
|
||||
'contains'.freeze => lambda do |cond, left, right|
|
||||
'==' => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
|
||||
'!=' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
||||
'<>' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
||||
'<' => :<,
|
||||
'>' => :>,
|
||||
'>=' => :>=,
|
||||
'<=' => :<=,
|
||||
'contains' => lambda do |_cond, left, right|
|
||||
if left && right && left.respond_to?(:include?)
|
||||
right = right.to_s if left.is_a?(String)
|
||||
left.include?(right)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
def self.operators
|
||||
@@ -33,9 +35,10 @@ module Liquid
|
||||
attr_accessor :left, :operator, :right
|
||||
|
||||
def initialize(left = nil, operator = nil, right = nil)
|
||||
@left = left
|
||||
@left = left
|
||||
@operator = operator
|
||||
@right = right
|
||||
@right = right
|
||||
|
||||
@child_relation = nil
|
||||
@child_condition = nil
|
||||
end
|
||||
@@ -60,12 +63,12 @@ module Liquid
|
||||
end
|
||||
|
||||
def or(condition)
|
||||
@child_relation = :or
|
||||
@child_relation = :or
|
||||
@child_condition = condition
|
||||
end
|
||||
|
||||
def and(condition)
|
||||
@child_relation = :and
|
||||
@child_relation = :and
|
||||
@child_condition = condition
|
||||
end
|
||||
|
||||
@@ -78,7 +81,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<Condition #{[@left, @operator, @right].compact.join(' '.freeze)}>"
|
||||
"#<Condition #{[@left, @operator, @right].compact.join(' ')}>"
|
||||
end
|
||||
|
||||
protected
|
||||
@@ -113,10 +116,10 @@ module Liquid
|
||||
# return this as the result.
|
||||
return context.evaluate(left) if op.nil?
|
||||
|
||||
left = context.evaluate(left)
|
||||
left = context.evaluate(left)
|
||||
right = context.evaluate(right)
|
||||
|
||||
operation = self.class.operators[op] || raise(Liquid::ArgumentError.new("Unknown operator #{op}"))
|
||||
operation = self.class.operators[op] || raise(Liquid::ArgumentError, "Unknown operator #{op}")
|
||||
|
||||
if operation.respond_to?(:call)
|
||||
operation.call(self, left, right)
|
||||
@@ -124,7 +127,7 @@ module Liquid
|
||||
begin
|
||||
left.send(operation, right)
|
||||
rescue ::ArgumentError => e
|
||||
raise Liquid::ArgumentError.new(e.message)
|
||||
raise Liquid::ArgumentError, e.message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Context keeps the variable stack and resolves variables, as well as keywords
|
||||
#
|
||||
@@ -16,18 +18,17 @@ module Liquid
|
||||
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
||||
|
||||
# rubocop:disable Metrics/ParameterLists
|
||||
def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_registers: {}, static_environments: {})
|
||||
new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_registers, static_environments)
|
||||
def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_environments: {})
|
||||
new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_environments)
|
||||
end
|
||||
|
||||
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_registers = {}, static_environments = {})
|
||||
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {})
|
||||
@environments = [environments]
|
||||
@environments.flatten!
|
||||
|
||||
@static_environments = [static_environments].flat_map(&:freeze).freeze
|
||||
@scopes = [(outer_scope || {})]
|
||||
@registers = registers
|
||||
@static_registers = static_registers.freeze
|
||||
@errors = []
|
||||
@partial = false
|
||||
@strict_variables = false
|
||||
@@ -35,15 +36,13 @@ module Liquid
|
||||
@base_scope_depth = 0
|
||||
squash_instance_assigns_with_environments
|
||||
|
||||
@this_stack_used = false
|
||||
|
||||
self.exception_renderer = Template.default_exception_renderer
|
||||
if rethrow_errors
|
||||
self.exception_renderer = ->(e) { raise }
|
||||
self.exception_renderer = ->(_e) { raise }
|
||||
end
|
||||
|
||||
@interrupts = []
|
||||
@filters = []
|
||||
@interrupts = []
|
||||
@filters = []
|
||||
@global_filter = nil
|
||||
end
|
||||
# rubocop:enable Metrics/ParameterLists
|
||||
@@ -53,7 +52,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def strainer
|
||||
@strainer ||= Strainer.create(self, @filters)
|
||||
@strainer ||= StrainerFactory.create(self, @filters)
|
||||
end
|
||||
|
||||
# Adds filters to this context.
|
||||
@@ -88,7 +87,7 @@ module Liquid
|
||||
def handle_error(e, line_number = nil)
|
||||
e = internal_error unless e.is_a?(Liquid::Error)
|
||||
e.template_name ||= template_name
|
||||
e.line_number ||= line_number
|
||||
e.line_number ||= line_number
|
||||
errors.push(e)
|
||||
exception_renderer.call(e).to_s
|
||||
end
|
||||
@@ -122,19 +121,11 @@ module Liquid
|
||||
# end
|
||||
#
|
||||
# context['var] #=> nil
|
||||
def stack(new_scope = nil)
|
||||
old_stack_used = @this_stack_used
|
||||
if new_scope
|
||||
push(new_scope)
|
||||
@this_stack_used = true
|
||||
else
|
||||
@this_stack_used = false
|
||||
end
|
||||
|
||||
def stack(new_scope = {})
|
||||
push(new_scope)
|
||||
yield
|
||||
ensure
|
||||
pop if @this_stack_used
|
||||
@this_stack_used = old_stack_used
|
||||
pop
|
||||
end
|
||||
|
||||
# Creates a new context inheriting resource limits, filters, environment etc.,
|
||||
@@ -145,13 +136,13 @@ module Liquid
|
||||
Context.build(
|
||||
resource_limits: resource_limits,
|
||||
static_environments: static_environments,
|
||||
static_registers: static_registers
|
||||
registers: StaticRegisters.new(registers)
|
||||
).tap do |subcontext|
|
||||
subcontext.base_scope_depth = base_scope_depth + 1
|
||||
subcontext.base_scope_depth = base_scope_depth + 1
|
||||
subcontext.exception_renderer = exception_renderer
|
||||
subcontext.filters = @filters
|
||||
subcontext.filters = @filters
|
||||
subcontext.strainer = nil
|
||||
subcontext.errors = errors
|
||||
subcontext.errors = errors
|
||||
subcontext.warnings = warnings
|
||||
end
|
||||
end
|
||||
@@ -162,10 +153,6 @@ module Liquid
|
||||
|
||||
# Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
|
||||
def []=(key, value)
|
||||
unless @this_stack_used
|
||||
@this_stack_used = true
|
||||
push({})
|
||||
end
|
||||
@scopes[0][key] = value
|
||||
end
|
||||
|
||||
@@ -201,7 +188,7 @@ module Liquid
|
||||
try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found)
|
||||
end
|
||||
|
||||
variable = variable.to_liquid
|
||||
variable = variable.to_liquid
|
||||
variable.context = self if variable.respond_to?(:context=)
|
||||
|
||||
variable
|
||||
@@ -215,7 +202,7 @@ module Liquid
|
||||
value = obj[key]
|
||||
|
||||
if value.is_a?(Proc) && obj.respond_to?(:[]=)
|
||||
obj[key] = (value.arity == 0) ? value.call : value.call(self)
|
||||
obj[key] = value.arity == 0 ? value.call : value.call(self)
|
||||
else
|
||||
value
|
||||
end
|
||||
@@ -246,7 +233,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def check_overflow
|
||||
raise StackLevelError, "Nesting too deep".freeze if overflow?
|
||||
raise StackLevelError, "Nesting too deep" if overflow?
|
||||
end
|
||||
|
||||
def overflow?
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Document < BlockBody
|
||||
def self.parse(tokens, parse_context)
|
||||
@@ -7,7 +9,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def parse(tokens, parse_context)
|
||||
super do |end_tag_name, end_tag_params|
|
||||
super do |end_tag_name, _end_tag_params|
|
||||
unknown_tag(end_tag_name, parse_context) if end_tag_name
|
||||
end
|
||||
rescue SyntaxError => e
|
||||
@@ -17,10 +19,10 @@ module Liquid
|
||||
|
||||
def unknown_tag(tag, parse_context)
|
||||
case tag
|
||||
when 'else'.freeze, 'end'.freeze
|
||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unexpected_outer_tag".freeze, tag: tag))
|
||||
when 'else', 'end'
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_outer_tag", tag: tag)
|
||||
else
|
||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unknown_tag".freeze, tag: tag))
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'set'
|
||||
|
||||
module Liquid
|
||||
@@ -25,7 +27,7 @@ module Liquid
|
||||
|
||||
# Catch all for the method
|
||||
def liquid_method_missing(method)
|
||||
return nil unless @context && @context.strict_variables
|
||||
return nil unless @context&.strict_variables
|
||||
raise Liquid::UndefinedDropMethod, "undefined method #{method}"
|
||||
end
|
||||
|
||||
@@ -67,7 +69,7 @@ module Liquid
|
||||
|
||||
if include?(Enumerable)
|
||||
blacklist += Enumerable.public_instance_methods
|
||||
blacklist -= [:sort, :count, :first, :min, :max, :include?]
|
||||
blacklist -= [:sort, :count, :first, :min, :max]
|
||||
end
|
||||
|
||||
whitelist = [:to_liquid] + (public_instance_methods - blacklist)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Error < ::StandardError
|
||||
attr_accessor :line_number
|
||||
@@ -5,7 +7,7 @@ module Liquid
|
||||
attr_accessor :markup_context
|
||||
|
||||
def to_s(with_prefix = true)
|
||||
str = ""
|
||||
str = +""
|
||||
str << message_prefix if with_prefix
|
||||
str << super()
|
||||
|
||||
@@ -20,12 +22,15 @@ module Liquid
|
||||
private
|
||||
|
||||
def message_prefix
|
||||
str = ""
|
||||
if is_a?(SyntaxError)
|
||||
str << "Liquid syntax error"
|
||||
else
|
||||
str << "Liquid error"
|
||||
end
|
||||
str = +""
|
||||
str <<
|
||||
if is_a?(SyntaxError)
|
||||
"Liquid syntax error"
|
||||
elsif is_a?(MemoryError)
|
||||
"Liquid memory limit error"
|
||||
else
|
||||
"Liquid error"
|
||||
end
|
||||
|
||||
if line_number
|
||||
str << " ("
|
||||
@@ -38,19 +43,24 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
ArgumentError = Class.new(Error)
|
||||
ContextError = Class.new(Error)
|
||||
FileSystemError = Class.new(Error)
|
||||
StandardError = Class.new(Error)
|
||||
SyntaxError = Class.new(Error)
|
||||
StackLevelError = Class.new(Error)
|
||||
TaintedError = Class.new(Error)
|
||||
MemoryError = Class.new(Error)
|
||||
ZeroDivisionError = Class.new(Error)
|
||||
FloatDomainError = Class.new(Error)
|
||||
UndefinedVariable = Class.new(Error)
|
||||
class MemoryError < Error
|
||||
RENDER_LENGTH_ERROR_MESSAGE = 'Too many bytes rendered.'
|
||||
RENDER_SCORE_ERROR_MESSAGE = 'Too many tags rendered.'
|
||||
ASSIGN_SCORE_ERROR_MESSAGE = 'Too many bytes assigned to variables.'
|
||||
end
|
||||
|
||||
ArgumentError = Class.new(Error)
|
||||
ContextError = Class.new(Error)
|
||||
FileSystemError = Class.new(Error)
|
||||
StandardError = Class.new(Error)
|
||||
SyntaxError = Class.new(Error)
|
||||
StackLevelError = Class.new(Error)
|
||||
TaintedError = Class.new(Error)
|
||||
ZeroDivisionError = Class.new(Error)
|
||||
FloatDomainError = Class.new(Error)
|
||||
UndefinedVariable = Class.new(Error)
|
||||
UndefinedDropMethod = Class.new(Error)
|
||||
UndefinedFilter = Class.new(Error)
|
||||
UndefinedFilter = Class.new(Error)
|
||||
MethodOverrideError = Class.new(Error)
|
||||
InternalError = Class.new(Error)
|
||||
InternalError = Class.new(Error)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Expression
|
||||
class MethodLiteral
|
||||
@@ -14,11 +16,11 @@ module Liquid
|
||||
end
|
||||
|
||||
LITERALS = {
|
||||
nil => nil, 'nil'.freeze => nil, 'null'.freeze => nil, ''.freeze => nil,
|
||||
'true'.freeze => true,
|
||||
'false'.freeze => false,
|
||||
'blank'.freeze => MethodLiteral.new(:blank?, '').freeze,
|
||||
'empty'.freeze => MethodLiteral.new(:empty?, '').freeze
|
||||
nil => nil, 'nil' => nil, 'null' => nil, '' => nil,
|
||||
'true' => true,
|
||||
'false' => false,
|
||||
'blank' => MethodLiteral.new(:blank?, '').freeze,
|
||||
'empty' => MethodLiteral.new(:empty?, '').freeze
|
||||
}.freeze
|
||||
|
||||
SINGLE_QUOTED_STRING = /\A'(.*)'\z/m
|
||||
@@ -33,13 +35,13 @@ module Liquid
|
||||
else
|
||||
case markup
|
||||
when SINGLE_QUOTED_STRING, DOUBLE_QUOTED_STRING
|
||||
$1
|
||||
Regexp.last_match(1)
|
||||
when INTEGERS_REGEX
|
||||
$1.to_i
|
||||
Regexp.last_match(1).to_i
|
||||
when RANGES_REGEX
|
||||
RangeLookup.parse($1, $2)
|
||||
RangeLookup.parse(Regexp.last_match(1), Regexp.last_match(2))
|
||||
when FLOATS_REGEX
|
||||
$1.to_f
|
||||
Regexp.last_match(1).to_f
|
||||
else
|
||||
VariableLookup.parse(markup)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'time'
|
||||
require 'date'
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# A Liquid file system is a way to let your templates retrieve other templates for use with the include tag.
|
||||
#
|
||||
@@ -44,8 +46,8 @@ module Liquid
|
||||
class LocalFileSystem
|
||||
attr_accessor :root
|
||||
|
||||
def initialize(root, pattern = "_%s.liquid".freeze)
|
||||
@root = root
|
||||
def initialize(root, pattern = "_%s.liquid")
|
||||
@root = root
|
||||
@pattern = pattern
|
||||
end
|
||||
|
||||
@@ -57,9 +59,9 @@ module Liquid
|
||||
end
|
||||
|
||||
def full_path(template_path)
|
||||
raise FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ /\A[^.\/][a-zA-Z0-9_\/]+\z/
|
||||
raise FileSystemError, "Illegal template name '#{template_path}'" unless %r{\A[^./][a-zA-Z0-9_/]+\z}.match?(template_path)
|
||||
|
||||
full_path = if template_path.include?('/'.freeze)
|
||||
full_path = if template_path.include?('/')
|
||||
File.join(root, File.dirname(template_path), @pattern % File.basename(template_path))
|
||||
else
|
||||
File.join(root, @pattern % template_path)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class ForloopDrop < Drop
|
||||
def initialize(name, length, parentloop)
|
||||
@name = name
|
||||
@length = length
|
||||
@name = name
|
||||
@length = length
|
||||
@parentloop = parentloop
|
||||
@index = 0
|
||||
@index = 0
|
||||
end
|
||||
|
||||
attr_reader :name, :length, :parentloop
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'yaml'
|
||||
|
||||
module Liquid
|
||||
@@ -26,13 +28,13 @@ module Liquid
|
||||
def interpolate(name, vars)
|
||||
name.gsub(/%\{(\w+)\}/) do
|
||||
# raise TranslationError, "Undefined key #{$1} for interpolation in translation #{name}" unless vars[$1.to_sym]
|
||||
(vars[$1.to_sym]).to_s
|
||||
(vars[Regexp.last_match(1).to_sym]).to_s
|
||||
end
|
||||
end
|
||||
|
||||
def deep_fetch_translation(name)
|
||||
name.split('.'.freeze).reduce(locale) do |level, cur|
|
||||
level[cur] or raise TranslationError, "Translation for #{name} does not exist in locale #{path}"
|
||||
name.split('.').reduce(locale) do |level, cur|
|
||||
level[cur] || raise(TranslationError, "Translation for #{name} does not exist in locale #{path}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# An interrupt is any command that breaks processing of a block (ex: a for loop).
|
||||
class Interrupt
|
||||
attr_reader :message
|
||||
|
||||
def initialize(message = nil)
|
||||
@message = message || "interrupt".freeze
|
||||
@message = message || "interrupt"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "strscan"
|
||||
module Liquid
|
||||
class Lexer
|
||||
SPECIALS = {
|
||||
'|'.freeze => :pipe,
|
||||
'.'.freeze => :dot,
|
||||
':'.freeze => :colon,
|
||||
','.freeze => :comma,
|
||||
'['.freeze => :open_square,
|
||||
']'.freeze => :close_square,
|
||||
'('.freeze => :open_round,
|
||||
')'.freeze => :close_round,
|
||||
'?'.freeze => :question,
|
||||
'-'.freeze => :dash
|
||||
'|' => :pipe,
|
||||
'.' => :dot,
|
||||
':' => :colon,
|
||||
',' => :comma,
|
||||
'[' => :open_square,
|
||||
']' => :close_square,
|
||||
'(' => :open_round,
|
||||
')' => :close_round,
|
||||
'?' => :question,
|
||||
'-' => :dash,
|
||||
}.freeze
|
||||
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
||||
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
||||
SINGLE_STRING_LITERAL = /'[^\']*'/
|
||||
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
||||
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
||||
DOTDOT = /\.\./
|
||||
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
|
||||
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
||||
DOTDOT = /\.\./
|
||||
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
|
||||
WHITESPACE_OR_NOTHING = /\s*/
|
||||
|
||||
def initialize(input)
|
||||
@@ -31,16 +33,21 @@ module Liquid
|
||||
until @ss.eos?
|
||||
@ss.skip(WHITESPACE_OR_NOTHING)
|
||||
break if @ss.eos?
|
||||
tok = case
|
||||
when t = @ss.scan(COMPARISON_OPERATOR) then [:comparison, t]
|
||||
when t = @ss.scan(SINGLE_STRING_LITERAL) then [:string, t]
|
||||
when t = @ss.scan(DOUBLE_STRING_LITERAL) then [:string, t]
|
||||
when t = @ss.scan(NUMBER_LITERAL) then [:number, t]
|
||||
when t = @ss.scan(IDENTIFIER) then [:id, t]
|
||||
when t = @ss.scan(DOTDOT) then [:dotdot, t]
|
||||
tok = if (t = @ss.scan(COMPARISON_OPERATOR))
|
||||
[:comparison, t]
|
||||
elsif (t = @ss.scan(SINGLE_STRING_LITERAL))
|
||||
[:string, t]
|
||||
elsif (t = @ss.scan(DOUBLE_STRING_LITERAL))
|
||||
[:string, t]
|
||||
elsif (t = @ss.scan(NUMBER_LITERAL))
|
||||
[:number, t]
|
||||
elsif (t = @ss.scan(IDENTIFIER))
|
||||
[:id, t]
|
||||
elsif (t = @ss.scan(DOTDOT))
|
||||
[:dotdot, t]
|
||||
else
|
||||
c = @ss.getch
|
||||
if s = SPECIALS[c]
|
||||
c = @ss.getch
|
||||
if (s = SPECIALS[c])
|
||||
[s, c]
|
||||
else
|
||||
raise SyntaxError, "Unexpected character #{c}"
|
||||
|
||||
@@ -25,3 +25,5 @@
|
||||
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
||||
argument:
|
||||
include: "Argument error in tag 'include' - Illegal template name"
|
||||
disabled:
|
||||
tag: "usage is not allowed in this context"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class ParseContext
|
||||
attr_accessor :locale, :line_number, :trim_whitespace, :depth
|
||||
@@ -5,9 +7,11 @@ module Liquid
|
||||
|
||||
def initialize(options = {})
|
||||
@template_options = options ? options.dup : {}
|
||||
@locale = @template_options[:locale] ||= I18n.new
|
||||
|
||||
@locale = @template_options[:locale] ||= I18n.new
|
||||
@warnings = []
|
||||
self.depth = 0
|
||||
|
||||
self.depth = 0
|
||||
self.partial = false
|
||||
end
|
||||
|
||||
@@ -18,8 +22,8 @@ module Liquid
|
||||
def partial=(value)
|
||||
@partial = value
|
||||
@options = value ? partial_options : @template_options
|
||||
|
||||
@error_mode = @options[:error_mode] || Template.error_mode
|
||||
value
|
||||
end
|
||||
|
||||
def partial_options
|
||||
@@ -28,7 +32,7 @@ module Liquid
|
||||
if dont_pass == true
|
||||
{ locale: locale }
|
||||
elsif dont_pass.is_a?(Array)
|
||||
@template_options.reject { |k, v| dont_pass.include?(k) }
|
||||
@template_options.reject { |k, _v| dont_pass.include?(k) }
|
||||
else
|
||||
@template_options
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def initialize(node, callbacks)
|
||||
@node = node
|
||||
@node = node
|
||||
@callbacks = callbacks
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ module Liquid
|
||||
item, new_context = @callbacks[node.class].call(node, context)
|
||||
[
|
||||
item,
|
||||
ParseTreeVisitor.for(node, @callbacks).visit(new_context || context)
|
||||
ParseTreeVisitor.for(node, @callbacks).visit(new_context || context),
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Parser
|
||||
def initialize(input)
|
||||
l = Lexer.new(input)
|
||||
l = Lexer.new(input)
|
||||
@tokens = l.tokenize
|
||||
@p = 0 # pointer to current location
|
||||
@p = 0 # pointer to current location
|
||||
end
|
||||
|
||||
def jump(point)
|
||||
@@ -51,7 +53,7 @@ module Liquid
|
||||
token = @tokens[@p]
|
||||
if token[0] == :id
|
||||
variable_signature
|
||||
elsif SINGLE_TOKEN_EXPRESSION_TYPES.include? token[0]
|
||||
elsif SINGLE_TOKEN_EXPRESSION_TYPES.include?(token[0])
|
||||
consume
|
||||
elsif token.first == :open_round
|
||||
consume
|
||||
@@ -66,10 +68,10 @@ module Liquid
|
||||
end
|
||||
|
||||
def argument
|
||||
str = ""
|
||||
str = +""
|
||||
# might be a keyword argument (identifier: expression)
|
||||
if look(:id) && look(:colon, 1)
|
||||
str << consume << consume << ' '.freeze
|
||||
str << consume << consume << ' '
|
||||
end
|
||||
|
||||
str << expression
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
module ParserSwitching
|
||||
def parse_with_selected_parser(markup)
|
||||
@@ -6,10 +8,10 @@ module Liquid
|
||||
when :lax then lax_parse(markup)
|
||||
when :warn
|
||||
begin
|
||||
return strict_parse_with_error_context(markup)
|
||||
strict_parse_with_error_context(markup)
|
||||
rescue SyntaxError => e
|
||||
parse_context.warnings << e
|
||||
return lax_parse(markup)
|
||||
lax_parse(markup)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -19,7 +21,7 @@ module Liquid
|
||||
def strict_parse_with_error_context(markup)
|
||||
strict_parse(markup)
|
||||
rescue SyntaxError => e
|
||||
e.line_number = line_number
|
||||
e.line_number = line_number
|
||||
e.markup_context = markup_context(markup)
|
||||
raise e
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class PartialCache
|
||||
def self.load(template_name, context:, parse_context:)
|
||||
@@ -6,10 +8,14 @@ module Liquid
|
||||
return cached if cached
|
||||
|
||||
file_system = (context.registers[:file_system] ||= Liquid::Template.file_system)
|
||||
source = file_system.read_template_file(template_name)
|
||||
source = file_system.read_template_file(template_name)
|
||||
|
||||
parse_context.partial = true
|
||||
|
||||
partial = Liquid::Template.parse(source, parse_context)
|
||||
template_factory = (context.registers[:template_factory] ||= Liquid::TemplateFactory.new)
|
||||
template = template_factory.for(template_name)
|
||||
|
||||
partial = template.parse(source, parse_context)
|
||||
cached_partials[template_name] = partial
|
||||
ensure
|
||||
parse_context.partial = false
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'liquid/profiler/hooks'
|
||||
|
||||
module Liquid
|
||||
@@ -44,7 +46,7 @@ module Liquid
|
||||
include Enumerable
|
||||
|
||||
class Timing
|
||||
attr_reader :code, :partial, :line_number, :children
|
||||
attr_reader :code, :partial, :line_number, :children, :total_time, :self_time
|
||||
|
||||
def initialize(node, partial)
|
||||
@code = node.respond_to?(:raw) ? node.raw : node
|
||||
@@ -62,7 +64,18 @@ module Liquid
|
||||
end
|
||||
|
||||
def finish
|
||||
@end_time = Time.now
|
||||
@end_time = Time.now
|
||||
@total_time = @end_time - @start_time
|
||||
|
||||
if @children.empty?
|
||||
@self_time = @total_time
|
||||
else
|
||||
total_children_time = 0
|
||||
@children.each do |child|
|
||||
total_children_time += child.total_time
|
||||
end
|
||||
@self_time = @total_time - total_children_time
|
||||
end
|
||||
end
|
||||
|
||||
def render_time
|
||||
@@ -96,14 +109,14 @@ module Liquid
|
||||
Thread.current[:liquid_profiler]
|
||||
end
|
||||
|
||||
def initialize
|
||||
@partial_stack = ["<root>"]
|
||||
def initialize(partial_name = "<root>")
|
||||
@partial_stack = [partial_name]
|
||||
|
||||
@root_timing = Timing.new("", current_partial)
|
||||
@root_timing = Timing.new("", current_partial)
|
||||
@timing_stack = [@root_timing]
|
||||
|
||||
@render_start_at = Time.now
|
||||
@render_end_at = @render_start_at
|
||||
@render_end_at = @render_start_at
|
||||
end
|
||||
|
||||
def start
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class BlockBody
|
||||
def render_node_with_profiling(context, output, node)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class RangeLookup
|
||||
def self.parse(start_markup, end_markup)
|
||||
start_obj = Expression.parse(start_markup)
|
||||
end_obj = Expression.parse(end_markup)
|
||||
end_obj = Expression.parse(end_markup)
|
||||
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
||||
new(start_obj, end_obj)
|
||||
else
|
||||
@@ -12,12 +14,12 @@ module Liquid
|
||||
|
||||
def initialize(start_obj, end_obj)
|
||||
@start_obj = start_obj
|
||||
@end_obj = end_obj
|
||||
@end_obj = end_obj
|
||||
end
|
||||
|
||||
def evaluate(context)
|
||||
start_int = to_integer(context.evaluate(@start_obj))
|
||||
end_int = to_integer(context.evaluate(@end_obj))
|
||||
end_int = to_integer(context.evaluate(@end_obj))
|
||||
start_int..end_int
|
||||
end
|
||||
|
||||
|
||||
6
lib/liquid/register.rb
Normal file
6
lib/liquid/register.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Register
|
||||
end
|
||||
end
|
||||
32
lib/liquid/registers/disabled_tags.rb
Normal file
32
lib/liquid/registers/disabled_tags.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
# frozen_string_literal: true
|
||||
module Liquid
|
||||
class DisabledTags < Register
|
||||
def initialize
|
||||
@disabled_tags = {}
|
||||
end
|
||||
|
||||
def disabled?(tag)
|
||||
@disabled_tags.key?(tag) && @disabled_tags[tag] > 0
|
||||
end
|
||||
|
||||
def disable(tags)
|
||||
tags.each(&method(:increment))
|
||||
yield
|
||||
ensure
|
||||
tags.each(&method(:decrement))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def increment(tag)
|
||||
@disabled_tags[tag] ||= 0
|
||||
@disabled_tags[tag] += 1
|
||||
end
|
||||
|
||||
def decrement(tag)
|
||||
@disabled_tags[tag] -= 1
|
||||
end
|
||||
end
|
||||
|
||||
Template.add_register(:disabled_tags, DisabledTags.new)
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class ResourceLimits
|
||||
attr_accessor :render_length, :render_score, :assign_score,
|
||||
@@ -5,15 +7,21 @@ module Liquid
|
||||
|
||||
def initialize(limits)
|
||||
@render_length_limit = limits[:render_length_limit]
|
||||
@render_score_limit = limits[:render_score_limit]
|
||||
@assign_score_limit = limits[:assign_score_limit]
|
||||
@render_score_limit = limits[:render_score_limit]
|
||||
@assign_score_limit = limits[:assign_score_limit]
|
||||
reset
|
||||
end
|
||||
|
||||
def reached?
|
||||
(@render_length_limit && @render_length > @render_length_limit) ||
|
||||
(@render_score_limit && @render_score > @render_score_limit) ||
|
||||
(@assign_score_limit && @assign_score > @assign_score_limit)
|
||||
def render_length_reached?
|
||||
@render_length_limit && @render_length > @render_length_limit
|
||||
end
|
||||
|
||||
def render_score_reached?
|
||||
@render_score_limit && @render_score > @render_score_limit
|
||||
end
|
||||
|
||||
def assign_score_reached?
|
||||
@assign_score_limit && @assign_score > @assign_score_limit
|
||||
end
|
||||
|
||||
def reset
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'cgi'
|
||||
require 'bigdecimal'
|
||||
|
||||
module Liquid
|
||||
module StandardFilters
|
||||
HTML_ESCAPE = {
|
||||
'&'.freeze => '&'.freeze,
|
||||
'>'.freeze => '>'.freeze,
|
||||
'<'.freeze => '<'.freeze,
|
||||
'"'.freeze => '"'.freeze,
|
||||
"'".freeze => '''.freeze
|
||||
'&' => '&',
|
||||
'>' => '>',
|
||||
'<' => '<',
|
||||
'"' => '"',
|
||||
"'" => ''',
|
||||
}.freeze
|
||||
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
||||
STRIP_HTML_BLOCKS = Regexp.union(
|
||||
/<script.*?<\/script>/m,
|
||||
STRIP_HTML_BLOCKS = Regexp.union(
|
||||
%r{<script.*?</script>}m,
|
||||
/<!--.*?-->/m,
|
||||
/<style.*?<\/style>/m
|
||||
%r{<style.*?</style>}m
|
||||
)
|
||||
STRIP_HTML_TAGS = /<.*?>/m
|
||||
|
||||
@@ -72,23 +74,28 @@ module Liquid
|
||||
end
|
||||
|
||||
# Truncate a string down to x characters
|
||||
def truncate(input, length = 50, truncate_string = "...".freeze)
|
||||
def truncate(input, length = 50, truncate_string = "...")
|
||||
return if input.nil?
|
||||
input_str = input.to_s
|
||||
length = Utils.to_integer(length)
|
||||
length = Utils.to_integer(length)
|
||||
|
||||
truncate_string_str = truncate_string.to_s
|
||||
|
||||
l = length - truncate_string_str.length
|
||||
l = 0 if l < 0
|
||||
|
||||
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
|
||||
end
|
||||
|
||||
def truncatewords(input, words = 15, truncate_string = "...".freeze)
|
||||
def truncatewords(input, words = 15, truncate_string = "...")
|
||||
return if input.nil?
|
||||
wordlist = input.to_s.split
|
||||
words = Utils.to_integer(words)
|
||||
words = Utils.to_integer(words)
|
||||
|
||||
l = words - 1
|
||||
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(" ").concat(truncate_string.to_s) : input
|
||||
end
|
||||
|
||||
# Split input string into an array of substrings separated by given pattern.
|
||||
@@ -113,7 +120,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def strip_html(input)
|
||||
empty = ''.freeze
|
||||
empty = ''
|
||||
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
||||
result.gsub!(STRIP_HTML_TAGS, empty)
|
||||
result
|
||||
@@ -121,18 +128,18 @@ module Liquid
|
||||
|
||||
# Remove all newlines from the string
|
||||
def strip_newlines(input)
|
||||
input.to_s.gsub(/\r?\n/, ''.freeze)
|
||||
input.to_s.gsub(/\r?\n/, '')
|
||||
end
|
||||
|
||||
# Join elements of the array with certain character between them
|
||||
def join(input, glue = ' '.freeze)
|
||||
InputIterator.new(input).join(glue)
|
||||
def join(input, glue = ' ')
|
||||
InputIterator.new(input, context).join(glue)
|
||||
end
|
||||
|
||||
# Sort elements of the array
|
||||
# provide optional property with which to sort an array of hashes or drops
|
||||
def sort(input, property = nil)
|
||||
ary = InputIterator.new(input)
|
||||
ary = InputIterator.new(input, context)
|
||||
|
||||
return [] if ary.empty?
|
||||
|
||||
@@ -152,7 +159,7 @@ module Liquid
|
||||
# Sort elements of an array ignoring case if strings
|
||||
# provide optional property with which to sort an array of hashes or drops
|
||||
def sort_natural(input, property = nil)
|
||||
ary = InputIterator.new(input)
|
||||
ary = InputIterator.new(input, context)
|
||||
|
||||
return [] if ary.empty?
|
||||
|
||||
@@ -172,7 +179,7 @@ module Liquid
|
||||
# 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)
|
||||
ary = InputIterator.new(input, context)
|
||||
|
||||
if ary.empty?
|
||||
[]
|
||||
@@ -194,7 +201,7 @@ module Liquid
|
||||
# Remove duplicate elements from an array
|
||||
# provide optional property with which to determine uniqueness
|
||||
def uniq(input, property = nil)
|
||||
ary = InputIterator.new(input)
|
||||
ary = InputIterator.new(input, context)
|
||||
|
||||
if property.nil?
|
||||
ary.uniq
|
||||
@@ -211,16 +218,16 @@ module Liquid
|
||||
|
||||
# Reverse the elements of an array
|
||||
def reverse(input)
|
||||
ary = InputIterator.new(input)
|
||||
ary = InputIterator.new(input, context)
|
||||
ary.reverse
|
||||
end
|
||||
|
||||
# map/collect on a given property
|
||||
def map(input, property)
|
||||
InputIterator.new(input).map do |e|
|
||||
InputIterator.new(input, context).map do |e|
|
||||
e = e.call if e.is_a?(Proc)
|
||||
|
||||
if property == "to_liquid".freeze
|
||||
if property == "to_liquid"
|
||||
e
|
||||
elsif e.respond_to?(:[])
|
||||
r = e[property]
|
||||
@@ -234,7 +241,7 @@ module Liquid
|
||||
# Remove nils within an array
|
||||
# provide optional property with which to check for nil
|
||||
def compact(input, property = nil)
|
||||
ary = InputIterator.new(input)
|
||||
ary = InputIterator.new(input, context)
|
||||
|
||||
if property.nil?
|
||||
ary.compact
|
||||
@@ -250,23 +257,23 @@ module Liquid
|
||||
end
|
||||
|
||||
# Replace occurrences of a string with another
|
||||
def replace(input, string, replacement = ''.freeze)
|
||||
def replace(input, string, replacement = '')
|
||||
input.to_s.gsub(string.to_s, replacement.to_s)
|
||||
end
|
||||
|
||||
# Replace the first occurrences of a string with another
|
||||
def replace_first(input, string, replacement = ''.freeze)
|
||||
def replace_first(input, string, replacement = '')
|
||||
input.to_s.sub(string.to_s, replacement.to_s)
|
||||
end
|
||||
|
||||
# remove a substring
|
||||
def remove(input, string)
|
||||
input.to_s.gsub(string.to_s, ''.freeze)
|
||||
input.to_s.gsub(string.to_s, '')
|
||||
end
|
||||
|
||||
# remove the first occurrences of a substring
|
||||
def remove_first(input, string)
|
||||
input.to_s.sub(string.to_s, ''.freeze)
|
||||
input.to_s.sub(string.to_s, '')
|
||||
end
|
||||
|
||||
# add one string to another
|
||||
@@ -276,9 +283,9 @@ module Liquid
|
||||
|
||||
def concat(input, array)
|
||||
unless array.respond_to?(:to_ary)
|
||||
raise ArgumentError.new("concat filter requires an array argument")
|
||||
raise ArgumentError, "concat filter requires an array argument"
|
||||
end
|
||||
InputIterator.new(input).concat(array)
|
||||
InputIterator.new(input, context).concat(array)
|
||||
end
|
||||
|
||||
# prepend a string to another
|
||||
@@ -288,7 +295,7 @@ module Liquid
|
||||
|
||||
# Add <br /> tags in front of all newlines in input string
|
||||
def newline_to_br(input)
|
||||
input.to_s.gsub(/\n/, "<br />\n".freeze)
|
||||
input.to_s.gsub(/\n/, "<br />\n")
|
||||
end
|
||||
|
||||
# Reformat a date using Ruby's core Time#strftime( string ) -> string
|
||||
@@ -325,7 +332,7 @@ module Liquid
|
||||
def date(input, format)
|
||||
return input if format.to_s.empty?
|
||||
|
||||
return input unless date = Utils.to_date(input)
|
||||
return input unless (date = Utils.to_date(input))
|
||||
|
||||
date.strftime(format.to_s)
|
||||
end
|
||||
@@ -419,19 +426,28 @@ module Liquid
|
||||
result.is_a?(BigDecimal) ? result.to_f : result
|
||||
end
|
||||
|
||||
def default(input, default_value = ''.freeze)
|
||||
if !input || input.respond_to?(:empty?) && input.empty?
|
||||
Usage.increment("default_filter_received_false_value") if input == false # See https://github.com/Shopify/liquid/issues/1127
|
||||
default_value
|
||||
else
|
||||
input
|
||||
end
|
||||
# Set a default value when the input is nil, false or empty
|
||||
#
|
||||
# Example:
|
||||
# {{ product.title | default: "No Title" }}
|
||||
#
|
||||
# Use `allow_false` when an input should only be tested against nil or empty and not false.
|
||||
#
|
||||
# Example:
|
||||
# {{ product.title | default: "No Title", allow_false: true }}
|
||||
#
|
||||
def default(input, default_value = '', options = {})
|
||||
options = {} unless options.is_a?(Hash)
|
||||
false_check = options['allow_false'] ? input.nil? : !input
|
||||
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :context
|
||||
|
||||
def raise_property_error(property)
|
||||
raise Liquid::ArgumentError.new("cannot select the property '#{property}'")
|
||||
raise Liquid::ArgumentError, "cannot select the property '#{property}'"
|
||||
end
|
||||
|
||||
def apply_operation(input, operand, operation)
|
||||
@@ -458,8 +474,9 @@ module Liquid
|
||||
class InputIterator
|
||||
include Enumerable
|
||||
|
||||
def initialize(input)
|
||||
@input = if input.is_a?(Array)
|
||||
def initialize(input, context)
|
||||
@context = context
|
||||
@input = if input.is_a?(Array)
|
||||
input.flatten
|
||||
elsif input.is_a?(Hash)
|
||||
[input]
|
||||
@@ -497,6 +514,7 @@ module Liquid
|
||||
|
||||
def each
|
||||
@input.each do |e|
|
||||
e.context = @context if e.respond_to?(:context=)
|
||||
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
|
||||
end
|
||||
end
|
||||
|
||||
36
lib/liquid/static_registers.rb
Normal file
36
lib/liquid/static_registers.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class StaticRegisters
|
||||
attr_reader :static, :registers
|
||||
|
||||
def initialize(registers = {})
|
||||
@static = registers.is_a?(StaticRegisters) ? registers.static : registers
|
||||
@registers = {}
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
@registers[key] = value
|
||||
end
|
||||
|
||||
def [](key)
|
||||
if @registers.key?(key)
|
||||
@registers[key]
|
||||
else
|
||||
@static[key]
|
||||
end
|
||||
end
|
||||
|
||||
def delete(key)
|
||||
@registers.delete(key)
|
||||
end
|
||||
|
||||
def fetch(key, default = nil)
|
||||
key?(key) ? self[key] : default
|
||||
end
|
||||
|
||||
def key?(key)
|
||||
@registers.key?(key) || @static.key?(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,66 +0,0 @@
|
||||
require 'set'
|
||||
|
||||
module Liquid
|
||||
# Strainer is the parent class for the filters system.
|
||||
# New filters are mixed into the strainer class which is then instantiated for each liquid template render run.
|
||||
#
|
||||
# The Strainer only allows method calls defined in filters given to it via Strainer.global_filter,
|
||||
# Context#add_filters or Template.register_filter
|
||||
class Strainer #:nodoc:
|
||||
@@global_strainer = Class.new(Strainer) do
|
||||
@filter_methods = Set.new
|
||||
end
|
||||
@@strainer_class_cache = Hash.new do |hash, filters|
|
||||
hash[filters] = Class.new(@@global_strainer) do
|
||||
@filter_methods = @@global_strainer.filter_methods.dup
|
||||
filters.each { |f| add_filter(f) }
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
class << self
|
||||
attr_reader :filter_methods
|
||||
end
|
||||
|
||||
def self.add_filter(filter)
|
||||
raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
|
||||
unless self.include?(filter)
|
||||
invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
|
||||
if invokable_non_public_methods.any?
|
||||
raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
|
||||
else
|
||||
send(:include, filter)
|
||||
@filter_methods.merge(filter.public_instance_methods.map(&:to_s))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.global_filter(filter)
|
||||
@@strainer_class_cache.clear
|
||||
@@global_strainer.add_filter(filter)
|
||||
end
|
||||
|
||||
def self.invokable?(method)
|
||||
@filter_methods.include?(method.to_s)
|
||||
end
|
||||
|
||||
def self.create(context, filters = [])
|
||||
@@strainer_class_cache[filters].new(context)
|
||||
end
|
||||
|
||||
def invoke(method, *args)
|
||||
if self.class.invokable?(method)
|
||||
send(method, *args)
|
||||
elsif @context && @context.strict_filters
|
||||
raise Liquid::UndefinedFilter, "undefined filter #{method}"
|
||||
else
|
||||
args.first
|
||||
end
|
||||
rescue ::ArgumentError => e
|
||||
raise Liquid::ArgumentError, e.message, e.backtrace
|
||||
end
|
||||
end
|
||||
end
|
||||
36
lib/liquid/strainer_factory.rb
Normal file
36
lib/liquid/strainer_factory.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# StrainerFactory is the factory for the filters system.
|
||||
module StrainerFactory
|
||||
extend self
|
||||
|
||||
def add_global_filter(filter)
|
||||
strainer_class_cache.clear
|
||||
global_filters << filter
|
||||
end
|
||||
|
||||
def create(context, filters = [])
|
||||
strainer_from_cache(filters).new(context)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def global_filters
|
||||
@global_filters ||= []
|
||||
end
|
||||
|
||||
def strainer_from_cache(filters)
|
||||
strainer_class_cache[filters] ||= begin
|
||||
klass = Class.new(StrainerTemplate)
|
||||
global_filters.each { |f| klass.add_filter(f) }
|
||||
filters.each { |f| klass.add_filter(f) }
|
||||
klass
|
||||
end
|
||||
end
|
||||
|
||||
def strainer_class_cache
|
||||
@strainer_class_cache ||= {}
|
||||
end
|
||||
end
|
||||
end
|
||||
53
lib/liquid/strainer_template.rb
Normal file
53
lib/liquid/strainer_template.rb
Normal file
@@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'set'
|
||||
|
||||
module Liquid
|
||||
# StrainerTemplate is the computed class for the filters system.
|
||||
# New filters are mixed into the strainer class which is then instantiated for each liquid template render run.
|
||||
#
|
||||
# The Strainer only allows method calls defined in filters given to it via StrainerFactory.add_global_filter,
|
||||
# Context#add_filters or Template.register_filter
|
||||
class StrainerTemplate
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
class << self
|
||||
def add_filter(filter)
|
||||
return if include?(filter)
|
||||
|
||||
invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
|
||||
if invokable_non_public_methods.any?
|
||||
raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
|
||||
end
|
||||
|
||||
include(filter)
|
||||
|
||||
filter_methods.merge(filter.public_instance_methods.map(&:to_s))
|
||||
end
|
||||
|
||||
def invokable?(method)
|
||||
filter_methods.include?(method.to_s)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filter_methods
|
||||
@filter_methods ||= Set.new
|
||||
end
|
||||
end
|
||||
|
||||
def invoke(method, *args)
|
||||
if self.class.invokable?(method)
|
||||
send(method, *args)
|
||||
elsif @context.strict_filters
|
||||
raise Liquid::UndefinedFilter, "undefined filter #{method}"
|
||||
else
|
||||
args.first
|
||||
end
|
||||
rescue ::ArgumentError => e
|
||||
raise Liquid::ArgumentError, e.message, e.backtrace
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,11 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class TablerowloopDrop < Drop
|
||||
def initialize(length, cols)
|
||||
@length = length
|
||||
@row = 1
|
||||
@col = 1
|
||||
@cols = cols
|
||||
@index = 0
|
||||
@row = 1
|
||||
@col = 1
|
||||
@cols = cols
|
||||
@index = 0
|
||||
end
|
||||
|
||||
attr_reader :length, :col, :row
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Tag
|
||||
attr_reader :nodelist, :tag_name, :line_number, :parse_context
|
||||
@@ -11,14 +13,22 @@ module Liquid
|
||||
tag
|
||||
end
|
||||
|
||||
def disable_tags(*tags)
|
||||
disabled_tags.push(*tags)
|
||||
end
|
||||
|
||||
private :new
|
||||
|
||||
def disabled_tags
|
||||
@disabled_tags ||= []
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, parse_context)
|
||||
@tag_name = tag_name
|
||||
@markup = markup
|
||||
@tag_name = tag_name
|
||||
@markup = markup
|
||||
@parse_context = parse_context
|
||||
@line_number = parse_context.line_number
|
||||
@line_number = parse_context.line_number
|
||||
end
|
||||
|
||||
def parse(_tokens)
|
||||
@@ -33,7 +43,15 @@ module Liquid
|
||||
end
|
||||
|
||||
def render(_context)
|
||||
''.freeze
|
||||
''
|
||||
end
|
||||
|
||||
def disabled?(context)
|
||||
context.registers[:disabled_tags].disabled?(tag_name)
|
||||
end
|
||||
|
||||
def disabled_error_message
|
||||
"#{tag_name} #{options[:locale].t('errors.disabled.tag')}"
|
||||
end
|
||||
|
||||
# For backwards compatibility with custom tags. In a future release, the semantics
|
||||
@@ -47,5 +65,9 @@ module Liquid
|
||||
def blank?
|
||||
false
|
||||
end
|
||||
|
||||
def disabled_tags
|
||||
self.class.disabled_tags
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Assign sets a variable in your template.
|
||||
#
|
||||
@@ -11,7 +13,7 @@ module Liquid
|
||||
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
|
||||
|
||||
def self.syntax_error_translation_key
|
||||
"errors.syntax.assign".freeze
|
||||
"errors.syntax.assign"
|
||||
end
|
||||
|
||||
attr_reader :to, :from
|
||||
@@ -19,10 +21,10 @@ module Liquid
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
if markup =~ Syntax
|
||||
@to = $1
|
||||
@from = Variable.new($2, options)
|
||||
@to = Regexp.last_match(1)
|
||||
@from = Variable.new(Regexp.last_match(2), options)
|
||||
else
|
||||
raise SyntaxError.new(options[:locale].t(self.class.syntax_error_translation_key))
|
||||
raise SyntaxError, options[:locale].t(self.class.syntax_error_translation_key)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -59,5 +61,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('assign'.freeze, Assign)
|
||||
Template.register_tag('assign', Assign)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Break tag to be used to break out of a for loop.
|
||||
#
|
||||
@@ -14,5 +16,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('break'.freeze, Break)
|
||||
Template.register_tag('break', Break)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Capture stores the result of a block into a variable without rendering it inplace.
|
||||
#
|
||||
@@ -16,9 +18,9 @@ module Liquid
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
if markup =~ Syntax
|
||||
@to = $1
|
||||
@to = Regexp.last_match(1)
|
||||
else
|
||||
raise SyntaxError.new(options[:locale].t("errors.syntax.capture"))
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.capture")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,5 +37,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('capture'.freeze, Capture)
|
||||
Template.register_tag('capture', Capture)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Case < Block
|
||||
Syntax = /(#{QuotedFragment})/o
|
||||
@@ -10,17 +12,15 @@ module Liquid
|
||||
@blocks = []
|
||||
|
||||
if markup =~ Syntax
|
||||
@left = Expression.parse($1)
|
||||
@left = Expression.parse(Regexp.last_match(1))
|
||||
else
|
||||
raise SyntaxError.new(options[:locale].t("errors.syntax.case".freeze))
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.case")
|
||||
end
|
||||
end
|
||||
|
||||
def parse(tokens)
|
||||
body = BlockBody.new
|
||||
while parse_body(body, tokens)
|
||||
body = @blocks.last.attachment
|
||||
end
|
||||
body = @blocks.last.attachment while parse_body(body, tokens)
|
||||
end
|
||||
|
||||
def nodelist
|
||||
@@ -29,9 +29,9 @@ module Liquid
|
||||
|
||||
def unknown_tag(tag, markup, tokens)
|
||||
case tag
|
||||
when 'when'.freeze
|
||||
when 'when'
|
||||
record_when_condition(markup)
|
||||
when 'else'.freeze
|
||||
when 'else'
|
||||
record_else_condition(markup)
|
||||
else
|
||||
super
|
||||
@@ -39,16 +39,14 @@ module Liquid
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
context.stack do
|
||||
execute_else_block = true
|
||||
execute_else_block = true
|
||||
|
||||
@blocks.each do |block|
|
||||
if block.else?
|
||||
block.attachment.render_to_output_buffer(context, output) if execute_else_block
|
||||
elsif block.evaluate(context)
|
||||
execute_else_block = false
|
||||
block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
@blocks.each do |block|
|
||||
if block.else?
|
||||
block.attachment.render_to_output_buffer(context, output) if execute_else_block
|
||||
elsif block.evaluate(context)
|
||||
execute_else_block = false
|
||||
block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -62,12 +60,12 @@ module Liquid
|
||||
|
||||
while markup
|
||||
unless markup =~ WhenSyntax
|
||||
raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_when".freeze))
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_when")
|
||||
end
|
||||
|
||||
markup = $2
|
||||
markup = Regexp.last_match(2)
|
||||
|
||||
block = Condition.new(@left, '=='.freeze, Expression.parse($1))
|
||||
block = Condition.new(@left, '==', Expression.parse(Regexp.last_match(1)))
|
||||
block.attach(body)
|
||||
@blocks << block
|
||||
end
|
||||
@@ -75,7 +73,7 @@ module Liquid
|
||||
|
||||
def record_else_condition(markup)
|
||||
unless markup.strip.empty?
|
||||
raise SyntaxError.new(options[:locale].t("errors.syntax.case_invalid_else".freeze))
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.case_invalid_else")
|
||||
end
|
||||
|
||||
block = ElseCondition.new
|
||||
@@ -90,5 +88,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('case'.freeze, Case)
|
||||
Template.register_tag('case', Case)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Comment < Block
|
||||
def render_to_output_buffer(_context, output)
|
||||
@@ -12,5 +14,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('comment'.freeze, Comment)
|
||||
Template.register_tag('comment', Comment)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Continue tag to be used to break out of a for loop.
|
||||
#
|
||||
@@ -14,5 +16,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('continue'.freeze, Continue)
|
||||
Template.register_tag('continue', Continue)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
|
||||
#
|
||||
@@ -21,38 +23,36 @@ module Liquid
|
||||
super
|
||||
case markup
|
||||
when NamedSyntax
|
||||
@variables = variables_from_string($2)
|
||||
@name = Expression.parse($1)
|
||||
@variables = variables_from_string(Regexp.last_match(2))
|
||||
@name = Expression.parse(Regexp.last_match(1))
|
||||
when SimpleSyntax
|
||||
@variables = variables_from_string(markup)
|
||||
@name = @variables.to_s
|
||||
@name = @variables.to_s
|
||||
else
|
||||
raise SyntaxError.new(options[:locale].t("errors.syntax.cycle".freeze))
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.cycle")
|
||||
end
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
context.registers[:cycle] ||= {}
|
||||
|
||||
context.stack do
|
||||
key = context.evaluate(@name)
|
||||
iteration = context.registers[:cycle][key].to_i
|
||||
key = context.evaluate(@name)
|
||||
iteration = context.registers[:cycle][key].to_i
|
||||
|
||||
val = 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 = 0 if iteration >= @variables.size
|
||||
context.registers[:cycle][key] = iteration
|
||||
if val.is_a?(Array)
|
||||
val = val.join
|
||||
elsif !val.is_a?(String)
|
||||
val = val.to_s
|
||||
end
|
||||
|
||||
output << val
|
||||
|
||||
iteration += 1
|
||||
iteration = 0 if iteration >= @variables.size
|
||||
|
||||
context.registers[:cycle][key] = iteration
|
||||
output
|
||||
end
|
||||
|
||||
@@ -61,7 +61,7 @@ module Liquid
|
||||
def variables_from_string(markup)
|
||||
markup.split(',').collect do |var|
|
||||
var =~ /\s*(#{QuotedFragment})\s*/o
|
||||
$1 ? Expression.parse($1) : nil
|
||||
Regexp.last_match(1) ? Expression.parse(Regexp.last_match(1)) : nil
|
||||
end.compact
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# decrement is used in a place where one needs to insert a counter
|
||||
# into a template, and needs the counter to survive across
|
||||
@@ -32,5 +34,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('decrement'.freeze, Decrement)
|
||||
Template.register_tag('decrement', Decrement)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Echo outputs an expression
|
||||
#
|
||||
@@ -16,9 +18,9 @@ module Liquid
|
||||
end
|
||||
|
||||
def render(context)
|
||||
@variable.render_to_output_buffer(context, '')
|
||||
@variable.render_to_output_buffer(context, +'')
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('echo'.freeze, Echo)
|
||||
Template.register_tag('echo', Echo)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# "For" iterates over an array or collection.
|
||||
# Several useful variables are available to you within the loop.
|
||||
@@ -66,7 +68,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def unknown_tag(tag, markup, tokens)
|
||||
return super unless tag == 'else'.freeze
|
||||
return super unless tag == 'else'
|
||||
@else_block = BlockBody.new
|
||||
end
|
||||
|
||||
@@ -86,31 +88,33 @@ module Liquid
|
||||
|
||||
def lax_parse(markup)
|
||||
if markup =~ Syntax
|
||||
@variable_name = $1
|
||||
collection_name = $2
|
||||
@reversed = !!$3
|
||||
@name = "#{@variable_name}-#{collection_name}"
|
||||
@variable_name = Regexp.last_match(1)
|
||||
collection_name = Regexp.last_match(2)
|
||||
@reversed = !!Regexp.last_match(3)
|
||||
@name = "#{@variable_name}-#{collection_name}"
|
||||
@collection_name = Expression.parse(collection_name)
|
||||
markup.scan(TagAttributes) do |key, value|
|
||||
set_attribute(key, value)
|
||||
end
|
||||
else
|
||||
raise SyntaxError.new(options[:locale].t("errors.syntax.for".freeze))
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.for")
|
||||
end
|
||||
end
|
||||
|
||||
def strict_parse(markup)
|
||||
p = Parser.new(markup)
|
||||
@variable_name = p.consume(:id)
|
||||
raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_in".freeze)) unless p.id?('in'.freeze)
|
||||
collection_name = p.expression
|
||||
@name = "#{@variable_name}-#{collection_name}"
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in')
|
||||
|
||||
collection_name = p.expression
|
||||
@collection_name = Expression.parse(collection_name)
|
||||
@reversed = p.id?('reversed'.freeze)
|
||||
|
||||
@name = "#{@variable_name}-#{collection_name}"
|
||||
@reversed = p.id?('reversed')
|
||||
|
||||
while p.look(:id) && p.look(:colon, 1)
|
||||
unless attribute = p.id?('limit'.freeze) || p.id?('offset'.freeze)
|
||||
raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_attribute".freeze))
|
||||
unless (attribute = p.id?('limit') || p.id?('offset'))
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
|
||||
end
|
||||
p.consume
|
||||
set_attribute(attribute, p.expression)
|
||||
@@ -154,7 +158,7 @@ module Liquid
|
||||
|
||||
def render_segment(context, output, segment)
|
||||
for_stack = context.registers[:for_stack] ||= []
|
||||
length = segment.length
|
||||
length = segment.length
|
||||
|
||||
context.stack do
|
||||
loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])
|
||||
@@ -162,7 +166,7 @@ module Liquid
|
||||
for_stack.push(loop_vars)
|
||||
|
||||
begin
|
||||
context['forloop'.freeze] = loop_vars
|
||||
context['forloop'] = loop_vars
|
||||
|
||||
segment.each do |item|
|
||||
context[@variable_name] = item
|
||||
@@ -170,11 +174,10 @@ module Liquid
|
||||
loop_vars.send(:increment!)
|
||||
|
||||
# Handle any interrupts if they exist.
|
||||
if context.interrupt?
|
||||
interrupt = context.pop_interrupt
|
||||
break if interrupt.is_a? BreakInterrupt
|
||||
next if interrupt.is_a? ContinueInterrupt
|
||||
end
|
||||
next unless context.interrupt?
|
||||
interrupt = context.pop_interrupt
|
||||
break if interrupt.is_a?(BreakInterrupt)
|
||||
next if interrupt.is_a?(ContinueInterrupt)
|
||||
end
|
||||
ensure
|
||||
for_stack.pop
|
||||
@@ -186,13 +189,13 @@ module Liquid
|
||||
|
||||
def set_attribute(key, expr)
|
||||
case key
|
||||
when 'offset'.freeze
|
||||
@from = if expr == 'continue'.freeze
|
||||
when 'offset'
|
||||
@from = if expr == 'continue'
|
||||
:continue
|
||||
else
|
||||
Expression.parse(expr)
|
||||
end
|
||||
when 'limit'.freeze
|
||||
when 'limit'
|
||||
@limit = Expression.parse(expr)
|
||||
end
|
||||
end
|
||||
@@ -212,5 +215,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('for'.freeze, For)
|
||||
Template.register_tag('for', For)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# If is the conditional block
|
||||
#
|
||||
@@ -10,16 +12,16 @@ module Liquid
|
||||
# There are {% if count < 5 %} less {% else %} more {% endif %} items than you need.
|
||||
#
|
||||
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
|
||||
BOOLEAN_OPERATORS = %w(and or).freeze
|
||||
BOOLEAN_OPERATORS = %w(and or).freeze
|
||||
|
||||
attr_reader :blocks
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
@blocks = []
|
||||
push_block('if'.freeze, markup)
|
||||
push_block('if', markup)
|
||||
end
|
||||
|
||||
def nodelist
|
||||
@@ -32,7 +34,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def unknown_tag(tag, markup, tokens)
|
||||
if ['elsif'.freeze, 'else'.freeze].include?(tag)
|
||||
if ['elsif', 'else'].include?(tag)
|
||||
push_block(tag, markup)
|
||||
else
|
||||
super
|
||||
@@ -40,11 +42,9 @@ module Liquid
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
context.stack do
|
||||
@blocks.each do |block|
|
||||
if block.evaluate(context)
|
||||
return block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
@blocks.each do |block|
|
||||
if block.evaluate(context)
|
||||
return block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,7 +54,7 @@ module Liquid
|
||||
private
|
||||
|
||||
def push_block(tag, markup)
|
||||
block = if tag == 'else'.freeze
|
||||
block = if tag == 'else'
|
||||
ElseCondition.new
|
||||
else
|
||||
parse_with_selected_parser(markup)
|
||||
@@ -66,17 +66,17 @@ module Liquid
|
||||
|
||||
def lax_parse(markup)
|
||||
expressions = markup.scan(ExpressionsAndOperators)
|
||||
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop =~ Syntax
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop =~ Syntax
|
||||
|
||||
condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
|
||||
condition = Condition.new(Expression.parse(Regexp.last_match(1)), Regexp.last_match(2), Expression.parse(Regexp.last_match(3)))
|
||||
|
||||
until expressions.empty?
|
||||
operator = expressions.pop.to_s.strip
|
||||
|
||||
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless expressions.pop.to_s =~ Syntax
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop.to_s =~ Syntax
|
||||
|
||||
new_condition = Condition.new(Expression.parse($1), $2, Expression.parse($3))
|
||||
raise(SyntaxError.new(options[:locale].t("errors.syntax.if".freeze))) unless BOOLEAN_OPERATORS.include?(operator)
|
||||
new_condition = Condition.new(Expression.parse(Regexp.last_match(1)), Regexp.last_match(2), Expression.parse(Regexp.last_match(3)))
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.if") unless BOOLEAN_OPERATORS.include?(operator)
|
||||
new_condition.send(operator, condition)
|
||||
condition = new_condition
|
||||
end
|
||||
@@ -94,7 +94,7 @@ module Liquid
|
||||
def parse_binary_comparisons(p)
|
||||
condition = parse_comparison(p)
|
||||
first_condition = condition
|
||||
while op = (p.id?('and'.freeze) || p.id?('or'.freeze))
|
||||
while (op = (p.id?('and') || p.id?('or')))
|
||||
child_condition = parse_comparison(p)
|
||||
condition.send(op, child_condition)
|
||||
condition = child_condition
|
||||
@@ -104,7 +104,7 @@ module Liquid
|
||||
|
||||
def parse_comparison(p)
|
||||
a = Expression.parse(p.expression)
|
||||
if op = p.consume?(:comparison)
|
||||
if (op = p.consume?(:comparison))
|
||||
b = Expression.parse(p.expression)
|
||||
Condition.new(a, op, b)
|
||||
else
|
||||
@@ -119,5 +119,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('if'.freeze, If)
|
||||
Template.register_tag('if', If)
|
||||
end
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Ifchanged < Block
|
||||
def render_to_output_buffer(context, output)
|
||||
context.stack do
|
||||
block_output = ''
|
||||
super(context, block_output)
|
||||
block_output = +''
|
||||
super(context, block_output)
|
||||
|
||||
if block_output != context.registers[:ifchanged]
|
||||
context.registers[:ifchanged] = block_output
|
||||
output << block_output
|
||||
end
|
||||
if block_output != context.registers[:ifchanged]
|
||||
context.registers[:ifchanged] = block_output
|
||||
output << block_output
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('ifchanged'.freeze, Ifchanged)
|
||||
Template.register_tag('ifchanged', Ifchanged)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Include allows templates to relate with other templates
|
||||
#
|
||||
@@ -14,28 +16,30 @@ module Liquid
|
||||
# {% include 'product' for products %}
|
||||
#
|
||||
class Include < Tag
|
||||
Syntax = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?/o
|
||||
SYNTAX = /(#{QuotedFragment}+)(\s+(?:with|for)\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
||||
Syntax = SYNTAX
|
||||
|
||||
attr_reader :template_name_expr, :variable_name_expr, :attributes
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
|
||||
if markup =~ Syntax
|
||||
if markup =~ SYNTAX
|
||||
|
||||
template_name = $1
|
||||
variable_name = $3
|
||||
template_name = Regexp.last_match(1)
|
||||
variable_name = Regexp.last_match(3)
|
||||
|
||||
@alias_name = Regexp.last_match(5)
|
||||
@variable_name_expr = variable_name ? Expression.parse(variable_name) : nil
|
||||
@template_name_expr = Expression.parse(template_name)
|
||||
@attributes = {}
|
||||
@attributes = {}
|
||||
|
||||
markup.scan(TagAttributes) do |key, value|
|
||||
@attributes[key] = Expression.parse(value)
|
||||
end
|
||||
|
||||
else
|
||||
raise SyntaxError.new(options[:locale].t("errors.syntax.include".freeze))
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.include")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,7 +48,7 @@ module Liquid
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
template_name = context.evaluate(@template_name_expr)
|
||||
raise ArgumentError.new(options[:locale].t("errors.argument.include")) unless template_name
|
||||
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name
|
||||
|
||||
partial = PartialCache.load(
|
||||
template_name,
|
||||
@@ -52,7 +56,7 @@ module Liquid
|
||||
parse_context: parse_context
|
||||
)
|
||||
|
||||
context_variable_name = template_name.split('/'.freeze).last
|
||||
context_variable_name = @alias_name || template_name.split('/').last
|
||||
|
||||
variable = if @variable_name_expr
|
||||
context.evaluate(@variable_name_expr)
|
||||
@@ -61,10 +65,10 @@ module Liquid
|
||||
end
|
||||
|
||||
old_template_name = context.template_name
|
||||
old_partial = context.partial
|
||||
old_partial = context.partial
|
||||
begin
|
||||
context.template_name = template_name
|
||||
context.partial = true
|
||||
context.partial = true
|
||||
context.stack do
|
||||
@attributes.each do |key, value|
|
||||
context[key] = context.evaluate(value)
|
||||
@@ -82,7 +86,7 @@ module Liquid
|
||||
end
|
||||
ensure
|
||||
context.template_name = old_template_name
|
||||
context.partial = old_partial
|
||||
context.partial = old_partial
|
||||
end
|
||||
|
||||
output
|
||||
@@ -95,11 +99,11 @@ module Liquid
|
||||
def children
|
||||
[
|
||||
@node.template_name_expr,
|
||||
@node.variable_name_expr
|
||||
@node.variable_name_expr,
|
||||
] + @node.attributes.values
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('include'.freeze, Include)
|
||||
Template.register_tag('include', Include)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# increment is used in a place where one needs to insert a counter
|
||||
# into a template, and needs the counter to survive across
|
||||
@@ -29,5 +31,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('increment'.freeze, Increment)
|
||||
Template.register_tag('increment', Increment)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Raw < Block
|
||||
Syntax = /\A\s*\z/
|
||||
@@ -10,16 +12,16 @@ module Liquid
|
||||
end
|
||||
|
||||
def parse(tokens)
|
||||
@body = ''
|
||||
while token = tokens.shift
|
||||
@body = +''
|
||||
while (token = tokens.shift)
|
||||
if token =~ FullTokenPossiblyInvalid
|
||||
@body << $1 if $1 != "".freeze
|
||||
return if block_delimiter == $2
|
||||
@body << Regexp.last_match(1) if Regexp.last_match(1) != ""
|
||||
return if block_delimiter == Regexp.last_match(2)
|
||||
end
|
||||
@body << token unless token.empty?
|
||||
end
|
||||
|
||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_never_closed", block_name: block_name)
|
||||
end
|
||||
|
||||
def render_to_output_buffer(_context, output)
|
||||
@@ -38,11 +40,11 @@ module Liquid
|
||||
protected
|
||||
|
||||
def ensure_valid_markup(tag_name, markup, parse_context)
|
||||
unless markup =~ Syntax
|
||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_unexpected_args".freeze, tag: tag_name))
|
||||
unless Syntax.match?(markup)
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.tag_unexpected_args", tag: tag_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('raw'.freeze, Raw)
|
||||
Template.register_tag('raw', Raw)
|
||||
end
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Render < Tag
|
||||
SYNTAX = /(#{QuotedString})#{QuotedFragment}*/o
|
||||
FOR = 'for'
|
||||
SYNTAX = /(#{QuotedString}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
||||
|
||||
disable_tags "include"
|
||||
|
||||
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
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.render") unless markup =~ SYNTAX
|
||||
|
||||
template_name = $1
|
||||
template_name = Regexp.last_match(1)
|
||||
with_or_for = Regexp.last_match(3)
|
||||
variable_name = Regexp.last_match(4)
|
||||
|
||||
@alias_name = Regexp.last_match(6)
|
||||
@variable_name_expr = variable_name ? Expression.parse(variable_name) : nil
|
||||
@template_name_expr = Expression.parse(template_name)
|
||||
@for = (with_or_for == FOR)
|
||||
|
||||
@attributes = {}
|
||||
markup.scan(TagAttributes) do |key, value|
|
||||
@@ -20,9 +30,13 @@ module Liquid
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
render_tag(context, output)
|
||||
end
|
||||
|
||||
def render_tag(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
|
||||
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name
|
||||
|
||||
partial = PartialCache.load(
|
||||
template_name,
|
||||
@@ -30,13 +44,29 @@ module Liquid
|
||||
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)
|
||||
context_variable_name = @alias_name || template_name.split('/').last
|
||||
|
||||
render_partial_func = ->(var, forloop) {
|
||||
inner_context = context.new_isolated_subcontext
|
||||
inner_context.template_name = template_name
|
||||
inner_context.partial = true
|
||||
inner_context['forloop'] = forloop if forloop
|
||||
|
||||
@attributes.each do |key, value|
|
||||
inner_context[key] = context.evaluate(value)
|
||||
end
|
||||
inner_context[context_variable_name] = var unless var.nil?
|
||||
partial.render_to_output_buffer(inner_context, output)
|
||||
forloop&.send(:increment!)
|
||||
}
|
||||
|
||||
variable = @variable_name_expr ? context.evaluate(@variable_name_expr) : nil
|
||||
if @for && variable.respond_to?(:each) && variable.respond_to?(:count)
|
||||
forloop = Liquid::ForloopDrop.new(template_name, variable.count, nil)
|
||||
variable.each { |var| render_partial_func.call(var, forloop) }
|
||||
else
|
||||
render_partial_func.call(variable, nil)
|
||||
end
|
||||
partial.render_to_output_buffer(inner_context, output)
|
||||
|
||||
output
|
||||
end
|
||||
@@ -50,5 +80,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('render'.freeze, Render)
|
||||
Template.register_tag('render', Render)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class TableRow < Block
|
||||
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
|
||||
@@ -7,33 +9,32 @@ module Liquid
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
if markup =~ Syntax
|
||||
@variable_name = $1
|
||||
@collection_name = Expression.parse($2)
|
||||
@attributes = {}
|
||||
@variable_name = Regexp.last_match(1)
|
||||
@collection_name = Expression.parse(Regexp.last_match(2))
|
||||
@attributes = {}
|
||||
markup.scan(TagAttributes) do |key, value|
|
||||
@attributes[key] = Expression.parse(value)
|
||||
end
|
||||
else
|
||||
raise SyntaxError.new(options[:locale].t("errors.syntax.table_row".freeze))
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.table_row")
|
||||
end
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
collection = context.evaluate(@collection_name) or return ''.freeze
|
||||
(collection = context.evaluate(@collection_name)) || (return '')
|
||||
|
||||
from = @attributes.key?('offset'.freeze) ? context.evaluate(@attributes['offset'.freeze]).to_i : 0
|
||||
to = @attributes.key?('limit'.freeze) ? from + context.evaluate(@attributes['limit'.freeze]).to_i : nil
|
||||
from = @attributes.key?('offset') ? context.evaluate(@attributes['offset']).to_i : 0
|
||||
to = @attributes.key?('limit') ? from + context.evaluate(@attributes['limit']).to_i : nil
|
||||
|
||||
collection = Utils.slice_collection(collection, from, to)
|
||||
length = collection.length
|
||||
|
||||
length = collection.length
|
||||
|
||||
cols = context.evaluate(@attributes['cols'.freeze]).to_i
|
||||
cols = context.evaluate(@attributes['cols']).to_i
|
||||
|
||||
output << "<tr class=\"row1\">\n"
|
||||
context.stack do
|
||||
tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
|
||||
context['tablerowloop'.freeze] = tablerowloop
|
||||
context['tablerowloop'] = tablerowloop
|
||||
|
||||
collection.each do |item|
|
||||
context[@variable_name] = item
|
||||
@@ -61,5 +62,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('tablerow'.freeze, TableRow)
|
||||
Template.register_tag('tablerow', TableRow)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'if'
|
||||
|
||||
module Liquid
|
||||
@@ -7,18 +9,16 @@ module Liquid
|
||||
#
|
||||
class Unless < If
|
||||
def render_to_output_buffer(context, output)
|
||||
context.stack do
|
||||
# First condition is interpreted backwards ( if not )
|
||||
first_block = @blocks.first
|
||||
unless first_block.evaluate(context)
|
||||
return first_block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
# First condition is interpreted backwards ( if not )
|
||||
first_block = @blocks.first
|
||||
unless first_block.evaluate(context)
|
||||
return first_block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
|
||||
# After the first condition unless works just like if
|
||||
@blocks[1..-1].each do |block|
|
||||
if block.evaluate(context)
|
||||
return block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
# After the first condition unless works just like if
|
||||
@blocks[1..-1].each do |block|
|
||||
if block.evaluate(context)
|
||||
return block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,5 +26,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('unless'.freeze, Unless)
|
||||
Template.register_tag('unless', Unless)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Templates are central to liquid.
|
||||
# Interpretating templates is a two step process. First you compile the
|
||||
@@ -16,13 +18,11 @@ module Liquid
|
||||
attr_accessor :root
|
||||
attr_reader :resource_limits, :warnings
|
||||
|
||||
@@file_system = BlankFileSystem.new
|
||||
|
||||
class TagRegistry
|
||||
include Enumerable
|
||||
|
||||
def initialize
|
||||
@tags = {}
|
||||
@tags = {}
|
||||
@cache = {}
|
||||
end
|
||||
|
||||
@@ -61,52 +61,59 @@ module Liquid
|
||||
# :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
|
||||
# :warn is the default and will give deprecation warnings when invalid syntax is used.
|
||||
# :strict will enforce correct syntax.
|
||||
attr_writer :error_mode
|
||||
attr_accessor :error_mode
|
||||
Template.error_mode = :lax
|
||||
|
||||
attr_reader :taint_mode
|
||||
|
||||
# Sets how strict the taint checker should be.
|
||||
# :lax is the default, and ignores the taint flag completely
|
||||
# :warn adds a warning, but does not interrupt the rendering
|
||||
# :error raises an error when tainted output is used
|
||||
attr_writer :taint_mode
|
||||
# @deprecated Since it is being deprecated in ruby itself.
|
||||
def taint_mode=(mode)
|
||||
taint_supported = Object.new.taint.tainted?
|
||||
if mode != :lax && !taint_supported
|
||||
raise NotImplementedError, "#{RUBY_ENGINE} #{RUBY_VERSION} doesn't support taint checking"
|
||||
end
|
||||
@taint_mode = mode
|
||||
end
|
||||
|
||||
Template.taint_mode = :lax
|
||||
|
||||
attr_accessor :default_exception_renderer
|
||||
Template.default_exception_renderer = lambda do |exception|
|
||||
exception
|
||||
end
|
||||
|
||||
def file_system
|
||||
@@file_system
|
||||
end
|
||||
attr_accessor :file_system
|
||||
Template.file_system = BlankFileSystem.new
|
||||
|
||||
def file_system=(obj)
|
||||
@@file_system = obj
|
||||
end
|
||||
attr_accessor :tags
|
||||
Template.tags = TagRegistry.new
|
||||
private :tags=
|
||||
|
||||
def register_tag(name, klass)
|
||||
tags[name.to_s] = klass
|
||||
end
|
||||
|
||||
def tags
|
||||
@tags ||= TagRegistry.new
|
||||
end
|
||||
attr_accessor :registers
|
||||
Template.registers = {}
|
||||
private :registers=
|
||||
|
||||
def error_mode
|
||||
@error_mode ||= :lax
|
||||
end
|
||||
|
||||
def taint_mode
|
||||
@taint_mode ||= :lax
|
||||
def add_register(name, klass)
|
||||
registers[name.to_sym] = klass
|
||||
end
|
||||
|
||||
# Pass a module with filter methods which should be available
|
||||
# to all liquid views. Good for registering the standard library
|
||||
def register_filter(mod)
|
||||
Strainer.global_filter(mod)
|
||||
StrainerFactory.add_global_filter(mod)
|
||||
end
|
||||
|
||||
def default_resource_limits
|
||||
@default_resource_limits ||= {}
|
||||
end
|
||||
attr_accessor :default_resource_limits
|
||||
Template.default_resource_limits = {}
|
||||
private :default_resource_limits=
|
||||
|
||||
# creates a new <tt>Template</tt> object from liquid source code
|
||||
# To enable profiling, pass in <tt>profile: true</tt> as an option.
|
||||
@@ -118,19 +125,19 @@ module Liquid
|
||||
end
|
||||
|
||||
def initialize
|
||||
@rethrow_errors = false
|
||||
@rethrow_errors = false
|
||||
@resource_limits = ResourceLimits.new(self.class.default_resource_limits)
|
||||
end
|
||||
|
||||
# Parse source code.
|
||||
# Returns self for easy chaining
|
||||
def parse(source, options = {})
|
||||
@options = options
|
||||
@profiling = options[:profile]
|
||||
@options = options
|
||||
@profiling = options[:profile]
|
||||
@line_numbers = options[:line_numbers] || @profiling
|
||||
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
||||
@root = Document.parse(tokenize(source), parse_context)
|
||||
@warnings = parse_context.warnings
|
||||
@root = Document.parse(tokenize(source), parse_context)
|
||||
@warnings = parse_context.warnings
|
||||
self
|
||||
end
|
||||
|
||||
@@ -165,19 +172,19 @@ module Liquid
|
||||
# filters and tags and might be useful to integrate liquid more with its host application
|
||||
#
|
||||
def render(*args)
|
||||
return ''.freeze if @root.nil?
|
||||
return '' if @root.nil?
|
||||
|
||||
context = case args.first
|
||||
when Liquid::Context
|
||||
c = args.shift
|
||||
|
||||
if @rethrow_errors
|
||||
c.exception_renderer = ->(e) { raise }
|
||||
c.exception_renderer = ->(_e) { raise }
|
||||
end
|
||||
|
||||
c
|
||||
when Liquid::Drop
|
||||
drop = args.shift
|
||||
drop = args.shift
|
||||
drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
|
||||
when Hash
|
||||
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
|
||||
@@ -189,18 +196,26 @@ module Liquid
|
||||
|
||||
output = nil
|
||||
|
||||
context_register = context.registers.is_a?(StaticRegisters) ? context.registers.static : context.registers
|
||||
|
||||
case args.last
|
||||
when Hash
|
||||
options = args.pop
|
||||
output = options[:output] if options[:output]
|
||||
output = options[:output] if options[:output]
|
||||
|
||||
registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
|
||||
options[:registers]&.each do |key, register|
|
||||
context_register[key] = register
|
||||
end
|
||||
|
||||
apply_options_to_context(context, options)
|
||||
when Module, Array
|
||||
context.add_filters(args.pop)
|
||||
end
|
||||
|
||||
Template.registers.each do |key, register|
|
||||
context_register[key] = register
|
||||
end
|
||||
|
||||
# Retrying a render resets resource usage
|
||||
context.resource_limits.reset
|
||||
|
||||
@@ -208,7 +223,7 @@ module Liquid
|
||||
# render the nodelist.
|
||||
# for performance reasons we get an array back here. join will make a string out of it.
|
||||
with_profiling(context) do
|
||||
@root.render_to_output_buffer(context, output || '')
|
||||
@root.render_to_output_buffer(context, output || +'')
|
||||
end
|
||||
rescue Liquid::MemoryError => e
|
||||
context.handle_error(e)
|
||||
@@ -236,7 +251,7 @@ module Liquid
|
||||
if @profiling && !context.partial
|
||||
raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler)
|
||||
|
||||
@profiler = Profiler.new
|
||||
@profiler = Profiler.new(context.template_name)
|
||||
@profiler.start
|
||||
|
||||
begin
|
||||
@@ -251,10 +266,10 @@ module Liquid
|
||||
|
||||
def apply_options_to_context(context, options)
|
||||
context.add_filters(options[:filters]) if options[:filters]
|
||||
context.global_filter = options[:global_filter] if options[:global_filter]
|
||||
context.global_filter = options[:global_filter] if options[:global_filter]
|
||||
context.exception_renderer = options[:exception_renderer] if options[:exception_renderer]
|
||||
context.strict_variables = options[:strict_variables] if options[:strict_variables]
|
||||
context.strict_filters = options[:strict_filters] if options[:strict_filters]
|
||||
context.strict_variables = options[:strict_variables] if options[:strict_variables]
|
||||
context.strict_filters = options[:strict_filters] if options[:strict_filters]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
9
lib/liquid/template_factory.rb
Normal file
9
lib/liquid/template_factory.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class TemplateFactory
|
||||
def for(_template_name)
|
||||
Liquid::Template.new
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,16 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class Tokenizer
|
||||
attr_reader :line_number, :for_liquid_tag
|
||||
|
||||
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
|
||||
@source = source
|
||||
@line_number = line_number || (line_numbers ? 1 : nil)
|
||||
@source = source
|
||||
@line_number = line_number || (line_numbers ? 1 : nil)
|
||||
@for_liquid_tag = for_liquid_tag
|
||||
@tokens = tokenize
|
||||
@tokens = tokenize
|
||||
end
|
||||
|
||||
def shift
|
||||
token = @tokens.shift or return
|
||||
(token = @tokens.shift) || return
|
||||
|
||||
if @line_number
|
||||
@line_number += @for_liquid_tag ? 1 : token.count("\n")
|
||||
@@ -29,7 +31,7 @@ module Liquid
|
||||
tokens = @source.split(TemplateParser)
|
||||
|
||||
# removes the rogue empty element at the beginning of the array
|
||||
tokens.shift if tokens[0] && tokens[0].empty?
|
||||
tokens.shift if tokens[0]&.empty?
|
||||
|
||||
tokens
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
module Usage
|
||||
def self.increment(name)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
module Utils
|
||||
def self.slice_collection(collection, from, to)
|
||||
@@ -10,7 +12,7 @@ module Liquid
|
||||
|
||||
def self.slice_collection_using_each(collection, from, to)
|
||||
segments = []
|
||||
index = 0
|
||||
index = 0
|
||||
|
||||
# Maintains Ruby 1.8.7 String#each behaviour on 1.9
|
||||
if collection.is_a?(String)
|
||||
@@ -50,7 +52,7 @@ module Liquid
|
||||
when Numeric
|
||||
obj
|
||||
when String
|
||||
(obj.strip =~ /\A-?\d+\.\d+\z/) ? BigDecimal(obj) : obj.to_i
|
||||
/\A-?\d+\.\d+\z/.match?(obj.strip) ? BigDecimal(obj) : obj.to_i
|
||||
else
|
||||
if obj.respond_to?(:to_number)
|
||||
obj.to_number
|
||||
@@ -69,7 +71,7 @@ module Liquid
|
||||
end
|
||||
|
||||
case obj
|
||||
when 'now'.freeze, 'today'.freeze
|
||||
when 'now', 'today'
|
||||
Time.now
|
||||
when /\A\d+\z/, Integer
|
||||
Time.at(obj.to_i)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# Holds variables. Variables are only loaded "just in time"
|
||||
# and are not evaluated as part of the render stage
|
||||
@@ -10,10 +12,10 @@ module Liquid
|
||||
# {{ user | link }}
|
||||
#
|
||||
class Variable
|
||||
FilterMarkupRegex = /#{FilterSeparator}\s*(.*)/om
|
||||
FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
|
||||
FilterArgsRegex = /(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o
|
||||
JustTagAttributes = /\A#{TagAttributes}\z/o
|
||||
FilterMarkupRegex = /#{FilterSeparator}\s*(.*)/om
|
||||
FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
|
||||
FilterArgsRegex = /(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o
|
||||
JustTagAttributes = /\A#{TagAttributes}\z/o
|
||||
MarkupWithQuotedFragment = /(#{QuotedFragment})(.*)/om
|
||||
|
||||
attr_accessor :filters, :name, :line_number
|
||||
@@ -23,10 +25,10 @@ module Liquid
|
||||
include ParserSwitching
|
||||
|
||||
def initialize(markup, parse_context)
|
||||
@markup = markup
|
||||
@name = nil
|
||||
@markup = markup
|
||||
@name = nil
|
||||
@parse_context = parse_context
|
||||
@line_number = parse_context.line_number
|
||||
@line_number = parse_context.line_number
|
||||
|
||||
parse_with_selected_parser(markup)
|
||||
end
|
||||
@@ -43,11 +45,11 @@ module Liquid
|
||||
@filters = []
|
||||
return unless markup =~ MarkupWithQuotedFragment
|
||||
|
||||
name_markup = $1
|
||||
filter_markup = $2
|
||||
@name = Expression.parse(name_markup)
|
||||
name_markup = Regexp.last_match(1)
|
||||
filter_markup = Regexp.last_match(2)
|
||||
@name = Expression.parse(name_markup)
|
||||
if filter_markup =~ FilterMarkupRegex
|
||||
filters = $1.scan(FilterParser)
|
||||
filters = Regexp.last_match(1).scan(FilterParser)
|
||||
filters.each do |f|
|
||||
next unless f =~ /\w+/
|
||||
filtername = Regexp.last_match(0)
|
||||
@@ -102,14 +104,22 @@ module Liquid
|
||||
output
|
||||
end
|
||||
|
||||
def disabled?(_context)
|
||||
false
|
||||
end
|
||||
|
||||
def disabled_tags
|
||||
[]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_filter_expressions(filter_name, unparsed_args)
|
||||
filter_args = []
|
||||
filter_args = []
|
||||
keyword_args = nil
|
||||
unparsed_args.each do |a|
|
||||
if matches = a.match(JustTagAttributes)
|
||||
keyword_args ||= {}
|
||||
if (matches = a.match(JustTagAttributes))
|
||||
keyword_args ||= {}
|
||||
keyword_args[matches[1]] = Expression.parse(matches[2])
|
||||
else
|
||||
filter_args << Expression.parse(a)
|
||||
@@ -121,7 +131,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def evaluate_filter_expressions(context, filter_args, filter_kwargs)
|
||||
parsed_args = filter_args.map{ |expr| context.evaluate(expr) }
|
||||
parsed_args = filter_args.map { |expr| context.evaluate(expr) }
|
||||
if filter_kwargs
|
||||
parsed_kwargs = {}
|
||||
filter_kwargs.each do |key, expr|
|
||||
@@ -133,14 +143,14 @@ module Liquid
|
||||
end
|
||||
|
||||
def taint_check(context, obj)
|
||||
return unless obj.tainted?
|
||||
return if Template.taint_mode == :lax
|
||||
return unless obj.tainted?
|
||||
|
||||
@markup =~ QuotedFragment
|
||||
name = Regexp.last_match(0)
|
||||
|
||||
error = TaintedError.new("variable '#{name}' is tainted and was not escaped")
|
||||
error.line_number = line_number
|
||||
error = TaintedError.new("variable '#{name}' is tainted and was not escaped")
|
||||
error.line_number = line_number
|
||||
error.template_name = context.template_name
|
||||
|
||||
case Template.taint_mode
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class VariableLookup
|
||||
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
|
||||
COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze].freeze
|
||||
COMMAND_METHODS = ['size', 'first', 'last'].freeze
|
||||
|
||||
attr_reader :name, :lookups
|
||||
|
||||
@@ -14,17 +16,17 @@ module Liquid
|
||||
|
||||
name = lookups.shift
|
||||
if name =~ SQUARE_BRACKETED
|
||||
name = Expression.parse($1)
|
||||
name = Expression.parse(Regexp.last_match(1))
|
||||
end
|
||||
@name = name
|
||||
|
||||
@lookups = lookups
|
||||
@lookups = lookups
|
||||
@command_flags = 0
|
||||
|
||||
@lookups.each_index do |i|
|
||||
lookup = lookups[i]
|
||||
if lookup =~ SQUARE_BRACKETED
|
||||
lookups[i] = Expression.parse($1)
|
||||
lookups[i] = Expression.parse(Regexp.last_match(1))
|
||||
elsif COMMAND_METHODS.include?(lookup)
|
||||
@command_flags |= 1 << i
|
||||
end
|
||||
@@ -32,7 +34,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def evaluate(context)
|
||||
name = context.evaluate(@name)
|
||||
name = context.evaluate(@name)
|
||||
object = context.find_variable(name)
|
||||
|
||||
@lookups.each_index do |i|
|
||||
@@ -45,7 +47,7 @@ module Liquid
|
||||
(object.respond_to?(:fetch) && key.is_a?(Integer)))
|
||||
|
||||
# if its a proc we will replace the entry with the proc
|
||||
res = context.lookup_and_evaluate(object, key)
|
||||
res = context.lookup_and_evaluate(object, key)
|
||||
object = res.to_liquid
|
||||
|
||||
# Some special cases. If the part wasn't in square brackets and
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# encoding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
VERSION = "4.0.3".freeze
|
||||
VERSION = "4.0.3"
|
||||
end
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# encoding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
lib = File.expand_path('../lib/', __FILE__)
|
||||
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
|
||||
require "liquid/version"
|
||||
|
||||
@@ -26,6 +27,6 @@ Gem::Specification.new do |s|
|
||||
|
||||
s.require_path = "lib"
|
||||
|
||||
s.add_development_dependency 'rake', '~> 11.3'
|
||||
s.add_development_dependency 'minitest'
|
||||
s.add_development_dependency('rake', '~> 11.3')
|
||||
s.add_development_dependency('minitest')
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'benchmark/ips'
|
||||
require_relative 'theme_runner'
|
||||
|
||||
@@ -5,7 +7,7 @@ Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
|
||||
profiler = ThemeRunner.new
|
||||
|
||||
Benchmark.ips do |x|
|
||||
x.time = 10
|
||||
x.time = 10
|
||||
x.warmup = 5
|
||||
|
||||
puts
|
||||
|
||||
@@ -21,8 +21,8 @@ class Profiler
|
||||
end
|
||||
|
||||
def profile(phase, &block)
|
||||
print LOG_LABEL
|
||||
print "#{phase}.. ".ljust(10)
|
||||
print(LOG_LABEL)
|
||||
print("#{phase}.. ".ljust(10))
|
||||
report = MemoryProfiler.report(&block)
|
||||
puts 'Done.'
|
||||
@headings << phase.capitalize
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'stackprof'
|
||||
require_relative 'theme_runner'
|
||||
|
||||
@@ -13,7 +15,7 @@ profiler.run
|
||||
end
|
||||
end
|
||||
|
||||
if profile_type == :cpu && graph_filename = ENV['GRAPH_FILENAME']
|
||||
if profile_type == :cpu && (graph_filename = ENV['GRAPH_FILENAME'])
|
||||
File.open(graph_filename, 'w') do |f|
|
||||
StackProf::Report.new(results).print_graphviz(nil, f)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CommentForm < Liquid::Block
|
||||
Syntax = /(#{Liquid::VariableSignature}+)/
|
||||
|
||||
@@ -5,10 +7,10 @@ class CommentForm < Liquid::Block
|
||||
super
|
||||
|
||||
if markup =~ Syntax
|
||||
@variable_name = $1
|
||||
@attributes = {}
|
||||
@variable_name = Regexp.last_match(1)
|
||||
@attributes = {}
|
||||
else
|
||||
raise SyntaxError.new("Syntax Error in 'comment_form' - Valid syntax: comment_form [article]")
|
||||
raise SyntaxError, "Syntax Error in 'comment_form' - Valid syntax: comment_form [article]"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,8 +22,8 @@ class CommentForm < Liquid::Block
|
||||
'posted_successfully?' => context.registers[:posted_successfully],
|
||||
'errors' => context['comment.errors'],
|
||||
'author' => context['comment.author'],
|
||||
'email' => context['comment.email'],
|
||||
'body' => context['comment.body']
|
||||
'email' => context['comment.email'],
|
||||
'body' => context['comment.body'],
|
||||
}
|
||||
|
||||
output << wrap_in_form(article, render_all(@nodelist, context, output))
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'yaml'
|
||||
|
||||
module Database
|
||||
@@ -16,9 +18,10 @@ module Database
|
||||
end
|
||||
|
||||
# key the tables by handles, as this is how liquid expects it.
|
||||
db = db.inject({}) do |assigns, (key, values)|
|
||||
assigns[key] = values.inject({}) { |h, v| h[v['handle']] = v; h; }
|
||||
assigns
|
||||
db = db.each_with_object({}) do |(key, values), assigns|
|
||||
assigns[key] = values.each_with_object({}) do |v, h|
|
||||
h[v['handle']] = v
|
||||
end
|
||||
end
|
||||
|
||||
# Some standard direct accessors so that the specialized templates
|
||||
@@ -29,9 +32,9 @@ module Database
|
||||
db['article'] = db['blog']['articles'].first
|
||||
|
||||
db['cart'] = {
|
||||
'total_price' => db['line_items'].values.inject(0) { |sum, item| sum += item['line_price'] * item['quantity'] },
|
||||
'item_count' => db['line_items'].values.inject(0) { |sum, item| sum += item['quantity'] },
|
||||
'items' => db['line_items'].values
|
||||
'total_price' => db['line_items'].values.inject(0) { |sum, item| sum + item['line_price'] * item['quantity'] },
|
||||
'item_count' => db['line_items'].values.inject(0) { |sum, item| sum + item['quantity'] },
|
||||
'items' => db['line_items'].values,
|
||||
}
|
||||
|
||||
db
|
||||
@@ -40,6 +43,6 @@ module Database
|
||||
end
|
||||
|
||||
if __FILE__ == $PROGRAM_NAME
|
||||
p Database.tables['collections']['frontpage'].keys
|
||||
p(Database.tables['collections']['frontpage'].keys)
|
||||
# p Database.tables['blog']['articles']
|
||||
end
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
|
||||
module JsonFilter
|
||||
def json(object)
|
||||
JSON.dump(object.reject { |k, v| k == "collections" })
|
||||
JSON.dump(object.reject { |k, _v| k == "collections" })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
$:.unshift __dir__ + '/../../lib'
|
||||
# frozen_string_literal: true
|
||||
|
||||
$LOAD_PATH.unshift(__dir__ + '/../../lib')
|
||||
require_relative '../../lib/liquid'
|
||||
|
||||
require_relative 'comment_form'
|
||||
@@ -9,11 +11,11 @@ require_relative 'shop_filter'
|
||||
require_relative 'tag_filter'
|
||||
require_relative 'weight_filter'
|
||||
|
||||
Liquid::Template.register_tag 'paginate', Paginate
|
||||
Liquid::Template.register_tag 'form', CommentForm
|
||||
Liquid::Template.register_tag('paginate', Paginate)
|
||||
Liquid::Template.register_tag('form', CommentForm)
|
||||
|
||||
Liquid::Template.register_filter JsonFilter
|
||||
Liquid::Template.register_filter MoneyFilter
|
||||
Liquid::Template.register_filter WeightFilter
|
||||
Liquid::Template.register_filter ShopFilter
|
||||
Liquid::Template.register_filter TagFilter
|
||||
Liquid::Template.register_filter(JsonFilter)
|
||||
Liquid::Template.register_filter(MoneyFilter)
|
||||
Liquid::Template.register_filter(WeightFilter)
|
||||
Liquid::Template.register_filter(ShopFilter)
|
||||
Liquid::Template.register_filter(TagFilter)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module MoneyFilter
|
||||
def money_with_currency(money)
|
||||
return '' if money.nil?
|
||||
sprintf("$ %.2f USD", money / 100.0)
|
||||
format("$ %.2f USD", money / 100.0)
|
||||
end
|
||||
|
||||
def money(money)
|
||||
return '' if money.nil?
|
||||
sprintf("$ %.2f", money / 100.0)
|
||||
format("$ %.2f", money / 100.0)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Paginate < Liquid::Block
|
||||
Syntax = /(#{Liquid::QuotedFragment})\s*(by\s*(\d+))?/
|
||||
Syntax = /(#{Liquid::QuotedFragment})\s*(by\s*(\d+))?/
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
|
||||
if markup =~ Syntax
|
||||
@collection_name = $1
|
||||
@page_size = if $2
|
||||
$3.to_i
|
||||
@collection_name = Regexp.last_match(1)
|
||||
@page_size = if Regexp.last_match(2)
|
||||
Regexp.last_match(3).to_i
|
||||
else
|
||||
20
|
||||
end
|
||||
@@ -17,7 +19,7 @@ class Paginate < Liquid::Block
|
||||
@attributes[key] = value
|
||||
end
|
||||
else
|
||||
raise SyntaxError.new("Syntax Error in tag 'paginate' - Valid syntax: paginate [collection] by number")
|
||||
raise SyntaxError, "Syntax Error in tag 'paginate' - Valid syntax: paginate [collection] by number"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,19 +27,19 @@ class Paginate < Liquid::Block
|
||||
@context = context
|
||||
|
||||
context.stack do
|
||||
current_page = context['current_page'].to_i
|
||||
current_page = context['current_page'].to_i
|
||||
|
||||
pagination = {
|
||||
'page_size' => @page_size,
|
||||
'current_page' => 5,
|
||||
'current_offset' => @page_size * 5
|
||||
'page_size' => @page_size,
|
||||
'current_page' => 5,
|
||||
'current_offset' => @page_size * 5,
|
||||
}
|
||||
|
||||
context['paginate'] = pagination
|
||||
|
||||
collection_size = context[@collection_name].size
|
||||
collection_size = context[@collection_name].size
|
||||
|
||||
raise ArgumentError.new("Cannot paginate array '#{@collection_name}'. Not found.") if collection_size.nil?
|
||||
raise ArgumentError, "Cannot paginate array '#{@collection_name}'. Not found." if collection_size.nil?
|
||||
|
||||
page_count = (collection_size.to_f / @page_size.to_f).to_f.ceil + 1
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ShopFilter
|
||||
def asset_url(input)
|
||||
"/files/1/[shop_id]/[shop_id]/assets/#{input}"
|
||||
@@ -29,7 +31,7 @@ module ShopFilter
|
||||
|
||||
def link_to_vendor(vendor)
|
||||
if vendor
|
||||
link_to vendor, url_for_vendor(vendor), vendor
|
||||
link_to(vendor, url_for_vendor(vendor), vendor)
|
||||
else
|
||||
'Unknown Vendor'
|
||||
end
|
||||
@@ -37,7 +39,7 @@ module ShopFilter
|
||||
|
||||
def link_to_type(type)
|
||||
if type
|
||||
link_to type, url_for_type(type), type
|
||||
link_to(type, url_for_type(type), type)
|
||||
else
|
||||
'Unknown Vendor'
|
||||
end
|
||||
@@ -52,15 +54,15 @@ module ShopFilter
|
||||
end
|
||||
|
||||
def product_img_url(url, style = 'small')
|
||||
unless url =~ /\Aproducts\/([\w\-\_]+)\.(\w{2,4})/
|
||||
unless url =~ %r{\Aproducts/([\w\-\_]+)\.(\w{2,4})}
|
||||
raise ArgumentError, 'filter "size" can only be called on product images'
|
||||
end
|
||||
|
||||
case style
|
||||
when 'original'
|
||||
return '/files/shops/random_number/' + url
|
||||
'/files/shops/random_number/' + url
|
||||
when 'grande', 'large', 'medium', 'compact', 'small', 'thumb', 'icon'
|
||||
"/files/shops/random_number/products/#{$1}_#{style}.#{$2}"
|
||||
"/files/shops/random_number/products/#{Regexp.last_match(1)}_#{style}.#{Regexp.last_match(2)}"
|
||||
else
|
||||
raise ArgumentError, 'valid parameters for filter "size" are: original, grande, large, medium, compact, small, thumb and icon '
|
||||
end
|
||||
@@ -70,16 +72,14 @@ module ShopFilter
|
||||
html = []
|
||||
html << %(<span class="prev">#{link_to(paginate['previous']['title'], paginate['previous']['url'])}</span>) if paginate['previous']
|
||||
|
||||
for part in paginate['parts']
|
||||
|
||||
if part['is_link']
|
||||
html << %(<span class="page">#{link_to(part['title'], part['url'])}</span>)
|
||||
paginate['parts'].each do |part|
|
||||
html << if part['is_link']
|
||||
%(<span class="page">#{link_to(part['title'], part['url'])}</span>)
|
||||
elsif part['title'].to_i == paginate['current_page'].to_i
|
||||
html << %(<span class="page current">#{part['title']}</span>)
|
||||
%(<span class="page current">#{part['title']}</span>)
|
||||
else
|
||||
html << %(<span class="deco">#{part['title']}</span>)
|
||||
%(<span class="deco">#{part['title']}</span>)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
html << %(<span class="next">#{link_to(paginate['next']['title'], paginate['next']['url'])}</span>) if paginate['next']
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module TagFilter
|
||||
def link_to_tag(label, tag)
|
||||
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tag}\">#{label}</a>"
|
||||
@@ -13,11 +15,11 @@ module TagFilter
|
||||
|
||||
def link_to_add_tag(label, tag)
|
||||
tags = (@context['current_tags'] + [tag]).uniq
|
||||
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tags.join("+")}\">#{label}</a>"
|
||||
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tags.join('+')}\">#{label}</a>"
|
||||
end
|
||||
|
||||
def link_to_remove_tag(label, tag)
|
||||
tags = (@context['current_tags'] - [tag]).uniq
|
||||
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tags.join("+")}\">#{label}</a>"
|
||||
"<a title=\"Show tag #{tag}\" href=\"/collections/#{@context['handle']}/#{tags.join('+')}\">#{label}</a>"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module WeightFilter
|
||||
def weight(grams)
|
||||
sprintf("%.2f", grams / 1000)
|
||||
format("%.2f", grams / 1000)
|
||||
end
|
||||
|
||||
def weight_with_unit(grams)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This profiler run simulates Shopify.
|
||||
# We are looking in the tests directory for liquid files and render them within the designated layout file.
|
||||
# We will also export a substantial database to liquid which the templates can render values of.
|
||||
@@ -31,7 +33,7 @@ class ThemeRunner
|
||||
{
|
||||
liquid: File.read(test),
|
||||
layout: (File.file?(theme_path) ? File.read(theme_path) : nil),
|
||||
template_name: test
|
||||
template_name: test,
|
||||
}
|
||||
end.compact
|
||||
|
||||
@@ -56,9 +58,9 @@ class ThemeRunner
|
||||
# `render` is called to benchmark just the render portion of liquid
|
||||
def render
|
||||
@compiled_tests.each do |test|
|
||||
tmpl = test[:tmpl]
|
||||
tmpl = test[:tmpl]
|
||||
assigns = test[:assigns]
|
||||
layout = test[:layout]
|
||||
layout = test[:layout]
|
||||
|
||||
if layout
|
||||
assigns['content_for_layout'] = tmpl.render!(assigns)
|
||||
@@ -72,7 +74,7 @@ class ThemeRunner
|
||||
private
|
||||
|
||||
def compile_and_render(template, layout, assigns, page_template, template_file)
|
||||
compiled_test = compile_test(template, layout, assigns, page_template, template_file)
|
||||
compiled_test = compile_test(template, layout, assigns, page_template, template_file)
|
||||
assigns['content_for_layout'] = compiled_test[:tmpl].render!(assigns)
|
||||
compiled_test[:layout].render!(assigns) if layout
|
||||
end
|
||||
@@ -86,7 +88,7 @@ class ThemeRunner
|
||||
end
|
||||
|
||||
def compile_test(template, layout, assigns, page_template, template_file)
|
||||
tmpl = init_template(page_template, template_file)
|
||||
tmpl = init_template(page_template, template_file)
|
||||
parsed_template = tmpl.parse(template).dup
|
||||
|
||||
if layout
|
||||
@@ -111,9 +113,9 @@ class ThemeRunner
|
||||
|
||||
# set up a new Liquid::Template object for use in `compile_and_render` and `compile_test`
|
||||
def init_template(page_template, template_file)
|
||||
tmpl = Liquid::Template.new
|
||||
tmpl.assigns['page_title'] = 'Page title'
|
||||
tmpl.assigns['template'] = page_template
|
||||
tmpl = Liquid::Template.new
|
||||
tmpl.assigns['page_title'] = 'Page title'
|
||||
tmpl.assigns['template'] = page_template
|
||||
tmpl.registers[:file_system] = ThemeRunner::FileSystem.new(File.dirname(template_file))
|
||||
tmpl
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class AssignTest < Minitest::Test
|
||||
@@ -8,9 +10,9 @@ class AssignTest < Minitest::Test
|
||||
{% assign this-thing = 'Print this-thing' %}
|
||||
{{ this-thing }}
|
||||
END_TEMPLATE
|
||||
template = Template.parse(template_source)
|
||||
rendered = template.render!
|
||||
assert_equal "Print this-thing", rendered.strip
|
||||
template = Template.parse(template_source)
|
||||
rendered = template.render!
|
||||
assert_equal("Print this-thing", rendered.strip)
|
||||
end
|
||||
|
||||
def test_assigned_variable
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class FoobarTag < Liquid::Tag
|
||||
def render_to_output_buffer(context, output)
|
||||
def render_to_output_buffer(_context, output)
|
||||
output << ' '
|
||||
output
|
||||
end
|
||||
@@ -94,9 +96,9 @@ class BlankTest < Minitest::Test
|
||||
|
||||
def test_include_is_blank
|
||||
Liquid::Template.file_system = BlankTestFileSystem.new
|
||||
assert_template_result "foobar" * (N + 1), wrap("{% include 'foobar' %}")
|
||||
assert_template_result " foobar " * (N + 1), wrap("{% include ' foobar ' %}")
|
||||
assert_template_result " " * (N + 1), wrap(" {% include ' ' %} ")
|
||||
assert_template_result("foobar" * (N + 1), wrap("{% include 'foobar' %}"))
|
||||
assert_template_result(" foobar " * (N + 1), wrap("{% include ' foobar ' %}"))
|
||||
assert_template_result(" " * (N + 1), wrap(" {% include ' ' %} "))
|
||||
end
|
||||
|
||||
def test_case_is_blank
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class BlockTest < Minitest::Test
|
||||
@@ -7,6 +9,6 @@ class BlockTest < Minitest::Test
|
||||
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"
|
||||
assert_equal(exc.message, "Liquid syntax error: 'endunless' is not a valid delimiter for if tags. use endif")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class CaptureTest < Minitest::Test
|
||||
@@ -12,9 +14,9 @@ class CaptureTest < Minitest::Test
|
||||
{% capture this-thing %}Print this-thing{% endcapture %}
|
||||
{{ this-thing }}
|
||||
END_TEMPLATE
|
||||
template = Template.parse(template_source)
|
||||
rendered = template.render!
|
||||
assert_equal "Print this-thing", rendered.strip
|
||||
template = Template.parse(template_source)
|
||||
rendered = template.render!
|
||||
assert_equal("Print this-thing", rendered.strip)
|
||||
end
|
||||
|
||||
def test_capture_to_variable_from_outer_scope_if_existing
|
||||
@@ -28,9 +30,9 @@ class CaptureTest < Minitest::Test
|
||||
{% endif %}
|
||||
{{var}}
|
||||
END_TEMPLATE
|
||||
template = Template.parse(template_source)
|
||||
rendered = template.render!
|
||||
assert_equal "test-string", rendered.gsub(/\s/, '')
|
||||
template = Template.parse(template_source)
|
||||
rendered = template.render!
|
||||
assert_equal("test-string", rendered.gsub(/\s/, ''))
|
||||
end
|
||||
|
||||
def test_assigning_from_capture
|
||||
@@ -43,8 +45,8 @@ class CaptureTest < Minitest::Test
|
||||
{% endfor %}
|
||||
{{ first }}-{{ second }}
|
||||
END_TEMPLATE
|
||||
template = Template.parse(template_source)
|
||||
rendered = template.render!
|
||||
assert_equal "3-3", rendered.gsub(/\s/, '')
|
||||
template = Template.parse(template_source)
|
||||
rendered = template.render!
|
||||
assert_equal("3-3", rendered.gsub(/\s/, ''))
|
||||
end
|
||||
end # CaptureTest
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ContextTest < Minitest::Test
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class DocumentTest < Minitest::Test
|
||||
@@ -7,13 +9,13 @@ class DocumentTest < Minitest::Test
|
||||
exc = assert_raises(SyntaxError) do
|
||||
Template.parse("{% else %}")
|
||||
end
|
||||
assert_equal exc.message, "Liquid syntax error: Unexpected outer 'else' tag"
|
||||
assert_equal(exc.message, "Liquid syntax error: Unexpected outer 'else' tag")
|
||||
end
|
||||
|
||||
def test_unknown_tag
|
||||
exc = assert_raises(SyntaxError) do
|
||||
Template.parse("{% foo %}")
|
||||
end
|
||||
assert_equal exc.message, "Liquid syntax error: Unknown tag 'foo'"
|
||||
assert_equal(exc.message, "Liquid syntax error: Unknown tag 'foo'")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ContextDrop < Liquid::Drop
|
||||
@@ -31,7 +33,7 @@ class ProductDrop < Liquid::Drop
|
||||
|
||||
class CatchallDrop < Liquid::Drop
|
||||
def liquid_method_missing(method)
|
||||
'catchall_method: ' << method.to_s
|
||||
"catchall_method: #{method}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,7 +50,7 @@ class ProductDrop < Liquid::Drop
|
||||
end
|
||||
|
||||
def user_input
|
||||
"foo".taint
|
||||
(+"foo").taint
|
||||
end
|
||||
|
||||
protected
|
||||
@@ -109,82 +111,89 @@ class DropsTest < Minitest::Test
|
||||
|
||||
def test_product_drop
|
||||
tpl = Liquid::Template.parse(' ')
|
||||
assert_equal ' ', tpl.render!('product' => ProductDrop.new)
|
||||
assert_equal(' ', tpl.render!('product' => ProductDrop.new))
|
||||
end
|
||||
|
||||
def test_rendering_raises_on_tainted_attr
|
||||
with_taint_mode(:error) do
|
||||
tpl = Liquid::Template.parse('{{ product.user_input }}')
|
||||
assert_raises TaintedError do
|
||||
if taint_supported?
|
||||
def test_rendering_raises_on_tainted_attr
|
||||
with_taint_mode(:error) do
|
||||
tpl = Liquid::Template.parse('{{ product.user_input }}')
|
||||
assert_raises TaintedError do
|
||||
tpl.render!('product' => ProductDrop.new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_rendering_warns_on_tainted_attr
|
||||
with_taint_mode(:warn) do
|
||||
tpl = Liquid::Template.parse('{{ product.user_input }}')
|
||||
context = Context.new('product' => ProductDrop.new)
|
||||
tpl.render!(context)
|
||||
assert_equal [Liquid::TaintedError], context.warnings.map(&:class)
|
||||
assert_equal "variable 'product.user_input' is tainted and was not escaped", context.warnings.first.to_s(false)
|
||||
end
|
||||
end
|
||||
|
||||
def test_rendering_doesnt_raise_on_escaped_tainted_attr
|
||||
with_taint_mode(:error) do
|
||||
tpl = Liquid::Template.parse('{{ product.user_input | escape }}')
|
||||
tpl.render!('product' => ProductDrop.new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_rendering_warns_on_tainted_attr
|
||||
with_taint_mode(:warn) do
|
||||
tpl = Liquid::Template.parse('{{ product.user_input }}')
|
||||
context = Context.new('product' => ProductDrop.new)
|
||||
tpl.render!(context)
|
||||
assert_equal [Liquid::TaintedError], context.warnings.map(&:class)
|
||||
assert_equal "variable 'product.user_input' is tainted and was not escaped", context.warnings.first.to_s(false)
|
||||
end
|
||||
end
|
||||
|
||||
def test_rendering_doesnt_raise_on_escaped_tainted_attr
|
||||
with_taint_mode(:error) do
|
||||
tpl = Liquid::Template.parse('{{ product.user_input | escape }}')
|
||||
tpl.render!('product' => ProductDrop.new)
|
||||
end
|
||||
end
|
||||
|
||||
def test_drop_does_only_respond_to_whitelisted_methods
|
||||
assert_equal "", Liquid::Template.parse("{{ product.inspect }}").render!('product' => ProductDrop.new)
|
||||
assert_equal "", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new)
|
||||
assert_equal "", Liquid::Template.parse("{{ product.whatever }}").render!('product' => ProductDrop.new)
|
||||
assert_equal "", Liquid::Template.parse('{{ product | map: "inspect" }}').render!('product' => ProductDrop.new)
|
||||
assert_equal "", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render!('product' => ProductDrop.new)
|
||||
assert_equal "", Liquid::Template.parse('{{ product | map: "whatever" }}').render!('product' => ProductDrop.new)
|
||||
assert_equal("", Liquid::Template.parse("{{ product.inspect }}").render!('product' => ProductDrop.new))
|
||||
assert_equal("", Liquid::Template.parse("{{ product.pretty_inspect }}").render!('product' => ProductDrop.new))
|
||||
assert_equal("", Liquid::Template.parse("{{ product.whatever }}").render!('product' => ProductDrop.new))
|
||||
assert_equal("", Liquid::Template.parse('{{ product | map: "inspect" }}').render!('product' => ProductDrop.new))
|
||||
assert_equal("", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render!('product' => ProductDrop.new))
|
||||
assert_equal("", Liquid::Template.parse('{{ product | map: "whatever" }}').render!('product' => ProductDrop.new))
|
||||
end
|
||||
|
||||
def test_drops_respond_to_to_liquid
|
||||
assert_equal "text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render!('product' => ProductDrop.new)
|
||||
assert_equal "text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render!('product' => ProductDrop.new)
|
||||
assert_equal("text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render!('product' => ProductDrop.new))
|
||||
assert_equal("text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render!('product' => ProductDrop.new))
|
||||
end
|
||||
|
||||
def test_text_drop
|
||||
output = Liquid::Template.parse(' {{ product.texts.text }} ').render!('product' => ProductDrop.new)
|
||||
assert_equal ' text1 ', output
|
||||
assert_equal(' text1 ', output)
|
||||
end
|
||||
|
||||
def test_catchall_unknown_method
|
||||
output = Liquid::Template.parse(' {{ product.catchall.unknown }} ').render!('product' => ProductDrop.new)
|
||||
assert_equal ' catchall_method: unknown ', output
|
||||
assert_equal(' catchall_method: unknown ', output)
|
||||
end
|
||||
|
||||
def test_catchall_integer_argument_drop
|
||||
output = Liquid::Template.parse(' {{ product.catchall[8] }} ').render!('product' => ProductDrop.new)
|
||||
assert_equal ' catchall_method: 8 ', output
|
||||
assert_equal(' catchall_method: 8 ', output)
|
||||
end
|
||||
|
||||
def test_text_array_drop
|
||||
output = Liquid::Template.parse('{% for text in product.texts.array %} {{text}} {% endfor %}').render!('product' => ProductDrop.new)
|
||||
assert_equal ' text1 text2 ', output
|
||||
assert_equal(' text1 text2 ', output)
|
||||
end
|
||||
|
||||
def test_context_drop
|
||||
output = Liquid::Template.parse(' {{ context.bar }} ').render!('context' => ContextDrop.new, 'bar' => "carrot")
|
||||
assert_equal ' carrot ', output
|
||||
assert_equal(' carrot ', output)
|
||||
end
|
||||
|
||||
def test_context_drop_array_with_map
|
||||
output = Liquid::Template.parse(' {{ contexts | map: "bar" }} ').render!('contexts' => [ContextDrop.new, ContextDrop.new], 'bar' => "carrot")
|
||||
assert_equal(' carrotcarrot ', output)
|
||||
end
|
||||
|
||||
def test_nested_context_drop
|
||||
output = Liquid::Template.parse(' {{ product.context.foo }} ').render!('product' => ProductDrop.new, 'foo' => "monkey")
|
||||
assert_equal ' monkey ', output
|
||||
assert_equal(' monkey ', output)
|
||||
end
|
||||
|
||||
def test_protected
|
||||
output = Liquid::Template.parse(' {{ product.callmenot }} ').render!('product' => ProductDrop.new)
|
||||
assert_equal ' ', output
|
||||
assert_equal(' ', output)
|
||||
end
|
||||
|
||||
def test_object_methods_not_allowed
|
||||
@@ -195,40 +204,40 @@ class DropsTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_scope
|
||||
assert_equal '1', Liquid::Template.parse('{{ context.scopes }}').render!('context' => ContextDrop.new)
|
||||
assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ context.scopes }}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
||||
assert_equal '3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
||||
assert_equal('1', Liquid::Template.parse('{{ context.scopes }}').render!('context' => ContextDrop.new))
|
||||
assert_equal('2', Liquid::Template.parse('{%for i in dummy%}{{ context.scopes }}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
||||
assert_equal('3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ context.scopes }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
||||
end
|
||||
|
||||
def test_scope_though_proc
|
||||
assert_equal '1', Liquid::Template.parse('{{ s }}').render!('context' => ContextDrop.new, 's' => proc{ |c| c['context.scopes'] })
|
||||
assert_equal '2', Liquid::Template.parse('{%for i in dummy%}{{ s }}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc{ |c| c['context.scopes'] }, 'dummy' => [1])
|
||||
assert_equal '3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc{ |c| c['context.scopes'] }, 'dummy' => [1])
|
||||
assert_equal('1', Liquid::Template.parse('{{ s }}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }))
|
||||
assert_equal('2', Liquid::Template.parse('{%for i in dummy%}{{ s }}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1]))
|
||||
assert_equal('3', Liquid::Template.parse('{%for i in dummy%}{%for i in dummy%}{{ s }}{%endfor%}{%endfor%}').render!('context' => ContextDrop.new, 's' => proc { |c| c['context.scopes'] }, 'dummy' => [1]))
|
||||
end
|
||||
|
||||
def test_scope_with_assigns
|
||||
assert_equal 'variable', Liquid::Template.parse('{% assign a = "variable"%}{{a}}').render!('context' => ContextDrop.new)
|
||||
assert_equal 'variable', Liquid::Template.parse('{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
||||
assert_equal 'test', Liquid::Template.parse('{% assign header_gif = "test"%}{{header_gif}}').render!('context' => ContextDrop.new)
|
||||
assert_equal 'test', Liquid::Template.parse("{% assign header_gif = 'test'%}{{header_gif}}").render!('context' => ContextDrop.new)
|
||||
assert_equal('variable', Liquid::Template.parse('{% assign a = "variable"%}{{a}}').render!('context' => ContextDrop.new))
|
||||
assert_equal('variable', Liquid::Template.parse('{% assign a = "variable"%}{%for i in dummy%}{{a}}{%endfor%}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
||||
assert_equal('test', Liquid::Template.parse('{% assign header_gif = "test"%}{{header_gif}}').render!('context' => ContextDrop.new))
|
||||
assert_equal('test', Liquid::Template.parse("{% assign header_gif = 'test'%}{{header_gif}}").render!('context' => ContextDrop.new))
|
||||
end
|
||||
|
||||
def test_scope_from_tags
|
||||
assert_equal '1', Liquid::Template.parse('{% for i in context.scopes_as_array %}{{i}}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
||||
assert_equal '12', Liquid::Template.parse('{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
||||
assert_equal '123', Liquid::Template.parse('{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1])
|
||||
assert_equal('1', Liquid::Template.parse('{% for i in context.scopes_as_array %}{{i}}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
||||
assert_equal('12', Liquid::Template.parse('{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
||||
assert_equal('123', Liquid::Template.parse('{%for a in dummy%}{%for a in dummy%}{% for i in context.scopes_as_array %}{{i}}{% endfor %}{% endfor %}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1]))
|
||||
end
|
||||
|
||||
def test_access_context_from_drop
|
||||
assert_equal '123', Liquid::Template.parse('{%for a in dummy%}{{ context.loop_pos }}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1, 2, 3])
|
||||
assert_equal('123', Liquid::Template.parse('{%for a in dummy%}{{ context.loop_pos }}{% endfor %}').render!('context' => ContextDrop.new, 'dummy' => [1, 2, 3]))
|
||||
end
|
||||
|
||||
def test_enumerable_drop
|
||||
assert_equal '123', Liquid::Template.parse('{% for c in collection %}{{c}}{% endfor %}').render!('collection' => EnumerableDrop.new)
|
||||
assert_equal('123', Liquid::Template.parse('{% for c in collection %}{{c}}{% endfor %}').render!('collection' => EnumerableDrop.new))
|
||||
end
|
||||
|
||||
def test_enumerable_drop_size
|
||||
assert_equal '3', Liquid::Template.parse('{{collection.size}}').render!('collection' => EnumerableDrop.new)
|
||||
assert_equal('3', Liquid::Template.parse('{{collection.size}}').render!('collection' => EnumerableDrop.new))
|
||||
end
|
||||
|
||||
def test_enumerable_drop_will_invoke_liquid_method_missing_for_clashing_method_names
|
||||
@@ -241,16 +250,16 @@ class DropsTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_some_enumerable_methods_still_get_invoked
|
||||
[ :count, :max ].each do |method|
|
||||
[:count, :max].each do |method|
|
||||
assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
|
||||
assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
|
||||
assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
|
||||
assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
|
||||
end
|
||||
|
||||
assert_equal "yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new)
|
||||
assert_equal("yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new))
|
||||
|
||||
[ :min, :first ].each do |method|
|
||||
[:min, :first].each do |method|
|
||||
assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
|
||||
assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
|
||||
assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
|
||||
@@ -259,15 +268,22 @@ class DropsTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_empty_string_value_access
|
||||
assert_equal '', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => '')
|
||||
assert_equal('', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => ''))
|
||||
end
|
||||
|
||||
def test_nil_value_access
|
||||
assert_equal '', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => nil)
|
||||
assert_equal('', Liquid::Template.parse('{{ product[value] }}').render!('product' => ProductDrop.new, 'value' => nil))
|
||||
end
|
||||
|
||||
def test_default_to_s_on_drops
|
||||
assert_equal 'ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new)
|
||||
assert_equal 'EnumerableDrop', Liquid::Template.parse('{{ collection }}').render!('collection' => EnumerableDrop.new)
|
||||
assert_equal('ProductDrop', Liquid::Template.parse("{{ product }}").render!('product' => ProductDrop.new))
|
||||
assert_equal('EnumerableDrop', Liquid::Template.parse('{{ collection }}').render!('collection' => EnumerableDrop.new))
|
||||
end
|
||||
|
||||
def test_invokable_methods
|
||||
assert_equal(%w(to_liquid catchall user_input context texts).to_set, ProductDrop.invokable_methods)
|
||||
assert_equal(%w(to_liquid scopes_as_array loop_pos scopes).to_set, ContextDrop.invokable_methods)
|
||||
assert_equal(%w(to_liquid size max min first count).to_set, EnumerableDrop.invokable_methods)
|
||||
assert_equal(%w(to_liquid max min sort count first).to_set, RealEnumerableDrop.invokable_methods)
|
||||
end
|
||||
end # DropsTest
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ErrorHandlingTest < Minitest::Test
|
||||
@@ -33,31 +35,31 @@ class ErrorHandlingTest < Minitest::Test
|
||||
TEXT
|
||||
|
||||
output = Liquid::Template.parse(template, line_numbers: true).render('errors' => ErrorDrop.new)
|
||||
assert_equal expected, output
|
||||
assert_equal(expected, output)
|
||||
end
|
||||
|
||||
def test_standard_error
|
||||
template = Liquid::Template.parse(' {{ errors.standard_error }} ')
|
||||
assert_equal ' Liquid error: standard error ', template.render('errors' => ErrorDrop.new)
|
||||
assert_equal(' Liquid error: standard error ', template.render('errors' => ErrorDrop.new))
|
||||
|
||||
assert_equal 1, template.errors.size
|
||||
assert_equal StandardError, template.errors.first.class
|
||||
assert_equal(1, template.errors.size)
|
||||
assert_equal(StandardError, template.errors.first.class)
|
||||
end
|
||||
|
||||
def test_syntax
|
||||
template = Liquid::Template.parse(' {{ errors.syntax_error }} ')
|
||||
assert_equal ' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new)
|
||||
assert_equal(' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new))
|
||||
|
||||
assert_equal 1, template.errors.size
|
||||
assert_equal SyntaxError, template.errors.first.class
|
||||
assert_equal(1, template.errors.size)
|
||||
assert_equal(SyntaxError, template.errors.first.class)
|
||||
end
|
||||
|
||||
def test_argument
|
||||
template = Liquid::Template.parse(' {{ errors.argument_error }} ')
|
||||
assert_equal ' Liquid error: argument error ', template.render('errors' => ErrorDrop.new)
|
||||
assert_equal(' Liquid error: argument error ', template.render('errors' => ErrorDrop.new))
|
||||
|
||||
assert_equal 1, template.errors.size
|
||||
assert_equal ArgumentError, template.errors.first.class
|
||||
assert_equal(1, template.errors.size)
|
||||
assert_equal(ArgumentError, template.errors.first.class)
|
||||
end
|
||||
|
||||
def test_missing_endtag_parse_time_error
|
||||
@@ -76,22 +78,21 @@ class ErrorHandlingTest < Minitest::Test
|
||||
|
||||
def test_lax_unrecognized_operator
|
||||
template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :lax)
|
||||
assert_equal ' Liquid error: Unknown operator =! ', template.render
|
||||
assert_equal 1, template.errors.size
|
||||
assert_equal Liquid::ArgumentError, template.errors.first.class
|
||||
assert_equal(' Liquid error: Unknown operator =! ', template.render)
|
||||
assert_equal(1, template.errors.size)
|
||||
assert_equal(Liquid::ArgumentError, template.errors.first.class)
|
||||
end
|
||||
|
||||
def test_with_line_numbers_adds_numbers_to_parser_errors
|
||||
err = assert_raises(SyntaxError) do
|
||||
Liquid::Template.parse(%q(
|
||||
Liquid::Template.parse('
|
||||
foobar
|
||||
|
||||
{% "cat" | foobar %}
|
||||
|
||||
bla
|
||||
),
|
||||
line_numbers: true
|
||||
)
|
||||
',
|
||||
line_numbers: true)
|
||||
end
|
||||
|
||||
assert_match(/Liquid syntax error \(line 4\)/, err.message)
|
||||
@@ -99,15 +100,14 @@ class ErrorHandlingTest < Minitest::Test
|
||||
|
||||
def test_with_line_numbers_adds_numbers_to_parser_errors_with_whitespace_trim
|
||||
err = assert_raises(SyntaxError) do
|
||||
Liquid::Template.parse(%q(
|
||||
Liquid::Template.parse('
|
||||
foobar
|
||||
|
||||
{%- "cat" | foobar -%}
|
||||
|
||||
bla
|
||||
),
|
||||
line_numbers: true
|
||||
)
|
||||
',
|
||||
line_numbers: true)
|
||||
end
|
||||
|
||||
assert_match(/Liquid syntax error \(line 4\)/, err.message)
|
||||
@@ -122,11 +122,10 @@ class ErrorHandlingTest < Minitest::Test
|
||||
bla
|
||||
',
|
||||
error_mode: :warn,
|
||||
line_numbers: true
|
||||
)
|
||||
line_numbers: true)
|
||||
|
||||
assert_equal ['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
|
||||
template.warnings.map(&:message)
|
||||
assert_equal(['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
|
||||
template.warnings.map(&:message))
|
||||
end
|
||||
|
||||
def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors
|
||||
@@ -139,11 +138,10 @@ class ErrorHandlingTest < Minitest::Test
|
||||
bla
|
||||
',
|
||||
error_mode: :strict,
|
||||
line_numbers: true
|
||||
)
|
||||
line_numbers: true)
|
||||
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)
|
||||
end
|
||||
|
||||
def test_syntax_errors_in_nested_blocks_have_correct_line_number
|
||||
@@ -157,41 +155,40 @@ class ErrorHandlingTest < Minitest::Test
|
||||
|
||||
bla
|
||||
',
|
||||
line_numbers: true
|
||||
)
|
||||
line_numbers: true)
|
||||
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)
|
||||
end
|
||||
|
||||
def test_strict_error_messages
|
||||
err = assert_raises(SyntaxError) do
|
||||
Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :strict)
|
||||
end
|
||||
assert_equal 'Liquid syntax error: Unexpected character = in "1 =! 2"', err.message
|
||||
assert_equal('Liquid syntax error: Unexpected character = in "1 =! 2"', err.message)
|
||||
|
||||
err = assert_raises(SyntaxError) do
|
||||
Liquid::Template.parse('{{%%%}}', error_mode: :strict)
|
||||
end
|
||||
assert_equal 'Liquid syntax error: Unexpected character % in "{{%%%}}"', err.message
|
||||
assert_equal('Liquid syntax error: Unexpected character % in "{{%%%}}"', err.message)
|
||||
end
|
||||
|
||||
def test_warnings
|
||||
template = Liquid::Template.parse('{% if ~~~ %}{{%%%}}{% else %}{{ hello. }}{% endif %}', error_mode: :warn)
|
||||
assert_equal 3, template.warnings.size
|
||||
assert_equal 'Unexpected character ~ in "~~~"', template.warnings[0].to_s(false)
|
||||
assert_equal 'Unexpected character % in "{{%%%}}"', template.warnings[1].to_s(false)
|
||||
assert_equal 'Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].to_s(false)
|
||||
assert_equal '', template.render
|
||||
assert_equal(3, template.warnings.size)
|
||||
assert_equal('Unexpected character ~ in "~~~"', template.warnings[0].to_s(false))
|
||||
assert_equal('Unexpected character % in "{{%%%}}"', template.warnings[1].to_s(false))
|
||||
assert_equal('Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].to_s(false))
|
||||
assert_equal('', template.render)
|
||||
end
|
||||
|
||||
def test_warning_line_numbers
|
||||
template = Liquid::Template.parse("{% if ~~~ %}\n{{%%%}}{% else %}\n{{ hello. }}{% endif %}", error_mode: :warn, line_numbers: true)
|
||||
assert_equal 'Liquid syntax error (line 1): Unexpected character ~ in "~~~"', template.warnings[0].message
|
||||
assert_equal 'Liquid syntax error (line 2): Unexpected character % in "{{%%%}}"', template.warnings[1].message
|
||||
assert_equal 'Liquid syntax error (line 3): Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message
|
||||
assert_equal 3, template.warnings.size
|
||||
assert_equal [1, 2, 3], template.warnings.map(&:line_number)
|
||||
assert_equal('Liquid syntax error (line 1): Unexpected character ~ in "~~~"', template.warnings[0].message)
|
||||
assert_equal('Liquid syntax error (line 2): Unexpected character % in "{{%%%}}"', template.warnings[1].message)
|
||||
assert_equal('Liquid syntax error (line 3): Expected id but found end_of_string in "{{ hello. }}"', template.warnings[2].message)
|
||||
assert_equal(3, template.warnings.size)
|
||||
assert_equal([1, 2, 3], template.warnings.map(&:line_number))
|
||||
end
|
||||
|
||||
# Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError
|
||||
@@ -205,41 +202,47 @@ class ErrorHandlingTest < Minitest::Test
|
||||
def test_default_exception_renderer_with_internal_error
|
||||
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
||||
|
||||
output = template.render({ 'errors' => ErrorDrop.new })
|
||||
output = template.render('errors' => ErrorDrop.new)
|
||||
|
||||
assert_equal 'This is a runtime error: Liquid error (line 1): internal', output
|
||||
assert_equal [Liquid::InternalError], template.errors.map(&:class)
|
||||
assert_equal('This is a runtime error: Liquid error (line 1): internal', output)
|
||||
assert_equal([Liquid::InternalError], template.errors.map(&:class))
|
||||
end
|
||||
|
||||
def test_setting_default_exception_renderer
|
||||
old_exception_renderer = Liquid::Template.default_exception_renderer
|
||||
exceptions = []
|
||||
Liquid::Template.default_exception_renderer = ->(e) { exceptions << e; '' }
|
||||
Liquid::Template.default_exception_renderer = ->(e) {
|
||||
exceptions << e
|
||||
''
|
||||
}
|
||||
template = Liquid::Template.parse('This is a runtime error: {{ errors.argument_error }}')
|
||||
|
||||
output = template.render({ 'errors' => ErrorDrop.new })
|
||||
output = template.render('errors' => ErrorDrop.new)
|
||||
|
||||
assert_equal 'This is a runtime error: ', output
|
||||
assert_equal [Liquid::ArgumentError], template.errors.map(&:class)
|
||||
assert_equal('This is a runtime error: ', output)
|
||||
assert_equal([Liquid::ArgumentError], template.errors.map(&:class))
|
||||
ensure
|
||||
Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer
|
||||
end
|
||||
|
||||
def test_exception_renderer_exposing_non_liquid_error
|
||||
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
||||
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
||||
exceptions = []
|
||||
handler = ->(e) { exceptions << e; e.cause }
|
||||
handler = ->(e) {
|
||||
exceptions << e
|
||||
e.cause
|
||||
}
|
||||
|
||||
output = template.render({ 'errors' => ErrorDrop.new }, exception_renderer: handler)
|
||||
|
||||
assert_equal 'This is a runtime error: runtime error', output
|
||||
assert_equal [Liquid::InternalError], exceptions.map(&:class)
|
||||
assert_equal exceptions, template.errors
|
||||
assert_equal '#<RuntimeError: runtime error>', exceptions.first.cause.inspect
|
||||
assert_equal('This is a runtime error: runtime error', output)
|
||||
assert_equal([Liquid::InternalError], exceptions.map(&:class))
|
||||
assert_equal(exceptions, template.errors)
|
||||
assert_equal('#<RuntimeError: runtime error>', exceptions.first.cause.inspect)
|
||||
end
|
||||
|
||||
class TestFileSystem
|
||||
def read_template_file(template_path)
|
||||
def read_template_file(_template_path)
|
||||
"{{ errors.argument_error }}"
|
||||
end
|
||||
end
|
||||
@@ -249,12 +252,13 @@ class ErrorHandlingTest < Minitest::Test
|
||||
|
||||
begin
|
||||
Liquid::Template.file_system = TestFileSystem.new
|
||||
|
||||
template = Liquid::Template.parse("Argument error:\n{% include 'product' %}", line_numbers: true)
|
||||
page = template.render('errors' => ErrorDrop.new)
|
||||
page = template.render('errors' => ErrorDrop.new)
|
||||
ensure
|
||||
Liquid::Template.file_system = old_file_system
|
||||
end
|
||||
assert_equal "Argument error:\nLiquid error (product line 1): argument error", page
|
||||
assert_equal "product", template.errors.first.template_name
|
||||
assert_equal("Argument error:\nLiquid error (product line 1): argument error", page)
|
||||
assert_equal("product", template.errors.first.template_name)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module MoneyFilter
|
||||
def money(input)
|
||||
sprintf(' %d$ ', input)
|
||||
format(' %d$ ', input)
|
||||
end
|
||||
|
||||
def money_with_underscore(input)
|
||||
sprintf(' %d$ ', input)
|
||||
format(' %d$ ', input)
|
||||
end
|
||||
end
|
||||
|
||||
module CanadianMoneyFilter
|
||||
def money(input)
|
||||
sprintf(' %d$ CAD ', input)
|
||||
format(' %d$ CAD ', input)
|
||||
end
|
||||
end
|
||||
|
||||
module SubstituteFilter
|
||||
def substitute(input, params = {})
|
||||
input.gsub(/%\{(\w+)\}/) { |match| params[$1] }
|
||||
input.gsub(/%\{(\w+)\}/) { |_match| params[Regexp.last_match(1)] }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -26,7 +28,7 @@ class FiltersTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
module OverrideObjectMethodFilter
|
||||
def tap(input)
|
||||
def tap(_input)
|
||||
"tap overridden"
|
||||
end
|
||||
end
|
||||
@@ -39,13 +41,13 @@ class FiltersTest < Minitest::Test
|
||||
@context['var'] = 1000
|
||||
@context.add_filters(MoneyFilter)
|
||||
|
||||
assert_equal ' 1000$ ', Template.parse("{{var | money}}").render(@context)
|
||||
assert_equal(' 1000$ ', Template.parse("{{var | money}}").render(@context))
|
||||
end
|
||||
|
||||
def test_underscore_in_filter_name
|
||||
@context['var'] = 1000
|
||||
@context.add_filters(MoneyFilter)
|
||||
assert_equal ' 1000$ ', Template.parse("{{var | money_with_underscore}}").render(@context)
|
||||
assert_equal(' 1000$ ', Template.parse("{{var | money_with_underscore}}").render(@context))
|
||||
end
|
||||
|
||||
def test_second_filter_overwrites_first
|
||||
@@ -53,103 +55,103 @@ class FiltersTest < Minitest::Test
|
||||
@context.add_filters(MoneyFilter)
|
||||
@context.add_filters(CanadianMoneyFilter)
|
||||
|
||||
assert_equal ' 1000$ CAD ', Template.parse("{{var | money}}").render(@context)
|
||||
assert_equal(' 1000$ CAD ', Template.parse("{{var | money}}").render(@context))
|
||||
end
|
||||
|
||||
def test_size
|
||||
@context['var'] = 'abcd'
|
||||
@context.add_filters(MoneyFilter)
|
||||
|
||||
assert_equal '4', Template.parse("{{var | size}}").render(@context)
|
||||
assert_equal('4', Template.parse("{{var | size}}").render(@context))
|
||||
end
|
||||
|
||||
def test_join
|
||||
@context['var'] = [1, 2, 3, 4]
|
||||
|
||||
assert_equal "1 2 3 4", Template.parse("{{var | join}}").render(@context)
|
||||
assert_equal("1 2 3 4", Template.parse("{{var | join}}").render(@context))
|
||||
end
|
||||
|
||||
def test_sort
|
||||
@context['value'] = 3
|
||||
@context['value'] = 3
|
||||
@context['numbers'] = [2, 1, 4, 3]
|
||||
@context['words'] = ['expected', 'as', 'alphabetic']
|
||||
@context['arrays'] = ['flower', 'are']
|
||||
@context['words'] = ['expected', 'as', 'alphabetic']
|
||||
@context['arrays'] = ['flower', 'are']
|
||||
@context['case_sensitive'] = ['sensitive', 'Expected', 'case']
|
||||
|
||||
assert_equal '1 2 3 4', Template.parse("{{numbers | sort | join}}").render(@context)
|
||||
assert_equal 'alphabetic as expected', Template.parse("{{words | sort | join}}").render(@context)
|
||||
assert_equal '3', Template.parse("{{value | sort}}").render(@context)
|
||||
assert_equal 'are flower', Template.parse("{{arrays | sort | join}}").render(@context)
|
||||
assert_equal 'Expected case sensitive', Template.parse("{{case_sensitive | sort | join}}").render(@context)
|
||||
assert_equal('1 2 3 4', Template.parse("{{numbers | sort | join}}").render(@context))
|
||||
assert_equal('alphabetic as expected', Template.parse("{{words | sort | join}}").render(@context))
|
||||
assert_equal('3', Template.parse("{{value | sort}}").render(@context))
|
||||
assert_equal('are flower', Template.parse("{{arrays | sort | join}}").render(@context))
|
||||
assert_equal('Expected case sensitive', Template.parse("{{case_sensitive | sort | join}}").render(@context))
|
||||
end
|
||||
|
||||
def test_sort_natural
|
||||
@context['words'] = ['case', 'Assert', 'Insensitive']
|
||||
@context['hashes'] = [{ 'a' => 'A' }, { 'a' => 'b' }, { 'a' => 'C' }]
|
||||
@context['words'] = ['case', 'Assert', 'Insensitive']
|
||||
@context['hashes'] = [{ 'a' => 'A' }, { 'a' => 'b' }, { 'a' => 'C' }]
|
||||
@context['objects'] = [TestObject.new('A'), TestObject.new('b'), TestObject.new('C')]
|
||||
|
||||
# Test strings
|
||||
assert_equal 'Assert case Insensitive', Template.parse("{{words | sort_natural | join}}").render(@context)
|
||||
assert_equal('Assert case Insensitive', Template.parse("{{words | sort_natural | join}}").render(@context))
|
||||
|
||||
# Test hashes
|
||||
assert_equal 'A b C', Template.parse("{{hashes | sort_natural: 'a' | map: 'a' | join}}").render(@context)
|
||||
assert_equal('A b C', Template.parse("{{hashes | sort_natural: 'a' | map: 'a' | join}}").render(@context))
|
||||
|
||||
# Test objects
|
||||
assert_equal 'A b C', Template.parse("{{objects | sort_natural: 'a' | map: 'a' | join}}").render(@context)
|
||||
assert_equal('A b C', Template.parse("{{objects | sort_natural: 'a' | map: 'a' | join}}").render(@context))
|
||||
end
|
||||
|
||||
def test_compact
|
||||
@context['words'] = ['a', nil, 'b', nil, 'c']
|
||||
@context['hashes'] = [{ 'a' => 'A' }, { 'a' => nil }, { 'a' => 'C' }]
|
||||
@context['words'] = ['a', nil, 'b', nil, 'c']
|
||||
@context['hashes'] = [{ 'a' => 'A' }, { 'a' => nil }, { 'a' => 'C' }]
|
||||
@context['objects'] = [TestObject.new('A'), TestObject.new(nil), TestObject.new('C')]
|
||||
|
||||
# Test strings
|
||||
assert_equal 'a b c', Template.parse("{{words | compact | join}}").render(@context)
|
||||
assert_equal('a b c', Template.parse("{{words | compact | join}}").render(@context))
|
||||
|
||||
# Test hashes
|
||||
assert_equal 'A C', Template.parse("{{hashes | compact: 'a' | map: 'a' | join}}").render(@context)
|
||||
assert_equal('A C', Template.parse("{{hashes | compact: 'a' | map: 'a' | join}}").render(@context))
|
||||
|
||||
# Test objects
|
||||
assert_equal 'A C', Template.parse("{{objects | compact: 'a' | map: 'a' | join}}").render(@context)
|
||||
assert_equal('A C', Template.parse("{{objects | compact: 'a' | map: 'a' | join}}").render(@context))
|
||||
end
|
||||
|
||||
def test_strip_html
|
||||
@context['var'] = "<b>bla blub</a>"
|
||||
|
||||
assert_equal "bla blub", Template.parse("{{ var | strip_html }}").render(@context)
|
||||
assert_equal("bla blub", Template.parse("{{ var | strip_html }}").render(@context))
|
||||
end
|
||||
|
||||
def test_strip_html_ignore_comments_with_html
|
||||
@context['var'] = "<!-- split and some <ul> tag --><b>bla blub</a>"
|
||||
|
||||
assert_equal "bla blub", Template.parse("{{ var | strip_html }}").render(@context)
|
||||
assert_equal("bla blub", Template.parse("{{ var | strip_html }}").render(@context))
|
||||
end
|
||||
|
||||
def test_capitalize
|
||||
@context['var'] = "blub"
|
||||
|
||||
assert_equal "Blub", Template.parse("{{ var | capitalize }}").render(@context)
|
||||
assert_equal("Blub", Template.parse("{{ var | capitalize }}").render(@context))
|
||||
end
|
||||
|
||||
def test_nonexistent_filter_is_ignored
|
||||
@context['var'] = 1000
|
||||
|
||||
assert_equal '1000', Template.parse("{{ var | xyzzy }}").render(@context)
|
||||
assert_equal('1000', Template.parse("{{ var | xyzzy }}").render(@context))
|
||||
end
|
||||
|
||||
def test_filter_with_keyword_arguments
|
||||
@context['surname'] = 'john'
|
||||
@context['input'] = 'hello %{first_name}, %{last_name}'
|
||||
@context['input'] = 'hello %{first_name}, %{last_name}'
|
||||
@context.add_filters(SubstituteFilter)
|
||||
output = Template.parse(%({{ input | substitute: first_name: surname, last_name: 'doe' }})).render(@context)
|
||||
assert_equal 'hello john, doe', output
|
||||
output = Template.parse(%({{ input | substitute: first_name: surname, last_name: 'doe' }})).render(@context)
|
||||
assert_equal('hello john, doe', output)
|
||||
end
|
||||
|
||||
def test_override_object_method_in_filter
|
||||
assert_equal "tap overridden", Template.parse("{{var | tap}}").render!({ 'var' => 1000 }, filters: [OverrideObjectMethodFilter])
|
||||
assert_equal("tap overridden", Template.parse("{{var | tap}}").render!({ 'var' => 1000 }, filters: [OverrideObjectMethodFilter]))
|
||||
|
||||
# tap still treated as a non-existent filter
|
||||
assert_equal "1000", Template.parse("{{var | tap}}").render!({ 'var' => 1000 })
|
||||
assert_equal("1000", Template.parse("{{var | tap}}").render!('var' => 1000))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -165,8 +167,8 @@ class FiltersInTemplate < Minitest::Test
|
||||
end
|
||||
|
||||
def test_local_filter_with_deprecated_syntax
|
||||
assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, CanadianMoneyFilter)
|
||||
assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, [CanadianMoneyFilter])
|
||||
assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, CanadianMoneyFilter))
|
||||
assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, [CanadianMoneyFilter]))
|
||||
end
|
||||
end # FiltersTest
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class HashOrderingTest < Minitest::Test
|
||||
module MoneyFilter
|
||||
def money(input)
|
||||
sprintf(' %d$ ', input)
|
||||
format(' %d$ ', input)
|
||||
end
|
||||
end
|
||||
|
||||
module CanadianMoneyFilter
|
||||
def money(input)
|
||||
sprintf(' %d$ CAD ', input)
|
||||
format(' %d$ CAD ', input)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module FunnyFilter
|
||||
def make_funny(input)
|
||||
def make_funny(_input)
|
||||
'LOL'
|
||||
end
|
||||
|
||||
@@ -32,7 +34,7 @@ class OutputTest < Minitest::Test
|
||||
def setup
|
||||
@assigns = {
|
||||
'best_cars' => 'bmw',
|
||||
'car' => { 'bmw' => 'good', 'gm' => 'bad' }
|
||||
'car' => { 'bmw' => 'good', 'gm' => 'bad' },
|
||||
}
|
||||
end
|
||||
|
||||
@@ -40,84 +42,84 @@ class OutputTest < Minitest::Test
|
||||
text = %( {{best_cars}} )
|
||||
|
||||
expected = %( bmw )
|
||||
assert_equal expected, Template.parse(text).render!(@assigns)
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns))
|
||||
end
|
||||
|
||||
def test_variable_traversing_with_two_brackets
|
||||
text = %({{ site.data.menu[include.menu][include.locale] }})
|
||||
assert_equal "it works!", Template.parse(text).render!(
|
||||
assert_equal("it works!", Template.parse(text).render!(
|
||||
"site" => { "data" => { "menu" => { "foo" => { "bar" => "it works!" } } } },
|
||||
"include" => { "menu" => "foo", "locale" => "bar" }
|
||||
)
|
||||
))
|
||||
end
|
||||
|
||||
def test_variable_traversing
|
||||
text = %( {{car.bmw}} {{car.gm}} {{car.bmw}} )
|
||||
|
||||
expected = %( good bad good )
|
||||
assert_equal expected, Template.parse(text).render!(@assigns)
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns))
|
||||
end
|
||||
|
||||
def test_variable_piping
|
||||
text = %( {{ car.gm | make_funny }} )
|
||||
text = %( {{ car.gm | make_funny }} )
|
||||
expected = %( LOL )
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
|
||||
end
|
||||
|
||||
def test_variable_piping_with_input
|
||||
text = %( {{ car.gm | cite_funny }} )
|
||||
text = %( {{ car.gm | cite_funny }} )
|
||||
expected = %( LOL: bad )
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
|
||||
end
|
||||
|
||||
def test_variable_piping_with_args
|
||||
text = %! {{ car.gm | add_smiley : ':-(' }} !
|
||||
text = %! {{ car.gm | add_smiley : ':-(' }} !
|
||||
expected = %| bad :-( |
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
|
||||
end
|
||||
|
||||
def test_variable_piping_with_no_args
|
||||
text = %( {{ car.gm | add_smiley }} )
|
||||
text = %( {{ car.gm | add_smiley }} )
|
||||
expected = %| bad :-) |
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
|
||||
end
|
||||
|
||||
def test_multiple_variable_piping_with_args
|
||||
text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} !
|
||||
text = %! {{ car.gm | add_smiley : ':-(' | add_smiley : ':-('}} !
|
||||
expected = %| bad :-( :-( |
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
|
||||
end
|
||||
|
||||
def test_variable_piping_with_multiple_args
|
||||
text = %( {{ car.gm | add_tag : 'span', 'bar'}} )
|
||||
text = %( {{ car.gm | add_tag : 'span', 'bar'}} )
|
||||
expected = %( <span id="bar">bad</span> )
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
|
||||
end
|
||||
|
||||
def test_variable_piping_with_variable_args
|
||||
text = %( {{ car.gm | add_tag : 'span', car.bmw}} )
|
||||
text = %( {{ car.gm | add_tag : 'span', car.bmw}} )
|
||||
expected = %( <span id="good">bad</span> )
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
|
||||
end
|
||||
|
||||
def test_multiple_pipings
|
||||
text = %( {{ best_cars | cite_funny | paragraph }} )
|
||||
text = %( {{ best_cars | cite_funny | paragraph }} )
|
||||
expected = %( <p>LOL: bmw</p> )
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
|
||||
end
|
||||
|
||||
def test_link_to
|
||||
text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} )
|
||||
text = %( {{ 'Typo' | link_to: 'http://typo.leetsoft.com' }} )
|
||||
expected = %( <a href="http://typo.leetsoft.com">Typo</a> )
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter])
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: [FunnyFilter]))
|
||||
end
|
||||
end # OutputTest
|
||||
|
||||
@@ -227,7 +227,7 @@ class ParseTreeVisitorTest < Minitest::Test
|
||||
[[nil, [
|
||||
[nil, [[nil, [["other", []]]]]],
|
||||
["test", []],
|
||||
["xs", []]
|
||||
["xs", []],
|
||||
]]],
|
||||
traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit
|
||||
)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class ParsingQuirksTest < Minitest::Test
|
||||
@@ -5,7 +7,7 @@ class ParsingQuirksTest < Minitest::Test
|
||||
|
||||
def test_parsing_css
|
||||
text = " div { font-weight: bold; } "
|
||||
assert_equal text, Template.parse(text).render!
|
||||
assert_equal(text, Template.parse(text).render!)
|
||||
end
|
||||
|
||||
def test_raise_on_single_close_bracet
|
||||
@@ -27,7 +29,7 @@ class ParsingQuirksTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_error_on_empty_filter
|
||||
assert Template.parse("{{test}}")
|
||||
assert(Template.parse("{{test}}"))
|
||||
|
||||
with_error_mode(:lax) do
|
||||
assert Template.parse("{{|test}}")
|
||||
@@ -62,15 +64,15 @@ class ParsingQuirksTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_no_error_on_lax_empty_filter
|
||||
assert Template.parse("{{test |a|b|}}", error_mode: :lax)
|
||||
assert Template.parse("{{test}}", error_mode: :lax)
|
||||
assert Template.parse("{{|test|}}", error_mode: :lax)
|
||||
assert(Template.parse("{{test |a|b|}}", error_mode: :lax))
|
||||
assert(Template.parse("{{test}}", error_mode: :lax))
|
||||
assert(Template.parse("{{|test|}}", error_mode: :lax))
|
||||
end
|
||||
|
||||
def test_meaningless_parens_lax
|
||||
with_error_mode(:lax) do
|
||||
assigns = { 'b' => 'bar', 'c' => 'baz' }
|
||||
markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
|
||||
markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
|
||||
assert_template_result(' YES ', "{% if #{markup} %} YES {% endif %}", assigns)
|
||||
end
|
||||
end
|
||||
|
||||
27
test/integration/registers/disabled_tags_test.rb
Normal file
27
test/integration/registers/disabled_tags_test.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class DisabledTagsTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
class DisableRaw < Block
|
||||
disable_tags "raw"
|
||||
end
|
||||
|
||||
class DisableRawEcho < Block
|
||||
disable_tags "raw", "echo"
|
||||
end
|
||||
|
||||
def test_disables_raw
|
||||
with_custom_tag('disable', DisableRaw) do
|
||||
assert_template_result 'raw usage is not allowed in this contextfoo', '{% disable %}{% raw %}Foobar{% endraw %}{% echo "foo" %}{% enddisable %}'
|
||||
end
|
||||
end
|
||||
|
||||
def test_disables_echo_and_raw
|
||||
with_custom_tag('disable', DisableRawEcho) do
|
||||
assert_template_result 'raw usage is not allowed in this contextecho usage is not allowed in this context', '{% disable %}{% raw %}Foobar{% endraw %}{% echo "foo" %}{% enddisable %}'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class RenderProfilingTest < Minitest::Test
|
||||
@@ -17,35 +19,35 @@ class RenderProfilingTest < Minitest::Test
|
||||
t = Template.parse("{{ 'a string' | upcase }}")
|
||||
t.render!
|
||||
|
||||
assert_nil t.profiler
|
||||
assert_nil(t.profiler)
|
||||
end
|
||||
|
||||
def test_parse_makes_available_simple_profiling
|
||||
t = Template.parse("{{ 'a string' | upcase }}", profile: true)
|
||||
t.render!
|
||||
|
||||
assert_equal 1, t.profiler.length
|
||||
assert_equal(1, t.profiler.length)
|
||||
|
||||
node = t.profiler[0]
|
||||
assert_equal " 'a string' | upcase ", node.code
|
||||
assert_equal(" 'a string' | upcase ", node.code)
|
||||
end
|
||||
|
||||
def test_render_ignores_raw_strings_when_profiling
|
||||
t = Template.parse("This is raw string\nstuff\nNewline", profile: true)
|
||||
t.render!
|
||||
|
||||
assert_equal 0, t.profiler.length
|
||||
assert_equal(0, t.profiler.length)
|
||||
end
|
||||
|
||||
def test_profiling_includes_line_numbers_of_liquid_nodes
|
||||
t = Template.parse("{{ 'a string' | upcase }}\n{% increment test %}", profile: true)
|
||||
t.render!
|
||||
assert_equal 2, t.profiler.length
|
||||
assert_equal(2, t.profiler.length)
|
||||
|
||||
# {{ 'a string' | upcase }}
|
||||
assert_equal 1, t.profiler[0].line_number
|
||||
assert_equal(1, t.profiler[0].line_number)
|
||||
# {{ increment test }}
|
||||
assert_equal 2, t.profiler[1].line_number
|
||||
assert_equal(2, t.profiler[1].line_number)
|
||||
end
|
||||
|
||||
def test_profiling_includes_line_numbers_of_included_partials
|
||||
@@ -55,9 +57,9 @@ class RenderProfilingTest < Minitest::Test
|
||||
included_children = t.profiler[0].children
|
||||
|
||||
# {% assign template_name = 'a_template' %}
|
||||
assert_equal 1, included_children[0].line_number
|
||||
assert_equal(1, included_children[0].line_number)
|
||||
# {{ template_name }}
|
||||
assert_equal 2, included_children[1].line_number
|
||||
assert_equal(2, included_children[1].line_number)
|
||||
end
|
||||
|
||||
def test_profiling_times_the_rendering_of_tokens
|
||||
@@ -65,14 +67,14 @@ class RenderProfilingTest < Minitest::Test
|
||||
t.render!
|
||||
|
||||
node = t.profiler[0]
|
||||
refute_nil node.render_time
|
||||
refute_nil(node.render_time)
|
||||
end
|
||||
|
||||
def test_profiling_times_the_entire_render
|
||||
t = Template.parse("{% include 'a_template' %}", profile: true)
|
||||
t.render!
|
||||
|
||||
assert t.profiler.total_render_time >= 0, "Total render time was not calculated"
|
||||
assert(t.profiler.total_render_time >= 0, "Total render time was not calculated")
|
||||
end
|
||||
|
||||
def test_profiling_uses_include_to_mark_children
|
||||
@@ -80,7 +82,7 @@ class RenderProfilingTest < Minitest::Test
|
||||
t.render!
|
||||
|
||||
include_node = t.profiler[1]
|
||||
assert_equal 2, include_node.children.length
|
||||
assert_equal(2, include_node.children.length)
|
||||
end
|
||||
|
||||
def test_profiling_marks_children_with_the_name_of_included_partial
|
||||
@@ -128,27 +130,42 @@ class RenderProfilingTest < Minitest::Test
|
||||
t.render!
|
||||
|
||||
timing_count = 0
|
||||
t.profiler.each do |timing|
|
||||
t.profiler.each do |_timing|
|
||||
timing_count += 1
|
||||
end
|
||||
|
||||
assert_equal 2, timing_count
|
||||
assert_equal(2, timing_count)
|
||||
end
|
||||
|
||||
def test_profiling_marks_children_of_if_blocks
|
||||
t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
|
||||
t.render!
|
||||
|
||||
assert_equal 1, t.profiler.length
|
||||
assert_equal 2, t.profiler[0].children.length
|
||||
assert_equal(1, t.profiler.length)
|
||||
assert_equal(2, t.profiler[0].children.length)
|
||||
end
|
||||
|
||||
def test_profiling_marks_children_of_for_blocks
|
||||
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
|
||||
t.render!({ "collection" => ["one", "two"] })
|
||||
t.render!("collection" => ["one", "two"])
|
||||
|
||||
assert_equal 1, t.profiler.length
|
||||
assert_equal(1, t.profiler.length)
|
||||
# Will profile each invocation of the for block
|
||||
assert_equal 2, t.profiler[0].children.length
|
||||
assert_equal(2, t.profiler[0].children.length)
|
||||
end
|
||||
|
||||
def test_profiling_supports_self_time
|
||||
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
|
||||
t.render!("collection" => ["one", "two"])
|
||||
leaf = t.profiler[0].children[0]
|
||||
|
||||
assert_operator(leaf.self_time, :>, 0)
|
||||
end
|
||||
|
||||
def test_profiling_supports_total_time
|
||||
t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
|
||||
t.render!
|
||||
|
||||
assert_operator(t.profiler[0].total_time, :>, 0)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
module SecurityFilter
|
||||
@@ -14,31 +16,31 @@ class SecurityTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_no_instance_eval
|
||||
text = %( {{ '1+1' | instance_eval }} )
|
||||
text = %( {{ '1+1' | instance_eval }} )
|
||||
expected = %( 1+1 )
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns)
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns))
|
||||
end
|
||||
|
||||
def test_no_existing_instance_eval
|
||||
text = %( {{ '1+1' | __instance_eval__ }} )
|
||||
text = %( {{ '1+1' | __instance_eval__ }} )
|
||||
expected = %( 1+1 )
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns)
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns))
|
||||
end
|
||||
|
||||
def test_no_instance_eval_after_mixing_in_new_filter
|
||||
text = %( {{ '1+1' | instance_eval }} )
|
||||
text = %( {{ '1+1' | instance_eval }} )
|
||||
expected = %( 1+1 )
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns)
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns))
|
||||
end
|
||||
|
||||
def test_no_instance_eval_later_in_chain
|
||||
text = %( {{ '1+1' | add_one | instance_eval }} )
|
||||
text = %( {{ '1+1' | add_one | instance_eval }} )
|
||||
expected = %( 1+1 + 1 )
|
||||
|
||||
assert_equal expected, Template.parse(text).render!(@assigns, filters: SecurityFilter)
|
||||
assert_equal(expected, Template.parse(text).render!(@assigns, filters: SecurityFilter))
|
||||
end
|
||||
|
||||
def test_does_not_add_filters_to_symbol_table
|
||||
@@ -47,32 +49,32 @@ class SecurityTest < Minitest::Test
|
||||
test = %( {{ "some_string" | a_bad_filter }} )
|
||||
|
||||
template = Template.parse(test)
|
||||
assert_equal [], (Symbol.all_symbols - current_symbols)
|
||||
assert_equal([], (Symbol.all_symbols - current_symbols))
|
||||
|
||||
template.render!
|
||||
assert_equal [], (Symbol.all_symbols - current_symbols)
|
||||
assert_equal([], (Symbol.all_symbols - current_symbols))
|
||||
end
|
||||
|
||||
def test_does_not_add_drop_methods_to_symbol_table
|
||||
current_symbols = Symbol.all_symbols
|
||||
|
||||
assigns = { 'drop' => Drop.new }
|
||||
assert_equal "", Template.parse("{{ drop.custom_method_1 }}", assigns).render!
|
||||
assert_equal "", Template.parse("{{ drop.custom_method_2 }}", assigns).render!
|
||||
assert_equal "", Template.parse("{{ drop.custom_method_3 }}", assigns).render!
|
||||
assert_equal("", Template.parse("{{ drop.custom_method_1 }}", assigns).render!)
|
||||
assert_equal("", Template.parse("{{ drop.custom_method_2 }}", assigns).render!)
|
||||
assert_equal("", Template.parse("{{ drop.custom_method_3 }}", assigns).render!)
|
||||
|
||||
assert_equal [], (Symbol.all_symbols - current_symbols)
|
||||
assert_equal([], (Symbol.all_symbols - current_symbols))
|
||||
end
|
||||
|
||||
def test_max_depth_nested_blocks_does_not_raise_exception
|
||||
depth = Liquid::Block::MAX_DEPTH
|
||||
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
|
||||
assert_equal "rendered", Template.parse(code).render!
|
||||
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
|
||||
assert_equal("rendered", Template.parse(code).render!)
|
||||
end
|
||||
|
||||
def test_more_than_max_depth_nested_blocks_raises_exception
|
||||
depth = Liquid::Block::MAX_DEPTH + 1
|
||||
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
|
||||
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
|
||||
assert_raises(Liquid::StackLevelError) do
|
||||
Template.parse(code).render!
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user