dramatis 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +7 -0
- data/License.txt +20 -0
- data/Manifest.txt +119 -0
- data/README.txt +57 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +17 -0
- data/examples/README.txt +20 -0
- data/examples/auction.rb +90 -0
- data/examples/bank/bank.rb +7 -0
- data/examples/bank/bank_test.rb +7 -0
- data/examples/exception.rb +40 -0
- data/examples/fib/conservative.rb +50 -0
- data/examples/fib/future.rb +5 -0
- data/examples/fib/original.rb +33 -0
- data/examples/fib/threads.rb +51 -0
- data/examples/im/distributed/chat/client.rb +49 -0
- data/examples/im/distributed/chat/screen/fox.rb +92 -0
- data/examples/im/distributed/chat/screen.rb +11 -0
- data/examples/im/distributed/chat/server.rb +72 -0
- data/examples/im/distributed/chat.rb +5 -0
- data/examples/im/distributed/client.rb +9 -0
- data/examples/im/distributed/run.rb +18 -0
- data/examples/im/distributed/server.rb +11 -0
- data/examples/im/single/chat/client.rb +50 -0
- data/examples/im/single/chat/screen/fox.rb +96 -0
- data/examples/im/single/chat/screen/wxs.rb +63 -0
- data/examples/im/single/chat/screen.rb +11 -0
- data/examples/im/single/chat/server.rb +72 -0
- data/examples/im/single/chat.rb +5 -0
- data/examples/im/single/fox.rb +18 -0
- data/examples/im/single/wxchat.rb +19 -0
- data/examples/pingpong/actor.rb +33 -0
- data/examples/pingpong/actor_rec.rb +34 -0
- data/examples/pingpong/pingpong.txt +315 -0
- data/examples/pingpong/scala.rb +41 -0
- data/examples/pingpong/serial.rb +26 -0
- data/examples/pretty.txt +108 -0
- data/examples/telephone/.irbrc +2 -0
- data/examples/telephone/3esl.txt +21877 -0
- data/examples/telephone/fifth/kid.rb +36 -0
- data/examples/telephone/fifth/run.rb +26 -0
- data/examples/telephone/first/kid.rb +31 -0
- data/examples/telephone/first/run.rb +20 -0
- data/examples/telephone/fourth/kid.rb +31 -0
- data/examples/telephone/fourth/run.rb +26 -0
- data/examples/telephone/mangler.rb +53 -0
- data/examples/telephone/second/kid.rb +26 -0
- data/examples/telephone/second/run.rb +20 -0
- data/examples/telephone/seventh/kid.rb +40 -0
- data/examples/telephone/seventh/run.rb +35 -0
- data/examples/telephone/seventh/test.rb +28 -0
- data/examples/telephone/seventh/test2.rb +10 -0
- data/examples/telephone/sixth/kid.rb +39 -0
- data/examples/telephone/sixth/run.rb +26 -0
- data/examples/telephone/third/kid.rb +31 -0
- data/examples/telephone/third/run.rb +21 -0
- data/lib/dramatis/actor/interface.rb +118 -0
- data/lib/dramatis/actor/name/interface.rb +128 -0
- data/lib/dramatis/actor/name.rb +44 -0
- data/lib/dramatis/actor.rb +96 -0
- data/lib/dramatis/deadlock.rb +123 -0
- data/lib/dramatis/error/uncaught.rb +19 -0
- data/lib/dramatis/error.rb +125 -0
- data/lib/dramatis/future/interface.rb +45 -0
- data/lib/dramatis/future.rb +32 -0
- data/lib/dramatis/runtime/actor/main.rb +3 -0
- data/lib/dramatis/runtime/actor.rb +294 -0
- data/lib/dramatis/runtime/gate.rb +244 -0
- data/lib/dramatis/runtime/scheduler.rb +374 -0
- data/lib/dramatis/runtime/task.rb +390 -0
- data/lib/dramatis/runtime/thread_pool.rb +149 -0
- data/lib/dramatis/runtime/timer.rb +5 -0
- data/lib/dramatis/runtime.rb +129 -0
- data/lib/dramatis/shoes/runtime.rb +7 -0
- data/lib/dramatis/shoes.rb +14 -0
- data/lib/dramatis/version.rb +8 -0
- data/lib/dramatis.rb +73 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/spec/dramatis/actor/become_spec.rb +17 -0
- data/spec/dramatis/actor/future_spec.rb +189 -0
- data/spec/dramatis/actor/name_spec.rb +141 -0
- data/spec/dramatis/actor/task_spec.rb +75 -0
- data/spec/dramatis/actor_spec.rb +492 -0
- data/spec/dramatis/dramatis_spec.rb +23 -0
- data/spec/dramatis/exc_spec.rb +78 -0
- data/spec/dramatis/runtime/gate_spec.rb +57 -0
- data/spec/dramatis/runtime/thread_pool.rb +30 -0
- data/spec/dramatis/shoes_spec.rb +11 -0
- data/spec/dramatis/simple_spec.rb +32 -0
- data/spec/exp_spec.rb +21 -0
- data/spec/simple2_spec.rb +36 -0
- data/spec/simple_spec.rb +30 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/thread_spec.rb +13 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/website.rake +17 -0
- data/test/jruby_lm.rb +13 -0
- data/test/test.rb +19 -0
- data/test/test10.rb +43 -0
- data/test/test11.rb +45 -0
- data/test/test12.rb +60 -0
- data/test/test13.rb +71 -0
- data/test/test2.rb +12 -0
- data/test/test3.rb +10 -0
- data/test/test4.rb +29 -0
- data/test/test5.rb +8 -0
- data/test/test6.rb +32 -0
- data/test/test7.rb +48 -0
- data/test/test8.rb +133 -0
- data/test/test9.rb +105 -0
- data/test/test_exc.rb +22 -0
- metadata +180 -0
@@ -0,0 +1,294 @@
|
|
1
|
+
module Dramatis; end
|
2
|
+
class Dramatis::Runtime; end
|
3
|
+
|
4
|
+
require 'dramatis/runtime/task'
|
5
|
+
require 'dramatis/runtime/gate'
|
6
|
+
require 'dramatis/runtime/timer'
|
7
|
+
require 'dramatis/actor/interface'
|
8
|
+
require 'dramatis/actor/name'
|
9
|
+
require 'thread'
|
10
|
+
|
11
|
+
begin require 'pp'; rescue Exception; end
|
12
|
+
|
13
|
+
class Dramatis::Runtime::Actor #:nodoc: all
|
14
|
+
|
15
|
+
def name
|
16
|
+
@name ||= Dramatis::Actor::Name.new self
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :object_interface
|
20
|
+
attr_reader :object
|
21
|
+
attr_reader :gate
|
22
|
+
attr_reader :call_thread
|
23
|
+
|
24
|
+
def initialize object = nil
|
25
|
+
@call_threading = false
|
26
|
+
@call_thread = nil
|
27
|
+
@object = object
|
28
|
+
@gate = Dramatis::Runtime::Gate.new
|
29
|
+
if !object
|
30
|
+
@gate.refuse :object
|
31
|
+
end
|
32
|
+
@gate.always( ( [ :object, :dramatis_exception ] ), true )
|
33
|
+
blocked!
|
34
|
+
@queue = []
|
35
|
+
@mutex = Mutex.new
|
36
|
+
@continuations = {}
|
37
|
+
@object_interface = Dramatis::Actor::Interface.new self
|
38
|
+
Dramatis::Runtime::Scheduler.current << self
|
39
|
+
end
|
40
|
+
|
41
|
+
def call_threading?
|
42
|
+
@call_threading
|
43
|
+
end
|
44
|
+
|
45
|
+
def enable_call_threading
|
46
|
+
@call_threading = true
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_call_thread? that
|
50
|
+
# warn "current_call_thread? #{@call_thread} #{(@call_thread and (@call_thread == that)).inspect} #{that}"
|
51
|
+
@call_thread and @call_thread == that
|
52
|
+
end
|
53
|
+
|
54
|
+
def object_initialize *args
|
55
|
+
@gate.accept :object
|
56
|
+
@object.send :initialize, *args
|
57
|
+
end
|
58
|
+
|
59
|
+
def yield; end
|
60
|
+
|
61
|
+
def bind object
|
62
|
+
raise Dramatis::Error::Bind if @object
|
63
|
+
@object = object
|
64
|
+
@gate.accept :object
|
65
|
+
name
|
66
|
+
end
|
67
|
+
|
68
|
+
def exception exception
|
69
|
+
if @object.respond_to? :dramatis_exception
|
70
|
+
@object.dramatis_exception exception
|
71
|
+
else
|
72
|
+
Dramatis::Runtime.current.exception exception
|
73
|
+
end
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
def deadlock e
|
78
|
+
tasks = nil
|
79
|
+
@mutex.synchronize do
|
80
|
+
tasks = @queue.dup
|
81
|
+
@queue.clear
|
82
|
+
end
|
83
|
+
tasks.each { |task| task.exception e }
|
84
|
+
end
|
85
|
+
|
86
|
+
def register_continuation c
|
87
|
+
# p "selfish", self, @continuations
|
88
|
+
# pp "csr", c.to_s
|
89
|
+
@continuations[c.to_s] = c
|
90
|
+
end
|
91
|
+
|
92
|
+
def actor_send args, opts
|
93
|
+
common_send :actor, args, opts
|
94
|
+
end
|
95
|
+
|
96
|
+
def object_send args, opts
|
97
|
+
t = nil
|
98
|
+
if opts[:continuation_send]
|
99
|
+
t = :continuation
|
100
|
+
args.unshift opts[:continuation_send]
|
101
|
+
else
|
102
|
+
t = :object
|
103
|
+
end
|
104
|
+
common_send t, args, opts
|
105
|
+
end
|
106
|
+
|
107
|
+
def common_send dest, args, opts
|
108
|
+
|
109
|
+
# warn "common send #{self} #{dest} #{args[0]}"
|
110
|
+
# warn "common send #{self} #{dest} #{args.join(' ')} #{opts.to_a.join(' ' )}"
|
111
|
+
|
112
|
+
task = Dramatis::Runtime::Task.new( self, dest, args, opts )
|
113
|
+
|
114
|
+
# warn "#{task.type} #{task.method}"
|
115
|
+
# warn "#{self} #{Thread.current} common send r? #{runnable?} g? #{@gate.accepts?( *( [ task.type, task.method ] + task.arguments ) ) } q #{@queue.length}"
|
116
|
+
@mutex.synchronize do
|
117
|
+
# FIX arguments to gate
|
118
|
+
if !runnable? and ( @gate.accepts?( *( [ task.type, task.method ] + task.arguments ) ) or current_call_thread?( task.call_thread ) )
|
119
|
+
runnable!
|
120
|
+
Dramatis::Runtime::Scheduler.current.schedule task
|
121
|
+
else
|
122
|
+
# warn "+>schd #{self} #{@queue.join(' ')}"
|
123
|
+
@queue << task
|
124
|
+
# warn "+<schd #{self} #{@queue.join(' ')}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
task.queued
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
def deliver dest, args, continuation, call_thread
|
133
|
+
old_call_thread = @call_thread
|
134
|
+
begin
|
135
|
+
@call_thread = call_thread
|
136
|
+
method = args.shift
|
137
|
+
# warn "switch " + dest.to_s + " " + method.to_s
|
138
|
+
result =
|
139
|
+
case dest
|
140
|
+
when :actor
|
141
|
+
# FIX: do name folding; needs test
|
142
|
+
self.send method, *args
|
143
|
+
# p "sent actor #{method}"
|
144
|
+
when :object
|
145
|
+
# p "send object #{@object} #{method} #{args.length}"
|
146
|
+
v = @object.send method, *args
|
147
|
+
if v.object_id == @object.object_id
|
148
|
+
v = name
|
149
|
+
end
|
150
|
+
# p "sent object #{method}"
|
151
|
+
v
|
152
|
+
when :continuation
|
153
|
+
# FIX: name folding?
|
154
|
+
# p "send continuation #{method}"
|
155
|
+
continuation_name = method
|
156
|
+
# warn "c is #{continuation_name}"
|
157
|
+
c = @continuations[continuation_name]
|
158
|
+
# pp "cs", @continuations.keys
|
159
|
+
raise "hell 0 #{Thread.current}" if !c
|
160
|
+
method = args.shift
|
161
|
+
method = case method
|
162
|
+
when :result; :continuation_result
|
163
|
+
when :exception; :continuation_exception
|
164
|
+
else; raise "hell *"
|
165
|
+
end
|
166
|
+
# pp c.to_s, "send", method, args
|
167
|
+
c.send method, *args
|
168
|
+
@continuations.delete continuation_name
|
169
|
+
# pp "csd", continuation_name, @continuations.keys
|
170
|
+
else
|
171
|
+
raise "hell 1: " + @dest.to_s
|
172
|
+
end
|
173
|
+
# p "call c '#{result}'"
|
174
|
+
# p continuation.to_s
|
175
|
+
continuation.result result
|
176
|
+
# p "called c #{result}"
|
177
|
+
rescue Exception => exception
|
178
|
+
# pp "0 exception ", exception
|
179
|
+
# pp exception.backtrace
|
180
|
+
begin
|
181
|
+
continuation.exception exception
|
182
|
+
rescue Exception => e
|
183
|
+
warn "double exception fault: #{e}"
|
184
|
+
pp e
|
185
|
+
raise e
|
186
|
+
end
|
187
|
+
ensure
|
188
|
+
@call_thread = old_call_thread
|
189
|
+
schedule
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# note called from task.rb, too
|
194
|
+
def schedule continuation = nil
|
195
|
+
@mutex.synchronize do
|
196
|
+
# warn ">schd #{self} #{@state} #{@queue.join(' ')}"
|
197
|
+
task = nil
|
198
|
+
index = 0
|
199
|
+
while task == nil and index < @queue.length do
|
200
|
+
candidate = @queue[index]
|
201
|
+
# FIX arugments?
|
202
|
+
if @gate.accepts?( *( [ candidate.type, candidate.method ] + candidate.arguments ) ) or
|
203
|
+
current_call_thread? candidate.call_thread
|
204
|
+
task = candidate
|
205
|
+
@queue[index,1] = []
|
206
|
+
end
|
207
|
+
index += 1
|
208
|
+
end
|
209
|
+
if task
|
210
|
+
Dramatis::Runtime::Scheduler.current.schedule task
|
211
|
+
else
|
212
|
+
blocked!
|
213
|
+
end
|
214
|
+
# warn "<schd #{self} #{@queue.join(' ')} #{@state} #{Thread.current}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def blocked!
|
219
|
+
# warn "blocked! #{self} #{@state}"
|
220
|
+
@state = :blocked
|
221
|
+
end
|
222
|
+
|
223
|
+
def runnable!
|
224
|
+
# warn "runnable! #{self} #{@state}"
|
225
|
+
@state = :runnable
|
226
|
+
end
|
227
|
+
|
228
|
+
def runnable?
|
229
|
+
# warn "runnable? #{self} #{@state}"
|
230
|
+
@state == :runnable
|
231
|
+
end
|
232
|
+
|
233
|
+
def timeout value, *args
|
234
|
+
@timer ||= Dramatis::Runtime::Timer.new
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
# Might be nice if this was broken out into another file ... YAGNI?
|
240
|
+
# This needs to be reworked. External threads and the main thread are
|
241
|
+
# related but different (there can only be one main thread). Mutliple
|
242
|
+
# at_exit calls are suspect. Of course, if the main actor tracks multilple
|
243
|
+
# runtimes, it might be okay.
|
244
|
+
|
245
|
+
class Dramatis::Runtime::Actor::Main < Dramatis::Runtime::Actor #:nodoc: all
|
246
|
+
|
247
|
+
class DefaultBehavior
|
248
|
+
|
249
|
+
class Exception < ::Exception; end
|
250
|
+
|
251
|
+
def method_missing *args
|
252
|
+
raise Exception.new( "must use Actor#become to enable main actor" )
|
253
|
+
end
|
254
|
+
|
255
|
+
def dramatis_exception e
|
256
|
+
if Dramatis::Runtime.current.warnings?
|
257
|
+
warn "exception on main thread: #{e}"
|
258
|
+
# pp caller
|
259
|
+
end
|
260
|
+
Dramatis::Runtime.current.exception e
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
@@current = nil
|
266
|
+
|
267
|
+
def self.current
|
268
|
+
@@current ||= self.new
|
269
|
+
end
|
270
|
+
|
271
|
+
def self.reset
|
272
|
+
@@current = nil
|
273
|
+
end
|
274
|
+
|
275
|
+
def quiesce
|
276
|
+
schedule
|
277
|
+
end
|
278
|
+
|
279
|
+
def finalize
|
280
|
+
if !@at_exit_run
|
281
|
+
@at_exit_run = true
|
282
|
+
schedule
|
283
|
+
Dramatis::Runtime::Scheduler.current.main_at_exit
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def initialize
|
288
|
+
super DefaultBehavior.new
|
289
|
+
@at_exit_run = false
|
290
|
+
at_exit { finalize }
|
291
|
+
runnable!
|
292
|
+
end
|
293
|
+
|
294
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
module Dramatis; end
|
2
|
+
class Dramatis::Runtime; end
|
3
|
+
|
4
|
+
begin require 'pp'; rescue Exception; end
|
5
|
+
|
6
|
+
class Dramatis::Runtime::Gate #:nodoc: all
|
7
|
+
|
8
|
+
def self.new
|
9
|
+
gate = Case.new
|
10
|
+
gate.accept Object
|
11
|
+
gate.accept :actor
|
12
|
+
gate.accept :continuation
|
13
|
+
gate.accept :object
|
14
|
+
gate
|
15
|
+
end
|
16
|
+
|
17
|
+
class Case
|
18
|
+
def always args, value, options = {}
|
19
|
+
_change @always, Array( args ), value, options
|
20
|
+
end
|
21
|
+
def only args, options = {}
|
22
|
+
_change @list, [ :object ], false, options
|
23
|
+
_change @list, [ :continuation ], false, options
|
24
|
+
_change @list, [ :continuation, Object, :exception ], true, options
|
25
|
+
_change @list, Array( args ), true, options
|
26
|
+
end
|
27
|
+
def default args, options = {}
|
28
|
+
_change @list, args, nil, options
|
29
|
+
end
|
30
|
+
def default_by_tag tag
|
31
|
+
_change @list, nil, nil, { :tag => tag }
|
32
|
+
end
|
33
|
+
def _change list, args, value, options = {}
|
34
|
+
inplace = options[ :inplace ]
|
35
|
+
tag = options[ :tag ]
|
36
|
+
# pp ">> #{args and args.join(' ')} #{value} #{inplace} #{tag}", list
|
37
|
+
prepend = true
|
38
|
+
tbd = []
|
39
|
+
list.each_with_index do |entry, list_index|
|
40
|
+
vector, result, entry_tag = entry
|
41
|
+
matches = true
|
42
|
+
if tag and entry_tag != tag
|
43
|
+
# p "#{tag} tag and #{entry_tag} dont' match"
|
44
|
+
next
|
45
|
+
end
|
46
|
+
if args
|
47
|
+
args.each_with_index do |arg, arg_index|
|
48
|
+
# p "compare #{vector[arg_index]} #{arg}"
|
49
|
+
if vector[arg_index] != arg
|
50
|
+
# p "matches"
|
51
|
+
matches = false
|
52
|
+
break
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
if matches
|
57
|
+
# p "matched"
|
58
|
+
if inplace and value != nil
|
59
|
+
# p "inplace"
|
60
|
+
list[list_index][1] = value
|
61
|
+
# FIX: tag?
|
62
|
+
prepend = false
|
63
|
+
else
|
64
|
+
# p "schedule remove #{list[list_index].join(' ')} at #{list_index}"
|
65
|
+
tbd << list_index
|
66
|
+
# list[list_index,1] = []
|
67
|
+
end
|
68
|
+
# break
|
69
|
+
end
|
70
|
+
end
|
71
|
+
tbd.reverse.each do |index|
|
72
|
+
# p "remove #{index} #{list[index].join(' ')} at #{index}"
|
73
|
+
list[index,1] = []
|
74
|
+
end
|
75
|
+
if prepend and value != nil
|
76
|
+
list.unshift [ args, value, tag ]
|
77
|
+
end
|
78
|
+
# pp "<< #{args and args.join(' ')} #{value} #{inplace} #{tag}", list
|
79
|
+
end
|
80
|
+
def change args, value, inplace
|
81
|
+
# pp "> #{args.join(' ')} #{value} #{inplace}", @list
|
82
|
+
prepend = true
|
83
|
+
@list.each_with_index do |entry, list_index|
|
84
|
+
vector, result = entry
|
85
|
+
matches = true
|
86
|
+
args.each_with_index do |arg, arg_index|
|
87
|
+
# p "compare #{vector[arg_index]} #{arg}"
|
88
|
+
if vector[arg_index] != arg
|
89
|
+
# p "matches"
|
90
|
+
matches = false
|
91
|
+
break
|
92
|
+
end
|
93
|
+
end
|
94
|
+
if matches
|
95
|
+
# p "matched"
|
96
|
+
if inplace
|
97
|
+
# p "inplace"
|
98
|
+
@list[list_index][1] = value
|
99
|
+
# FIX: tag?
|
100
|
+
prepend = false
|
101
|
+
else
|
102
|
+
@list[list_index,1] = []
|
103
|
+
end
|
104
|
+
break
|
105
|
+
end
|
106
|
+
end
|
107
|
+
if prepend
|
108
|
+
@list.unshift [ args, value ]
|
109
|
+
end
|
110
|
+
# pp "<", @list
|
111
|
+
end
|
112
|
+
def accept *args
|
113
|
+
change args, true, false
|
114
|
+
end
|
115
|
+
def refuse *args
|
116
|
+
change args, false, false
|
117
|
+
end
|
118
|
+
def update value, *args
|
119
|
+
end
|
120
|
+
def accepts? *args
|
121
|
+
# p "accepts? #{args.join(" ")}"
|
122
|
+
accepted = nil
|
123
|
+
( @always + @list ).each do |entry|
|
124
|
+
vector, result = entry
|
125
|
+
matches = true
|
126
|
+
vector.each_with_index do |v, i|
|
127
|
+
# warn "#{v} #{args[i]} => #{v === args[i]} so #{result}"
|
128
|
+
if not( v === args[i] )
|
129
|
+
matches = false
|
130
|
+
break
|
131
|
+
end
|
132
|
+
end
|
133
|
+
if matches
|
134
|
+
accepted = result
|
135
|
+
break
|
136
|
+
end
|
137
|
+
end
|
138
|
+
# warn "last '#{accepted}'"
|
139
|
+
# p "accepts? #{args.join(" ")} => '#{accepted}'"
|
140
|
+
accepted == true
|
141
|
+
end
|
142
|
+
def initialize
|
143
|
+
@always = []
|
144
|
+
@list = []
|
145
|
+
end
|
146
|
+
def list
|
147
|
+
@list
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
class Constant
|
152
|
+
def initialize constant
|
153
|
+
@constant = constant
|
154
|
+
end
|
155
|
+
def accepts? *args
|
156
|
+
@constant
|
157
|
+
end
|
158
|
+
def refuse *args
|
159
|
+
warn "HELLL"
|
160
|
+
raise "hell"
|
161
|
+
end
|
162
|
+
def accept *args
|
163
|
+
warn "HELLL"
|
164
|
+
raise "hell"
|
165
|
+
end
|
166
|
+
def default *args
|
167
|
+
warn "HELLL"
|
168
|
+
raise "hell"
|
169
|
+
end
|
170
|
+
def set_default *args
|
171
|
+
warn "HELLL"
|
172
|
+
raise "hell"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
ACCEPT = Constant.new true
|
177
|
+
REJECT = Constant.new false
|
178
|
+
|
179
|
+
class Hash
|
180
|
+
|
181
|
+
def initialize default, hash = {}
|
182
|
+
@default = default
|
183
|
+
@hash = hash.clone
|
184
|
+
@queued = {}
|
185
|
+
track and warn "create default = #{@default} hash = [#{@hash.keys.join(' ')}]"
|
186
|
+
@always = []
|
187
|
+
@call_thread = nil
|
188
|
+
end
|
189
|
+
|
190
|
+
def track; false; end
|
191
|
+
|
192
|
+
def always *args
|
193
|
+
track and warn "always [#{args.join(' ')}]"
|
194
|
+
@always << args
|
195
|
+
end
|
196
|
+
|
197
|
+
def refuse *args
|
198
|
+
track and warn "refuse [#{args.join(' ')}]"
|
199
|
+
if args.length == 1
|
200
|
+
@hash[args[0]] = REJECT
|
201
|
+
else
|
202
|
+
@hash[args[0]].refuse( *args[1,args.length] )
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def accept *args
|
207
|
+
track and warn "accept [#{args.join(' ')}]"
|
208
|
+
if args.length == 1
|
209
|
+
@hash[args[0]] = ACCEPT
|
210
|
+
else
|
211
|
+
@hash[args[0]].accept( *args[1,args.length] )
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def default *args
|
216
|
+
track and warn "default [#{args.join(' ')}]"
|
217
|
+
if args.length == 1
|
218
|
+
@hash.delete args[0]
|
219
|
+
else
|
220
|
+
@hash[args[0]].default( *args[1,args.length] )
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def set_default value, *args
|
225
|
+
track and warn "default = #{value} [#{args.join(' ')}] #{args.length}"
|
226
|
+
if args.length == 0
|
227
|
+
@default = value
|
228
|
+
else
|
229
|
+
@hash[args[0]].set_default value, *args[1,args.length]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def accepts? *args
|
234
|
+
v = nil
|
235
|
+
if v == nil
|
236
|
+
v = @hash.has_key?( args[0] ) ? @hash[args[0]].accepts?( *args[1,args.length] ) : @default
|
237
|
+
end
|
238
|
+
track and warn "accepts? [#{args.join(' ')}] => #{v}"
|
239
|
+
v
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|