← 所有文章

china_regions:一个 25 Star Ruby Gem 的诞生

我为什么要写一个中国行政区划的 Ruby 库,为什么文档比代码本身更重要,以及那 25 个 Star 背后真正的原因。


这一切始于一个表单。

当时我们在做一个 Rails 项目,注册流程里需要一个省市区三级联动下拉框。听起来很简单,其实不然。

中国有 34 个省级行政区、333 个地级市、2800 多个县级区划。这些数据每隔几年还会随着国家统计局的更新而变化。每个需要这个功能的应用,都得自己从头解决:找数据来源、清洗数据、导入数据库、建立关联关系、写前端联动逻辑。我亲眼见过三个不同团队在三家不同公司把这件事做得一塌糊涂。

于是我写了 china_regions

这个 Gem 做了什么

本质上,china_regions 是一个 Rails Engine,提供三个数据库模型——ProvinceCityDistrict——预装了来自民政部的官方数据。加进 Gemfile,跑一下生成器,跑一下数据库迁移,你的应用就有了一套完整的、可查询的中国行政区划层级结构。

Province.all           # 34 个省/直辖市/自治区
City.where(province: Province.find_by(name: "广东省"))  # 广东的城市
District.all.count     # 2800+

生成器还会复制本地化语言包,支持中英双语——同一个地区对象,根据 I18n.locale 的设置可以显示”广东省”或”Guangdong Province”。

前端方面有一个 JavaScript 辅助器来驱动级联选择——选完省,城市下拉框自动填充;选完城市,区县下拉框自动填充。它直接兼容标准的 Rails 表单辅助方法。

数据问题

搞定数据比搞定代码难多了。官方来源是国家统计局网站,但数据是以嵌套 HTML 表格的形式发布的,没有 API。我写了一个爬虫,用民政部名单做校验,再用第二个来源做交叉比对,把出入的地方挑出来。

数据本身有些特殊之处。北京、上海、天津、重庆这四个直辖市既是省级单位,又包含在功能上相当于地级市的”区”。全国的行政层级也不是统一的两层或三层,而是混合的。我做了一些判断和取舍,可能并不完美,但能覆盖 95% 的实际使用场景。

我发布的最新版本使用的是 2018 年修订的数据集——彼时最新的官方公告。保持数据更新是最大的维护成本:每当国家统计局更新数据,我就得重新跑爬虫、验证差异、发布新版本。

为什么能获得 25 个 Star

我想诚实地说:这段代码没什么特别之处。它就是一个标准的 Rails Engine,复杂度适中。之所以有人给它 Star,是因为这个问题很常见,而替代方案都不够好。

当时我能找到的解决方案,要么是已经废弃的(数据过时、生成器失效),要么是需要手动操作的(要自己加载 CSV),要么是依赖外部 API 的(查询地区信息还要发网络请求)。china_regions 同时解决了这三个问题:数据有人维护、零配置、本地查询。

我从中得到的教训是:把一个枯燥但真实的问题解决好,比把一个有趣的问题解决得漂亮更重要。省市区三级联动本身没什么意思,但每个做中国市场 Rails 项目的开发者都会碰到它。这个受众群体已经足够大,值得专门做一个 Gem。

文档才是真正的产品

v0.1 版本我犯了个错误:README 就三条要点和一段代码示例。我觉得开发者看源码就能搞明白了。

他们没搞明白。Issues 一条条涌来:“怎么自定义模型?""在 PostgreSQL 上跑数据库迁移报字段名冲突了。""怎么添加自定义地区?""这个库用了哪些 locale key?”

每个问题看了源码五分钟以内都能回答。但人们不看源码,他们看 README;README 答不上来,就开 Issue。

我从头重写了文档——安装步骤、配置说明、模型用法、表单辅助示例、常见报错的排查方法、数据更新流程。Issue 数量直接减半。

文档是你的代码和使用者之间的界面。实现再完美,界面让人费解,就不会有人用。README 不是事后补充的工作,对于一个库来说,它是你交付的最重要的东西。

如果重做

更智能的数据更新机制。 我会设计一个合理的更新流程——比如一个 rake 任务,检查国家统计局网站的变化并做差异比对——而不是现在这种手动爬取再替换的方式。

把数据层和 Engine 层分开。 这个 Gem 把数据和功能绑在了一起。如果有人需要针对特定用途自定义区划层级,他就得跟这个 Gem 对着干,而不是复用它。更干净的设计应该把数据(做成数据 Gem)和 Rails 集成层分开。

从一开始就在多个 Rails 版本上跑 CI。 随着 Rails 版本演进,我做了不少痛苦的向后兼容适配。如果一开始就针对 Ruby 和 Rails 的版本矩阵做持续集成,很多问题早就能发现了。

Gem 在 github.com/encoreshao/china_regions。如果你在用 Rails 做中国市场的产品,它能帮你省下大概一周的开发时间。

Encore Shao

全栈工程师 & AI 研究员,就职于上海 Ekohe。10 年以上经验,专注于 Rails 应用与 Agentic AI 系统。