musa-dsl 0.14.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/Gemfile +20 -0
- data/LICENSE.md +157 -0
- data/README.md +8 -0
- data/lib/musa-dsl/core-ext/array-apply-get.rb +18 -0
- data/lib/musa-dsl/core-ext/array-explode-ranges.rb +29 -0
- data/lib/musa-dsl/core-ext/array-to-neumas.rb +28 -0
- data/lib/musa-dsl/core-ext/array-to-serie.rb +20 -0
- data/lib/musa-dsl/core-ext/arrayfy.rb +15 -0
- data/lib/musa-dsl/core-ext/as-context-run.rb +44 -0
- data/lib/musa-dsl/core-ext/duplicate.rb +134 -0
- data/lib/musa-dsl/core-ext/dynamic-proxy.rb +55 -0
- data/lib/musa-dsl/core-ext/inspect-nice.rb +28 -0
- data/lib/musa-dsl/core-ext/key-parameters-procedure-binder.rb +85 -0
- data/lib/musa-dsl/core-ext/proc-nice.rb +13 -0
- data/lib/musa-dsl/core-ext/send-nice.rb +21 -0
- data/lib/musa-dsl/core-ext/string-to-neumas.rb +27 -0
- data/lib/musa-dsl/core-ext.rb +13 -0
- data/lib/musa-dsl/datasets/gdv-decorators.rb +221 -0
- data/lib/musa-dsl/datasets/gdv.rb +499 -0
- data/lib/musa-dsl/datasets/pdv.rb +44 -0
- data/lib/musa-dsl/datasets.rb +5 -0
- data/lib/musa-dsl/generative/darwin.rb +145 -0
- data/lib/musa-dsl/generative/generative-grammar.rb +294 -0
- data/lib/musa-dsl/generative/markov.rb +78 -0
- data/lib/musa-dsl/generative/rules.rb +282 -0
- data/lib/musa-dsl/generative/variatio.rb +331 -0
- data/lib/musa-dsl/generative.rb +5 -0
- data/lib/musa-dsl/midi/midi-recorder.rb +83 -0
- data/lib/musa-dsl/midi/midi-voices.rb +274 -0
- data/lib/musa-dsl/midi.rb +2 -0
- data/lib/musa-dsl/music/chord-definition.rb +99 -0
- data/lib/musa-dsl/music/chord-definitions.rb +13 -0
- data/lib/musa-dsl/music/chords.rb +326 -0
- data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +204 -0
- data/lib/musa-dsl/music/scales.rb +584 -0
- data/lib/musa-dsl/music.rb +6 -0
- data/lib/musa-dsl/neuma/neuma.rb +181 -0
- data/lib/musa-dsl/neuma.rb +1 -0
- data/lib/musa-dsl/neumalang/neumalang.citrus +294 -0
- data/lib/musa-dsl/neumalang/neumalang.rb +179 -0
- data/lib/musa-dsl/neumalang.rb +3 -0
- data/lib/musa-dsl/repl/repl.rb +143 -0
- data/lib/musa-dsl/repl.rb +1 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +189 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +354 -0
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +382 -0
- data/lib/musa-dsl/sequencer/base-sequencer-public.rb +261 -0
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +94 -0
- data/lib/musa-dsl/sequencer/sequencer.rb +3 -0
- data/lib/musa-dsl/sequencer.rb +1 -0
- data/lib/musa-dsl/series/base-series.rb +245 -0
- data/lib/musa-dsl/series/hash-serie-splitter.rb +194 -0
- data/lib/musa-dsl/series/holder-serie.rb +87 -0
- data/lib/musa-dsl/series/main-serie-constructors.rb +726 -0
- data/lib/musa-dsl/series/main-serie-operations.rb +1151 -0
- data/lib/musa-dsl/series/proxy-serie.rb +69 -0
- data/lib/musa-dsl/series/queue-serie.rb +94 -0
- data/lib/musa-dsl/series/series.rb +8 -0
- data/lib/musa-dsl/series.rb +1 -0
- data/lib/musa-dsl/transport/clock.rb +36 -0
- data/lib/musa-dsl/transport/dummy-clock.rb +47 -0
- data/lib/musa-dsl/transport/external-tick-clock.rb +31 -0
- data/lib/musa-dsl/transport/input-midi-clock.rb +124 -0
- data/lib/musa-dsl/transport/timer-clock.rb +102 -0
- data/lib/musa-dsl/transport/timer.rb +40 -0
- data/lib/musa-dsl/transport/transport.rb +137 -0
- data/lib/musa-dsl/transport.rb +9 -0
- data/lib/musa-dsl.rb +17 -0
- data/musa-dsl.gemspec +17 -0
- metadata +174 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
class Musa::Sequencer
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegators :@sequencer, :raw_at, :tick, :on_debug_at, :on_block_error
|
7
|
+
def_delegators :@sequencer, :on_fast_forward, :ticks_per_bar, :round, :position=, :size, :event_handler, :empty?
|
8
|
+
|
9
|
+
def_delegators :@context, :position, :log
|
10
|
+
def_delegators :@context, :with, :now, :at, :wait, :play, :every, :move
|
11
|
+
def_delegators :@context, :everying, :playing, :moving
|
12
|
+
def_delegators :@context, :launch, :on
|
13
|
+
|
14
|
+
def initialize(beats_per_bar, ticks_per_beat, sequencer: nil, do_log: nil, &block)
|
15
|
+
@sequencer ||= Musa::BaseSequencer.new beats_per_bar, ticks_per_beat, do_log: do_log
|
16
|
+
@context = DSLContext.new @sequencer
|
17
|
+
|
18
|
+
with &block if block
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset
|
22
|
+
@sequencer.reset
|
23
|
+
end
|
24
|
+
|
25
|
+
class DSLContext
|
26
|
+
extend Forwardable
|
27
|
+
|
28
|
+
attr_reader :sequencer
|
29
|
+
|
30
|
+
def_delegators :@sequencer, :launch, :on,
|
31
|
+
:position, :everying, :playing, :moving,
|
32
|
+
:ticks_per_bar, :round, :log, :inspect
|
33
|
+
|
34
|
+
def initialize(sequencer)
|
35
|
+
@sequencer = sequencer
|
36
|
+
end
|
37
|
+
|
38
|
+
def with(*value_parameters, **key_parameters, &block)
|
39
|
+
block ||= proc {}
|
40
|
+
|
41
|
+
_as_context_run block, value_parameters, key_parameters
|
42
|
+
end
|
43
|
+
|
44
|
+
def now(*value_parameters, **key_parameters, &block)
|
45
|
+
block ||= proc {}
|
46
|
+
|
47
|
+
@sequencer.now *value_parameters, **key_parameters do |*value_args, **key_args|
|
48
|
+
_as_context_run block, value_args, key_args
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def at(*value_parameters, **key_parameters, &block)
|
53
|
+
block ||= proc {}
|
54
|
+
|
55
|
+
@sequencer.at *value_parameters, **key_parameters do |*value_args, **key_args|
|
56
|
+
_as_context_run block, value_args, key_args
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def wait(*value_parameters, **key_parameters, &block)
|
61
|
+
block ||= proc {}
|
62
|
+
|
63
|
+
@sequencer.wait *value_parameters, **key_parameters do | *values, **key_values |
|
64
|
+
_as_context_run block, values, key_values
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def play(*value_parameters, **key_parameters, &block)
|
69
|
+
block ||= proc {}
|
70
|
+
|
71
|
+
@sequencer.play *value_parameters, **key_parameters do |*value_args, **key_args|
|
72
|
+
_as_context_run block, value_args, key_args
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def every(*value_parameters, **key_parameters, &block)
|
77
|
+
block ||= proc {}
|
78
|
+
|
79
|
+
@sequencer.every *value_parameters, **key_parameters do |*value_args, **key_args|
|
80
|
+
_as_context_run block, value_args, KeyParametersProcedureBinder.new(block).apply(key_args)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def move(*value_parameters, **key_parameters, &block)
|
85
|
+
block ||= proc {}
|
86
|
+
|
87
|
+
@sequencer.move *value_parameters, **key_parameters do |*value_args, **key_args|
|
88
|
+
_as_context_run block, value_args, key_args
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private_constant :DSLContext
|
94
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'musa-dsl/sequencer/sequencer'
|
@@ -0,0 +1,245 @@
|
|
1
|
+
require 'musa-dsl/core-ext/duplicate'
|
2
|
+
require 'musa-dsl/generative/generative-grammar'
|
3
|
+
|
4
|
+
module Musa
|
5
|
+
module SerieOperations end
|
6
|
+
|
7
|
+
module SeriePrototyping
|
8
|
+
def prototype?
|
9
|
+
@is_instance ? false : true
|
10
|
+
end
|
11
|
+
|
12
|
+
def instance?
|
13
|
+
@is_instance ? true : false
|
14
|
+
end
|
15
|
+
|
16
|
+
def prototype
|
17
|
+
if @is_instance
|
18
|
+
@instance_of || (@instance_of = clone.tap(&:_prototype).mark_as_prototype!)
|
19
|
+
else
|
20
|
+
self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def _prototype
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
alias_method :p, :prototype
|
29
|
+
|
30
|
+
def mark_as_prototype!
|
31
|
+
@is_instance = nil
|
32
|
+
freeze
|
33
|
+
end
|
34
|
+
|
35
|
+
protected :_prototype, :mark_as_prototype!
|
36
|
+
|
37
|
+
def mark_regarding!(source)
|
38
|
+
if source.prototype?
|
39
|
+
mark_as_prototype!
|
40
|
+
else
|
41
|
+
mark_as_instance!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
protected :mark_regarding!
|
46
|
+
|
47
|
+
def instance
|
48
|
+
if @is_instance
|
49
|
+
self
|
50
|
+
else
|
51
|
+
clone(freeze: false).tap(&:_instance).mark_as_instance!(self)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method :i, :instance
|
56
|
+
|
57
|
+
def _instance
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def mark_as_instance!(prototype = nil)
|
62
|
+
@instance_of = prototype
|
63
|
+
@is_instance = true
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
protected :_instance, :mark_as_instance!
|
68
|
+
|
69
|
+
class PrototypingSerieError < RuntimeError
|
70
|
+
def initialize(message = nil)
|
71
|
+
message ||= 'This serie is a prototype serie: cannot be consumed. To consume the serie use an instance serie via .instance method'
|
72
|
+
super message
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module Serie
|
78
|
+
include SeriePrototyping
|
79
|
+
include SerieOperations
|
80
|
+
|
81
|
+
def restart
|
82
|
+
raise PrototypingSerieError unless @is_instance
|
83
|
+
|
84
|
+
@_have_peeked_next_value = false
|
85
|
+
@_peeked_next_value = nil
|
86
|
+
@_have_current_value = false
|
87
|
+
@_current_value = nil
|
88
|
+
|
89
|
+
_restart if respond_to? :_restart
|
90
|
+
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
def next_value
|
95
|
+
raise PrototypingSerieError unless @is_instance
|
96
|
+
|
97
|
+
unless @_have_current_value && @_current_value.nil?
|
98
|
+
if @_have_peeked_next_value
|
99
|
+
@_have_peeked_next_value = false
|
100
|
+
@_current_value = @_peeked_next_value
|
101
|
+
else
|
102
|
+
@_current_value = _next_value
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
propagate_value @_current_value
|
107
|
+
|
108
|
+
@_current_value
|
109
|
+
end
|
110
|
+
|
111
|
+
alias_method :v, :next_value
|
112
|
+
|
113
|
+
def peek_next_value
|
114
|
+
raise PrototypingSerieError unless @is_instance
|
115
|
+
|
116
|
+
unless @_have_peeked_next_value
|
117
|
+
@_have_peeked_next_value = true
|
118
|
+
@_peeked_next_value = _next_value
|
119
|
+
end
|
120
|
+
|
121
|
+
@_peeked_next_value
|
122
|
+
end
|
123
|
+
|
124
|
+
def current_value
|
125
|
+
raise PrototypingSerieError unless @is_instance
|
126
|
+
|
127
|
+
@_current_value
|
128
|
+
end
|
129
|
+
|
130
|
+
def infinite?
|
131
|
+
false
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_a(recursive: nil, duplicate: nil, restart: nil, dr: nil)
|
135
|
+
recursive ||= false
|
136
|
+
|
137
|
+
dr ||= instance?
|
138
|
+
|
139
|
+
duplicate = dr if duplicate.nil?
|
140
|
+
restart = dr if restart.nil?
|
141
|
+
|
142
|
+
raise 'Cannot convert to array an infinite serie' if infinite?
|
143
|
+
|
144
|
+
array = []
|
145
|
+
|
146
|
+
serie = instance
|
147
|
+
|
148
|
+
serie = serie.duplicate if duplicate
|
149
|
+
serie = serie.restart if restart
|
150
|
+
|
151
|
+
while value = serie.next_value
|
152
|
+
array << if recursive
|
153
|
+
process_for_to_a(value)
|
154
|
+
else
|
155
|
+
value
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
array
|
160
|
+
end
|
161
|
+
|
162
|
+
alias_method :a, :to_a
|
163
|
+
|
164
|
+
def to_node(**attributes)
|
165
|
+
Nodificator.to_node(self, **attributes)
|
166
|
+
end
|
167
|
+
|
168
|
+
alias_method :node, :to_node
|
169
|
+
|
170
|
+
class Nodificator
|
171
|
+
extend Musa::GenerativeGrammar
|
172
|
+
|
173
|
+
def self.to_node(serie, **attributes)
|
174
|
+
N(serie, **attributes)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
private_constant :Nodificator
|
179
|
+
|
180
|
+
protected
|
181
|
+
|
182
|
+
def propagate_value(value)
|
183
|
+
@_slaves.each { |s| s.push_next_value value } if @_slaves
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
def process_for_to_a(value)
|
189
|
+
case value
|
190
|
+
when Serie
|
191
|
+
value.to_a(recursive: true, restart: false, duplicate: false)
|
192
|
+
when Array
|
193
|
+
a = value.clone
|
194
|
+
a.collect! { |v| v.is_a?(Serie) ? v.to_a(recursive: true, restart: false, duplicate: false) : process_for_to_a(v) }
|
195
|
+
when Hash
|
196
|
+
h = value.clone
|
197
|
+
h.transform_values! { |v| v.is_a?(Serie) ? v.to_a(recursive: true, restart: false, duplicate: false) : process_for_to_a(v) }
|
198
|
+
else
|
199
|
+
value
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
class Slave
|
206
|
+
include Serie
|
207
|
+
|
208
|
+
attr_reader :master
|
209
|
+
|
210
|
+
def initialize(master)
|
211
|
+
@master = master
|
212
|
+
@next_value = []
|
213
|
+
end
|
214
|
+
|
215
|
+
def _restart
|
216
|
+
throw OperationNotAllowedError, "SlaveSerie #{self}: slave series cannot be restarted"
|
217
|
+
end
|
218
|
+
|
219
|
+
def next_value
|
220
|
+
value = @next_value.shift
|
221
|
+
|
222
|
+
raise "Warning: slave serie #{self} has lost sync with his master serie #{@master}" if value.nil? && !@master.peek_next_value.nil?
|
223
|
+
|
224
|
+
propagate_value value
|
225
|
+
|
226
|
+
value
|
227
|
+
end
|
228
|
+
|
229
|
+
def peek_next_value
|
230
|
+
value = @next_value.first
|
231
|
+
|
232
|
+
raise "Warning: slave serie #{self} has lost sync with his master serie #{@master}" if value.nil? && !@master.peek_next_value.nil?
|
233
|
+
|
234
|
+
value
|
235
|
+
end
|
236
|
+
|
237
|
+
def infinite?
|
238
|
+
@master.infinite?
|
239
|
+
end
|
240
|
+
|
241
|
+
def push_next_value(value)
|
242
|
+
@next_value << value
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module Musa
|
2
|
+
# TODO: adapt to series prototyping
|
3
|
+
# TODO: full test cases
|
4
|
+
|
5
|
+
module SerieOperations
|
6
|
+
def split(buffered: nil, master: nil)
|
7
|
+
buffered ||= false
|
8
|
+
|
9
|
+
return HashSplitter.new HashSplitter::KeyProxy.new(self) if master.nil? && !buffered
|
10
|
+
return HashSplitter.new HashSplitter::MasterSlaveKeyProxy.new(self, master) if !master.nil? && !buffered
|
11
|
+
return HashSplitter.new HashSplitter::BufferedKeyProxy.new(self) if buffered
|
12
|
+
end
|
13
|
+
|
14
|
+
class HashSplitter
|
15
|
+
def initialize(proxy)
|
16
|
+
@proxy = proxy
|
17
|
+
@series = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](key)
|
21
|
+
serie = if @series.key? key
|
22
|
+
@series[key]
|
23
|
+
else
|
24
|
+
@series[key] = Splitted.new(@proxy, key: key)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class KeyProxy
|
29
|
+
def initialize(hash_serie)
|
30
|
+
@serie = hash_serie.instance
|
31
|
+
@values = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def restart
|
35
|
+
@serie.restart
|
36
|
+
@values = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def next_value(key)
|
40
|
+
return nil unless @values
|
41
|
+
|
42
|
+
value = @values[key]
|
43
|
+
|
44
|
+
if value.nil?
|
45
|
+
before_values = @values.collect { |k, v| [k, v] unless v.nil? }.compact.to_h
|
46
|
+
|
47
|
+
@values = @serie.next_value
|
48
|
+
value = @values[key] if @values
|
49
|
+
|
50
|
+
warn "Warning: splitted serie #{@serie} values #{before_values} are being lost" if !value.nil? && !before_values.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
@values[key] = nil if @values
|
54
|
+
|
55
|
+
value
|
56
|
+
end
|
57
|
+
|
58
|
+
def peek_next_value(key)
|
59
|
+
value = @values[key]
|
60
|
+
|
61
|
+
if value.nil?
|
62
|
+
peek_values = @serie.peek_next_value
|
63
|
+
value = peek_values[key] if peek_values
|
64
|
+
end
|
65
|
+
|
66
|
+
value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class BufferedKeyProxy
|
71
|
+
def initialize(hash_serie)
|
72
|
+
@serie = hash_serie.instance
|
73
|
+
@values = {}
|
74
|
+
end
|
75
|
+
|
76
|
+
def restart
|
77
|
+
@serie.restart
|
78
|
+
@values = {}
|
79
|
+
end
|
80
|
+
|
81
|
+
def next_value(key)
|
82
|
+
value = nil
|
83
|
+
|
84
|
+
if @values[key].nil? || @values[key].empty?
|
85
|
+
hash_value = @serie.next_value
|
86
|
+
|
87
|
+
if hash_value
|
88
|
+
hash_value.each do |k, v|
|
89
|
+
@values[k] = [] if @values[k].nil?
|
90
|
+
@values[k] << v
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
value = @values[key].shift if @values[key]
|
96
|
+
|
97
|
+
value
|
98
|
+
end
|
99
|
+
|
100
|
+
def peek_next_value(key)
|
101
|
+
value = nil
|
102
|
+
|
103
|
+
if @values[key] && !@values[key].empty?
|
104
|
+
value = @values[key].first
|
105
|
+
else
|
106
|
+
peek_values = @serie.peek_next_value
|
107
|
+
value = peek_values[key] if peek_values
|
108
|
+
end
|
109
|
+
|
110
|
+
value
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class MasterSlaveKeyProxy
|
115
|
+
def initialize(hash_serie, master)
|
116
|
+
@serie = hash_serie.instance
|
117
|
+
@master = master
|
118
|
+
@values = {}
|
119
|
+
@values_counter = {}
|
120
|
+
end
|
121
|
+
|
122
|
+
def restart
|
123
|
+
@serie.restart
|
124
|
+
@values = {}
|
125
|
+
@values_counter = {}
|
126
|
+
end
|
127
|
+
|
128
|
+
def next_value(key)
|
129
|
+
return nil unless @values
|
130
|
+
|
131
|
+
value = @values[key]
|
132
|
+
|
133
|
+
if value.nil?
|
134
|
+
@values = @serie.next_value
|
135
|
+
|
136
|
+
value = @values[key] if @values
|
137
|
+
|
138
|
+
# warn "Info: splitted serie #{@serie} use count on next_value: #{@values_counter}"
|
139
|
+
@values_counter = {}
|
140
|
+
end
|
141
|
+
|
142
|
+
@values_counter[key] ||= 0
|
143
|
+
@values_counter[key] += 1
|
144
|
+
|
145
|
+
@values[key] = nil if key == @master && @values
|
146
|
+
|
147
|
+
value
|
148
|
+
end
|
149
|
+
|
150
|
+
def peek_next_value(key)
|
151
|
+
return nil unless @values
|
152
|
+
|
153
|
+
value = @values[key]
|
154
|
+
|
155
|
+
if value.nil?
|
156
|
+
peek_values = @serie.peek_next_value
|
157
|
+
value = peek_values[key] if peek_values
|
158
|
+
end
|
159
|
+
|
160
|
+
value
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class Splitted
|
165
|
+
include Serie
|
166
|
+
|
167
|
+
def initialize(proxy, key:)
|
168
|
+
@proxy = proxy
|
169
|
+
@key = key
|
170
|
+
|
171
|
+
mark_as_instance!
|
172
|
+
end
|
173
|
+
|
174
|
+
def _prototype
|
175
|
+
raise PrototypingSerieError, 'Cannot get prototype of a splitted serie'
|
176
|
+
end
|
177
|
+
|
178
|
+
def _restart
|
179
|
+
@proxy.restart
|
180
|
+
end
|
181
|
+
|
182
|
+
def next_value
|
183
|
+
@proxy.next_value(@key)
|
184
|
+
end
|
185
|
+
|
186
|
+
def peek_next_value
|
187
|
+
@proxy.peek_next_value(@key)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
private_constant :HashSplitter
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Musa
|
2
|
+
module Series
|
3
|
+
# TODO: adapt to series prototyping
|
4
|
+
|
5
|
+
def HOLDER(serie = nil)
|
6
|
+
Holder.new(serie)
|
7
|
+
end
|
8
|
+
|
9
|
+
class Holder
|
10
|
+
include Serie
|
11
|
+
|
12
|
+
attr_reader :hold, :next
|
13
|
+
|
14
|
+
def initialize(serie)
|
15
|
+
@hold = serie.instance if serie
|
16
|
+
@next = []
|
17
|
+
|
18
|
+
mark_as_instance!
|
19
|
+
end
|
20
|
+
|
21
|
+
def hold=(serie)
|
22
|
+
@hold = serie.instance
|
23
|
+
end
|
24
|
+
|
25
|
+
def <<(serie)
|
26
|
+
if @hold.nil?
|
27
|
+
@hold = serie.instance
|
28
|
+
else
|
29
|
+
@next << serie.instance
|
30
|
+
end
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def _prototype
|
36
|
+
raise PrototypingSerieError, 'Cannot get prototype of a proxy serie'
|
37
|
+
end
|
38
|
+
|
39
|
+
def restart
|
40
|
+
if @next.empty? && @hold
|
41
|
+
@hold.restart
|
42
|
+
else
|
43
|
+
@hold = @next.shift
|
44
|
+
end
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_value
|
50
|
+
@hold.current_value if @hold
|
51
|
+
end
|
52
|
+
|
53
|
+
def next_value
|
54
|
+
@hold.next_value if @hold
|
55
|
+
end
|
56
|
+
|
57
|
+
def peek_next_value
|
58
|
+
@hold.peek_next_value if @hold
|
59
|
+
end
|
60
|
+
|
61
|
+
def infinite?
|
62
|
+
@hold.infinite? if @hold
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def method_missing(method_name, *args, **key_args, &block)
|
68
|
+
if @hold && @hold.respond_to?(method_name)
|
69
|
+
@hold.send_nice method_name, *args, **key_args, &block
|
70
|
+
else
|
71
|
+
super
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def respond_to_missing?(method_name, include_private)
|
76
|
+
@hold && @hold.respond_to?(method_name, include_private) || super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module SerieOperations
|
82
|
+
# TODO add test case
|
83
|
+
def hold
|
84
|
+
Series::Holder.new self
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|