jekyll-tally-tags 0.1.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,260 @@
1
+ module Jekyll
2
+ module TallyTags
3
+ class Counter
4
+
5
+ # 两处赋值
6
+ # @param [Document] doc
7
+ # @param [String] key
8
+ # @param [Object] value
9
+ def self.set_doc_data(doc, key, value)
10
+ doc.data[key] = value
11
+ doc.data[DATA][key] = value
12
+ end
13
+
14
+ # 两处添加
15
+ # @param [Document] doc
16
+ # @param [String] key
17
+ # @param [Object] value
18
+ def self.add_doc_data(doc, key, value)
19
+ doc.data[key] = [] if !doc.data[key]
20
+ doc.data[DATA][key] = [] if !doc.data[DATA][key]
21
+ doc.data[key] << value
22
+ doc.data[DATA][key] << value
23
+ end
24
+
25
+ # @param [Float] number
26
+ # @param [String] format
27
+ def self.to_f_s(number, format)
28
+ if number - number.round != 0
29
+ format % number.round(2).to_s
30
+ else
31
+ format % number.round.to_s
32
+ end
33
+ end
34
+
35
+ # @param [Array, Object]
36
+ # @return [Array]
37
+ def self.to_array(single_or_array, default)
38
+ if !single_or_array
39
+ return default
40
+ end
41
+ if single_or_array.is_a?(Array)
42
+ single_or_array
43
+ else
44
+ [single_or_array]
45
+ end
46
+ end
47
+
48
+ # @param [Array<Array<String>>] merge_list
49
+ # @param [Array<Document>] docs
50
+ # @param [String] find
51
+ # @param [String] to
52
+ def self.combine_merge_list(merge_list, docs, find, to)
53
+ merge_list.each do |merges|
54
+ docs.each do |doc|
55
+ (0..doc.data[find].size - 1).each do |index|
56
+ if merges.include?(doc.data[find][index])
57
+ self.add_doc_data(doc, to, merges) unless doc.data[to].include?(merges)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # @param [Array<String>] items 原始数据
65
+ # @param [Integer, Array<Integer>] deep_key 合并类型
66
+ # @return [Array<Array<String>>]
67
+ def self.merge_all(items, deep_key)
68
+ hash = {}
69
+ is_deep = false
70
+ if items.size > 2
71
+ deep_key = ALL if !deep_key
72
+ # all 情况
73
+ if deep_key.is_a?(String)
74
+ if deep_key == ALL
75
+ is_deep = true
76
+ (2..items.size).each do |deep|
77
+ hash[deep] = self.merge(items, nil, nil, deep, 0, 0, 0)
78
+ end
79
+ end
80
+ end
81
+
82
+ # 单个
83
+ if deep_key.is_a?(Integer)
84
+ if deep_key != 0 && deep_key >= 2
85
+ is_deep = true
86
+ hash[deep_key] = self.merge(items, nil, nil, deep_key, 0, 0, 0)
87
+ end
88
+ end
89
+
90
+ # 数组
91
+ if deep_key.is_a?(Array)
92
+ deep_key.each do |deep|
93
+ if deep != 0 && deep >= 2
94
+ is_deep = true
95
+ hash[deep] = self.merge(items, nil, nil, deep, 0, 0, 0)
96
+ end
97
+ end
98
+ end
99
+
100
+ if !is_deep
101
+ Jekyll.logger.warn(COUNTER, "#{deep_key}类型未知")
102
+ end
103
+
104
+ all = []
105
+ hash.values.each do |values|
106
+ all += values
107
+ end
108
+ all
109
+ else
110
+ [items]
111
+ end
112
+ end
113
+
114
+ # @param [Array<String>] items 原始数据
115
+ # @param [Array<Array<String>>] deep_items 对应深度数据的保存数组
116
+ # @param [Array<String>] into_items 遍历时 当前深度 的 保存数组 会 添加到 对应深度数据的保存数组
117
+ # @param [Integer] deep 总深度
118
+ # @param [Integer] cur_deep 当前深度
119
+ # @param [Integer] s 起始下标
120
+ # @param [Integer] e 结束下标
121
+ # @return [Array<Array<String>>]
122
+ def self.merge(items, deep_items, into_items, deep, cur_deep, s, e)
123
+ if !deep_items
124
+ deep_items = []
125
+ into_items = []
126
+ cur_deep = 0
127
+ s = 0
128
+ e = items.size - deep
129
+ end
130
+ (s..e).each do |i|
131
+ temp = Array.new(into_items) << items[i]
132
+ if cur_deep == deep - 1
133
+ deep_items << temp
134
+ else
135
+ deep_items += self.merge(items, [], temp, deep, cur_deep + 1, i + 1, e + 1)
136
+ end
137
+ end
138
+ deep_items
139
+ end
140
+
141
+ # @param [Integer] a
142
+ # @param [Integer] b
143
+ def self.sum(a, b)
144
+ a.to_f + b.to_f
145
+ end
146
+
147
+ # @param [Integer] a
148
+ # @param [Integer] b
149
+ def self.div(a, b)
150
+ a.to_f / b.to_f
151
+ end
152
+
153
+ # @param [Integer] a
154
+ # @param [Integer] b
155
+ def self.mul(a, b)
156
+ a.to_f * b.to_f
157
+ end
158
+
159
+ # @param [Integer] a
160
+ # @param [Integer] b
161
+ def self.sub(a, b)
162
+ a.to_f - b.to_f
163
+ end
164
+
165
+ # @example
166
+ # ":sub_2和:2:3" => [":sub_2", "和", ":2", ":3"]
167
+ # @param [String]
168
+ # @return [Array<String>]
169
+ def self.partition_all(formatter)
170
+ result = []
171
+ temps = formatter.partition(/:\w+/)
172
+ (0..temps.size - 1).each do |index|
173
+ temp = temps[index]
174
+ if index == temps.size - 1
175
+ if !temp.empty?
176
+ result += self.partition_all(temp)
177
+ end
178
+ else
179
+ if temp && !temp.empty?
180
+ result << temp
181
+ end
182
+ end
183
+ end
184
+ result
185
+ end
186
+
187
+ # @param [Array<String>] values
188
+ # @param [Array<String>] formatters
189
+ def self.formatValues(values, formatters)
190
+ results = []
191
+ formatters.each do |formatter|
192
+ # 如果是数字的话
193
+ if formatter.is_a?(Integer)
194
+ results << values[formatter]
195
+ next
196
+ end
197
+
198
+ # @type [Hash<Integer => Integer>]
199
+ params = {} #下标 数组
200
+ # @type [Hash<Integer => String>]
201
+ methods = {} #方法 数组
202
+
203
+ splits = self.partition_all(formatter)
204
+ split_hash = {}
205
+ splits.each_index do |index|
206
+ split = splits[index]
207
+ # 保存到 `hash` 内
208
+ split_hash[index] = split
209
+ if split.match?(/^:[a-zA-Z0-9_]+/)
210
+ # 判断是不是满足初始条件
211
+ if split.match?(/^:[0-9]+/)
212
+ value_index = split.match(/[0-9]+/)[0].to_i
213
+ params[index] = values[value_index]
214
+ else
215
+ methods[index] = split
216
+ end
217
+ end
218
+ end
219
+
220
+ result = ""
221
+ if methods.empty?
222
+ splits.each_index do |index|
223
+ if params.has_key?(index)
224
+ result += params[index]
225
+ else
226
+ result += splits[index]
227
+ end
228
+ end
229
+ else
230
+ methods.keys.sort.reverse_each do |index|
231
+ param_count = methods[index].match(/[0-9]/)[0].to_i
232
+ method_match = methods[index].match(/[a-zA-Z]+/).to_s
233
+ method = self.method(method_match)
234
+ args = []
235
+ (1..param_count).each do |i|
236
+ if params.include?(index + i)
237
+ args[i - 1] = params.delete(index + i) # 在对应位置上删掉该参数
238
+ end
239
+ if methods.include?(index + i)
240
+ args[i - 1] = methods.delete(index + i) # 在对应位置上删掉该参数
241
+ end
242
+ split_hash.delete(index + i)
243
+ end
244
+ methods[index] = method.call(*args)
245
+ end
246
+ split_hash.keys.sort.each do |index|
247
+ if methods.has_key?(index)
248
+ result += methods[index].to_s
249
+ else
250
+ result += split_hash[index]
251
+ end
252
+ end
253
+ end
254
+ results << result
255
+ end
256
+ results
257
+ end
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,134 @@
1
+ module Jekyll
2
+ module TallyTags
3
+
4
+ Jekyll::Hooks.register :site, :after_init do |_|
5
+ # 修改正则让 `2021-01-01.md` 就算无后缀也可读
6
+ old = Jekyll::Document::DATE_FILENAME_MATCHER
7
+ new = NO_NAME
8
+ if old != new
9
+ Jekyll::Document::DATE_FILENAME_MATCHER = new
10
+ end
11
+ end
12
+
13
+ Jekyll::Hooks.register :site, :post_read do |site|
14
+
15
+ # 首先获取所有在 `tally` 内 `list` 的值
16
+ tally_configs = site.config.fetch(TALLY, {})
17
+ next unless tally_configs && !tally_configs.empty?
18
+ # 然后获取默认配置 没有也是可以的
19
+ # @type [Hash<String => Array<Integer, String>>]
20
+ default_template = tally_configs[DEFAULT]
21
+
22
+ if default_template
23
+ # 初始化默认的模板
24
+ default_template.each_key do |key|
25
+ default_template[key] = Counter.to_array(default_template[key], nil)
26
+ end
27
+ end
28
+
29
+ # 先判断有没有对应的配置
30
+ # @type [Hash<String => Array<Integer, String>>]
31
+ templates = tally_configs[TEMPLATES]
32
+ # 必须有模板才可以解析
33
+ next unless templates && !templates.empty?
34
+
35
+ # @type [Array<Document>]
36
+ docs = site.posts.docs # 文章里的文档 (也就是 `yaml`)
37
+ id = 0 # id
38
+
39
+ no_scan_docs = [] # 无法扫描的文档
40
+ scanned_docs = [] # 创建新的文档
41
+
42
+ # 先遍历模板
43
+ templates.each_key do |template_key|
44
+ template = templates[template_key]
45
+ template.each_key do |key|
46
+ # 依据默认模板生成新的模板
47
+ template[key] = Counter.to_array(template[key], default_template[key])
48
+ end
49
+ end
50
+
51
+ # 后便利文档
52
+ docs.each do |doc|
53
+ # 判断这个文档里面有没有对应的 `template`
54
+ doc_has_template = doc.data.keys & templates.keys
55
+ if !doc_has_template || doc_has_template.empty?
56
+ no_scan_docs << doc
57
+ next
58
+ end
59
+
60
+ doc_has_template.each do |template_key|
61
+ csv_list = doc[template_key]
62
+ template = templates[template_key]
63
+ # 下一步 如果 有值 且 不为空
64
+ next unless csv_list && !csv_list.empty?
65
+
66
+ # 如果不是数组的话
67
+ if !csv_list.is_a?(Array)
68
+ Jekyll.logger.warn(template_key, "#{doc.path}里的数据不为数组, 将不解析该字段")
69
+ next
70
+ end
71
+
72
+ keys = []
73
+ csv_list.each_index do |csv_index|
74
+ csv = csv_list[csv_index]
75
+ # 正则处理 把",,,"这样的 分割成" , , , "
76
+ csv.gsub!(/[,|,|\s]+?/, " , ")
77
+ # lstrip rstrip 去掉前后空格
78
+ # @type [Array<String>]
79
+ values = csv.split(',').each(&:lstrip!).each(&:rstrip!)
80
+ # 判断有没有 `keys` 如果没有 第一行就作为 `keys` 因为第一行作为 `keys` 就是像极了 `csv`
81
+ if (!template[KEYS] || template[KEYS].empty?) && csv_index == 0
82
+ keys = values
83
+ next
84
+ end
85
+ # 初始化数据
86
+ datum = { ID => id, PERMALINK => "/#{ID}/#{id}" }
87
+ # 对当前 `template` 所有 `key` 遍历
88
+ template.each_key do |key|
89
+ datum[key] = Counter.formatValues(values, template[key])
90
+ end
91
+
92
+ # 对 `values` 所有 内容 遍历
93
+ values.each_index do |index|
94
+ datum[keys[index]] = values[index]
95
+ end
96
+
97
+ # 可能会死循环 `Document.new` 还会发消息
98
+ # TODO: 后续 采用 `Document` 子类
99
+ new_doc = Document.new(doc.path, site: site, collection: site.posts)
100
+ new_doc.data.replace(doc.data)
101
+ new_doc.data[DATA] = {}
102
+ # 重新赋值
103
+ datum.each_key do |key|
104
+ Counter.set_doc_data(new_doc, key, datum[key])
105
+ end
106
+ scanned_docs << new_doc
107
+ id += 1
108
+ end
109
+ end
110
+ end
111
+ # @type [Array<Document>]
112
+ all_docs = no_scan_docs + scanned_docs
113
+ # 判断是不是需要 开启组合模式
114
+ combine_configs = tally_configs[COMBINE]
115
+ if combine_configs && combine_configs.is_a?(Array)
116
+ combine_configs.each do |config|
117
+ find_key = config[FIND]
118
+ combine_key = config[TO]
119
+ deep_key = config[DEEP]
120
+ all_find_keys = []
121
+ all_docs.each do |doc|
122
+ doc.data[combine_key] = []
123
+ # 找到所有 `key`
124
+ all_find_keys += doc.data[find_key]
125
+ end
126
+ merges = Counter.merge_all(all_find_keys.uniq!, deep_key)
127
+ Counter.combine_merge_list(merges, all_docs, find_key, combine_key)
128
+ end
129
+ end
130
+
131
+ site.posts.docs = all_docs
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,57 @@
1
+ module Jekyll
2
+ DEBUG = false
3
+ Jekyll::Hooks.register :site, :after_init do |data|
4
+ puts 'site after_init 在网站初始化时,但是在设置和渲染之前,适合用来修改网站的配置项' if DEBUG
5
+ end
6
+ Jekyll::Hooks.register :site, :after_reset do |data|
7
+ puts 'site after_reset 网站重置之后' if DEBUG
8
+ end
9
+ Jekyll::Hooks.register :site, :post_read do |site|
10
+ puts 'site post_read 在网站数据从磁盘中读取并加载之后' if DEBUG
11
+ end
12
+ Jekyll::Hooks.register :site, :pre_render do |data|
13
+ puts 'site pre_render 在渲染整个网站之前' if DEBUG
14
+ end
15
+ Jekyll::Hooks.register :site, :post_render do |data|
16
+ puts 'site post_render 在渲染整个网站之后,但是在写入任何文件之前' if DEBUG
17
+ end
18
+ Jekyll::Hooks.register :site, :post_write do |data|
19
+ puts 'site post_write 在将整个网站写入磁盘之后' if DEBUG
20
+ end
21
+ Jekyll::Hooks.register :pages, :post_init do |data|
22
+ puts 'pages post_init 每次页面被初始化的时候' if DEBUG
23
+ end
24
+ Jekyll::Hooks.register :pages, :pre_render do |data|
25
+ puts 'pages pre_render 在渲染页面之前' if DEBUG
26
+ end
27
+ Jekyll::Hooks.register :pages, :post_render do |data|
28
+ puts 'pages post_render 在页面渲染之后,但是在页面写入磁盘之前' if DEBUG
29
+ end
30
+ Jekyll::Hooks.register :pages, :post_write do |data|
31
+ puts 'pages post_write 在页面写入磁盘之后' if DEBUG
32
+ end
33
+ Jekyll::Hooks.register :posts, :post_init do |data|
34
+ puts 'posts post_init 每次博客被初始化的时候' if DEBUG
35
+ end
36
+ Jekyll::Hooks.register :posts, :pre_render do |data|
37
+ puts 'posts pre_render 在博客被渲染之前' if DEBUG
38
+ end
39
+ Jekyll::Hooks.register :posts, :post_render do |data|
40
+ puts 'posts post_render 在博客渲染之后,但是在被写入磁盘之前' if DEBUG
41
+ end
42
+ Jekyll::Hooks.register :posts, :post_write do |data|
43
+ puts 'posts post_write 在博客被写入磁盘之后' if DEBUG
44
+ end
45
+ Jekyll::Hooks.register :documents, :post_init do |data|
46
+ puts 'documents post_init 每次文档被初始化的时候' if DEBUG
47
+ end
48
+ Jekyll::Hooks.register :documents, :pre_render do |data|
49
+ puts 'documents pre_render 在渲染文档之前' if DEBUG
50
+ end
51
+ Jekyll::Hooks.register :documents, :post_render do |data|
52
+ puts 'documents post_render 在渲染文档之后,但是在被写入磁盘之前' if DEBUG
53
+ end
54
+ Jekyll::Hooks.register :documents, :post_write do |data|
55
+ puts 'documents post_write 在文档被写入磁盘之后' if DEBUG
56
+ end
57
+ end