concurrent-ruby 0.7.0.rc1-x86-mingw32 → 0.7.0.rc2-x86-mingw32
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 +8 -8
- data/README.md +3 -2
- data/ext/concurrent_ruby_ext/atomic_boolean.c +48 -0
- data/ext/concurrent_ruby_ext/atomic_boolean.h +16 -0
- data/ext/concurrent_ruby_ext/atomic_fixnum.c +50 -0
- data/ext/concurrent_ruby_ext/atomic_fixnum.h +13 -0
- data/ext/concurrent_ruby_ext/atomic_reference.c +44 -44
- data/ext/concurrent_ruby_ext/atomic_reference.h +8 -0
- data/ext/concurrent_ruby_ext/rb_concurrent.c +32 -3
- data/ext/concurrent_ruby_ext/ruby_193_compatible.h +28 -0
- data/lib/1.9/concurrent_ruby_ext.so +0 -0
- data/lib/2.0/concurrent_ruby_ext.so +0 -0
- data/lib/concurrent.rb +2 -1
- data/lib/concurrent/actor.rb +104 -0
- data/lib/concurrent/{actress → actor}/ad_hoc.rb +2 -3
- data/lib/concurrent/actor/behaviour.rb +70 -0
- data/lib/concurrent/actor/behaviour/abstract.rb +48 -0
- data/lib/concurrent/actor/behaviour/awaits.rb +21 -0
- data/lib/concurrent/actor/behaviour/buffer.rb +54 -0
- data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +12 -0
- data/lib/concurrent/actor/behaviour/executes_context.rb +18 -0
- data/lib/concurrent/actor/behaviour/linking.rb +42 -0
- data/lib/concurrent/actor/behaviour/pausing.rb +77 -0
- data/lib/concurrent/actor/behaviour/removes_child.rb +16 -0
- data/lib/concurrent/actor/behaviour/sets_results.rb +36 -0
- data/lib/concurrent/actor/behaviour/supervised.rb +58 -0
- data/lib/concurrent/actor/behaviour/supervising.rb +34 -0
- data/lib/concurrent/actor/behaviour/terminates_children.rb +13 -0
- data/lib/concurrent/actor/behaviour/termination.rb +54 -0
- data/lib/concurrent/actor/context.rb +153 -0
- data/lib/concurrent/actor/core.rb +213 -0
- data/lib/concurrent/actor/default_dead_letter_handler.rb +9 -0
- data/lib/concurrent/{actress → actor}/envelope.rb +1 -1
- data/lib/concurrent/actor/errors.rb +27 -0
- data/lib/concurrent/actor/internal_delegations.rb +49 -0
- data/lib/concurrent/{actress/core_delegations.rb → actor/public_delegations.rb} +11 -13
- data/lib/concurrent/{actress → actor}/reference.rb +25 -8
- data/lib/concurrent/actor/root.rb +37 -0
- data/lib/concurrent/{actress → actor}/type_check.rb +1 -1
- data/lib/concurrent/actor/utills.rb +7 -0
- data/lib/concurrent/actor/utils/broadcast.rb +36 -0
- data/lib/concurrent/actress.rb +2 -224
- data/lib/concurrent/agent.rb +10 -12
- data/lib/concurrent/atomic.rb +32 -1
- data/lib/concurrent/atomic/atomic_boolean.rb +55 -13
- data/lib/concurrent/atomic/atomic_fixnum.rb +54 -16
- data/lib/concurrent/atomic/synchronization.rb +51 -0
- data/lib/concurrent/atomic/thread_local_var.rb +15 -50
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
- data/lib/concurrent/atomic_reference/ruby.rb +15 -0
- data/lib/concurrent/atomics.rb +1 -0
- data/lib/concurrent/channel/unbuffered_channel.rb +2 -1
- data/lib/concurrent/configuration.rb +6 -3
- data/lib/concurrent/dataflow.rb +20 -3
- data/lib/concurrent/delay.rb +23 -31
- data/lib/concurrent/executor/executor.rb +7 -2
- data/lib/concurrent/executor/timer_set.rb +1 -1
- data/lib/concurrent/future.rb +2 -1
- data/lib/concurrent/lazy_register.rb +58 -0
- data/lib/concurrent/options_parser.rb +4 -2
- data/lib/concurrent/promise.rb +2 -1
- data/lib/concurrent/scheduled_task.rb +6 -5
- data/lib/concurrent/tvar.rb +6 -10
- data/lib/concurrent/utility/processor_count.rb +4 -2
- data/lib/concurrent/version.rb +1 -1
- data/lib/concurrent_ruby_ext.so +0 -0
- metadata +37 -10
- data/lib/concurrent/actress/context.rb +0 -98
- data/lib/concurrent/actress/core.rb +0 -228
- data/lib/concurrent/actress/errors.rb +0 -14
@@ -0,0 +1,34 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actor
|
3
|
+
module Behaviour
|
4
|
+
class Supervising < Abstract
|
5
|
+
def initialize(core, subsequent, handle, strategy)
|
6
|
+
super core, subsequent
|
7
|
+
@handle = Match! handle, :terminate!, :resume!, :reset!, :restart!
|
8
|
+
@strategy = case @handle
|
9
|
+
when :terminate!
|
10
|
+
Match! strategy, nil
|
11
|
+
when :resume!
|
12
|
+
Match! strategy, :one_for_one
|
13
|
+
when :reset!, :restart!
|
14
|
+
Match! strategy, :one_for_one, :one_for_all
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_envelope(envelope)
|
19
|
+
case envelope.message
|
20
|
+
when Exception, :paused
|
21
|
+
receivers = if @strategy == :one_for_all
|
22
|
+
children
|
23
|
+
else
|
24
|
+
[envelope.sender]
|
25
|
+
end
|
26
|
+
receivers.each { |ch| ch << @handle }
|
27
|
+
else
|
28
|
+
pass envelope
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actor
|
3
|
+
module Behaviour
|
4
|
+
# Terminates all children when the actor terminates.
|
5
|
+
class TerminatesChildren < Abstract
|
6
|
+
def on_event(event)
|
7
|
+
children.each { |ch| ch << :terminate! } if event == :terminated
|
8
|
+
super event
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actor
|
3
|
+
module Behaviour
|
4
|
+
|
5
|
+
# Handles actor termination.
|
6
|
+
# @note Actor rejects envelopes when terminated.
|
7
|
+
class Termination < Abstract
|
8
|
+
|
9
|
+
# @!attribute [r] terminated
|
10
|
+
# @return [Event] event which will become set when actor is terminated.
|
11
|
+
attr_reader :terminated
|
12
|
+
|
13
|
+
def initialize(core, subsequent)
|
14
|
+
super core, subsequent
|
15
|
+
@terminated = Event.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# @note Actor rejects envelopes when terminated.
|
19
|
+
# @return [true, false] if actor is terminated
|
20
|
+
def terminated?
|
21
|
+
@terminated.set?
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_envelope(envelope)
|
25
|
+
case envelope.message
|
26
|
+
when :terminated?
|
27
|
+
terminated?
|
28
|
+
when :terminate!
|
29
|
+
terminate!
|
30
|
+
when :terminated_event
|
31
|
+
terminated
|
32
|
+
else
|
33
|
+
if terminated?
|
34
|
+
reject_envelope envelope
|
35
|
+
MESSAGE_PROCESSED
|
36
|
+
else
|
37
|
+
pass envelope
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Terminates the actor. Any Envelope received after termination is rejected.
|
43
|
+
# Terminates all its children, does not wait until they are terminated.
|
44
|
+
def terminate!
|
45
|
+
return true if terminated?
|
46
|
+
terminated.set
|
47
|
+
broadcast(:terminated)
|
48
|
+
parent << :remove_child if parent
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actor
|
3
|
+
|
4
|
+
# Abstract implementation of Actor context. Children has to implement
|
5
|
+
# {AbstractContext#on_message} and {AbstractContext#behaviour_definition} methods.
|
6
|
+
# There are two implementations:
|
7
|
+
#
|
8
|
+
# - {Context}
|
9
|
+
#
|
10
|
+
# > {include:Actor::Context}
|
11
|
+
#
|
12
|
+
# - {RestartingContext}.
|
13
|
+
#
|
14
|
+
# > {include:Actor::RestartingContext}
|
15
|
+
class AbstractContext
|
16
|
+
include TypeCheck
|
17
|
+
include InternalDelegations
|
18
|
+
|
19
|
+
attr_reader :core
|
20
|
+
|
21
|
+
# @abstract override to define Actor's behaviour
|
22
|
+
# @param [Object] message
|
23
|
+
# @return [Object] a result which will be used to set the IVar supplied to Reference#ask
|
24
|
+
# @note self should not be returned (or sent to other actors), {#reference} should be used
|
25
|
+
# instead
|
26
|
+
def on_message(message)
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
# override to add custom code invocation on events like `:terminated`, `:resumed`, `anError`.
|
31
|
+
def on_event(event)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @api private
|
35
|
+
def on_envelope(envelope)
|
36
|
+
@envelope = envelope
|
37
|
+
on_message envelope.message
|
38
|
+
ensure
|
39
|
+
@envelope = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# if you want to pass the message to next behaviour, usually {Behaviour::ErrorsOnUnknownMessage}
|
43
|
+
def pass
|
44
|
+
core.behaviour!(Behaviour::ExecutesContext).pass envelope
|
45
|
+
end
|
46
|
+
|
47
|
+
# Defines an actor responsible for dead letters. Any rejected message send with
|
48
|
+
# {Reference#tell} is sent there, a message with ivar is considered already monitored for
|
49
|
+
# failures. Default behaviour is to use {AbstractContext#dead_letter_routing} of the parent,
|
50
|
+
# so if no {AbstractContext#dead_letter_routing} method is overridden in parent-chain the message ends up in
|
51
|
+
# `Actor.root.dead_letter_routing` agent which will log warning.
|
52
|
+
# @return [Reference]
|
53
|
+
def dead_letter_routing
|
54
|
+
parent.dead_letter_routing
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Array<Array(Behavior::Abstract, Array<Object>)>]
|
58
|
+
def behaviour_definition
|
59
|
+
raise NotImplementedError
|
60
|
+
end
|
61
|
+
|
62
|
+
# @return [Envelope] current envelope, accessible inside #on_message processing
|
63
|
+
def envelope
|
64
|
+
@envelope or raise 'envelope not set'
|
65
|
+
end
|
66
|
+
|
67
|
+
# override if different class for reference is needed
|
68
|
+
# @return [CLass] descendant of {Reference}
|
69
|
+
def default_reference_class
|
70
|
+
Reference
|
71
|
+
end
|
72
|
+
|
73
|
+
def tell(message)
|
74
|
+
reference.tell message
|
75
|
+
end
|
76
|
+
|
77
|
+
def ask(message)
|
78
|
+
raise 'actor cannot ask itself'
|
79
|
+
end
|
80
|
+
|
81
|
+
alias_method :<<, :tell
|
82
|
+
alias_method :ask!, :ask
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def initialize_core(core)
|
87
|
+
@core = Type! core, Core
|
88
|
+
end
|
89
|
+
|
90
|
+
# behaves as {Concurrent::Actor.spawn} but :class is auto-inserted based on receiver
|
91
|
+
def self.spawn(name_or_opts, *args, &block)
|
92
|
+
Actor.spawn spawn_optionify(name_or_opts, *args), &block
|
93
|
+
end
|
94
|
+
|
95
|
+
# behaves as {Concurrent::Actor.spawn!} but :class is auto-inserted based on receiver
|
96
|
+
def self.spawn!(name_or_opts, *args, &block)
|
97
|
+
Actor.spawn! spawn_optionify(name_or_opts, *args), &block
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def self.spawn_optionify(name_or_opts, *args)
|
103
|
+
if name_or_opts.is_a? Hash
|
104
|
+
if name_or_opts.key?(:class) && name_or_opts[:class] != self
|
105
|
+
raise ArgumentError,
|
106
|
+
':class option is ignored when calling on context class, use Actor.spawn instead'
|
107
|
+
end
|
108
|
+
name_or_opts.merge class: self
|
109
|
+
else
|
110
|
+
{ class: self, name: name_or_opts, args: args }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# to avoid confusion with Kernel.spawn
|
115
|
+
undef_method :spawn
|
116
|
+
end
|
117
|
+
|
118
|
+
# Basic Context of an Actor.
|
119
|
+
#
|
120
|
+
# - linking
|
121
|
+
# - terminates on error
|
122
|
+
#
|
123
|
+
# TODO describe behaviour
|
124
|
+
# TODO usage
|
125
|
+
# @example ping
|
126
|
+
# class Ping < Context
|
127
|
+
# def on_message(message)
|
128
|
+
# message
|
129
|
+
# end
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# Ping.spawn(:ping1).ask(:m).value #=> :m
|
133
|
+
class Context < AbstractContext
|
134
|
+
def behaviour_definition
|
135
|
+
Behaviour.basic_behaviour_definition
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Context of an Actor for complex robust systems.
|
140
|
+
#
|
141
|
+
# - linking
|
142
|
+
# - supervising
|
143
|
+
# - pauses on error
|
144
|
+
#
|
145
|
+
# TODO describe behaviour
|
146
|
+
# TODO usage
|
147
|
+
class RestartingContext < AbstractContext
|
148
|
+
def behaviour_definition
|
149
|
+
Behaviour.restarting_behaviour_definition
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Actor
|
3
|
+
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
# Core of the actor
|
7
|
+
# @note Whole class should be considered private. An user should use {Context}s and {Reference}s only.
|
8
|
+
# @note devel: core should not block on anything, e.g. it cannot wait on children to terminate
|
9
|
+
# that would eat up all threads in task pool and deadlock
|
10
|
+
class Core
|
11
|
+
include TypeCheck
|
12
|
+
include Concurrent::Logging
|
13
|
+
include Synchronization
|
14
|
+
|
15
|
+
# @!attribute [r] reference
|
16
|
+
# @return [Reference] reference to this actor which can be safely passed around
|
17
|
+
# @!attribute [r] name
|
18
|
+
# @return [String] the name of this instance, it should be uniq (not enforced right now)
|
19
|
+
# @!attribute [r] path
|
20
|
+
# @return [String] a path of this actor. It is used for easier orientation and logging.
|
21
|
+
# Path is constructed recursively with: `parent.path + self.name` up to a {Actor.root},
|
22
|
+
# e.g. `/an_actor/its_child`.
|
23
|
+
# (It will also probably form a supervision path (failures will be reported up to parents)
|
24
|
+
# in future versions.)
|
25
|
+
# @!attribute [r] executor
|
26
|
+
# @return [Executor] which is used to process messages
|
27
|
+
# @!attribute [r] actor_class
|
28
|
+
# @return [Context] a class including {Context} representing Actor's behaviour
|
29
|
+
attr_reader :reference, :name, :path, :executor, :context_class, :context, :behaviour_definition
|
30
|
+
|
31
|
+
# @option opts [String] name
|
32
|
+
# @option opts [Reference, nil] parent of an actor spawning this one
|
33
|
+
# @option opts [Class] reference a custom descendant of {Reference} to use
|
34
|
+
# @option opts [Context] actor_class a class to be instantiated defining Actor's behaviour
|
35
|
+
# @option opts [Array<Object>] args arguments for actor_class instantiation
|
36
|
+
# @option opts [Executor] executor, default is `Concurrent.configuration.global_task_pool`
|
37
|
+
# @option opts [true, false] link, atomically link the actor to its parent
|
38
|
+
# @option opts [true, false] supervise, atomically supervise the actor by its parent
|
39
|
+
# @option opts [Array<Array(Behavior::Abstract, Array<Object>)>] behaviour_definition, array of pairs
|
40
|
+
# where each pair is behaviour class and its args, see {Behaviour.basic_behaviour}
|
41
|
+
# @option opts [IVar, nil] initialized, if present it'll be set or failed after {Context} initialization
|
42
|
+
# @option opts [Proc, nil] logger a proc accepting (level, progname, message = nil, &block) params,
|
43
|
+
# can be used to hook actor instance to any logging system
|
44
|
+
# @param [Proc] block for class instantiation
|
45
|
+
def initialize(opts = {}, &block)
|
46
|
+
synchronize do
|
47
|
+
@mailbox = Array.new
|
48
|
+
@serialized_execution = SerializedExecution.new
|
49
|
+
@executor = Type! opts.fetch(:executor, Concurrent.configuration.global_task_pool), Executor
|
50
|
+
@children = Set.new
|
51
|
+
@context_class = Child! opts.fetch(:class), AbstractContext
|
52
|
+
allocate_context
|
53
|
+
@reference = (Child! opts[:reference_class] || @context.default_reference_class, Reference).new self
|
54
|
+
@name = (Type! opts.fetch(:name), String, Symbol).to_s
|
55
|
+
|
56
|
+
parent = opts[:parent]
|
57
|
+
@parent_core = (Type! parent, Reference, NilClass) && parent.send(:core)
|
58
|
+
if @parent_core.nil? && @name != '/'
|
59
|
+
raise 'only root has no parent'
|
60
|
+
end
|
61
|
+
|
62
|
+
@path = @parent_core ? File.join(@parent_core.path, @name) : @name
|
63
|
+
@logger = opts[:logger]
|
64
|
+
|
65
|
+
@parent_core.add_child reference if @parent_core
|
66
|
+
|
67
|
+
initialize_behaviours opts
|
68
|
+
|
69
|
+
@args = opts.fetch(:args, [])
|
70
|
+
@block = block
|
71
|
+
initialized = Type! opts[:initialized], IVar, NilClass
|
72
|
+
|
73
|
+
messages = []
|
74
|
+
messages << :link if opts[:link]
|
75
|
+
messages << :supervise if opts[:supervise]
|
76
|
+
|
77
|
+
schedule_execution do
|
78
|
+
begin
|
79
|
+
build_context
|
80
|
+
|
81
|
+
messages.each do |message|
|
82
|
+
handle_envelope Envelope.new(message, nil, parent, reference)
|
83
|
+
end
|
84
|
+
|
85
|
+
initialized.set true if initialized
|
86
|
+
rescue => ex
|
87
|
+
log ERROR, ex
|
88
|
+
@first_behaviour.terminate!
|
89
|
+
initialized.fail ex if initialized
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [Reference, nil] of parent actor
|
96
|
+
def parent
|
97
|
+
@parent_core && @parent_core.reference
|
98
|
+
end
|
99
|
+
|
100
|
+
# @see AbstractContext#dead_letter_routing
|
101
|
+
def dead_letter_routing
|
102
|
+
@context.dead_letter_routing
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [Array<Reference>] of children actors
|
106
|
+
def children
|
107
|
+
guard!
|
108
|
+
@children.to_a
|
109
|
+
end
|
110
|
+
|
111
|
+
# @api private
|
112
|
+
def add_child(child)
|
113
|
+
guard!
|
114
|
+
Type! child, Reference
|
115
|
+
@children.add child
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
|
119
|
+
# @api private
|
120
|
+
def remove_child(child)
|
121
|
+
guard!
|
122
|
+
Type! child, Reference
|
123
|
+
@children.delete child
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
# is executed by Reference scheduling processing of new messages
|
128
|
+
# can be called from other alternative Reference implementations
|
129
|
+
# @param [Envelope] envelope
|
130
|
+
def on_envelope(envelope)
|
131
|
+
schedule_execution { handle_envelope envelope }
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
|
135
|
+
# ensures that we are inside of the executor
|
136
|
+
def guard!
|
137
|
+
unless Actor.current == reference
|
138
|
+
raise "can be called only inside actor #{reference} but was #{Actor.current}"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def log(level, message = nil, &block)
|
143
|
+
super level, @path, message, &block
|
144
|
+
end
|
145
|
+
|
146
|
+
# Schedules blocks to be executed on executor sequentially,
|
147
|
+
# sets Actress.current
|
148
|
+
def schedule_execution
|
149
|
+
@serialized_execution.post(@executor) do
|
150
|
+
synchronize do
|
151
|
+
begin
|
152
|
+
Thread.current[:__current_actor__] = reference
|
153
|
+
yield
|
154
|
+
rescue => e
|
155
|
+
log FATAL, e
|
156
|
+
ensure
|
157
|
+
Thread.current[:__current_actor__] = nil
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
nil
|
163
|
+
end
|
164
|
+
|
165
|
+
def broadcast(event)
|
166
|
+
@first_behaviour.on_event(event)
|
167
|
+
end
|
168
|
+
|
169
|
+
# @param [Class] behaviour_class
|
170
|
+
# @return [Behaviour::Abstract, nil] based on behaviour_class
|
171
|
+
def behaviour(behaviour_class)
|
172
|
+
@behaviours[behaviour_class]
|
173
|
+
end
|
174
|
+
|
175
|
+
# @param [Class] behaviour_class
|
176
|
+
# @return [Behaviour::Abstract] based on behaviour_class
|
177
|
+
# @raise [KeyError] when no behaviour
|
178
|
+
def behaviour!(behaviour_class)
|
179
|
+
@behaviours.fetch behaviour_class
|
180
|
+
end
|
181
|
+
|
182
|
+
# @api private
|
183
|
+
def allocate_context
|
184
|
+
@context = @context_class.allocate
|
185
|
+
end
|
186
|
+
|
187
|
+
# @api private
|
188
|
+
def build_context
|
189
|
+
@context.send :initialize_core, self
|
190
|
+
@context.send :initialize, *@args, &@block
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
def handle_envelope(envelope)
|
196
|
+
log DEBUG, "received #{envelope.message.inspect} from #{envelope.sender}"
|
197
|
+
@first_behaviour.on_envelope envelope
|
198
|
+
end
|
199
|
+
|
200
|
+
def initialize_behaviours(opts)
|
201
|
+
@behaviour_definition = (Type! opts[:behaviour_definition] || @context.behaviour_definition, Array).each do |v|
|
202
|
+
Type! v, Array
|
203
|
+
Match! v.size, 2
|
204
|
+
Child! v[0], Behaviour::Abstract
|
205
|
+
Type! v[1], Array
|
206
|
+
end
|
207
|
+
@behaviours = {}
|
208
|
+
@first_behaviour = @behaviour_definition.reverse.
|
209
|
+
reduce(nil) { |last, (behaviour, args)| @behaviours[behaviour] = behaviour.new(self, last, *args) }
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|