大脑充血 Vol.99
最近一直在小步缓行,不急不忙地把事情做好。一方面是上班之后的确没有大块且高精力的时间来做自己想做的事情了,只能把事情拆得很碎,一点一点来做。这几天在读波拉尼奥的短篇集《地球上最后的夜晚》,每篇真的很短,大概十几二十页,可以每天上班前没有压力地读上一两篇。我很喜欢他的文风,平静却具有冲击力,不做作,就是普通人生活的感觉。
weepinbell
weepinbell 是我半年前开始写的 Clojure 项目,它是个 Webmention 接收器。如果你不知道这是什么,可以阅读我写的《 Webmention 简明指南 》
最近我又把它捡了起来,发布了 v0.5.0 版本,并且仍然在更新。我还在 Issues 里写了很多虽然写着欢迎贡献,但更多是写给自己看的计划(因为项目拒绝合并 LLM 生成的代码,而我非常清楚既关心 IndieWeb 又会写 Clojure,还有 Codeberg 账号,并且愿意给我贡献代码的人,很有可能根本不存在吧!)。
这两周我大概做了这些事情:
- 把用来给 Webmention 生成 ID 的 MD5 换成了 SHA256,后者的抗碰撞性会更好。虽然到最后我都会截断成 32 位字符,光是生成 ID 似乎也不需要考虑安全性,但…… 好吧说实话我已经忘记为什么我要换了。
- 换掉了一大半之前胡乱引入的依赖,把包大小从
46MB降低到了15MB。一些简单的功能就自己写 Clojure 代码或者调 Java 标准库实现了。依赖减少之后真的轻了很多,顺带也重构了一些不太合理的代码。 - Webhook 支持携带数据了,比方说可以给自己的 Bark 或 ntfy 端点发送请求,同时在 Headers 或 POST Body 里包含 Webmention 相关的信息。这样就能在收到请求时看到是谁向自己发送了 Webmention,具体的来源和目标 URL 又是什么。如果有的话,还可以显示对方的 microformats 头像。 用户可以自己写个 Clojure 函数构造请求体,或者用预设的
:ntfy和:bark函数,总之很容易拓展。 - 由于没有稳定且完整的 microformats 解析器可以用(也就是说没有 Clojure 或 Java 库),打算用 Will Noriss 维护的 Go 语言 解析器 。我绞尽脑汁只想出用微服务和 gRPC,或者让程序用 Shell 执行 CLI 并解析命令输出这两种方法,最后求助了 LLM,才恍然大悟:原来 JVM 上可以直接运行 WebAssembly! 然后就写了 mf2clj ,并且第一次把软件包发布到 Clojars 上。Clojure 程序可以直接使用这个库,不过实际上这个程序是在运行嵌入其中的 WebAssembly,而这个 WebAssembly 文件是从 Go 编译过来的。 WebAssembly 真是伟大的发明!
目前 weepinbell 只会把数据存储在一个 .edn 文件里,这对个人使用来说完全足够了。不过我在考虑用 weepinbell 提供公共的 Webmention 接收服务,所以打算实现 SQLite 存储。由于 SQL 数据库都大差不差,所以之后复用代码实现 PostgreSQL 存储(或者用户想要自己写一个别的实现)也很容易。
此外感谢 Cytrogen ,她可是我的第一个用户啊!
tempest
一两个月前我就想要重写现在用的 Hugo 主题,因为里面遗留了太多我如今看来不太满意的设计。比起大刀阔斧地修改,不如重新开始。很快我又意识到,我并不想写 HTML,那为什么不用 Lisp 呢?
刚学 Clojure 没多久的时候我写过一篇《 网页即列表! 》,那时我就讲过用 S-Expression 来表示 XML 会干净很多(或者说叫 X-Expression),手敲代码的感觉也会更令人愉悦。除了 Clojure 的 Hiccup,Common Lisp 也有名为 spinneret 的库可以把 Lisp 转换为 HTML:
(with-html-string (:a :href "/about" "About"))
;; => "About"
所以我在想,既然已经能用 Lisp 生成 HTML 了,那只要稍做拓展,就能用来生成 Go Templates,同时支持 Hugo 的某些特性,终于也可以将 {{ mustache }} 语法消除掉了。
不用写这种代码:
{{ with .Params.tags }}
{{ range . }}
{{ .Title }}
{{ end }}
{{ end }}
只需要这样:
(mu:with-range (mu:c :*params :tags)
(:a :href (mu:c :*permalink) (mu:c :*title)))
于是就有了 tempest 。写 Hugo 主题的时候,只需要执行 tempest watch .,就可以实时地把 .lisp 文件转换为 .html 文件,近乎无感。
不过,上述示例代码还没有实现。本质上,tempest 只是对要输入给 spinneret 的列表做了些预处理而已,大多数时候只是在转换字符串,写着写着就发现了一些逻辑上很难理清楚的地方。比如 mu:c(Muastache chain)如果是生成 {{ .Params.tags }} 这个字符串的话,那放在 if 等语句内部时要怎么办,难道要额外写一个函数用来生成 .Params.tags 吗?
反正目前只有我一个人用,慢慢来也没事,先去忙点其他的,之后再说。
又投简历
经前辈提醒,我才意识到最近秋招,我应该投简历开始找工作了。我确实不打算在我实习的这家公司久待,但想起求职又觉得焦虑,我真的不擅长展示自己。
最近听了《不把天聊死》的 新一期 播客,主题是租房和搬家。几位主播聊到「主动搬家」和「被动搬家」,发现彼此对主动与被动的定义有些差别。有些人认为自己选择去某个城市,求学或求职,都是主动的,而另一些人认为,自己当时根本不想离开这个保护自己的空间,学业和事业的变动似乎都是无奈之举。
我更偏向后者,不过也有些微妙。我是很想要主动离开某个环境,去寻求更好或者说更合适的机会的,但我讨厌搬家,我讨厌把自己的生活塞进小小的纸箱子和搬家袋里面,我讨厌不断丢东西压缩行李,我讨厌把生活的一部分丢弃,我讨厌适应新的生活区域。搬家是我必须要做的取舍。我也不想买房之后被钉在某个地方的感觉,我渴望随时能前往另一个城市的自由,不过自由有代价,我必须承受生活变动的痛苦。
找实习的时候我还在犹豫,一直在重庆和成都附近寻找机会,最后不知怎么地,误打误撞来了济南。听播客主播聊起搬家这件事情有多难,需要寻求朋友帮助时,我才意识到我搬家时产生的各种焦虑和自责完全没有必要,这件事情本来就很难。然而我现在已经到了很远的地方,似乎也适应了,我是有能力走远的。
于是这次秋招我就开始往上海、深圳和北京投简历了。说实话,其实还有别的私心,我在重庆走路老是被电动车惊扰,不得安宁(我很喜欢散步),来了山东之后才发现这里更糟,很多路完全不是设计给人走的——小区门口没有人行道,人只能和电瓶车共用空间;在路边走到一半经常发现前面被围栏围住了,只能从马路上走;过红绿灯时,我时常环顾四周,发现身边全是电动车,没有一个行人;过马路时,明明是绿灯,我竟然被径直开过来的车鸣笛警告,疑惑之余,一辆电瓶车从我身边穿过,我就这样被围堵。
我只是印象里记得,似乎几年前就有不少沿海城市推出了限制电瓶车的政策,似乎也有不少街道适合走路。当然,这完全有可能只是源自于我对 小地方的怨恨 。我知道这还挺不健康的。
呼,其实我貌似已经错过秋招的黄金时段了,而我现在也没投出去几份。没事,慢慢来吧,逼自己也没用,为什么一定要急着把事情做完呢?
- 这个服务大概会叫作 ringabell——ring a bell,和披头士的 Ringo 没有关系。