svg_icon 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,100 @@
1
+ # svg_icon fetch CLI — 设计文档
2
+
3
+ - 日期:2026-08-18
4
+ - 状态:已批准
5
+
6
+ ## 背景
7
+
8
+ gem 内置的 icon set json(bi/bx/lucide/heroicons)来自 [iconify/icon-sets](https://github.com/iconify/icon-sets/tree/master/json)。把所有 icon set 打包进 gem 不现实。需要一个 CLI 命令,让用户从 iconify/icon-sets 抓取任意 icon set 到项目本地,并通过配置使用。
9
+
10
+ ## 目标
11
+
12
+ - `svg_icon fetch <name>` 从 iconify/icon-sets 下载 `<name>.json` 到项目 `config/svg_icons/` 目录
13
+ - 下载的 json 作为独立 icon set 使用:`config.icon = "<name>"` 时优先加载它,与内置 lucide 等平级
14
+ - 零新增运行时依赖(标准库 `net/http` + 现有 `multi_json`)
15
+ - 下载后离线可用,建议提交进版本控制
16
+
17
+ ## 架构
18
+
19
+ ### 1. CLI(`exe/svg_icon`)
20
+
21
+ ```
22
+ $ svg_icon fetch bi
23
+ Fetching https://raw.githubusercontent.com/iconify/icon-sets/master/json/bi.json
24
+ Saved to config/svg_icons/bi.json
25
+ ```
26
+
27
+ - 使用 `OptionParser` 做子命令分发
28
+ - `fetch <name>` 行为:
29
+ - 下载 URL:`https://raw.githubusercontent.com/iconify/icon-sets/master/json/<name>.json`
30
+ - 目标:`<icons_path>/<name>.json`,目录不存在时自动创建
31
+ - 先写临时文件,校验成功后 rename 到目标;失败不留下半截文件
32
+ - 目标已存在则覆盖(作为更新手段)
33
+ - 校验规则:JSON 可解析,且是 Hash 且含 `icons`(Hash)键
34
+ - 错误处理(全部 stderr + exit 1):
35
+ - 网络错误 / HTTP 非 200 → `FetchError`
36
+ - JSON 无效或不含 `icons` → `FetchError`
37
+ - 缺少子命令或参数 → usage 提示,exit 1
38
+
39
+ ### 2. 配置扩展(`lib/svg_icon/configuration.rb`)
40
+
41
+ - 新增 `attr_accessor :icons_path`,默认 `File.join(Dir.pwd, "config/svg_icons")`
42
+ - 语义:外部 icon set 的查找目录
43
+
44
+ ### 3. 数据查找(`lib/svg_icon.rb`)
45
+
46
+ `file_data` 查找顺序:
47
+
48
+ 1. 外部 `<icons_path>/<icon>.json`(存在则优先)
49
+ 2. 内置 `lib/data/<icon>.json`
50
+ 3. 都不存在 → 现有 `SvgIcon::Error`("Icon data file not found")
51
+
52
+ 外部优先:用户下载的版本覆盖内置,且内置 bi/bx 等与下载同名时以外部为准(数据更新)。
53
+
54
+ ### 4. Fetcher(`lib/svg_icon/fetcher.rb`)
55
+
56
+ ```ruby
57
+ module SvgIcon
58
+ class FetchError < Error; end
59
+
60
+ class Fetcher
61
+ def initialize(base_url: DEFAULT_BASE_URL, http: Net::HTTP, ...)
62
+ def fetch(name, destination) # destination 是完整目标文件路径(含文件名),由调用方组装
63
+ # 返回 bool/抛出 FetchError
64
+ end
65
+ end
66
+ ```
67
+
68
+ - `base_url` 和 http 客户端可注入,便于测试
69
+ - 下载 → 解析校验 → 写临时文件 → rename
70
+
71
+ ## 错误处理汇总
72
+
73
+ | 场景 | 行为 |
74
+ | --- | --- |
75
+ | 网络错误 / DNS 失败 | `SvgIcon::FetchError`,exit 1 |
76
+ | HTTP 404(icon set 不存在) | `SvgIcon::FetchError`,exit 1 |
77
+ | 返回体不是合法 JSON | `SvgIcon::FetchError`,exit 1 |
78
+ | JSON 不含 `icons` 对象 | `SvgIcon::FetchError`,exit 1 |
79
+ | `icons_path` 目录不可写 | 原样异常,exit 1 |
80
+
81
+ ## 测试(minitest)
82
+
83
+ - **fetcher_test.rb**(`test/fetcher_test.rb`)
84
+ - 成功下载并写入正确文件(mock HTTP)
85
+ - 404 → FetchError
86
+ - 无效 JSON → FetchError
87
+ - 缺 `icons` 键 → FetchError
88
+ - 失败时不留下临时/半截文件
89
+ - **查找顺序**(`test/svg_icon_test.rb` 扩展)
90
+ - `icons_path` 存在同名文件 → 加载外部
91
+ - 外部没有 → 回退内置
92
+ - 都没有 → `SvgIcon::Error`
93
+ - **CLI 集成**(`test/cli_test.rb`)
94
+ - 在临时目录跑 `svg_icon fetch <name>`,断言 exit 0、文件写入
95
+ - 无效参数 → exit 1
96
+
97
+ ## README 更新
98
+
99
+ - 新增 "Fetching icon sets" 章节:安装、`svg_icon fetch bi` 用法、`config.icon` / `config.icons_path` 示例
100
+ - 建议把 `config/svg_icons/` 提交进版本控制,部署无需重新抓取
data/exe/svg_icon ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "svg_icon"
5
+ require "svg_icon/cli"
6
+
7
+ exit(SvgIcon::CLI.run(ARGV) || 0)