cc 1.2.1 → 1.3.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 +4 -4
- data/monkey-patch.rb +375 -1
- data/tree.rb +184 -184
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f8386675ee1a8b68cb73c020b620f2b314b0778a15c22a852bc230955155275f
|
|
4
|
+
data.tar.gz: 58d7146b6769792681d5454bb665fd9f7694c85b56ea2d2c4b91d6a519839173
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 946c2bc2b698b1a4e05e03224005038281f57712a228c82ff6d9ff635a3d34921741969ce058c75716dbd9ee9823d16f2224214b71bec7904ec8c8314ed481df
|
|
7
|
+
data.tar.gz: c77d03c47b193c321a22f52b23841be5b5284b91839309294d93a48bc577bd1a31ca2375befba9576c9e8cbd91b212140e6a397a1c99c18c75722b51d1828a16
|
data/monkey-patch.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# HOW TO USE #
|
|
5
5
|
############################################################################################################
|
|
6
6
|
|
|
7
|
-
=begin
|
|
7
|
+
=begin # 旧版
|
|
8
8
|
require 'cc'
|
|
9
9
|
CC.use 'monkey-patch'
|
|
10
10
|
|
|
@@ -50,6 +50,380 @@
|
|
|
50
50
|
p a.compize(Object), a.mod1, a.mod2, a.mod1_20110101
|
|
51
51
|
=end
|
|
52
52
|
|
|
53
|
+
=begin # 新版
|
|
54
|
+
puts "=" * 60
|
|
55
|
+
puts "模块级方法版本控制系统 v4 演示"
|
|
56
|
+
puts "=" * 60
|
|
57
|
+
|
|
58
|
+
# ========================================
|
|
59
|
+
# 定义版本化模块
|
|
60
|
+
# ========================================
|
|
61
|
+
module Calculator
|
|
62
|
+
include MonkeyPatch
|
|
63
|
+
|
|
64
|
+
# 方式1: 传统 block
|
|
65
|
+
ADD_V1 = defv(:add) { |a, b| a + b }
|
|
66
|
+
puts "[方式1] 定义 add v1, 签名: #{ADD_V1}"
|
|
67
|
+
|
|
68
|
+
ADD_V2 = defv(:add) { |a, b|
|
|
69
|
+
result = a + b
|
|
70
|
+
puts "[v2] 计算 #{a} + #{b} = #{result}"
|
|
71
|
+
result
|
|
72
|
+
}
|
|
73
|
+
puts "[方式1] 定义 add v2, 签名: #{ADD_V2}"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# 方式2: 从外部数组批量生成
|
|
77
|
+
EXTERNAL_SPECS = [
|
|
78
|
+
[:greet, "World", proc { |name| "Hello, #{name}!" }],
|
|
79
|
+
[:multiply, 1, proc { |a, b| a * b }],
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
module GreetingService
|
|
83
|
+
include MonkeyPatch
|
|
84
|
+
|
|
85
|
+
EXTERNAL_SPECS.each do |spec|
|
|
86
|
+
name, default, body = spec
|
|
87
|
+
sig = defv(name, default, body)
|
|
88
|
+
puts "[方式2] 定义 #{name}, 默认=#{default.inspect}, 签名: #{sig}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# ========================================
|
|
93
|
+
# 测试 module_function 效果
|
|
94
|
+
# ========================================
|
|
95
|
+
puts "\n" + "-" * 50
|
|
96
|
+
puts "测试 module_function 效果(v4 核心特性)"
|
|
97
|
+
puts "-" * 50
|
|
98
|
+
|
|
99
|
+
# 实例调用
|
|
100
|
+
obj = Object.new.extend(Calculator)
|
|
101
|
+
puts "obj.add(2, 3) = #{obj.add(2, 3)}"
|
|
102
|
+
|
|
103
|
+
# 模块方法调用(module_function 效果)
|
|
104
|
+
puts "Calculator.add(10, 20) = #{Calculator.add(10, 20)}"
|
|
105
|
+
puts "Calculator.send(:add, 5, 7) = #{Calculator.send(:add, 5, 7)}"
|
|
106
|
+
|
|
107
|
+
# 模块方法也走版本控制
|
|
108
|
+
puts "\n--- 模块方法版本切换 ---"
|
|
109
|
+
puts "当前默认: Calculator.add(1, 1) = #{Calculator.add(1, 1)}"
|
|
110
|
+
|
|
111
|
+
Calculator.rollback_to(:add, Calculator::ADD_V1)
|
|
112
|
+
puts "回滚到 v1: Calculator.add(1, 1) = #{Calculator.add(1, 1)}"
|
|
113
|
+
|
|
114
|
+
Calculator.rollback_to(:add, Calculator::ADD_V2)
|
|
115
|
+
puts "恢复 v2: Calculator.add(1, 1) = #{Calculator.add(1, 1)}"
|
|
116
|
+
|
|
117
|
+
# 实例调用同步受影响(共享版本库)
|
|
118
|
+
puts "obj.add(1, 1) = #{obj.add(1, 1)} (与模块方法同步)"
|
|
119
|
+
|
|
120
|
+
# ========================================
|
|
121
|
+
# 测试 GreetingService
|
|
122
|
+
# ========================================
|
|
123
|
+
puts "\n" + "-" * 50
|
|
124
|
+
puts "测试 GreetingService(方式2 + module_function)"
|
|
125
|
+
puts "-" * 50
|
|
126
|
+
|
|
127
|
+
puts "GreetingService.greet = #{GreetingService.greet}"
|
|
128
|
+
puts 'GreetingService.greet("Alice") = ' + GreetingService.greet("Alice").to_s
|
|
129
|
+
puts "GreetingService.multiply(3, 4) = #{GreetingService.multiply(3, 4)}"
|
|
130
|
+
|
|
131
|
+
# 实例调用
|
|
132
|
+
g = Object.new.extend(GreetingService)
|
|
133
|
+
puts "g.greet = #{g.greet}"
|
|
134
|
+
puts 'g.greet("Bob") = ' + g.greet("Bob").to_s
|
|
135
|
+
|
|
136
|
+
# ========================================
|
|
137
|
+
# 版本历史
|
|
138
|
+
# ========================================
|
|
139
|
+
puts "\n" + "-" * 50
|
|
140
|
+
puts "版本历史"
|
|
141
|
+
puts "-" * 50
|
|
142
|
+
|
|
143
|
+
Calculator.version_history(:add).each do |v|
|
|
144
|
+
puts " Calculator.add: #{v[:signature]} @ #{v[:timestamp]}"
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
GreetingService.version_history(:greet).each do |v|
|
|
148
|
+
puts " GreetingService.greet: #{v[:signature]} @ #{v[:timestamp]}"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# ========================================
|
|
152
|
+
# 跨模块隔离
|
|
153
|
+
# ========================================
|
|
154
|
+
puts "\n" + "=" * 60
|
|
155
|
+
puts "跨模块隔离测试"
|
|
156
|
+
puts "=" * 60
|
|
157
|
+
puts "Calculator 版本库: #{Calculator.versioned_methods.inspect}"
|
|
158
|
+
puts "GreetingService 版本库: #{GreetingService.versioned_methods.inspect}"
|
|
159
|
+
|
|
160
|
+
# ========================================
|
|
161
|
+
# 全局注册表
|
|
162
|
+
# ========================================
|
|
163
|
+
puts "\n" + "=" * 60
|
|
164
|
+
puts "全局注册表结构"
|
|
165
|
+
puts "=" * 60
|
|
166
|
+
MonkeyPatch::VERSION_REGISTRY.each do |mod, methods|
|
|
167
|
+
puts "模块: #{mod}"
|
|
168
|
+
methods.each do |name, versions|
|
|
169
|
+
puts " 方法: #{name}"
|
|
170
|
+
versions.each do |sig, vm|
|
|
171
|
+
next if sig == :__latest__
|
|
172
|
+
puts " [#{sig}] => #{vm}"
|
|
173
|
+
end
|
|
174
|
+
puts " [LATEST] => #{versions[:__latest__]}"
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
=end
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# ============================================================
|
|
181
|
+
# 新版本MonkeyPatch
|
|
182
|
+
# 模块级方法版本控制系统 (Module Method Versioning) - v4
|
|
183
|
+
# ============================================================
|
|
184
|
+
# 特性:
|
|
185
|
+
# 1. 模块作为方法仓库,每个方法名对应一个版本链
|
|
186
|
+
# 2. 支持按签名回溯历史版本
|
|
187
|
+
# 3. 不传签名时默认调取最新版本
|
|
188
|
+
# 4. 模块间版本隔离,互不干扰
|
|
189
|
+
# 5. 支持两种调用方式:
|
|
190
|
+
# - 传统 block: defv(:name) { |a| ... }
|
|
191
|
+
# - 数组重构: defv(:name, default_param, proc)
|
|
192
|
+
# 6. module_function 支持:模块方法也走版本控制
|
|
193
|
+
# - obj.a 和 A.a 共享同一个版本库
|
|
194
|
+
# ============================================================
|
|
195
|
+
|
|
196
|
+
module MonkeyPatch
|
|
197
|
+
VERSION_REGISTRY = {}
|
|
198
|
+
SIGNATURE_COUNTER = Hash.new(0)
|
|
199
|
+
|
|
200
|
+
class VersionedMethod
|
|
201
|
+
attr_reader :name, :signature, :body, :timestamp, :module_ref
|
|
202
|
+
|
|
203
|
+
def initialize(name:, signature:, body:, module_ref:)
|
|
204
|
+
@name = name
|
|
205
|
+
@signature = signature
|
|
206
|
+
@body = body
|
|
207
|
+
@timestamp = Time.now
|
|
208
|
+
@module_ref = module_ref
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def call(*args)
|
|
212
|
+
outer_block = block_given? ? Proc.new : nil
|
|
213
|
+
if outer_block
|
|
214
|
+
@module_ref.instance_exec(*args, outer_block, &@body)
|
|
215
|
+
else
|
|
216
|
+
@module_ref.instance_exec(*args, &@body)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def to_s
|
|
221
|
+
"#<VersionedMethod #{@module_ref}##{@name}@#{@signature} #{@timestamp}>"
|
|
222
|
+
end
|
|
223
|
+
alias inspect to_s
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def self.included(base)
|
|
227
|
+
base.extend(ClassMethods)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# ----------------------------------------------------------
|
|
231
|
+
# 类方法接口
|
|
232
|
+
# ----------------------------------------------------------
|
|
233
|
+
module ClassMethods
|
|
234
|
+
def define_versioned_method(name, *args, signature: nil, &block)
|
|
235
|
+
if block_given?
|
|
236
|
+
_register_version(name, signature: signature, body: block)
|
|
237
|
+
elsif args.size >= 2 && args[1].is_a?(Proc)
|
|
238
|
+
default_param = args[0]
|
|
239
|
+
body_proc = args[1]
|
|
240
|
+
|
|
241
|
+
wrapped = proc { |*call_args|
|
|
242
|
+
if call_args.empty?
|
|
243
|
+
body_proc.call(default_param)
|
|
244
|
+
else
|
|
245
|
+
body_proc.call(*call_args)
|
|
246
|
+
end
|
|
247
|
+
}
|
|
248
|
+
_register_version(name, signature: signature, body: wrapped)
|
|
249
|
+
else
|
|
250
|
+
raise ArgumentError, "必须提供方法块,或传入 (name, default_param, proc)"
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def defv(name, *args, &block)
|
|
255
|
+
if block_given?
|
|
256
|
+
define_versioned_method(name, *args, &block)
|
|
257
|
+
elsif args.size >= 1 && args[-1].is_a?(Proc)
|
|
258
|
+
if args.size >= 2 && args[0].is_a?(String) && args[1].is_a?(Proc)
|
|
259
|
+
define_versioned_method(name, *args)
|
|
260
|
+
elsif args.size == 1
|
|
261
|
+
define_versioned_method(name, nil, args[0])
|
|
262
|
+
else
|
|
263
|
+
define_versioned_method(name, *args)
|
|
264
|
+
end
|
|
265
|
+
else
|
|
266
|
+
raise ArgumentError, "defv 需要 block 或 (default_param, proc)"
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def get_versioned_method(name, signature = nil)
|
|
271
|
+
versions = _method_versions[name]
|
|
272
|
+
return nil unless versions
|
|
273
|
+
|
|
274
|
+
sig = signature || versions[:__latest__]
|
|
275
|
+
return nil unless sig
|
|
276
|
+
|
|
277
|
+
versions[sig]
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def call_versioned(name, signature = nil, *args, &block)
|
|
281
|
+
vm = get_versioned_method(name, signature)
|
|
282
|
+
raise NameError, "方法 `#{name}` 版本 `#{signature || 'latest'}` 不存在" unless vm
|
|
283
|
+
|
|
284
|
+
vm.call(*args, &block)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def method_versions(name)
|
|
288
|
+
versions = _method_versions[name] || {}
|
|
289
|
+
versions.reject { |k, _| k == :__latest__ }.transform_values(&:to_s)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def versioned_methods
|
|
293
|
+
_method_versions.keys
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def version_history(name)
|
|
297
|
+
return [] unless _method_versions[name]
|
|
298
|
+
|
|
299
|
+
_method_versions[name]
|
|
300
|
+
.reject { |k, _| k == :__latest__ }
|
|
301
|
+
.map { |sig, vm| { signature: sig, timestamp: vm.timestamp, method: vm } }
|
|
302
|
+
.sort_by { |h| h[:timestamp] }
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def rollback_to(name, signature)
|
|
306
|
+
versions = _method_versions[name]
|
|
307
|
+
raise NameError, "方法 `#{name}` 不存在" unless versions
|
|
308
|
+
raise KeyError, "签名 `#{signature}` 不存在" unless versions[signature]
|
|
309
|
+
|
|
310
|
+
versions[:__latest__] = signature
|
|
311
|
+
signature
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def remove_version(name, signature)
|
|
315
|
+
versions = _method_versions[name]
|
|
316
|
+
return false unless versions&.key?(signature)
|
|
317
|
+
|
|
318
|
+
if versions.reject { |k, _| k == :__latest__ }.size <= 1
|
|
319
|
+
raise "不能删除方法 `#{name}` 的唯一版本"
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
versions.delete(signature)
|
|
323
|
+
|
|
324
|
+
if versions[:__latest__] == signature
|
|
325
|
+
latest = versions.reject { |k, _| k == :__latest__ }.keys.last
|
|
326
|
+
versions[:__latest__] = latest
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
true
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def clear_versions(name)
|
|
333
|
+
_method_versions.delete(name)
|
|
334
|
+
if method_defined?(name)
|
|
335
|
+
remove_method(name)
|
|
336
|
+
end
|
|
337
|
+
if singleton_class.method_defined?(name)
|
|
338
|
+
singleton_class.remove_method(name)
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
private
|
|
343
|
+
|
|
344
|
+
def _register_version(name, signature: nil, body:)
|
|
345
|
+
sig = signature || _generate_signature(name)
|
|
346
|
+
|
|
347
|
+
if _method_versions[name]&.key?(sig)
|
|
348
|
+
raise ArgumentError, "方法 `#{name}` 的签名 `#{sig}` 已存在"
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
versioned = VersionedMethod.new(
|
|
352
|
+
name: name,
|
|
353
|
+
signature: sig,
|
|
354
|
+
body: body,
|
|
355
|
+
module_ref: self
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
_method_versions[name] ||= {}
|
|
359
|
+
_method_versions[name][sig] = versioned
|
|
360
|
+
_method_versions[name][:__latest__] = sig
|
|
361
|
+
|
|
362
|
+
# 安装实例方法调度器(首次定义时)
|
|
363
|
+
unless method_defined?(name) || private_method_defined?(name)
|
|
364
|
+
_install_instance_dispatcher(name)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# 安装模块方法调度器(首次定义时)
|
|
368
|
+
unless singleton_class.method_defined?(name)
|
|
369
|
+
_install_module_dispatcher(name)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
sig
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def _generate_signature(name)
|
|
376
|
+
SIGNATURE_COUNTER[name] += 1
|
|
377
|
+
"#{name}_v#{SIGNATURE_COUNTER[name]}_#{Time.now.to_i}"
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def _method_versions
|
|
381
|
+
VERSION_REGISTRY[self] ||= {}
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# 实例方法调度器:obj.a 调用时路由到最新版本
|
|
385
|
+
def _install_instance_dispatcher(name)
|
|
386
|
+
define_method(name) do |*args, &block|
|
|
387
|
+
mod = self.class.ancestors.find { |m| m.respond_to?(:get_versioned_method) }
|
|
388
|
+
mod ||= self.singleton_class.ancestors.find { |m| m.respond_to?(:get_versioned_method) }
|
|
389
|
+
|
|
390
|
+
versions = MonkeyPatch::VERSION_REGISTRY[mod]&.[](name)
|
|
391
|
+
unless versions
|
|
392
|
+
raise NoMethodError, "未找到版本化方法 `#{name}`"
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
latest_sig = versions[:__latest__]
|
|
396
|
+
latest = versions[latest_sig]
|
|
397
|
+
|
|
398
|
+
unless latest
|
|
399
|
+
raise NoMethodError, "方法 `#{name}` 没有可用版本"
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
latest.call(*args, &block)
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# 模块方法调度器:A.a 调用时路由到最新版本
|
|
407
|
+
def _install_module_dispatcher(name)
|
|
408
|
+
singleton_class.send(:define_method, name) do |*args, &block|
|
|
409
|
+
# 模块方法直接在当前模块上调用 call_versioned
|
|
410
|
+
call_versioned(name, nil, *args, &block)
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# 实例方法接口
|
|
416
|
+
def call_version(name, signature = nil, *args, &block)
|
|
417
|
+
mod = self.class.ancestors.find { |m| m.respond_to?(:call_versioned) }
|
|
418
|
+
mod&.call_versioned(name, signature, *args, &block)
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
# ============================================================
|
|
424
|
+
# 旧版本MonkeyPatch
|
|
425
|
+
# ============================================================
|
|
426
|
+
|
|
53
427
|
class Object
|
|
54
428
|
attr_accessor :main_version, :versions
|
|
55
429
|
|
data/tree.rb
CHANGED
|
@@ -1,184 +1,184 @@
|
|
|
1
|
-
#coding:utf-8
|
|
2
|
-
|
|
3
|
-
############################################################################################################
|
|
4
|
-
# The Class Tree is a recursive hash-generator based on Hash. #
|
|
5
|
-
# #
|
|
6
|
-
# HOW TO USE #
|
|
7
|
-
############################################################################################################
|
|
8
|
-
|
|
9
|
-
=begin
|
|
10
|
-
require 'cc'
|
|
11
|
-
CC.use 'tree'
|
|
12
|
-
|
|
13
|
-
# 奇怪的数据结构增加了!!!
|
|
14
|
-
a = Tree.init
|
|
15
|
-
|
|
16
|
-
# 逐个赋值
|
|
17
|
-
a[1][2][3] = :'4'
|
|
18
|
-
a['1']['2']['3']=4
|
|
19
|
-
p a
|
|
20
|
-
puts Array.new(32,'-').join
|
|
21
|
-
|
|
22
|
-
# 直接挂载
|
|
23
|
-
a.mount '1/2/3/4/5/6', '7'
|
|
24
|
-
p a
|
|
25
|
-
a['1/2/3'] = 4
|
|
26
|
-
p a
|
|
27
|
-
puts Array.new(32,'-').join
|
|
28
|
-
|
|
29
|
-
# 不能给根目录赋值
|
|
30
|
-
begin
|
|
31
|
-
a['/'] = :root
|
|
32
|
-
rescue Tree::PathError => exception
|
|
33
|
-
puts "#{exception.class}: #{exception.message}\n #{exception.backtrace.join("\n ")}"
|
|
34
|
-
end
|
|
35
|
-
begin
|
|
36
|
-
a.mount '/', :home
|
|
37
|
-
rescue Tree::PathError => exception
|
|
38
|
-
puts "#{exception.class}: #{exception.message}\n #{exception.backtrace.join("\n ")}"
|
|
39
|
-
end
|
|
40
|
-
p a.route('/')==a['/']
|
|
41
|
-
puts Array.new(32,'-').join
|
|
42
|
-
|
|
43
|
-
# 合并到某分支,连接到路径
|
|
44
|
-
b = Tree.init
|
|
45
|
-
b['a/b/c'] = :d
|
|
46
|
-
a.emerge '1/2/3', b['a']
|
|
47
|
-
p a # a['1/2/b'].parent==a['1/2/3'].parent # a['1/2/3'] is 4 and not respond to parent
|
|
48
|
-
begin
|
|
49
|
-
a.contact '1/2/3', b['a']
|
|
50
|
-
rescue Tree::PathError => exception
|
|
51
|
-
puts "#{exception.class}: #{exception.message}\n #{exception.backtrace.join("\n ")}"
|
|
52
|
-
end
|
|
53
|
-
a.contact '1/2', b
|
|
54
|
-
p a
|
|
55
|
-
puts Array.new(32,'-').join
|
|
56
|
-
|
|
57
|
-
# 回溯父节点
|
|
58
|
-
t = Tree.init
|
|
59
|
-
p t['a/b/c'] = :d
|
|
60
|
-
p t
|
|
61
|
-
puts Array.new(32,'-').join
|
|
62
|
-
p '1:',t['1'],t['1/'].parent
|
|
63
|
-
puts Array.new(32,'-').join
|
|
64
|
-
p '1/2:',t['/1/2'],t['1/2'].parent
|
|
65
|
-
puts Array.new(32,'-').join
|
|
66
|
-
p '1/2/3:',t['1/2/3/'],t['1/2/3'].parent
|
|
67
|
-
puts Array.new(32,'-').join
|
|
68
|
-
p '1/2/3/4:',t['1/2/3/4'],t['1/2/3/4'].parent
|
|
69
|
-
puts Array.new(32,'-').join
|
|
70
|
-
p 'a:',t['a'],t['a'].parent
|
|
71
|
-
puts Array.new(32,'-').join
|
|
72
|
-
p 'a/b:',t['a/b'],t['a/b'].parent
|
|
73
|
-
puts Array.new(32,'-').join
|
|
74
|
-
require './exception'
|
|
75
|
-
p 'a/b/c:',t['a/b/c'],t['/a/b/c'].try(:parent)
|
|
76
|
-
puts Array.new(32,'-').join
|
|
77
|
-
p 'a/b/c/d:',(begin;t['a/b/c/d'];rescue(Exception);'cant pass path="a/b/c/d" with leaf["a/b/c"]';end)
|
|
78
|
-
puts Array.new(32,'-').join
|
|
79
|
-
=end
|
|
80
|
-
|
|
81
|
-
class Tree < Hash
|
|
82
|
-
VERSION = '0.2.0'
|
|
83
|
-
|
|
84
|
-
def self.init
|
|
85
|
-
Tree.new{|tree, path|tree[path] = Tree.new(tree,&tree.default_proc) }
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def self.clear
|
|
89
|
-
self.init
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
attr_accessor :parent
|
|
93
|
-
|
|
94
|
-
def initialize parent=nil
|
|
95
|
-
@parent=parent
|
|
96
|
-
super()
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def [] key
|
|
100
|
-
unless key.instance_of?(String) && key.include?('/')
|
|
101
|
-
child = super(key)
|
|
102
|
-
child.parent = self if child.respond_to? :parent
|
|
103
|
-
return child
|
|
104
|
-
end
|
|
105
|
-
key.include?('/') ? send(:route,key) : super(key)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def []= key, value
|
|
109
|
-
unless key.instance_of?(String) && key.include?('/')
|
|
110
|
-
super(key,value)
|
|
111
|
-
child = self[key]
|
|
112
|
-
child.parent = self if child.respond_to? :parent
|
|
113
|
-
return child
|
|
114
|
-
end
|
|
115
|
-
key.include?('/') ? (key=='/' ? (raise PathError, "Class Tree can't use '/' as a key except rootself.") : send(:mount,key,value)) : super(key,value)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def route path
|
|
119
|
-
unless path.instance_of?(String) && path.include?('/')
|
|
120
|
-
post = self[path]
|
|
121
|
-
post.parent = self if post.respond_to? :parent
|
|
122
|
-
return post
|
|
123
|
-
end
|
|
124
|
-
return self if path=='/'
|
|
125
|
-
hops = path.split('/')
|
|
126
|
-
hops.delete ''
|
|
127
|
-
curr = hops.shift
|
|
128
|
-
if hops.empty?
|
|
129
|
-
self[curr]
|
|
130
|
-
else
|
|
131
|
-
# if a leaf exists, any path walkthrough leaf would not pass;
|
|
132
|
-
# if no leaf exists, any path walkthrough could be pass.
|
|
133
|
-
raise PathError, "The current expected path`#{path}` not exist." unless self[curr].respond_to? :route
|
|
134
|
-
self[curr].send "route", hops.join('/')
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Caution: #mount will arrive the deepest leaf and change the passthrough
|
|
139
|
-
def mount path, store
|
|
140
|
-
unless path.instance_of?(String) && path.include?('/')
|
|
141
|
-
self[path]=store
|
|
142
|
-
post = self[path]
|
|
143
|
-
post.parent = self if post.respond_to? :parent
|
|
144
|
-
return post
|
|
145
|
-
end
|
|
146
|
-
raise PathError, "Class Tree can't mount an value on the root." if path=='/'
|
|
147
|
-
hops = path.split('/')
|
|
148
|
-
hops.delete ''
|
|
149
|
-
curr = hops.shift
|
|
150
|
-
if hops.empty?
|
|
151
|
-
self[curr]=store
|
|
152
|
-
self.delete nil # except ['/']=store to {nil=>store}
|
|
153
|
-
else
|
|
154
|
-
self[curr]=Tree.init
|
|
155
|
-
# self[curr].send "route", hops.join('/')
|
|
156
|
-
self[curr].send "mount", hops.join('/'), store
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# Caution: #emerge can adapt on HashObject but loose the tree-ability
|
|
161
|
-
def emerge path, tree
|
|
162
|
-
if tree.is_a?(Hash)
|
|
163
|
-
hops = path.split("/")
|
|
164
|
-
popz = hops.pop
|
|
165
|
-
curr = hops.join('/')
|
|
166
|
-
target = hops.empty? ? self[popz] : self.route(curr)
|
|
167
|
-
raise PathError, "The current node [\"#{path}\"]=>#{target} not respond to contact/merge." unless target.respond_to? :route
|
|
168
|
-
target.merge!(tree)
|
|
169
|
-
return target
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def contact path, tree
|
|
174
|
-
if tree.is_a?(Tree)
|
|
175
|
-
target = self.route path
|
|
176
|
-
raise PathError, "The current node [\"#{path}\"]=>#{target} not respond to contact/merge." unless target.respond_to? :route
|
|
177
|
-
target.merge!(tree)
|
|
178
|
-
tree.parent = target
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
class PathError < Exception
|
|
183
|
-
end
|
|
184
|
-
end
|
|
1
|
+
#coding:utf-8
|
|
2
|
+
|
|
3
|
+
############################################################################################################
|
|
4
|
+
# The Class Tree is a recursive hash-generator based on Hash. #
|
|
5
|
+
# #
|
|
6
|
+
# HOW TO USE #
|
|
7
|
+
############################################################################################################
|
|
8
|
+
|
|
9
|
+
=begin
|
|
10
|
+
require 'cc'
|
|
11
|
+
CC.use 'tree'
|
|
12
|
+
|
|
13
|
+
# 奇怪的数据结构增加了!!!
|
|
14
|
+
a = Tree.init
|
|
15
|
+
|
|
16
|
+
# 逐个赋值
|
|
17
|
+
a[1][2][3] = :'4'
|
|
18
|
+
a['1']['2']['3']=4
|
|
19
|
+
p a
|
|
20
|
+
puts Array.new(32,'-').join
|
|
21
|
+
|
|
22
|
+
# 直接挂载
|
|
23
|
+
a.mount '1/2/3/4/5/6', '7'
|
|
24
|
+
p a
|
|
25
|
+
a['1/2/3'] = 4
|
|
26
|
+
p a
|
|
27
|
+
puts Array.new(32,'-').join
|
|
28
|
+
|
|
29
|
+
# 不能给根目录赋值
|
|
30
|
+
begin
|
|
31
|
+
a['/'] = :root
|
|
32
|
+
rescue Tree::PathError => exception
|
|
33
|
+
puts "#{exception.class}: #{exception.message}\n #{exception.backtrace.join("\n ")}"
|
|
34
|
+
end
|
|
35
|
+
begin
|
|
36
|
+
a.mount '/', :home
|
|
37
|
+
rescue Tree::PathError => exception
|
|
38
|
+
puts "#{exception.class}: #{exception.message}\n #{exception.backtrace.join("\n ")}"
|
|
39
|
+
end
|
|
40
|
+
p a.route('/')==a['/']
|
|
41
|
+
puts Array.new(32,'-').join
|
|
42
|
+
|
|
43
|
+
# 合并到某分支,连接到路径
|
|
44
|
+
b = Tree.init
|
|
45
|
+
b['a/b/c'] = :d
|
|
46
|
+
a.emerge '1/2/3', b['a']
|
|
47
|
+
p a # a['1/2/b'].parent==a['1/2/3'].parent # a['1/2/3'] is 4 and not respond to parent
|
|
48
|
+
begin
|
|
49
|
+
a.contact '1/2/3', b['a']
|
|
50
|
+
rescue Tree::PathError => exception
|
|
51
|
+
puts "#{exception.class}: #{exception.message}\n #{exception.backtrace.join("\n ")}"
|
|
52
|
+
end
|
|
53
|
+
a.contact '1/2', b
|
|
54
|
+
p a
|
|
55
|
+
puts Array.new(32,'-').join
|
|
56
|
+
|
|
57
|
+
# 回溯父节点
|
|
58
|
+
t = Tree.init
|
|
59
|
+
p t['a/b/c'] = :d
|
|
60
|
+
p t
|
|
61
|
+
puts Array.new(32,'-').join
|
|
62
|
+
p '1:',t['1'],t['1/'].parent
|
|
63
|
+
puts Array.new(32,'-').join
|
|
64
|
+
p '1/2:',t['/1/2'],t['1/2'].parent
|
|
65
|
+
puts Array.new(32,'-').join
|
|
66
|
+
p '1/2/3:',t['1/2/3/'],t['1/2/3'].parent
|
|
67
|
+
puts Array.new(32,'-').join
|
|
68
|
+
p '1/2/3/4:',t['1/2/3/4'],t['1/2/3/4'].parent
|
|
69
|
+
puts Array.new(32,'-').join
|
|
70
|
+
p 'a:',t['a'],t['a'].parent
|
|
71
|
+
puts Array.new(32,'-').join
|
|
72
|
+
p 'a/b:',t['a/b'],t['a/b'].parent
|
|
73
|
+
puts Array.new(32,'-').join
|
|
74
|
+
require './exception'
|
|
75
|
+
p 'a/b/c:',t['a/b/c'],t['/a/b/c'].try(:parent)
|
|
76
|
+
puts Array.new(32,'-').join
|
|
77
|
+
p 'a/b/c/d:',(begin;t['a/b/c/d'];rescue(Exception);'cant pass path="a/b/c/d" with leaf["a/b/c"]';end)
|
|
78
|
+
puts Array.new(32,'-').join
|
|
79
|
+
=end
|
|
80
|
+
|
|
81
|
+
class Tree < Hash
|
|
82
|
+
VERSION = '0.2.0'
|
|
83
|
+
|
|
84
|
+
def self.init
|
|
85
|
+
Tree.new{|tree, path|tree[path] = Tree.new(tree,&tree.default_proc) }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def self.clear
|
|
89
|
+
self.init
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
attr_accessor :parent
|
|
93
|
+
|
|
94
|
+
def initialize parent=nil
|
|
95
|
+
@parent=parent
|
|
96
|
+
super()
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def [] key
|
|
100
|
+
unless key.instance_of?(String) && key.include?('/')
|
|
101
|
+
child = super(key)
|
|
102
|
+
child.parent = self if child.respond_to? :parent
|
|
103
|
+
return child
|
|
104
|
+
end
|
|
105
|
+
key.include?('/') ? send(:route,key) : super(key)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def []= key, value
|
|
109
|
+
unless key.instance_of?(String) && key.include?('/')
|
|
110
|
+
super(key,value)
|
|
111
|
+
child = self[key]
|
|
112
|
+
child.parent = self if child.respond_to? :parent
|
|
113
|
+
return child
|
|
114
|
+
end
|
|
115
|
+
key.include?('/') ? (key=='/' ? (raise PathError, "Class Tree can't use '/' as a key except rootself.") : send(:mount,key,value)) : super(key,value)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def route path
|
|
119
|
+
unless path.instance_of?(String) && path.include?('/')
|
|
120
|
+
post = self[path]
|
|
121
|
+
post.parent = self if post.respond_to? :parent
|
|
122
|
+
return post
|
|
123
|
+
end
|
|
124
|
+
return self if path=='/'
|
|
125
|
+
hops = path.split('/')
|
|
126
|
+
hops.delete ''
|
|
127
|
+
curr = hops.shift
|
|
128
|
+
if hops.empty?
|
|
129
|
+
self[curr]
|
|
130
|
+
else
|
|
131
|
+
# if a leaf exists, any path walkthrough leaf would not pass;
|
|
132
|
+
# if no leaf exists, any path walkthrough could be pass.
|
|
133
|
+
raise PathError, "The current expected path`#{path}` not exist." unless self[curr].respond_to? :route
|
|
134
|
+
self[curr].send "route", hops.join('/')
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Caution: #mount will arrive the deepest leaf and change the passthrough
|
|
139
|
+
def mount path, store
|
|
140
|
+
unless path.instance_of?(String) && path.include?('/')
|
|
141
|
+
self[path]=store
|
|
142
|
+
post = self[path]
|
|
143
|
+
post.parent = self if post.respond_to? :parent
|
|
144
|
+
return post
|
|
145
|
+
end
|
|
146
|
+
raise PathError, "Class Tree can't mount an value on the root." if path=='/'
|
|
147
|
+
hops = path.split('/')
|
|
148
|
+
hops.delete ''
|
|
149
|
+
curr = hops.shift
|
|
150
|
+
if hops.empty?
|
|
151
|
+
self[curr]=store
|
|
152
|
+
self.delete nil # except ['/']=store to {nil=>store}
|
|
153
|
+
else
|
|
154
|
+
self[curr]=Tree.init
|
|
155
|
+
# self[curr].send "route", hops.join('/')
|
|
156
|
+
self[curr].send "mount", hops.join('/'), store
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Caution: #emerge can adapt on HashObject but loose the tree-ability
|
|
161
|
+
def emerge path, tree
|
|
162
|
+
if tree.is_a?(Hash)
|
|
163
|
+
hops = path.split("/")
|
|
164
|
+
popz = hops.pop
|
|
165
|
+
curr = hops.join('/')
|
|
166
|
+
target = hops.empty? ? self[popz] : self.route(curr)
|
|
167
|
+
raise PathError, "The current node [\"#{path}\"]=>#{target} not respond to contact/merge." unless target.respond_to? :route
|
|
168
|
+
target.merge!(tree)
|
|
169
|
+
return target
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def contact path, tree
|
|
174
|
+
if tree.is_a?(Tree)
|
|
175
|
+
target = self.route path
|
|
176
|
+
raise PathError, "The current node [\"#{path}\"]=>#{target} not respond to contact/merge." unless target.respond_to? :route
|
|
177
|
+
target.merge!(tree)
|
|
178
|
+
tree.parent = target
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
class PathError < Exception
|
|
183
|
+
end
|
|
184
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matt
|
|
@@ -54,7 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
54
54
|
- !ruby/object:Gem::Version
|
|
55
55
|
version: '0'
|
|
56
56
|
requirements: []
|
|
57
|
-
rubygems_version:
|
|
57
|
+
rubygems_version: 4.0.10
|
|
58
58
|
specification_version: 4
|
|
59
59
|
summary: custom core
|
|
60
60
|
test_files: []
|