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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/monkey-patch.rb +375 -1
  3. data/tree.rb +184 -184
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4fc0484d6d2387f8043520b7981b52be8968f1a233cf5e110395a2dcc9ab5fa8
4
- data.tar.gz: 88f19e9a08e517ccdebb8d9f5d8dbb8afec0dd675d6b0c6df057c40703d75993
3
+ metadata.gz: f8386675ee1a8b68cb73c020b620f2b314b0778a15c22a852bc230955155275f
4
+ data.tar.gz: 58d7146b6769792681d5454bb665fd9f7694c85b56ea2d2c4b91d6a519839173
5
5
  SHA512:
6
- metadata.gz: 371bdf3d14c78623c029bd58aff0002383f256dc9346ab5f6586b0ad7920690c9698338d1d73ccbffcab07019d0c9a132ced0365d6f7198c3c7040371329900e
7
- data.tar.gz: 874c41883cb25ddf44790b37d8b7589ef5ff87bb07e25d5d7908f2553dedb3b9cc96af751a7886902839e3e7084931cdd1a30b547c8c6c43a63721bf0b4af6b8
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.2.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: 3.6.9
57
+ rubygems_version: 4.0.10
58
58
  specification_version: 4
59
59
  summary: custom core
60
60
  test_files: []