Jekyll 3.4.0 版本解析:group_by_exp分组过滤器与 Windows 时区管理的实战指南
【免费下载链接】jekyll:globe_with_meridians: Jekyll is a blog-aware static site generator in Ruby项目地址: https://gitcode.com/gh_mirrors/je/jekyll
Jekyll 3.4.0 是 2017 年 1 月发布的一个以修复为主、同时带来三项关键变化的版本:新增按表达式分组的内置 Liquid 过滤器group_by_exp、在 Windows 平台上实现对 IANA 时区代码的准确支持,以及一批主题、includes、permalinks 相关文档的改进。本文以官方发布公告为骨架,结合当前仓库中的过滤器实现、配置解析与测试用例源码,完整讲解这两项核心能力的用法、底层原理与注意事项,帮助你升级到 3.4.0 后立刻上手。
一、版本定位:一次小而精的更新
Jekyll 3.4.0(发布公告见 docs/_posts/2017-01-18-jekyll-3-4-0-released.markdown)延续了 3.x 系列「bug fixes + 增量特性」的节奏。公告将本次更新概括为三点:
- 新增
group_by_exp过滤器,与已有的where_by_exp形成「筛选 / 分组」两套表达式驱动的组合拳; - Windows 用户可以使用 IANA 时区代码(如
Asia/Shanghai)在_config.yml中准确设置timezone; - 官方文档围绕 themes、includes、permalinks 进行了改进,并同步补充了历史变更记录。
其余大量工作是社区贡献的常规 bug 修复。升级方式与平常一致:gem update jekyll或修改Gemfile中的版本约束后bundle update jekyll。
二、核心新特性:group_by_exp表达式分组过滤器
1. 与group_by/where_by_exp的关系
在 Jekyll 中,group_by过滤器负责「按某个属性值分组」,而where_by_exp负责「按表达式筛选」。3.4.0 新增的group_by_exp则是「按 Liquid 表达式的结果分组」,它把group_by的分组能力和where_by_exp的表达式灵活性结合到了一起。
三者的分工可以这样理解:
| 过滤器 | 能力 | 典型场景 |
|---|---|---|
where/where_exp | 按属性或表达式筛选数组,返回子集 | 只保留某类文章 |
group_by | 按属性值分组 | 按标签、年份归类 |
group_by_exp(3.4.0 新增) | 按表达式计算结果分组 | 按年份截断后的值、大写后的值等派生值归类 |
2. 基本用法与官方示例
group_by_exp的签名是三个参数:输入数组、循环变量名、表达式字符串。
{{ site.members | group_by_exp: "item", "item.graduation_year | truncate: 3, ''" }}这段示例来自官方过滤器文档数据 docs/_data/jekyll_filters.yml:对site.members中每个成员的graduation_year先做truncate: 3, ''(截取前 3 位且不追加省略号),再按截取结果分组,例如把2013、2014归入"201",把2009归入"200"。输出是形如{"name"=>"201", "items"=>[...]}的分组数组,每组都带有name、items和size三个键。
对照源码 lib/jekyll/filters/grouping_filters.rb 可以看到实现逻辑:
def group_by_exp(input, variable, expression) return input unless groupable?(input) parsed_expr = parse_expression(expression) @context.stack do groups = input.group_by do |item| @context[variable] = item parsed_expr.render(@context) end grouped_array(groups) end end关键点在于:
- 表达式是运行时解析的:
parse_expression把传入的字符串封装成Liquid::Variable,在遍历每个元素时把元素赋给循环变量并渲染表达式,因此表达式可以任意复杂——调用过滤器、拼接字符串、比较日期都行; - 支持任意可枚举对象:
groupable?只要求输入响应group_by,数组、Hash 均可; - 分组结果统一结构:
grouped_array将 Ruby 的Hash#group_by结果转换为{"name" => ..., "items" => [...], "size" => ...}的数组,方便模板中直接用group.name、group.items、group.size渲染。
3. 对比group_by的异同
group_by(input, property)的实现(lib/jekyll/filters/grouping_filters.rb)等价于把表达式固定为「读取某个属性」。测试用例 test/test_filters.rb 专门验证了这一点:
should "be equivalent of group_by" do actual = @filter.group_by_exp(@filter.site.pages, "page", "page.layout") expected = @filter.group_by(@filter.site.pages, "layout") assert_equal expected, actual end也就是说,group_by_exp: "item", "item.layout"与group_by: "layout"结果完全一致,前者只是更通用的超集。
4. 更多实战示例
按表达式派生值分组(例如按版本号主版本归类):
{% assign groups = site.data.versions | group_by_exp: "item", "item.version | split: '.' | first" %}该用法在 test/test_filters.rb 中有对应的测试数据与断言。
直接按整个元素分组(表达式为变量本身):
{{ "a,b,c,d" | split: "," | group_by_exp: "item", "item" }}测试 test/test_filters.rb 验证了这种写法按元素本身分组,每个元素自成一组。
配合数组与 Hash 输入均可工作;对非数组输入(如字符串)则原样返回,测试 test/test_filters.rb 确认了这一兜底行为。
5. 与where_by_exp的组合应用
where_by_exp负责按表达式筛选,group_by_exp负责按表达式分组,二者可串联使用。例如先筛选出未归档的文章,再按「文章标题首字母大写后的年份」分组:
{% assign active = site.posts | where_exp: "post", "post.published == true" %} {% assign groups = active | group_by_exp: "post", "post.title | truncate: 4, ''" %}在 3.4.0 时代,where_exp的表达式支持已相当完整,后续版本(4.0)进一步加入了and/or二元运算符支持(见 docs/_docs/liquid/filters.md),但分组能力从 3.4.0 起就已具备group_by_exp这一通用形态。
三、Windows 时区管理:IANA 时区代码的准确支持
1. 背景:Windows 上时区为何一直是痛点
Windows 没有原生 zoneinfo 数据,Ruby 解释器无法理解 IANA 时区名(如Asia/Shanghai)。此前在 Windows 上使用自定义时区,TZ环境变量会退化为 UTC/GMT 00:00;虽然也可以用 POSIX 格式(如EST+5)手动定义,但处理夏令时(DST)规则变化时既不直观也不可靠。
3.4.0 的解决方案是:借助tzinfogem 按 IANA 时区数据库计算偏移量,并在内部换算成 Windows 可识别的 POSIX 风格定义。
2. 配置方式
在_config.yml中照常设置timezone(该键的默认值为nil,表示使用本地时区,见 lib/jekyll/configuration.rb):
timezone: Asia/ShanghaiWindows 用户需要确保Gemfile中包含tzinfo与tzinfo-data。3.4.0 起新建的站点会在Gemfile中默认写入以下内容(见 docs/_docs/installation/windows.md),老站点需要手动补充:
# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem # and associated library. platforms :mingw, :x64_mingw, :mswin, :jruby do gem "tzinfo", ">= 1", "< 3" gem "tzinfo-data" end注意:platforms声明只在 Windows(及 JRuby)环境下才安装这些 gem,不影响 Linux/macOS 的依赖树。
3. 底层实现原理
核心逻辑位于 lib/jekyll/utils/win_tz.rb 的WinTZ.calculate方法:
def calculate(timezone, now = Time.now) External.require_with_graceful_fail("tzinfo") unless defined?(TZInfo) tz = TZInfo::Timezone.get(timezone) offset = tz.period_for_utc(now.getutc).utc_total_offset # POSIX style definition reverses the offset sign. sign = offset.positive? ? "-" : "+" ... "WTZ#{sign}#{time}" end关键步骤:
- 通过
TZInfo::Timezone.get(timezone)加载 IANA 时区定义; - 用
period_for_utc与utc_total_offset获取当前时刻相对 UTC 的总偏移(含 DST 调整),注意源码注释说明这是为了兼容 tzinfo v1; - POSIX 时区符号约定与直觉相反:西半球时区(如美国东部 EST,UTC-5)表示为
EST+5,因此代码在偏移为正时输出-、偏移为负时输出+; - 支持非整小时偏移,例如
Australia/Eucla(UTC+8:45)换算为WTZ-08:45; - 返回的
WTZ...字符串最终用于重新定义ENV["TZ"],使 Jekyll 在 Windows 上按真实时区处理文章日期。
测试 test/test_win_tz.rb 系统验证了这一点:
| IANA 时区 | 冬季(1 月) | 夏季(7 月) |
|---|---|---|
America/New_York | WTZ+05:00 | WTZ+04:00(DST 生效) |
Europe/Paris | WTZ-01:00 | WTZ-02:00(DST 生效) |
Australia/Eucla | WTZ-08:45 | — |
UTC | WTZ+00:00 | — |
可以看到 DST 被正确计入:纽约冬季 UTC-5、夏季 UTC-4;巴黎冬季 UTC+1、夏季 UTC+2。
4. 适用前提与限制
- 该能力面向Windows 与 JRuby 平台;Linux/macOS 自带 zoneinfo,无需此机制;
- 必须安装
tzinfo(且tzinfo-data提供时区数据库),否则会通过require_with_graceful_fail优雅降级并给出提示; timezone配置值必须是 IANA 数据库中的合法代码(如Asia/Shanghai、America/New_York)。
四、3.4.0 配套文档更新与升级建议
公告同时提到文档在 themes、includes、permalinks 方面得到改进。本仓库的对应文档包括:
- 过滤器参考:docs/_docs/liquid/filters.md(
group_by_exp从 3.4.0 起在 docs/_data/jekyll_filters.yml 中带版本徽标展示); - Windows 安装与时区管理:docs/_docs/installation/windows.md;
- 完整变更历史可查阅仓库 History.markdown 中 v3.4.0 一节。
升级到 3.4.0 的建议顺序:
- 更新
Gemfile版本约束并执行bundle update jekyll; - Windows 用户检查并补齐
tzinfo/tzinfo-data依赖; - 在
_config.yml中确认timezone使用 IANA 代码; - 将手写循环分组的模板改为
group_by_exp,简化维护。
五、小结
Jekyll 3.4.0 用两个精准的特性解决了实际问题:group_by_exp让模板作者可以用任意 Liquid 表达式分组数据(源码 lib/jekyll/filters/grouping_filters.rb 与测试 test/test_filters.rb 提供了完整的实现与验证),而 Windows 时区支持让跨平台站点的日期显示不再漂移(源码 lib/jekyll/utils/win_tz.rb 与测试 test/test_win_tz.rb 覆盖了含 DST 与奇数偏移量的全部情形)。对升级用户而言,这两项能力立即可用,也是 3.x 系列在模板表达能力与平台健壮性上继续演进的重要一步。
【免费下载链接】jekyll:globe_with_meridians: Jekyll is a blog-aware static site generator in Ruby项目地址: https://gitcode.com/gh_mirrors/je/jekyll
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考