Compare commits

...
Author SHA1 Message Date
Michael Go b5b36665e6 avoid new regex allocation in util functions 2025-10-15 10:08:13 -03:00
iainandGitHub 9bb7fbf123 Merge pull request #1968 from Shopify/shopify-dev-docs-formatting
Inline some information that previously lived at category level
2025-07-03 10:59:58 -04:00
Iain Campbell 8555fd8a20 inline warning previuosly at category level 2025-06-27 16:57:00 -04:00
James MengandGitHub 9bd408f5d0 Merge pull request #1965 from Shopify/jm/bump_liquid
Bump Liquid to 5.8.7
2025-06-09 12:42:27 -07:00
James Meng 79b831d96c Bump Liquid to 5.8.7 2025-06-09 12:38:04 -07:00
James MengandGitHub aebd75e5e8 Merge pull request #1954 from Shopify/jm/doc_body
Expose tag body in the Doc tag
2025-06-09 12:25:19 -07:00
James Meng 7f2f8a226b Add tests for new public methods 2025-06-09 12:24:59 -07:00
James Meng 7b2b25fda1 Fix Doc tag blank? method to check body content
Previously the blank? method always returned true. Now it properly checks
if the body is empty, making the tag behavior consistent with other tags.

Also updated test to use whitespace control for cleaner assertions.
2025-06-09 11:36:18 -07:00
James Meng 8548b96a97 Remove body attr_reader and initiliaze @body instance variable in parse method 2025-06-06 13:08:06 -07:00
upgrade-umpire[bot]andGitHub fc96e66e14 Merge pull request #1953 from Shopify/actions-commit
Applying Merge
2025-06-05 20:15:14 +00:00
James Meng 79a771d724 Add test for doc tag capturing token before enddoc 2025-06-04 19:30:47 -07:00
James Meng 65b1dedac5 Expose tag body in the Doc tag 2025-06-04 12:04:41 -07:00
Ian Ker-SeymerandGitHub f375d7b3aa Add unit test for custom Liquid tag registration (#1960)
Adds EnvironmentTest to verify custom tag registration and rendering.
2025-05-22 11:38:10 -04:00
Brian Chen 6b3f6c6fb4 update github actions to commits 2025-04-29 14:04:22 -04:00
12 changed files with 173 additions and 15 deletions
+6
View File
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
+4 -4
View File
@@ -31,8 +31,8 @@ jobs:
- { ruby: ruby-head, allowed-failure: false, rubyopt: "--yjit" }
name: Test Ruby ${{ matrix.entry.ruby }}
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- uses: ruby/setup-ruby@dffc446db9ba5a0c4446edb5bca1c5c473a806c5 # v1.235.0
with:
ruby-version: ${{ matrix.entry.ruby }}
bundler-cache: true
@@ -45,8 +45,8 @@ jobs:
memory_profile:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- uses: ruby/setup-ruby@dffc446db9ba5a0c4446edb5bca1c5c473a806c5 # v1.235.0
with:
bundler-cache: true
- run: bundle exec rake memory_profile:run
+2 -1
View File
@@ -1,6 +1,7 @@
# Liquid Change Log
## 5.8.1 (unreleased)
## 5.8.7
* Expose body content in the `Doc` tag [James Meng]
## 5.8.1
+4
View File
@@ -9,6 +9,10 @@ module Liquid
# Creates a new variable.
# @liquid_description
# You can create variables of any [basic type](/docs/api/liquid/basics#types), [object](/docs/api/liquid/objects), or object property.
#
# > Caution:
# > Predefined Liquid objects can be overridden by variables with the same name.
# > To make sure that you can access all Liquid objects, make sure that your variable name doesn't match a predefined object's name.
# @liquid_syntax
# {% assign variable_name = value %}
# @liquid_syntax_keyword variable_name The name of the variable being created.
+4
View File
@@ -9,6 +9,10 @@ module Liquid
# Creates a new variable with a string value.
# @liquid_description
# You can create complex strings with Liquid logic and variables.
#
# > Caution:
# > Predefined Liquid objects can be overridden by variables with the same name.
# > To make sure that you can access all Liquid objects, make sure that your variable name doesn't match a predefined object's name.
# @liquid_syntax
# {% capture variable %}
# value
+4
View File
@@ -7,6 +7,10 @@ module Liquid
# @liquid_name decrement
# @liquid_summary
# Creates a new variable, with a default value of -1, that's decreased by 1 with each subsequent call.
#
# > Caution:
# > Predefined Liquid objects can be overridden by variables with the same name.
# > To make sure that you can access all Liquid objects, make sure that your variable name doesn't match a predefined object's name.
# @liquid_description
# Variables that are declared with `decrement` are unique to the [layout](/themes/architecture/layouts), [template](/themes/architecture/templates),
# or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
+6 -2
View File
@@ -36,6 +36,8 @@ module Liquid
end
def parse(tokens)
@body = +""
while (token = tokens.shift)
tag_name = token =~ BlockBody::FullTokenPossiblyInvalid && Regexp.last_match(2)
@@ -43,8 +45,10 @@ module Liquid
if tag_name == block_delimiter
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
@body << Regexp.last_match(1) if Regexp.last_match(1) != ""
return
end
@body << token unless token.empty?
end
raise_tag_never_closed(block_name)
@@ -55,11 +59,11 @@ module Liquid
end
def blank?
true
@body.empty?
end
def nodelist
[]
[@body]
end
private
+4
View File
@@ -7,6 +7,10 @@ module Liquid
# @liquid_name increment
# @liquid_summary
# Creates a new variable, with a default value of 0, that's increased by 1 with each subsequent call.
#
# > Caution:
# > Predefined Liquid objects can be overridden by variables with the same name.
# > To make sure that you can access all Liquid objects, make sure that your variable name doesn't match a predefined object's name.
# @liquid_description
# Variables that are declared with `increment` are unique to the [layout](/themes/architecture/layouts), [template](/themes/architecture/templates),
# or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
+5 -2
View File
@@ -2,6 +2,9 @@
module Liquid
module Utils
DECIMAL_REGEX = /\A-?\d+\.\d+\z/
UNIX_TIMESTAMP_REGEX = /\A\d+\z/
def self.slice_collection(collection, from, to)
if (from != 0 || !to.nil?) && collection.respond_to?(:load_slice)
collection.load_slice(from, to)
@@ -52,7 +55,7 @@ module Liquid
when Numeric
obj
when String
/\A-?\d+\.\d+\z/.match?(obj.strip) ? BigDecimal(obj) : obj.to_i
DECIMAL_REGEX.match?(obj.strip) ? BigDecimal(obj) : obj.to_i
else
if obj.respond_to?(:to_number)
obj.to_number
@@ -73,7 +76,7 @@ module Liquid
case obj
when 'now', 'today'
Time.now
when /\A\d+\z/, Integer
when UNIX_TIMESTAMP_REGEX, Integer
Time.at(obj.to_i)
when String
Time.parse(obj)
+1 -1
View File
@@ -2,5 +2,5 @@
# frozen_string_literal: true
module Liquid
VERSION = "5.8.6"
VERSION = "5.8.7"
end
+25
View File
@@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'test_helper'
class EnvironmentTest < Minitest::Test
include Liquid
class UnsubscribeFooter < Liquid::Tag
def render(_context)
'Unsubscribe Footer'
end
end
def test_custom_tag
email_environment = Liquid::Environment.build do |environment|
environment.register_tag("unsubscribe_footer", UnsubscribeFooter)
end
assert(email_environment.tags["unsubscribe_footer"])
assert(email_environment.tag_for_name("unsubscribe_footer"))
template = Liquid::Template.parse("{% unsubscribe_footer %}", environment: email_environment)
assert_equal('Unsubscribe Footer', template.render)
end
end
+108 -5
View File
@@ -20,6 +20,21 @@ class DocTagUnitTest < Minitest::Test
assert_template_result('', template)
end
def test_doc_tag_body_content
doc_content = " Documentation content\n @param {string} foo - test\n"
template_source = "{% doc %}#{doc_content}{% enddoc %}"
doc_tag = nil
ParseTreeVisitor
.for(Template.parse(template_source).root)
.add_callback_for(Liquid::Doc) do |tag|
doc_tag = tag
end
.visit
assert_equal(doc_content, doc_tag.nodelist.first.to_s)
end
def test_doc_tag_does_not_support_extra_arguments
error = assert_raises(Liquid::SyntaxError) do
template = <<~LIQUID.chomp
@@ -116,6 +131,20 @@ class DocTagUnitTest < Minitest::Test
assert_template_result('', template)
end
def test_doc_tag_captures_token_before_enddoc
template_source = "{% doc %}{{ incomplete{% enddoc %}"
doc_tag = nil
ParseTreeVisitor
.for(Template.parse(template_source).root)
.add_callback_for(Liquid::Doc) do |tag|
doc_tag = tag
end
.visit
assert_equal("{{ incomplete", doc_tag.nodelist.first.to_s)
end
def test_doc_tag_preserves_error_line_numbers
template = Liquid::Template.parse(<<~LIQUID.chomp, line_numbers: true)
{% doc %}
@@ -145,11 +174,11 @@ class DocTagUnitTest < Minitest::Test
def test_doc_tag_delimiter_handling
assert_template_result('', <<~LIQUID.chomp)
{% if true %}
{% doc %}
{% docEXTRA %}wut{% enddocEXTRA %}xyz
{% enddoc %}
{% endif %}
{%- if true -%}
{%- doc -%}
{%- docEXTRA -%}wut{% enddocEXTRA -%}xyz
{%- enddoc -%}
{%- endif -%}
LIQUID
assert_template_result('', "{% doc %}123{% enddoc xyz %}")
@@ -167,6 +196,80 @@ class DocTagUnitTest < Minitest::Test
)
end
def test_doc_tag_blank_with_empty_content
template_source = "{% doc %}{% enddoc %}"
doc_tag = nil
ParseTreeVisitor
.for(Template.parse(template_source).root)
.add_callback_for(Liquid::Doc) do |tag|
doc_tag = tag
end
.visit
assert_equal(true, doc_tag.blank?)
end
def test_doc_tag_blank_with_content
template_source = "{% doc %}Some documentation{% enddoc %}"
doc_tag = nil
ParseTreeVisitor
.for(Template.parse(template_source).root)
.add_callback_for(Liquid::Doc) do |tag|
doc_tag = tag
end
.visit
assert_equal(false, doc_tag.blank?)
end
def test_doc_tag_blank_with_whitespace_only
template_source = "{% doc %} {% enddoc %}"
doc_tag = nil
ParseTreeVisitor
.for(Template.parse(template_source).root)
.add_callback_for(Liquid::Doc) do |tag|
doc_tag = tag
end
.visit
assert_equal(false, doc_tag.blank?)
end
def test_doc_tag_nodelist_returns_array_with_body
doc_content = "Documentation content\n@param {string} foo"
template_source = "{% doc %}#{doc_content}{% enddoc %}"
doc_tag = nil
ParseTreeVisitor
.for(Template.parse(template_source).root)
.add_callback_for(Liquid::Doc) do |tag|
doc_tag = tag
end
.visit
assert_equal([doc_content], doc_tag.nodelist)
assert_equal(1, doc_tag.nodelist.length)
assert_equal(doc_content, doc_tag.nodelist.first)
end
def test_doc_tag_nodelist_with_empty_content
template_source = "{% doc %}{% enddoc %}"
doc_tag = nil
ParseTreeVisitor
.for(Template.parse(template_source).root)
.add_callback_for(Liquid::Doc) do |tag|
doc_tag = tag
end
.visit
assert_equal([""], doc_tag.nodelist)
assert_equal(1, doc_tag.nodelist.length)
end
private
def traversal(template)