Skip to content

Commit 0c6b2eb

Browse files
committed
Make Shopify-shaped benchmarks portable
1 parent a46ea1d commit 0c6b2eb

6 files changed

Lines changed: 48 additions & 30 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,8 +570,8 @@ The benchmark suite currently includes 10 realistic templates:
570570
| `bench_storefront_cart_page` | Standard-Liquid cart totals, discounts, and line items |
571571
| `bench_storefront_order_email` | Standard-Liquid transactional order email |
572572
| `bench_storefront_cms_page` | Standard-Liquid content-management page |
573-
| `shopify_theme_full_page` | Full Shopify Dream theme layout using Shopify filters |
574-
| `shopify_theme_product_page` | Shopify Dream product page using Shopify filters |
573+
| `shopify_theme_full_page` | Shopify-shaped Dream theme layout using portable Liquid |
574+
| `shopify_theme_product_page` | Shopify-shaped Dream product page using portable Liquid |
575575

576576
Selecting these specs through `liquid-spec run ADAPTER -s benchmarks` checks correctness
577577
without collecting timings; use `liquid-spec bench` for performance measurements.

docs/implementers/shopify-theme-filters.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@ optional: true
77

88
# Shopify Theme Filters
99

10-
The theme benchmark templates (`specs/benchmarks/theme_*.yml`) use Shopify-specific filters and
11-
tags that are not part of core Liquid. This document provides a complete reference implementation
12-
in Ruby that can serve as pseudocode for implementing these in any language.
10+
The default benchmark templates are intentionally portable and do not require Shopify
11+
filters. Optional Shopify theme suites still exercise filters and tags that are not part
12+
of core Liquid. This document provides a reference implementation in Ruby that can serve
13+
as pseudocode when an adapter deliberately targets that Shopify-specific surface.
1314

14-
These are required when your adapter declares `features: [:shopify_filters]`.
15+
These helpers apply to specs tagged `shopify_filters`; they are not required for
16+
`liquid-spec bench`.
1517

1618
---
1719

1820
## Complete Reference Implementation
1921

20-
The following Ruby module implements all required filters. Register it with your Liquid
21-
environment to pass the `shopify_helpers.yml` specs and run the theme benchmarks.
22+
The following Ruby module illustrates the filter behavior. Register equivalent helpers
23+
only when running Shopify-specific suites.
2224

2325
```ruby
2426
# Reference implementation of Shopify theme filters.

specs/benchmarks/storefront.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
_metadata:
33
hint: |
44
Full storefront benchmark using ONLY standard Liquid features (no Shopify-specific
5-
filters/tags). This is the non-Shopify equivalent of the theme_* benchmarks.
5+
filters/tags). It complements the smaller Shopify-shaped portable theme benchmarks.
66
Exercises: render, include (static + dynamic), for loops, nested for loops,
77
forloop object, if/elsif/else/unless, case/when, assign, capture, increment,
88
decrement, cycle, tablerow, comment, raw, liquid tag, echo, whitespace control,

specs/benchmarks/theme_layout.yml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
---
22
_metadata:
3-
hint: Real Shopify Dream theme benchmark — requires shopify_filters feature
4-
doc: implementers/shopify-theme-filters.md
5-
features:
6-
- shopify_filters
3+
hint: |
4+
Shopify-shaped Dream theme layout benchmark expressed with portable Liquid.
5+
Asset paths use standard string filters, so no Shopify runtime is required.
76
data_files:
87
- _data/theme_database.yml
98
specs:
@@ -19,7 +18,7 @@ specs:
1918
<title>{{ page_title }}{% if page_title %} - {% endif %}{{ shop.name }}</title>
2019
2120
<link rel="canonical" href="{{ canonical_url }}">
22-
<link rel="stylesheet" href="{{ 'theme.css' | asset_url }}">
21+
<link rel="stylesheet" href="{{ 'theme.css' | prepend: '/assets/' }}">
2322
2423
{{ content_for_header }}
2524
</head>
@@ -108,7 +107,7 @@ specs:
108107
</footer>
109108
</div>
110109
111-
<script src="{{ 'theme.js' | asset_url }}" defer></script>
110+
<script src="{{ 'theme.js' | prepend: '/assets/' }}" defer></script>
112111
</body>
113112
</html>
114113
environment:

specs/benchmarks/theme_product.yml

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
---
22
_metadata:
3-
hint: Real Shopify Dream theme benchmark — requires shopify_filters feature
4-
doc: implementers/shopify-theme-filters.md
5-
features:
6-
- shopify_filters
3+
hint: |
4+
Shopify-shaped Dream product benchmark expressed with portable Liquid.
5+
CDN paths, cents conversion, and tag slugs use standard filters only.
76
data_files:
87
- _data/theme_database.yml
98
specs:
@@ -26,12 +25,12 @@ specs:
2625
<div class="product__media">
2726
<div class="product__gallery">
2827
<div class="product__main-image">
29-
<img src="{{ product.featured_image | product_img_url: 'large' }}" alt="{{ product.title | escape }}">
28+
<img src="{{ product.featured_image | prepend: '/cdn/' }}" alt="{{ product.title | escape }}">
3029
</div>
3130
<div class="product__thumbnails">
3231
{% for image in product.images %}
3332
<button class="product__thumbnail-item" data-image-index="{{ forloop.index }}">
34-
<img src="{{ image | product_img_url: 'small' }}" alt="{{ product.title | escape }} - {{ forloop.index }}">
33+
<img src="{{ image | prepend: '/cdn/' }}" alt="{{ product.title | escape }} - {{ forloop.index }}">
3534
</button>
3635
{% endfor %}
3736
</div>
@@ -43,11 +42,11 @@ specs:
4342
4443
<div class="product__pricing">
4544
{% if product.compare_at_price > product.price %}
46-
<span class="product__price product__price--sale">{{ product.price | money }}</span>
47-
<span class="product__price product__price--compare">{{ product.compare_at_price | money }}</span>
45+
<span class="product__price product__price--sale">${{ product.price | divided_by: 100.0 }}</span>
46+
<span class="product__price product__price--compare">${{ product.compare_at_price | divided_by: 100.0 }}</span>
4847
<span class="product__badge">Sale</span>
4948
{% else %}
50-
<span class="product__price">{{ product.price | money }}</span>
49+
<span class="product__price">${{ product.price | divided_by: 100.0 }}</span>
5150
{% endif %}
5251
</div>
5352
@@ -63,7 +62,7 @@ specs:
6362
<select name="id" id="ProductSelect" class="product-form__select">
6463
{% for variant in product.variants %}
6564
<option value="{{ variant.id }}" {% if variant.available == false %}disabled{% endif %}>
66-
{{ variant.title }} - {{ variant.price | money }}{% if variant.available == false %} - Sold out{% endif %}
65+
{{ variant.title }} - ${{ variant.price | divided_by: 100.0 }}{% if variant.available == false %} - Sold out{% endif %}
6766
</option>
6867
{% endfor %}
6968
</select>
@@ -100,7 +99,7 @@ specs:
10099
<ul class="tag-list">
101100
{% for tag in product.tags %}
102101
<li class="tag-list__item">
103-
<a href="/collections/all/{{ tag | handle }}" class="tag">{{ tag }}</a>
102+
<a href="/collections/all/{{ tag | downcase | replace: ' ', '-' | url_encode }}" class="tag">{{ tag }}</a>
104103
</li>
105104
{% endfor %}
106105
</ul>
@@ -177,16 +176,16 @@ specs:
177176
<div class="product-card">
178177
<a href="{{ product.url }}" class="product-card__link">
179178
<div class="product-card__image">
180-
<img src="{{ product.featured_image | product_img_url: 'medium' }}" alt="{{ product.title | escape }}">
179+
<img src="{{ product.featured_image | prepend: '/cdn/' }}" alt="{{ product.title | escape }}">
181180
</div>
182181
<div class="product-card__info">
183182
<h3 class="product-card__title">{{ product.title }}</h3>
184183
<div class="product-card__price">
185184
{% if product.compare_at_price > product.price %}
186-
<span class="product-card__price--sale">{{ product.price | money }}</span>
187-
<span class="product-card__price--compare">{{ product.compare_at_price | money }}</span>
185+
<span class="product-card__price--sale">${{ product.price | divided_by: 100.0 }}</span>
186+
<span class="product-card__price--compare">${{ product.compare_at_price | divided_by: 100.0 }}</span>
188187
{% else %}
189-
<span class="product-card__price--regular">{{ product.price | money }}</span>
188+
<span class="product-card__price--regular">${{ product.price | divided_by: 100.0 }}</span>
190189
{% endif %}
191190
</div>
192191
</div>

test/benchmark_test.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,31 @@
33
require_relative "test_helper"
44
require "liquid/spec/cli/benchmark"
55
require "liquid/spec/cli/fork_benchmark"
6+
require "liquid/spec/suite"
67

78
class BenchmarkTest < Minitest::Test
89
Benchmark = Liquid::Spec::CLI::Benchmark
910
ForkBenchmark = Liquid::Spec::CLI::ForkBenchmark
1011

1112
FakeSpec = Struct.new(:name)
1213

14+
def test_shopify_shaped_benchmarks_use_only_portable_liquid
15+
suite = Liquid::Spec::Suite.find(:benchmarks)
16+
specs = Liquid::Spec::SpecLoader.load_suite(suite).select do |spec|
17+
%w[shopify_theme_full_page shopify_theme_product_page].include?(spec.name)
18+
end
19+
20+
assert_equal 2, specs.size
21+
specs.each do |spec|
22+
source = ([spec.template] + spec.raw_filesystem.values).join("\n")
23+
assert_empty spec.features.grep(/shopify/), "#{spec.name} should not require Shopify features"
24+
refute_match(/\|\s*(?:asset_url|product_img_url|money|handle)\b/, source,
25+
"#{spec.name} should not use Shopify-only filters")
26+
refute_match(/\{%[-]?\s*(?:schema|style|section|paginate|form)\b/, source,
27+
"#{spec.name} should not use Shopify-only tags")
28+
end
29+
end
30+
1331
def test_compact_preserves_sub_microsecond_precision_and_integer_ns
1432
value = {
1533
mean: 0.000_000_432_123,

0 commit comments

Comments
 (0)