musa-dsl 0.22.3 → 0.23.1

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.
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