musa-dsl 0.14.16
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 +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
|