liquid 5.3.0 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +20 -1
  3. data/README.md +5 -5
  4. data/lib/liquid/block.rb +8 -4
  5. data/lib/liquid/block_body.rb +21 -6
  6. data/lib/liquid/condition.rb +9 -4
  7. data/lib/liquid/context.rb +13 -5
  8. data/lib/liquid/drop.rb +4 -0
  9. data/lib/liquid/errors.rb +16 -15
  10. data/lib/liquid/expression.rb +4 -1
  11. data/lib/liquid/forloop_drop.rb +45 -5
  12. data/lib/liquid/lexer.rb +2 -3
  13. data/lib/liquid/locales/en.yml +7 -5
  14. data/lib/liquid/partial_cache.rb +15 -6
  15. data/lib/liquid/range_lookup.rb +11 -1
  16. data/lib/liquid/{static_registers.rb → registers.rb} +13 -10
  17. data/lib/liquid/standardfilters.rb +480 -68
  18. data/lib/liquid/strainer_factory.rb +4 -0
  19. data/lib/liquid/strainer_template.rb +4 -0
  20. data/lib/liquid/tablerowloop_drop.rb +58 -1
  21. data/lib/liquid/tag/disabler.rb +0 -8
  22. data/lib/liquid/tag.rb +10 -3
  23. data/lib/liquid/tags/assign.rb +12 -8
  24. data/lib/liquid/tags/break.rb +8 -0
  25. data/lib/liquid/tags/capture.rb +13 -10
  26. data/lib/liquid/tags/case.rb +22 -1
  27. data/lib/liquid/tags/comment.rb +72 -0
  28. data/lib/liquid/tags/continue.rb +8 -9
  29. data/lib/liquid/tags/cycle.rb +12 -11
  30. data/lib/liquid/tags/decrement.rb +22 -20
  31. data/lib/liquid/tags/echo.rb +16 -9
  32. data/lib/liquid/tags/for.rb +25 -46
  33. data/lib/liquid/tags/if.rb +12 -10
  34. data/lib/liquid/tags/include.rb +21 -17
  35. data/lib/liquid/tags/increment.rb +22 -17
  36. data/lib/liquid/tags/inline_comment.rb +30 -0
  37. data/lib/liquid/tags/raw.rb +13 -2
  38. data/lib/liquid/tags/render.rb +37 -8
  39. data/lib/liquid/tags/table_row.rb +33 -3
  40. data/lib/liquid/tags/unless.rb +17 -6
  41. data/lib/liquid/template.rb +11 -4
  42. data/lib/liquid/tokenizer.rb +9 -3
  43. data/lib/liquid/variable.rb +4 -4
  44. data/lib/liquid/variable_lookup.rb +10 -7
  45. data/lib/liquid/version.rb +1 -1
  46. data/lib/liquid.rb +3 -3
  47. metadata +7 -123
  48. data/lib/liquid/register.rb +0 -6
  49. data/test/fixtures/en_locale.yml +0 -9
  50. data/test/integration/assign_test.rb +0 -117
  51. data/test/integration/blank_test.rb +0 -109
  52. data/test/integration/block_test.rb +0 -58
  53. data/test/integration/capture_test.rb +0 -58
  54. data/test/integration/context_test.rb +0 -634
  55. data/test/integration/document_test.rb +0 -21
  56. data/test/integration/drop_test.rb +0 -257
  57. data/test/integration/error_handling_test.rb +0 -272
  58. data/test/integration/expression_test.rb +0 -46
  59. data/test/integration/filter_kwarg_test.rb +0 -24
  60. data/test/integration/filter_test.rb +0 -189
  61. data/test/integration/hash_ordering_test.rb +0 -25
  62. data/test/integration/output_test.rb +0 -125
  63. data/test/integration/parsing_quirks_test.rb +0 -134
  64. data/test/integration/profiler_test.rb +0 -240
  65. data/test/integration/security_test.rb +0 -89
  66. data/test/integration/standard_filter_test.rb +0 -925
  67. data/test/integration/tag/disableable_test.rb +0 -59
  68. data/test/integration/tag_test.rb +0 -45
  69. data/test/integration/tags/break_tag_test.rb +0 -17
  70. data/test/integration/tags/continue_tag_test.rb +0 -17
  71. data/test/integration/tags/echo_test.rb +0 -13
  72. data/test/integration/tags/for_tag_test.rb +0 -466
  73. data/test/integration/tags/if_else_tag_test.rb +0 -190
  74. data/test/integration/tags/include_tag_test.rb +0 -269
  75. data/test/integration/tags/increment_tag_test.rb +0 -25
  76. data/test/integration/tags/liquid_tag_test.rb +0 -116
  77. data/test/integration/tags/raw_tag_test.rb +0 -34
  78. data/test/integration/tags/render_tag_test.rb +0 -213
  79. data/test/integration/tags/standard_tag_test.rb +0 -303
  80. data/test/integration/tags/statements_test.rb +0 -113
  81. data/test/integration/tags/table_row_test.rb +0 -66
  82. data/test/integration/tags/unless_else_tag_test.rb +0 -28
  83. data/test/integration/template_test.rb +0 -340
  84. data/test/integration/trim_mode_test.rb +0 -563
  85. data/test/integration/variable_test.rb +0 -138
  86. data/test/test_helper.rb +0 -207
  87. data/test/unit/block_unit_test.rb +0 -53
  88. data/test/unit/condition_unit_test.rb +0 -181
  89. data/test/unit/file_system_unit_test.rb +0 -37
  90. data/test/unit/i18n_unit_test.rb +0 -39
  91. data/test/unit/lexer_unit_test.rb +0 -53
  92. data/test/unit/parse_tree_visitor_test.rb +0 -261
  93. data/test/unit/parser_unit_test.rb +0 -84
  94. data/test/unit/partial_cache_unit_test.rb +0 -128
  95. data/test/unit/regexp_unit_test.rb +0 -46
  96. data/test/unit/static_registers_unit_test.rb +0 -156
  97. data/test/unit/strainer_factory_unit_test.rb +0 -101
  98. data/test/unit/strainer_template_unit_test.rb +0 -82
  99. data/test/unit/tag_unit_test.rb +0 -23
  100. data/test/unit/tags/case_tag_unit_test.rb +0 -12
  101. data/test/unit/tags/for_tag_unit_test.rb +0 -15
  102. data/test/unit/tags/if_tag_unit_test.rb +0 -10
  103. data/test/unit/template_factory_unit_test.rb +0 -12
  104. data/test/unit/template_unit_test.rb +0 -87
  105. data/test/unit/tokenizer_unit_test.rb +0 -62
  106. data/test/unit/variable_unit_test.rb +0 -164
@@ -14,6 +14,10 @@ module Liquid
14
14
  strainer_from_cache(filters).new(context)
15
15
  end
16
16
 
17
+ def global_filter_names
18
+ GlobalCache.filter_method_names
19
+ end
20
+
17
21
  GlobalCache = Class.new(StrainerTemplate)
18
22
 
19
23
  private
@@ -36,6 +36,10 @@ module Liquid
36
36
  subclass.instance_variable_set(:@filter_methods, @filter_methods.dup)
37
37
  end
38
38
 
39
+ def filter_method_names
40
+ filter_methods.map(&:to_s).to_a
41
+ end
42
+
39
43
  private
40
44
 
41
45
  def filter_methods
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
+ # @liquid_public_docs
5
+ # @liquid_type object
6
+ # @liquid_name tablerowloop
7
+ # @liquid_summary
8
+ # Information about a parent [`tablerow` loop](/docs/api/liquid/tags/tablerow).
4
9
  class TablerowloopDrop < Drop
5
10
  def initialize(length, cols)
6
11
  @length = length
@@ -10,40 +15,92 @@ module Liquid
10
15
  @index = 0
11
16
  end
12
17
 
13
- attr_reader :length, :col, :row
18
+ # @liquid_public_docs
19
+ # @liquid_summary
20
+ # The total number of iterations in the loop.
21
+ # @liquid_return [number]
22
+ attr_reader :length
14
23
 
24
+ # @liquid_public_docs
25
+ # @liquid_summary
26
+ # The 1-based index of the current column.
27
+ # @liquid_return [number]
28
+ attr_reader :col
29
+
30
+ # @liquid_public_docs
31
+ # @liquid_summary
32
+ # The 1-based index of current row.
33
+ # @liquid_return [number]
34
+ attr_reader :row
35
+
36
+ # @liquid_public_docs
37
+ # @liquid_summary
38
+ # The 1-based index of the current iteration.
39
+ # @liquid_return [number]
15
40
  def index
16
41
  @index + 1
17
42
  end
18
43
 
44
+ # @liquid_public_docs
45
+ # @liquid_summary
46
+ # The 0-based index of the current iteration.
47
+ # @liquid_return [number]
19
48
  def index0
20
49
  @index
21
50
  end
22
51
 
52
+ # @liquid_public_docs
53
+ # @liquid_summary
54
+ # The 0-based index of the current column.
55
+ # @liquid_return [number]
23
56
  def col0
24
57
  @col - 1
25
58
  end
26
59
 
60
+ # @liquid_public_docs
61
+ # @liquid_summary
62
+ # The 1-based index of the current iteration, in reverse order.
63
+ # @liquid_return [number]
27
64
  def rindex
28
65
  @length - @index
29
66
  end
30
67
 
68
+ # @liquid_public_docs
69
+ # @liquid_summary
70
+ # The 0-based index of the current iteration, in reverse order.
71
+ # @liquid_return [number]
31
72
  def rindex0
32
73
  @length - @index - 1
33
74
  end
34
75
 
76
+ # @liquid_public_docs
77
+ # @liquid_summary
78
+ # Returns `true` if the current iteration is the first. Returns `false` if not.
79
+ # @liquid_return [boolean]
35
80
  def first
36
81
  @index == 0
37
82
  end
38
83
 
84
+ # @liquid_public_docs
85
+ # @liquid_summary
86
+ # Returns `true` if the current iteration is the last. Returns `false` if not.
87
+ # @liquid_return [boolean]
39
88
  def last
40
89
  @index == @length - 1
41
90
  end
42
91
 
92
+ # @liquid_public_docs
93
+ # @liquid_summary
94
+ # Returns `true` if the current column is the first in the row. Returns `false` if not.
95
+ # @liquid_return [boolean]
43
96
  def col_first
44
97
  @col == 1
45
98
  end
46
99
 
100
+ # @liquid_public_docs
101
+ # @liquid_summary
102
+ # Returns `true` if the current column is the last in the row. Returns `false` if not.
103
+ # @liquid_return [boolean]
47
104
  def col_last
48
105
  @col == @cols
49
106
  end
@@ -3,14 +3,6 @@
3
3
  module Liquid
4
4
  class Tag
5
5
  module Disabler
6
- module ClassMethods
7
- attr_reader :disabled_tags
8
- end
9
-
10
- def self.prepended(base)
11
- base.extend(ClassMethods)
12
- end
13
-
14
6
  def render_to_output_buffer(context, output)
15
7
  context.with_disabled_tags(self.class.disabled_tags) do
16
8
  super
data/lib/liquid/tag.rb CHANGED
@@ -14,12 +14,18 @@ module Liquid
14
14
  end
15
15
 
16
16
  def disable_tags(*tag_names)
17
- @disabled_tags ||= []
18
- @disabled_tags.concat(tag_names)
17
+ tag_names += disabled_tags
18
+ define_singleton_method(:disabled_tags) { tag_names }
19
19
  prepend(Disabler)
20
20
  end
21
21
 
22
22
  private :new
23
+
24
+ protected
25
+
26
+ def disabled_tags
27
+ []
28
+ end
23
29
  end
24
30
 
25
31
  def initialize(tag_name, markup, parse_context)
@@ -48,7 +54,8 @@ module Liquid
48
54
  # of the `render_to_output_buffer` method will become the default and the `render`
49
55
  # method will be removed.
50
56
  def render_to_output_buffer(context, output)
51
- output << render(context)
57
+ render_result = render(context)
58
+ output << render_result if render_result
52
59
  output
53
60
  end
54
61
 
@@ -1,14 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
- # Assign sets a variable in your template.
5
- #
6
- # {% assign foo = 'monkey' %}
7
- #
8
- # You can then use the variable later in the page.
9
- #
10
- # {{ foo }}
11
- #
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category variable
7
+ # @liquid_name assign
8
+ # @liquid_summary
9
+ # Creates a new variable.
10
+ # @liquid_description
11
+ # You can create variables of any [basic type](/docs/api/liquid/basics#types), [object](/docs/api/liquid/objects), or object property.
12
+ # @liquid_syntax
13
+ # {% assign variable_name = value %}
14
+ # @liquid_syntax_keyword variable_name The name of the variable being created.
15
+ # @liquid_syntax_keyword value The value you want to assign to the variable.
12
16
  class Assign < Tag
13
17
  Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
14
18
 
@@ -10,6 +10,14 @@ module Liquid
10
10
  # {% endif %}
11
11
  # {% endfor %}
12
12
  #
13
+ # @liquid_public_docs
14
+ # @liquid_type tag
15
+ # @liquid_category iteration
16
+ # @liquid_name break
17
+ # @liquid_summary
18
+ # Stops a [`for` loop](/docs/api/liquid/tags/for) from iterating.
19
+ # @liquid_syntax
20
+ # {% break %}
13
21
  class Break < Tag
14
22
  INTERRUPT = BreakInterrupt.new.freeze
15
23
 
@@ -1,17 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
- # Capture stores the result of a block into a variable without rendering it inplace.
5
- #
6
- # {% capture heading %}
7
- # Monkeys!
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category variable
7
+ # @liquid_name capture
8
+ # @liquid_summary
9
+ # Creates a new variable with a string value.
10
+ # @liquid_description
11
+ # You can create complex strings with Liquid logic and variables.
12
+ # @liquid_syntax
13
+ # {% capture variable %}
14
+ # value
8
15
  # {% endcapture %}
9
- # ...
10
- # <h1>{{ heading }}</h1>
11
- #
12
- # Capture is useful for saving content for use later in your template, such as
13
- # in a sidebar or footer.
14
- #
16
+ # @liquid_syntax_keyword variable The name of the variable being created.
17
+ # @liquid_syntax_keyword value The value you want to assign to the variable.
15
18
  class Capture < Block
16
19
  Syntax = /(#{VariableSignature}+)/o
17
20
 
@@ -1,6 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category conditional
7
+ # @liquid_name case
8
+ # @liquid_summary
9
+ # Renders a specific expression depending on the value of a specific variable.
10
+ # @liquid_syntax
11
+ # {% case variable %}
12
+ # {% when first_value %}
13
+ # first_expression
14
+ # {% when second_value %}
15
+ # second_expression
16
+ # {% else %}
17
+ # third_expression
18
+ # {% endcase %}
19
+ # @liquid_syntax_keyword variable The name of the variable you want to base your case statement on.
20
+ # @liquid_syntax_keyword first_value A specific value to check for.
21
+ # @liquid_syntax_keyword second_value A specific value to check for.
22
+ # @liquid_syntax_keyword first_expression An expression to be rendered when the variable's value matches `first_value`.
23
+ # @liquid_syntax_keyword second_expression An expression to be rendered when the variable's value matches `second_value`.
24
+ # @liquid_syntax_keyword third_expression An expression to be rendered when the variable's value has no match.
4
25
  class Case < Block
5
26
  Syntax = /(#{QuotedFragment})/o
6
27
  WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om
@@ -56,7 +77,7 @@ module Liquid
56
77
  end
57
78
 
58
79
  result = Liquid::Utils.to_liquid_value(
59
- block.evaluate(context)
80
+ block.evaluate(context),
60
81
  )
61
82
 
62
83
  if result
@@ -1,6 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category syntax
7
+ # @liquid_name comment
8
+ # @liquid_summary
9
+ # Prevents an expression from being rendered or output.
10
+ # @liquid_description
11
+ # Any text inside `comment` tags won't be output, and any Liquid code will be parsed, but not executed.
12
+ # @liquid_syntax
13
+ # {% comment %}
14
+ # content
15
+ # {% endcomment %}
16
+ # @liquid_syntax_keyword content The content of the comment.
4
17
  class Comment < Block
5
18
  def render_to_output_buffer(_context, output)
6
19
  output
@@ -12,6 +25,65 @@ module Liquid
12
25
  def blank?
13
26
  true
14
27
  end
28
+
29
+ private
30
+
31
+ def parse_body(body, tokenizer)
32
+ if parse_context.depth >= MAX_DEPTH
33
+ raise StackLevelError, "Nesting too deep"
34
+ end
35
+
36
+ parse_context.depth += 1
37
+ comment_tag_depth = 1
38
+
39
+ begin
40
+ # Consume tokens without creating child nodes.
41
+ # The children tag doesn't require to be a valid Liquid except the comment and raw tag.
42
+ # The child comment and raw tag must be closed.
43
+ while (token = tokenizer.send(:shift))
44
+ tag_name = if tokenizer.for_liquid_tag
45
+ next if token.empty? || token.match?(BlockBody::WhitespaceOrNothing)
46
+
47
+ tag_name_match = BlockBody::LiquidTagToken.match(token)
48
+
49
+ next if tag_name_match.nil?
50
+
51
+ tag_name_match[1]
52
+ else
53
+ token =~ BlockBody::FullToken
54
+ Regexp.last_match(2)
55
+ end
56
+
57
+ case tag_name
58
+ when "raw"
59
+ parse_raw_tag_body(tokenizer)
60
+ when "comment"
61
+ comment_tag_depth += 1
62
+ when "endcomment"
63
+ comment_tag_depth -= 1
64
+ end
65
+
66
+ if comment_tag_depth.zero?
67
+ parse_context.trim_whitespace = (token[-3] == WhitespaceControl) unless tokenizer.for_liquid_tag
68
+ return false
69
+ end
70
+ end
71
+
72
+ raise_tag_never_closed(block_name)
73
+ ensure
74
+ parse_context.depth -= 1
75
+ end
76
+
77
+ false
78
+ end
79
+
80
+ def parse_raw_tag_body(tokenizer)
81
+ while (token = tokenizer.send(:shift))
82
+ return if token =~ BlockBody::FullTokenPossiblyInvalid && "endraw" == Regexp.last_match(2)
83
+ end
84
+
85
+ raise_tag_never_closed("raw")
86
+ end
15
87
  end
16
88
 
17
89
  Template.register_tag('comment', Comment)
@@ -1,15 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
- # Continue tag to be used to break out of a for loop.
5
- #
6
- # == Basic Usage:
7
- # {% for item in collection %}
8
- # {% if item.condition %}
9
- # {% continue %}
10
- # {% endif %}
11
- # {% endfor %}
12
- #
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category iteration
7
+ # @liquid_name continue
8
+ # @liquid_summary
9
+ # Causes a [`for` loop](/docs/api/liquid/tags/for) to skip to the next iteration.
10
+ # @liquid_syntax
11
+ # {% continue %}
13
12
  class Continue < Tag
14
13
  INTERRUPT = ContinueInterrupt.new.freeze
15
14
 
@@ -1,18 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
- # Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
5
- #
6
- # {% for item in items %}
7
- # <div class="{% cycle 'red', 'green', 'blue' %}"> {{ item }} </div>
8
- # {% end %}
9
- #
10
- # <div class="red"> Item one </div>
11
- # <div class="green"> Item two </div>
12
- # <div class="blue"> Item three </div>
13
- # <div class="red"> Item four </div>
14
- # <div class="green"> Item five</div>
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category iteration
7
+ # @liquid_name cycle
8
+ # @liquid_summary
9
+ # Loops through a group of strings and outputs them one at a time for each iteration of a [`for` loop](/docs/api/liquid/tags/for).
10
+ # @liquid_description
11
+ # The `cycle` tag must be used inside a `for` loop.
15
12
  #
13
+ # > Tip:
14
+ # > Use the `cycle` tag to output text in a predictable pattern. For example, to apply odd/even classes to rows in a table.
15
+ # @liquid_syntax
16
+ # {% cycle string, string, ... %}
16
17
  class Cycle < Tag
17
18
  SimpleSyntax = /\A#{QuotedFragment}+/o
18
19
  NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
@@ -1,34 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
- # decrement is used in a place where one needs to insert a counter
5
- # into a template, and needs the counter to survive across
6
- # multiple instantiations of the template.
7
- # NOTE: decrement is a pre-decrement, --i,
8
- # while increment is post: i++.
9
- #
10
- # (To achieve the survival, the application must keep the context)
11
- #
12
- # if the variable does not exist, it is created with value 0.
13
-
14
- # Hello: {% decrement variable %}
15
- #
16
- # gives you:
17
- #
18
- # Hello: -1
19
- # Hello: -2
20
- # Hello: -3
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category variable
7
+ # @liquid_name decrement
8
+ # @liquid_summary
9
+ # Creates a new variable, with a default value of -1, that's decreased by 1 with each subsequent call.
10
+ # @liquid_description
11
+ # Variables that are declared with `decrement` are unique to the [layout](/themes/architecture/layouts), [template](/themes/architecture/templates),
12
+ # or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
13
+ # [snippets](/themes/architecture#snippets) included in the file.
21
14
  #
15
+ # Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/docs/api/liquid/tags/assign)
16
+ # and [`capture`](/docs/api/liquid/tags/capture). However, `decrement` and [`increment`](/docs/api/liquid/tags/increment) share
17
+ # variables.
18
+ # @liquid_syntax
19
+ # {% decrement variable_name %}
20
+ # @liquid_syntax_keyword variable_name The name of the variable being decremented.
22
21
  class Decrement < Tag
22
+ attr_reader :variable_name
23
+
23
24
  def initialize(tag_name, markup, options)
24
25
  super
25
- @variable = markup.strip
26
+ @variable_name = markup.strip
26
27
  end
27
28
 
28
29
  def render_to_output_buffer(context, output)
29
- value = context.environments.first[@variable] ||= 0
30
+ counter_environment = context.environments.first
31
+ value = counter_environment[@variable_name] || 0
30
32
  value -= 1
31
- context.environments.first[@variable] = value
33
+ counter_environment[@variable_name] = value
32
34
  output << value.to_s
33
35
  output
34
36
  end
@@ -1,16 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
- # Echo outputs an expression
5
- #
6
- # {% echo monkey %}
7
- # {% echo user.name %}
8
- #
9
- # This is identical to variable output syntax, like {{ foo }}, but works
10
- # inside {% liquid %} tags. The full syntax is supported, including filters:
11
- #
12
- # {% echo user | link %}
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category syntax
7
+ # @liquid_name echo
8
+ # @liquid_summary
9
+ # Outputs an expression.
10
+ # @liquid_description
11
+ # Using the `echo` tag is the same as wrapping an expression in curly brackets (`{{` and `}}`). However, unlike the curly
12
+ # bracket method, you can use the `echo` tag inside [`liquid` tags](/docs/api/liquid/tags/liquid).
13
13
  #
14
+ # > Tip:
15
+ # > You can use [filters](/docs/api/liquid/filters) on expressions inside `echo` tags.
16
+ # @liquid_syntax
17
+ # {% liquid
18
+ # echo expression
19
+ # %}
20
+ # @liquid_syntax_keyword expression The expression to be output.
14
21
  class Echo < Tag
15
22
  attr_reader :variable
16
23
 
@@ -1,50 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
- # "For" iterates over an array or collection.
5
- # Several useful variables are available to you within the loop.
6
- #
7
- # == Basic usage:
8
- # {% for item in collection %}
9
- # {{ forloop.index }}: {{ item.name }}
10
- # {% endfor %}
11
- #
12
- # == Advanced usage:
13
- # {% for item in collection %}
14
- # <div {% if forloop.first %}class="first"{% endif %}>
15
- # Item {{ forloop.index }}: {{ item.name }}
16
- # </div>
17
- # {% else %}
18
- # There is nothing in the collection.
19
- # {% endfor %}
20
- #
21
- # You can also define a limit and offset much like SQL. Remember
22
- # that offset starts at 0 for the first item.
23
- #
24
- # {% for item in collection limit:5 offset:10 %}
25
- # {{ item.name }}
26
- # {% end %}
27
- #
28
- # To reverse the for loop simply use {% for item in collection reversed %} (note that the flag's spelling is different to the filter `reverse`)
29
- #
30
- # == Available variables:
31
- #
32
- # forloop.name:: 'item-collection'
33
- # forloop.length:: Length of the loop
34
- # forloop.index:: The current item's position in the collection;
35
- # forloop.index starts at 1.
36
- # This is helpful for non-programmers who start believe
37
- # the first item in an array is 1, not 0.
38
- # forloop.index0:: The current item's position in the collection
39
- # where the first item is 0
40
- # forloop.rindex:: Number of items remaining in the loop
41
- # (length - index) where 1 is the last item.
42
- # forloop.rindex0:: Number of items remaining in the loop
43
- # where 0 is the last item.
44
- # forloop.first:: Returns true if the item is the first item.
45
- # forloop.last:: Returns true if the item is the last item.
46
- # forloop.parentloop:: Provides access to the parent loop, if present.
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category iteration
7
+ # @liquid_name for
8
+ # @liquid_summary
9
+ # Renders an expression for every item in an array.
10
+ # @liquid_description
11
+ # You can do a maximum of 50 iterations with a `for` loop. If you need to iterate over more than 50 items, then use the
12
+ # [`paginate` tag](/docs/api/liquid/tags/paginate) to split the items over multiple pages.
47
13
  #
14
+ # > Tip:
15
+ # > Every `for` loop has an associated [`forloop` object](/docs/api/liquid/objects/forloop) with information about the loop.
16
+ # @liquid_syntax
17
+ # {% for variable in array %}
18
+ # expression
19
+ # {% endfor %}
20
+ # @liquid_syntax_keyword variable The current item in the array.
21
+ # @liquid_syntax_keyword array The array to iterate over.
22
+ # @liquid_syntax_keyword expression The expression to render for each iteration.
23
+ # @liquid_optional_param limit [number] The number of iterations to perform.
24
+ # @liquid_optional_param offset [number] The 1-based index to start iterating at.
25
+ # @liquid_optional_param range [untyped] A custom numeric range to iterate over.
26
+ # @liquid_optional_param reversed [untyped] Iterate in reverse order.
48
27
  class For < Block
49
28
  Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
50
29
 
@@ -119,11 +98,12 @@ module Liquid
119
98
  @name = "#{@variable_name}-#{collection_name}"
120
99
  @reversed = p.id?('reversed')
121
100
 
122
- while p.look(:id) && p.look(:colon, 1)
101
+ while p.look(:comma) || p.look(:id)
102
+ p.consume?(:comma)
123
103
  unless (attribute = p.id?('limit') || p.id?('offset'))
124
104
  raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
125
105
  end
126
- p.consume
106
+ p.consume(:colon)
127
107
  set_attribute(attribute, p.expression)
128
108
  end
129
109
  p.consume(:end_of_string)
@@ -198,7 +178,6 @@ module Liquid
198
178
  case key
199
179
  when 'offset'
200
180
  @from = if expr == 'continue'
201
- Usage.increment('for_offset_continue')
202
181
  :continue
203
182
  else
204
183
  parse_expression(expr)
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Liquid
4
- # If is the conditional block
5
- #
6
- # {% if user.admin %}
7
- # Admin user!
8
- # {% else %}
9
- # Not admin user
4
+ # @liquid_public_docs
5
+ # @liquid_type tag
6
+ # @liquid_category conditional
7
+ # @liquid_name if
8
+ # @liquid_summary
9
+ # Renders an expression if a specific condition is `true`.
10
+ # @liquid_syntax
11
+ # {% if condition %}
12
+ # expression
10
13
  # {% endif %}
11
- #
12
- # There are {% if count < 5 %} less {% else %} more {% endif %} items than you need.
13
- #
14
+ # @liquid_syntax_keyword condition The condition to evaluate.
15
+ # @liquid_syntax_keyword expression The expression to render if the condition is met.
14
16
  class If < Block
15
17
  Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
16
18
  ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
@@ -51,7 +53,7 @@ module Liquid
51
53
  def render_to_output_buffer(context, output)
52
54
  @blocks.each do |block|
53
55
  result = Liquid::Utils.to_liquid_value(
54
- block.evaluate(context)
56
+ block.evaluate(context),
55
57
  )
56
58
 
57
59
  if result