musa-dsl 0.22.3 → 0.23.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/lib/musa-dsl.rb +14 -8
  4. data/lib/musa-dsl/core-ext/deep-copy.rb +12 -1
  5. data/lib/musa-dsl/core-ext/inspect-nice.rb +1 -2
  6. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +13 -11
  7. data/lib/musa-dsl/datasets/p.rb +41 -16
  8. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +14 -12
  9. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +32 -6
  10. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +24 -10
  11. data/lib/musa-dsl/generative/backboner.rb +6 -11
  12. data/lib/musa-dsl/generative/generative-grammar.rb +1 -3
  13. data/lib/musa-dsl/generative/markov.rb +10 -6
  14. data/lib/musa-dsl/logger/logger.rb +6 -1
  15. data/lib/musa-dsl/matrix/matrix.rb +9 -7
  16. data/lib/musa-dsl/midi/midi-voices.rb +8 -7
  17. data/lib/musa-dsl/music/scales.rb +1 -1
  18. data/lib/musa-dsl/neumalang/neumalang.rb +1 -1
  19. data/lib/musa-dsl/neumas/array-to-neumas.rb +1 -1
  20. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +9 -4
  21. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +30 -129
  22. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +10 -24
  23. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +9 -9
  24. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +3 -5
  25. data/lib/musa-dsl/sequencer/base-sequencer.rb +14 -23
  26. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +9 -7
  27. data/lib/musa-dsl/sequencer/sequencer.rb +7 -0
  28. data/lib/musa-dsl/series/base-series.rb +293 -144
  29. data/lib/musa-dsl/series/buffer-serie.rb +237 -0
  30. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +136 -105
  31. data/lib/musa-dsl/series/main-serie-constructors.rb +251 -156
  32. data/lib/musa-dsl/series/main-serie-operations.rb +308 -303
  33. data/lib/musa-dsl/series/proxy-serie.rb +21 -41
  34. data/lib/musa-dsl/series/quantizer-serie.rb +44 -46
  35. data/lib/musa-dsl/series/queue-serie.rb +39 -43
  36. data/lib/musa-dsl/series/series-composer.rb +149 -0
  37. data/lib/musa-dsl/series/series.rb +6 -3
  38. data/lib/musa-dsl/series/timed-serie.rb +343 -0
  39. data/musa-dsl.gemspec +13 -3
  40. metadata +10 -11
  41. data/lib/musa-dsl/series/flattener-timed-serie.rb +0 -61
  42. data/lib/musa-dsl/series/holder-serie.rb +0 -87
  43. data/lib/musa-dsl/series/union-timed-series.rb +0 -109
@@ -1,6 +1,6 @@
1
1
  module Musa
2
2
  module Series
3
- module SerieOperations
3
+ module Operations
4
4
  def autorestart
5
5
  Autorestart.new self
6
6
  end
@@ -108,8 +108,12 @@ module Musa
108
108
 
109
109
  alias_method :eval, :with
110
110
 
111
- def map(&yield_block)
112
- ProcessWith.new self, &yield_block
111
+ def map(&block)
112
+ ProcessWith.new self, &block
113
+ end
114
+
115
+ def anticipate(&block)
116
+ Anticipate.new self, &block
113
117
  end
114
118
 
115
119
  ###
@@ -117,34 +121,36 @@ module Musa
117
121
  ###
118
122
 
119
123
  class ProcessWith
120
- include Musa::Extension::SmartProcBinder
121
- include Serie
122
-
123
- attr_reader :source, :on_restart, :block
124
- def with_sources; @sources; end
124
+ include Serie.with(source: true, sources: true, sources_as: :with_sources, smart_block: true)
125
125
 
126
126
  def initialize(serie, with_series = nil, on_restart = nil, &block)
127
- @source = serie
128
- @sources = with_series || {}
129
- @on_restart = on_restart
130
- @block = SmartProcBinder.new(block) if block_given?
127
+ self.source = serie
128
+ self.with_sources = with_series || {}
129
+ self.on_restart = on_restart
130
+ self.proc = block if block
131
+
132
+ init
133
+ end
131
134
 
132
- if @source.prototype?
133
- @sources = @sources.transform_values { |s| s.prototype }
135
+ def on_restart(&block)
136
+ if block
137
+ @on_restart = block
134
138
  else
135
- @sources = @sources.transform_values { |s| s.instance }
139
+ @on_restart
136
140
  end
141
+ end
137
142
 
138
- mark_regarding! @source
143
+ def on_restart=(block)
144
+ @on_restart = block
139
145
  end
140
146
 
141
- def _restart
147
+ private def _restart
142
148
  @source.restart
143
- @sources.values.each { |s| s.restart }
149
+ @sources.values.each(&:restart)
144
150
  @on_restart.call if @on_restart
145
151
  end
146
152
 
147
- def _next_value
153
+ private def _next_value
148
154
  main = @source.next_value
149
155
  others = @sources.transform_values { |v| v.next_value }
150
156
 
@@ -168,31 +174,50 @@ module Musa
168
174
 
169
175
  private_constant :ProcessWith
170
176
 
171
- class Switcher
172
- include Serie
177
+ class Anticipate
178
+ include Serie.with(source: true, block: true)
173
179
 
174
- attr_accessor :sources
175
- def selector; @source; end
180
+ def initialize(serie, &block)
181
+ self.source = serie
182
+ self.proc = block
176
183
 
177
- def initialize(selector, indexed_series, hash_series)
184
+ init
185
+ end
178
186
 
179
- @source = selector
180
- get = @source.prototype? ? :prototype : :instance
187
+ private def _restart
188
+ @source.restart
189
+ end
181
190
 
182
- if indexed_series && !indexed_series.empty?
183
- @sources = indexed_series.collect(&get)
184
- elsif hash_series && !hash_series.empty?
185
- @sources = hash_series.clone.transform_values(&get)
186
- end
191
+ private def _next_value
192
+ previous_value = @source.current_value
193
+ value = @source.next_value
194
+ peek_next_value = @source.peek_next_value
187
195
 
188
- if get == :_prototype!
189
- @sources.freeze
196
+ if value.nil?
197
+ nil
198
+ else
199
+ @block.call(previous_value, value, peek_next_value)
190
200
  end
201
+ end
191
202
 
192
- mark_regarding! @source
203
+ def infinite?
204
+ @source.infinite?
205
+ end
206
+ end
207
+
208
+ private_constant :Anticipate
209
+
210
+ class Switcher
211
+ include Serie.with(source: true, sources: true, sources_as: :options)
212
+
213
+ def initialize(selector, indexed_series, hash_series)
214
+ self.source = selector
215
+ self.options = indexed_series || hash_series
216
+
217
+ init
193
218
  end
194
219
 
195
- def _restart
220
+ private def _restart
196
221
  @source.restart
197
222
  if @sources.is_a? Array
198
223
  @sources.each(&:restart)
@@ -201,7 +226,7 @@ module Musa
201
226
  end
202
227
  end
203
228
 
204
- def _next_value
229
+ private def _next_value
205
230
  value = nil
206
231
 
207
232
  index_or_key = @source.next_value
@@ -219,46 +244,31 @@ module Musa
219
244
  private_constant :Switcher
220
245
 
221
246
  class MultiplexSelector
222
- include Serie
223
-
224
- def selector; @source; end
225
- attr_accessor :sources
247
+ include Serie.with(source: true, sources: true, sources_as: :options)
226
248
 
227
249
  def initialize(selector, indexed_series, hash_series)
228
- @source = selector
229
- get = @source.prototype? ? :prototype : :instance
230
-
231
- if indexed_series && !indexed_series.empty?
232
- @sources = indexed_series.collect(&get)
233
- elsif hash_series && !hash_series.empty?
234
- @sources = hash_series.clone.transform_values(&get)
235
- end
236
-
237
- _restart false
238
-
239
- if get == :_prototype!
240
- @sources.freeze
241
- end
250
+ self.source = selector
251
+ self.options = indexed_series || hash_series
242
252
 
243
- mark_regarding! @source
253
+ init
244
254
  end
245
255
 
246
- def _restart(restart_sources = true)
256
+ private def _init
247
257
  @current_value = nil
258
+ @first = true
259
+ end
248
260
 
249
- if restart_sources
250
- @source.restart
251
- if @sources.is_a? Array
252
- @sources.each(&:restart)
253
- elsif @sources.is_a? Hash
254
- @sources.each { |_key, serie| serie.restart }
255
- end
256
- end
261
+ private def _restart
262
+ @source.restart
257
263
 
258
- @first = true
264
+ if @sources.is_a? Array
265
+ @sources.each(&:restart)
266
+ elsif @sources.is_a? Hash
267
+ @sources.values.each(&:restart)
268
+ end
259
269
  end
260
270
 
261
- def _next_value
271
+ private def _next_value
262
272
  @current_value =
263
273
  if @first || !@current_value.nil?
264
274
  @first = false
@@ -278,34 +288,21 @@ module Musa
278
288
  private_constant :MultiplexSelector
279
289
 
280
290
  class SwitchFullSerie
281
- include Serie
282
-
283
- def selector; @source; end
284
- attr_accessor :sources
291
+ include Serie.with(source: true, sources: true, sources_as: :options)
285
292
 
286
293
  def initialize(selector, indexed_series, hash_series)
287
- @source = selector
288
- get = @source.prototype? ? :prototype : :instance
289
-
290
- if indexed_series && !indexed_series.empty?
291
- @sources = indexed_series.collect(&get)
292
- elsif hash_series && !hash_series.empty?
293
- @sources = hash_series.clone.transform_values(&get)
294
- end
295
-
296
- if get == :_prototype!
297
- @sources.freeze
298
- end
294
+ self.source = selector
295
+ self.options = indexed_series || hash_series
299
296
 
300
- mark_regarding! @source
297
+ init
301
298
  end
302
299
 
303
- def _restart
300
+ private def _restart
304
301
  @source.restart
305
302
  @sources.each(&:restart)
306
303
  end
307
304
 
308
- def _next_value
305
+ private def _next_value
309
306
  value = nil
310
307
 
311
308
  value = @sources[@index_or_key].next_value unless @index_or_key.nil?
@@ -327,21 +324,18 @@ module Musa
327
324
  private_constant :SwitchFullSerie
328
325
 
329
326
  class InfiniteRepeater
330
- include Serie
331
-
332
- attr_reader :source
327
+ include Serie.with(source: true)
333
328
 
334
329
  def initialize(serie)
335
- @source = serie
336
-
337
- mark_regarding! @source
330
+ self.source = serie
331
+ init
338
332
  end
339
333
 
340
- def _restart
334
+ private def _restart
341
335
  @source.restart
342
336
  end
343
337
 
344
- def _next_value
338
+ private def _next_value
345
339
  value = @source.next_value
346
340
 
347
341
  if value.nil?
@@ -360,38 +354,47 @@ module Musa
360
354
  private_constant :InfiniteRepeater
361
355
 
362
356
  class Repeater
363
- include Serie
364
-
365
- attr_reader :source, :times, :condition
357
+ include Serie.with(source: true)
366
358
 
367
359
  def initialize(serie, times = nil, &condition)
368
- @source = serie
360
+ self.source = serie
361
+ self.times = times
362
+ self.condition = condition
369
363
 
370
- @times = times
371
- @external_condition = condition
364
+ init
365
+ end
372
366
 
373
- _restart false
374
- @condition = calculate_condition
367
+ attr_reader :times
375
368
 
376
- mark_regarding! @source
369
+ def times=(value)
370
+ @times = value
371
+ calculate_condition
377
372
  end
378
373
 
379
- def _prototype!
380
- super
381
- @condition = calculate_condition
374
+ def condition(&block)
375
+ if block
376
+ @external_condition = block
377
+ calculate_condition
378
+ else
379
+ @external_condition
380
+ end
382
381
  end
383
382
 
384
- def _instance!
385
- super
386
- @condition = calculate_condition
383
+ def condition=(block)
384
+ @external_condition = block
385
+ calculate_condition
387
386
  end
388
387
 
389
- def _restart(restart_sources = true)
388
+ private def _init
390
389
  @count = 0
391
- @source.restart if restart_sources
390
+ calculate_condition
391
+ end
392
+
393
+ private def _restart
394
+ @source.restart
392
395
  end
393
396
 
394
- def _next_value
397
+ private def _next_value
395
398
  value = @source.next_value
396
399
 
397
400
  if value.nil?
@@ -411,38 +414,39 @@ module Musa
411
414
  end
412
415
 
413
416
  private def calculate_condition
414
- if @external_condition
415
- @external_condition
416
- elsif @times
417
- proc { @count < @times }
418
- else
419
- proc { false }
420
- end
417
+ @condition = if @external_condition
418
+ @external_condition
419
+ elsif @times
420
+ proc { @count < @times }
421
+ else
422
+ proc { false }
423
+ end
421
424
  end
422
425
  end
423
426
 
424
427
  private_constant :Repeater
425
428
 
426
429
  class LengthLimiter
427
- include Serie
428
-
429
- attr_reader :source, :length
430
+ include Serie.with(source: true)
430
431
 
431
432
  def initialize(serie, length)
432
- @source = serie
433
- @length = length
434
-
435
- _restart false
433
+ self.source = serie
434
+ self.length = length
436
435
 
437
- mark_regarding! @source
436
+ init
438
437
  end
439
438
 
440
- def _restart(restart_sources = true)
439
+ attr_accessor :length
440
+
441
+ private def _init
441
442
  @position = 0
442
- @source.restart if restart_sources
443
443
  end
444
444
 
445
- def _next_value
445
+ private def _restart
446
+ @source.restart
447
+ end
448
+
449
+ private def _next_value
446
450
  if @position < @length
447
451
  @position += 1
448
452
  @source.next_value
@@ -457,25 +461,26 @@ module Musa
457
461
  private_constant :LengthLimiter
458
462
 
459
463
  class Skipper
460
- include Serie
461
-
462
- attr_reader :source, :length
464
+ include Serie.with(source: true)
463
465
 
464
466
  def initialize(serie, length)
465
- @source = serie
466
- @length = length
467
+ self.source = serie
468
+ self.length = length
467
469
 
468
- _restart false
469
-
470
- mark_regarding! @source
470
+ init
471
471
  end
472
472
 
473
- def _restart(restart_sources = true)
474
- @source.restart if restart_sources
473
+ attr_accessor :length
474
+
475
+ private def _init
475
476
  @first = true
476
477
  end
477
478
 
478
- def _next_value
479
+ private def _restart
480
+ @source.restart
481
+ end
482
+
483
+ private def _next_value
479
484
  @length.times { @source.next_value } if @first
480
485
  @first = nil
481
486
 
@@ -490,40 +495,25 @@ module Musa
490
495
  private_constant :Skipper
491
496
 
492
497
  class Flattener
493
- include Serie
494
-
495
- attr_reader :source
498
+ include Serie.base
496
499
 
497
500
  def initialize(serie)
498
501
  @source = serie
499
-
500
- _restart false
501
-
502
502
  mark_regarding! @source
503
+ init
503
504
  end
504
505
 
505
- def _prototype!
506
- super
507
- _restart false
508
- end
509
-
510
- def _instance!
511
- super
512
- _restart false
506
+ private def _init
507
+ @stack = [@source]
508
+ @restart_each_serie = false
513
509
  end
514
510
 
515
- def _restart(restart_sources = true)
516
- if restart_sources
517
- @source.restart
518
- @restart_each_serie = true
519
- else
520
- @restart_each_serie = false
521
- end
522
-
523
- @stack = [@source]
511
+ private def _restart
512
+ @source.restart
513
+ @restart_each_serie = true
524
514
  end
525
515
 
526
- def _next_value
516
+ private def _next_value
527
517
  if @stack.last
528
518
  value = @stack.last.next_value
529
519
 
@@ -550,26 +540,24 @@ module Musa
550
540
  private_constant :Flattener
551
541
 
552
542
  class MergeSerieOfSeries
553
- include Serie
554
-
555
- attr_reader :source
543
+ include Serie.with(source: true)
556
544
 
557
545
  def initialize(serie)
558
- @source = serie
559
- _restart false
560
-
561
- mark_regarding! @source
546
+ self.source = serie
547
+ init
562
548
  end
563
549
 
564
- def _restart(restart_sources = true)
565
- if restart_sources
566
- @source.restart
567
- @restart_each_serie = true
568
- end
550
+ private def _init
569
551
  @current_serie = nil
552
+ @restart_each_serie = false
553
+ end
554
+
555
+ private def _restart
556
+ @source.restart
557
+ @restart_each_serie = true
570
558
  end
571
559
 
572
- def _next_value
560
+ private def _next_value
573
561
  value = nil
574
562
 
575
563
  restart_current_serie_if_needed = false
@@ -606,27 +594,28 @@ module Musa
606
594
  private_constant :MergeSerieOfSeries
607
595
 
608
596
  class Processor
609
- include Musa::Extension::SmartProcBinder
610
- include Serie
611
-
612
- attr_reader :source
597
+ include Serie.with(source: true, smart_block: true)
613
598
 
614
599
  def initialize(serie, parameters, &processor)
615
- @source = serie
616
- @parameters = parameters
617
- @processor = SmartProcBinder.new(processor)
600
+ self.source = serie
618
601
 
619
- _restart false
602
+ self.parameters = parameters
603
+ self.proc = processor if processor
620
604
 
621
- mark_regarding! @source
605
+ init
622
606
  end
623
607
 
624
- def _restart(restart_source = true)
625
- @source.restart if restart_source
608
+ attr_accessor :parameters
609
+
610
+ private def _init
626
611
  @pending_values = []
627
612
  end
628
613
 
629
- def _next_value
614
+ private def _restart
615
+ @source.restart
616
+ end
617
+
618
+ private def _next_value
630
619
  if @pending_values.empty?
631
620
 
632
621
  v = @source.next_value
@@ -634,7 +623,7 @@ module Musa
634
623
  if v.nil?
635
624
  nil
636
625
  else
637
- value = @processor.call(v, **@parameters)
626
+ value = @block.call(v, **@parameters)
638
627
 
639
628
  if value.is_a?(Array)
640
629
  @pending_values = value
@@ -660,23 +649,22 @@ module Musa
660
649
  end
661
650
 
662
651
  class Autorestart
663
- include Serie
664
-
665
- attr_reader :source
652
+ include Serie.with(source: true)
666
653
 
667
654
  def initialize(serie)
668
- @source = serie
655
+ self.source = serie
656
+ init
657
+ end
669
658
 
659
+ private def _init
670
660
  @restart_on_next = false
671
-
672
- mark_regarding! @source
673
661
  end
674
662
 
675
- def _restart
663
+ private def _restart
676
664
  @source.restart
677
665
  end
678
666
 
679
- def _next_value
667
+ private def _next_value
680
668
  if @restart_on_next
681
669
  @source.restart
682
670
  @restart_on_next = false
@@ -693,53 +681,59 @@ module Musa
693
681
  private_constant :Autorestart
694
682
 
695
683
  class Cutter
696
- include Serie
697
-
698
- attr_reader :source, :length
684
+ include Serie.with(source: true)
699
685
 
700
686
  def initialize(serie, length)
701
- @source = serie
702
- @length = length
687
+ self.source = serie
688
+ self.length = length
689
+ init
690
+ end
703
691
 
704
- mark_regarding! @source
692
+ def source=(serie)
693
+ super
694
+ @previous&.source = serie
705
695
  end
706
696
 
707
- def _restart
708
- @source.restart
697
+ attr_reader :length
698
+
699
+ def length=(value)
700
+ @length = value
701
+ @previous&.length = value
709
702
  end
710
703
 
711
- def _next_value
712
- @previous.materialize if @previous
704
+ private def _restart
705
+ @source.restart
706
+ end
713
707
 
708
+ private def _next_value
709
+ @previous&.materialize
714
710
  @previous = CutSerie.new @source, @length if @source.peek_next_value
715
711
  end
716
712
 
717
- private
718
-
719
713
  class CutSerie
720
- include Serie
714
+ include Serie.with(source: true)
721
715
 
722
716
  def initialize(serie, length)
723
- @source = serie
724
- @length = length
717
+ self.source = serie.instance
718
+ self.length = length
725
719
 
726
720
  @values = []
727
- _restart
728
-
729
- mark_as_instance!
721
+ init
730
722
  end
731
723
 
724
+ attr_accessor :length
725
+
732
726
  def _prototype!
733
727
  # TODO review why cannot get prototype of a cut serie
734
- raise PrototypingSerieError, 'Cannot get prototype of a cut serie'
728
+ raise PrototypingError, 'Cannot get prototype of a cut serie'
735
729
  end
736
730
 
737
- def _restart
731
+ private def _init
738
732
  @count = 0
739
733
  end
740
734
 
741
- def _next_value
742
- value ||= @values[@count]
735
+ private def _next_value
736
+ value = @values[@count]
743
737
  value ||= @values[@count] = @source.next_value if @count < @length
744
738
 
745
739
  @count += 1
@@ -751,30 +745,29 @@ module Musa
751
745
  (@values.size..@length - 1).each { |i| @values[i] = @source.next_value }
752
746
  end
753
747
  end
748
+
749
+ private_constant :CutSerie
754
750
  end
755
751
 
756
752
  private_constant :Cutter
757
753
 
758
754
  class Locker
759
- include Serie
760
-
761
- attr_reader :source
755
+ include Serie.with(source: true)
762
756
 
763
757
  def initialize(serie)
764
- @source = serie
758
+ self.source = serie
759
+
765
760
  @values = []
766
761
  @first_round = true
767
762
 
768
- _restart
769
-
770
- mark_regarding! @source
763
+ init
771
764
  end
772
765
 
773
- def _restart
766
+ private def _init
774
767
  @index = 0
775
768
  end
776
769
 
777
- def _next_value
770
+ private def _next_value
778
771
  if @first_round
779
772
  value = @source.next_value
780
773
 
@@ -796,34 +789,33 @@ module Musa
796
789
  private_constant :Locker
797
790
 
798
791
  class Reverser
799
- include Serie
800
-
801
- attr_reader :source
792
+ include Serie.with(source: true)
802
793
 
803
794
  def initialize(serie)
804
- @source = serie
805
- _restart false, false
806
-
807
- mark_regarding! @source
795
+ self.source = serie
796
+ init
808
797
  end
809
798
 
810
- def _instance!
799
+ def source=(serie)
800
+ raise ArgumentError, "A serie to reverse can't be infinite" if serie.infinite?
811
801
  super
812
- _restart false, true
802
+ init
813
803
  end
814
804
 
815
- def _restart(restart_sources = true, get_reversed = true)
816
- @source.restart if restart_sources
817
- @reversed = FromArray.new(next_values_array_of(@source).reverse).instance if get_reversed
805
+ private def _init
806
+ @reversed = nil
818
807
  end
819
808
 
820
- def _next_value
821
- @reversed.next_value
809
+ private def _restart
810
+ @source.restart
822
811
  end
823
812
 
824
- private
813
+ private def _next_value
814
+ @reversed ||= Constructors.S(*next_values_array_of(@source).reverse).instance
815
+ @reversed.next_value
816
+ end
825
817
 
826
- def next_values_array_of(serie)
818
+ private def next_values_array_of(serie)
827
819
  array = []
828
820
 
829
821
  until (value = serie.next_value).nil?
@@ -837,25 +829,27 @@ module Musa
837
829
  private_constant :Reverser
838
830
 
839
831
  class Randomizer
840
- include Serie
841
-
842
- attr_reader :source, :random
832
+ include Serie.with(source: true)
843
833
 
844
834
  def initialize(serie, random)
845
- @source = serie
846
- @random = random
835
+ self.source = serie
836
+ self.random = random
847
837
 
848
- _restart false
838
+ init
839
+ end
849
840
 
850
- mark_regarding! @source
841
+ attr_accessor :random
842
+
843
+ private def _init
844
+ @values = @source.to_a(duplicate: false, restart: false)
851
845
  end
852
846
 
853
- def _restart(restart_sources = true)
854
- @source.restart if restart_sources
855
- @values = @source.to_a
847
+ private def _restart
848
+ @source.restart
849
+ @values = @source.to_a(duplicate: false, restart: false)
856
850
  end
857
851
 
858
- def _next_value
852
+ private def _next_value
859
853
  if !@values.empty?
860
854
  position = @random.rand(0...@values.size)
861
855
  value = @values[position]
@@ -872,30 +866,46 @@ module Musa
872
866
  private_constant :Randomizer
873
867
 
874
868
  class Shifter
875
- include Serie
876
-
877
- attr_reader :source, :shift
869
+ include Serie.with(source: true)
878
870
 
879
871
  def initialize(serie, shift)
880
- raise ArgumentError, "cannot shift to right an infinite serie #{serie}" if shift > 0 && serie.infinite?
881
- raise ArgumentError, 'cannot shift to right: function not yet implemented' if shift > 0
872
+ self.source = serie
873
+ self.shift = shift
882
874
 
883
- @source = serie
884
- @shift = shift
885
-
886
- _restart false
875
+ init
876
+ end
887
877
 
888
- mark_regarding! @source
878
+ def source=(serie)
879
+ raise ArgumentError, "cannot shift to right an infinite serie" if @shift > 0 && serie.infinite?
880
+ super
881
+ # should _restart(false) ??? if so, we lost the shifted values of the previous serie; if not we don't shift the new serie values
882
+ # I think it's better to not _restart unless it's explicitly called by the caller
889
883
  end
890
884
 
891
- def _restart(restart_sources = true)
892
- @source.restart if restart_sources
885
+ attr_reader :shift
886
+
887
+ def shift=(value)
888
+ raise ArgumentError, "cannot shift to right an infinite serie" if value > 0 && @source&.infinite?
889
+ raise NotImplementedError, 'cannot shift to right: function not yet implemented' if value > 0
893
890
 
891
+ @shift = value
892
+ end
893
+
894
+ private def _init
894
895
  @shifted = []
895
- @shift.abs.times { @shifted << @source.next_value } if @shift < 0
896
+ @first = true
896
897
  end
897
898
 
898
- def _next_value
899
+ private def _restart
900
+ @source.restart
901
+ end
902
+
903
+ private def _next_value
904
+ if @first
905
+ @shift.abs.times { @shifted << @source.next_value } if @shift < 0
906
+ @first = false
907
+ end
908
+
899
909
  value = @source.next_value
900
910
  return value unless value.nil?
901
911
 
@@ -906,28 +916,28 @@ module Musa
906
916
  private_constant :Shifter
907
917
 
908
918
  class Remover
909
- include Serie
910
-
911
- attr_reader :source
919
+ include Serie.with(source: true, block: true)
912
920
 
913
921
  def initialize(serie, &block)
914
- @source = serie
915
- @block = block
916
- @history = []
922
+ self.source = serie
923
+ self.proc = block
917
924
 
918
- _restart false
925
+ @history = []
919
926
 
920
- mark_regarding! @source
927
+ init
921
928
  end
922
929
 
923
- def _restart(restart_sources = true)
924
- @source.restart if restart_sources
930
+ private def _init
925
931
  @history.clear
926
932
  end
927
933
 
928
- def _next_value
934
+ private def _restart
935
+ @source.restart
936
+ end
937
+
938
+ private def _next_value
929
939
  if value = @source.next_value
930
- while @block.call(value, @history)
940
+ while value && @block.call(value, @history)
931
941
  @history << value
932
942
  value = @source.next_value
933
943
  end
@@ -940,24 +950,20 @@ module Musa
940
950
  private_constant :Remover
941
951
 
942
952
  class Selector
943
- include Serie
944
-
945
- attr_reader :source
953
+ include Serie.with(source: true, block: true)
946
954
 
947
955
  def initialize(serie, &block)
948
- @source = serie
949
- @block = block
956
+ self.source = serie
957
+ self.proc = block
950
958
 
951
- _restart false
952
-
953
- mark_regarding! @source
959
+ init
954
960
  end
955
961
 
956
- def _restart(restart_sources = true)
957
- @source.restart if restart_sources
962
+ private def _restart
963
+ @source.restart
958
964
  end
959
965
 
960
- def _next_value
966
+ private def _next_value
961
967
  value = @source.next_value
962
968
  until value.nil? || @block.call(value)
963
969
  value = @source.next_value
@@ -969,23 +975,22 @@ module Musa
969
975
  private_constant :Selector
970
976
 
971
977
  class HashFromSeriesArray
972
- include Serie
973
-
974
- attr_reader :source, :keys
978
+ include Serie.with(source: true)
975
979
 
976
980
  def initialize(serie, keys)
977
- @source = serie
978
- @keys = keys
979
- _restart false
981
+ self.source = serie
982
+ self.keys = keys
980
983
 
981
- mark_regarding! @source
984
+ init
982
985
  end
983
986
 
984
- def _restart(restart_sources = true)
985
- @source.restart if restart_sources
987
+ attr_accessor :keys
988
+
989
+ private def _restart
990
+ @source.restart
986
991
  end
987
992
 
988
- def _next_value
993
+ private def _next_value
989
994
  array = @source.next_value
990
995
 
991
996
  return nil unless array