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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.idea/.gitignore +8 -0
- data/.idea/inspectionProfiles/Project_Default.xml +6 -0
- data/.idea/jekyll-tally-tags.iml +44 -0
- data/.idea/misc.xml +4 -0
- data/.idea/modules.xml +8 -0
- data/.idea/vcs.xml +6 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +74 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +5 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/jekyll-tally-tags.gemspec +28 -0
- data/lib/jekyll-tally-tags/classify.rb +196 -0
- data/lib/jekyll-tally-tags/counter.rb +260 -0
- data/lib/jekyll-tally-tags/hooks_doc.rb +134 -0
- data/lib/jekyll-tally-tags/hooks_logs.rb +57 -0
- data/lib/jekyll-tally-tags/subject.rb +143 -0
- data/lib/jekyll-tally-tags/version.rb +54 -0
- metadata +101 -0
@@ -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
|