musa-dsl 0.22.6 → 0.23.0
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 +4 -4
- data/Gemfile +3 -1
- data/lib/musa-dsl.rb +14 -8
- data/lib/musa-dsl/core-ext/deep-copy.rb +12 -1
- data/lib/musa-dsl/core-ext/smart-proc-binder.rb +13 -11
- data/lib/musa-dsl/datasets/p.rb +14 -10
- data/lib/musa-dsl/generative/backboner.rb +6 -11
- data/lib/musa-dsl/generative/generative-grammar.rb +1 -3
- data/lib/musa-dsl/generative/markov.rb +10 -6
- data/lib/musa-dsl/neumalang/neumalang.rb +1 -1
- data/lib/musa-dsl/neumas/array-to-neumas.rb +1 -1
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +2 -2
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +6 -6
- data/lib/musa-dsl/series/base-series.rb +293 -144
- data/lib/musa-dsl/series/buffer-serie.rb +237 -0
- data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +128 -129
- data/lib/musa-dsl/series/main-serie-constructors.rb +247 -154
- data/lib/musa-dsl/series/main-serie-operations.rb +281 -316
- data/lib/musa-dsl/series/proxy-serie.rb +21 -41
- data/lib/musa-dsl/series/quantizer-serie.rb +38 -38
- data/lib/musa-dsl/series/queue-serie.rb +39 -43
- data/lib/musa-dsl/series/series-composer.rb +149 -0
- data/lib/musa-dsl/series/series.rb +5 -1
- data/lib/musa-dsl/series/timed-serie.rb +106 -119
- data/musa-dsl.gemspec +12 -2
- metadata +7 -7
- data/lib/musa-dsl/series/holder-serie.rb +0 -87
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4a1fb5e2fc74d27266ed15f6edcc335c61b580f3c8fa83fd2c425066230a42b
|
4
|
+
data.tar.gz: 5778babbeeab02ea23d511369946990209929b284e0025f65a67602a1c8fa504
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33c4b5ab4b14c941ed36ede8919961f0c9bc4d0ec6acf34f2b02f297483c17dfb2e23d270118143c7199f2e073cf001591ad393cae09cbc41e75ac20d8f82c7a
|
7
|
+
data.tar.gz: 4ffcea9a0a82bd7f97ef659ba0a3b03d9b319980df8987986ee0f63eb694d144fc03d0299b3a87665b78a52fafb5d9b840cf10e7461f36cbfe58b30ce4712f22
|
data/Gemfile
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
+
gem 'logger', '~> 1.4', '>= 1.4.3'
|
4
|
+
|
3
5
|
group :neuma do
|
4
6
|
gem 'citrus', '~> 3.0.0'
|
5
7
|
end
|
@@ -10,8 +12,8 @@ group :transport do
|
|
10
12
|
end
|
11
13
|
|
12
14
|
group :test do
|
13
|
-
gem 'rspec', '~> 3.0'
|
14
15
|
gem 'descriptive-statistics'
|
16
|
+
gem 'rspec', '~> 3.0'
|
15
17
|
end
|
16
18
|
|
17
19
|
group :documentation do
|
data/lib/musa-dsl.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Musa
|
2
|
-
VERSION = '0.
|
2
|
+
VERSION = '0.23.0'
|
3
3
|
end
|
4
4
|
|
5
5
|
require_relative 'musa-dsl/core-ext'
|
@@ -27,21 +27,30 @@ require_relative 'musa-dsl/music'
|
|
27
27
|
require_relative 'musa-dsl/generative'
|
28
28
|
|
29
29
|
module Musa::All
|
30
|
+
# Core
|
31
|
+
#
|
30
32
|
include Musa::Logger
|
31
33
|
|
32
34
|
include Musa::Clock
|
33
35
|
include Musa::Transport
|
34
36
|
include Musa::Sequencer
|
35
37
|
|
36
|
-
include Musa::
|
37
|
-
include Musa::Chords
|
38
|
+
include Musa::Series
|
38
39
|
include Musa::Datasets
|
39
40
|
|
40
41
|
include Musa::Neumalang
|
41
42
|
include Musa::Neumas
|
42
|
-
include Musa::Matrix
|
43
43
|
|
44
|
-
include Musa::
|
44
|
+
include Musa::Transcription
|
45
|
+
|
46
|
+
include Musa::REPL
|
47
|
+
|
48
|
+
# Extensions: ojo, el nombre extensions ya se usa para algunos paquetes de core-ext que funcionan con Refinements
|
49
|
+
#
|
50
|
+
include Musa::Scales
|
51
|
+
include Musa::Chords
|
52
|
+
|
53
|
+
include Musa::Matrix
|
45
54
|
|
46
55
|
include Musa::Darwin
|
47
56
|
include Musa::Markov
|
@@ -53,8 +62,5 @@ module Musa::All
|
|
53
62
|
|
54
63
|
include Musa::MusicXML
|
55
64
|
|
56
|
-
include Musa::Transcription
|
57
65
|
include Musa::Transcriptors
|
58
|
-
|
59
|
-
include Musa::REPL
|
60
66
|
end
|
@@ -118,7 +118,18 @@ module Musa
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def deep_copy_proc(register, object, method, freeze)
|
121
|
-
|
121
|
+
if (receiver_dup = registered(object.binding.receiver, register))
|
122
|
+
register(register,
|
123
|
+
object,
|
124
|
+
proc do |*args, **kargs|
|
125
|
+
# when the receiver of the proc is also a duplicated object
|
126
|
+
# the new copy of the proc should be the new object, not the original one.
|
127
|
+
#
|
128
|
+
receiver_dup.instance_exec(object, *args, **kargs, &object)
|
129
|
+
end)
|
130
|
+
else
|
131
|
+
register(register, object, object.dup)
|
132
|
+
end
|
122
133
|
end
|
123
134
|
|
124
135
|
def deep_copy_instance_variables(register, object, duplication, method, freeze)
|
@@ -2,8 +2,8 @@ module Musa
|
|
2
2
|
module Extension
|
3
3
|
module SmartProcBinder
|
4
4
|
class SmartProcBinder
|
5
|
-
def initialize(
|
6
|
-
@
|
5
|
+
def initialize(block, on_rescue: nil)
|
6
|
+
@block = block
|
7
7
|
@on_rescue = on_rescue
|
8
8
|
|
9
9
|
@key_parameters = {}
|
@@ -12,7 +12,7 @@ module Musa
|
|
12
12
|
@value_parameters_count = 0
|
13
13
|
@has_value_rest = false
|
14
14
|
|
15
|
-
|
15
|
+
block.parameters.each do |parameter|
|
16
16
|
@key_parameters[parameter[1]] = nil if parameter[0] == :key || parameter[0] == :keyreq
|
17
17
|
@has_key_rest = true if parameter[0] == :keyrest
|
18
18
|
|
@@ -21,8 +21,12 @@ module Musa
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
def proc
|
25
|
+
@block
|
26
|
+
end
|
27
|
+
|
24
28
|
def parameters
|
25
|
-
@
|
29
|
+
@block.parameters
|
26
30
|
end
|
27
31
|
|
28
32
|
def call(*value_parameters, **key_parameters)
|
@@ -41,26 +45,24 @@ module Musa
|
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
44
|
-
def __call(value_parameters, key_parameters)
|
48
|
+
private def __call(value_parameters, key_parameters)
|
45
49
|
effective_value_parameters, effective_key_parameters = apply(*value_parameters, **key_parameters)
|
46
50
|
|
47
51
|
if effective_key_parameters.empty?
|
48
52
|
if effective_value_parameters.empty?
|
49
|
-
@
|
53
|
+
@block.call
|
50
54
|
else
|
51
|
-
@
|
55
|
+
@block.call *effective_value_parameters
|
52
56
|
end
|
53
57
|
else
|
54
58
|
if effective_value_parameters.empty?
|
55
|
-
@
|
59
|
+
@block.call **effective_key_parameters
|
56
60
|
else
|
57
|
-
@
|
61
|
+
@block.call *effective_value_parameters, **effective_key_parameters
|
58
62
|
end
|
59
63
|
end
|
60
64
|
end
|
61
65
|
|
62
|
-
private :__call
|
63
|
-
|
64
66
|
def key?(key)
|
65
67
|
@has_key_rest || @key_parameters.include?(key)
|
66
68
|
end
|
data/lib/musa-dsl/datasets/p.rb
CHANGED
@@ -13,7 +13,7 @@ module Musa::Datasets
|
|
13
13
|
|
14
14
|
# TODO if instead of using clone (needed because of p.shift) we use index counter the P elements would be evaluated on the last moment
|
15
15
|
|
16
|
-
Musa::Series::E(clone, base_duration) do |p, base_duration|
|
16
|
+
Musa::Series::Constructors.E(clone, base_duration) do |p, base_duration|
|
17
17
|
(p.size >= 3) ?
|
18
18
|
{ from: p.shift,
|
19
19
|
duration: p.shift * base_duration,
|
@@ -46,30 +46,34 @@ module Musa::Datasets
|
|
46
46
|
end
|
47
47
|
|
48
48
|
class PtoTimedSerie
|
49
|
-
include Musa::Series::Serie
|
50
|
-
|
51
|
-
attr_reader :origin
|
49
|
+
include Musa::Series::Serie.base
|
52
50
|
|
53
51
|
def initialize(origin, base_duration, time_start)
|
54
52
|
@origin = origin
|
55
53
|
@base_duration = base_duration
|
56
54
|
@time_start = time_start
|
57
55
|
|
58
|
-
|
56
|
+
init
|
59
57
|
|
60
58
|
mark_as_prototype!
|
61
59
|
end
|
62
60
|
|
63
|
-
|
64
|
-
|
61
|
+
attr_accessor :origin
|
62
|
+
attr_accessor :base_duration
|
63
|
+
attr_accessor :time_start
|
64
|
+
|
65
|
+
private def _init
|
66
|
+
@index = 0
|
65
67
|
@time = @time_start
|
66
68
|
end
|
67
69
|
|
68
|
-
def _next_value
|
69
|
-
if value = @
|
70
|
+
private def _next_value
|
71
|
+
if value = @origin[@index]
|
72
|
+
@index += 1
|
70
73
|
r = { time: @time, value: value }.extend(AbsTimed)
|
71
74
|
|
72
|
-
delta_time = @
|
75
|
+
delta_time = @origin[@index]
|
76
|
+
@index += 1
|
73
77
|
@time += delta_time * @base_duration if delta_time
|
74
78
|
|
75
79
|
r
|
@@ -3,13 +3,8 @@ require_relative '../core-ext/with'
|
|
3
3
|
|
4
4
|
using Musa::Extension::Arrayfy
|
5
5
|
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# crear rama tb debe recibir la serie de la history -> ya lo hace
|
9
|
-
# crear rama puede repetirse (hasta terminar según ended_when) -> no
|
10
|
-
#
|
11
|
-
# hacer que pueda funcionar en tiempo real? le vas suministrando seeds y le vas diciendo qué opción has elegido (p.ej. para hacer un armonizador en tiempo real)
|
12
|
-
# esto mismo sería aplicable en otros generadores? variatio/darwin? generative-grammar? markov?
|
6
|
+
# TODO hacer que pueda funcionar en tiempo real? le vas suministrando seeds y le vas diciendo qué opción has elegido (p.ej. para hacer un armonizador en tiempo real)
|
7
|
+
# TODO esto mismo sería aplicable en otros generadores? variatio/darwin? generative-grammar? markov?
|
13
8
|
|
14
9
|
module Musa
|
15
10
|
module Backboner
|
@@ -17,12 +12,12 @@ module Musa
|
|
17
12
|
include Musa::Extension::With
|
18
13
|
|
19
14
|
def initialize(&block)
|
20
|
-
@
|
15
|
+
@dsl = RulesEvalContext.new(&block)
|
21
16
|
end
|
22
17
|
|
23
18
|
def generate_possibilities(object, confirmed_node = nil, node = nil, grow_rules = nil)
|
24
19
|
node ||= Node.new
|
25
|
-
grow_rules ||= @
|
20
|
+
grow_rules ||= @dsl._grow_rules
|
26
21
|
|
27
22
|
history = confirmed_node.history if confirmed_node
|
28
23
|
history ||= []
|
@@ -33,9 +28,9 @@ module Musa
|
|
33
28
|
if grow_rule
|
34
29
|
grow_rule.generate_possibilities(object, history).each do |new_object|
|
35
30
|
new_node = Node.new new_object, node
|
36
|
-
new_node.mark_as_ended! if @
|
31
|
+
new_node.mark_as_ended! if @dsl._ended? new_object
|
37
32
|
|
38
|
-
rejection = @
|
33
|
+
rejection = @dsl._cut_rules.find { |cut_rule| cut_rule.rejects?(new_object, history) }
|
39
34
|
# TODO: include rejection secondary reasons in rejection message
|
40
35
|
|
41
36
|
new_node.reject! rejection if rejection
|
@@ -100,9 +100,7 @@ module Musa
|
|
100
100
|
options[index].to_serie.to_node
|
101
101
|
end
|
102
102
|
|
103
|
-
def to_serie(flatten:
|
104
|
-
flatten ||= true
|
105
|
-
|
103
|
+
def to_serie(flatten: true, &condition)
|
106
104
|
serie = _options(&condition).collect { |o| o.collect(&:content) }.to_serie(of_series: true).merge
|
107
105
|
serie = serie.flatten if flatten
|
108
106
|
|
@@ -7,9 +7,8 @@ module Musa
|
|
7
7
|
module Markov
|
8
8
|
class Markov
|
9
9
|
include Musa::Extension::SmartProcBinder
|
10
|
-
include Musa::Series::Serie
|
10
|
+
include Musa::Series::Serie.base
|
11
11
|
|
12
|
-
attr_accessor :start, :finish, :random, :transitions
|
13
12
|
|
14
13
|
def initialize(transitions:, start:, finish: nil, random: nil)
|
15
14
|
@transitions = transitions.clone.freeze
|
@@ -22,23 +21,28 @@ module Musa
|
|
22
21
|
|
23
22
|
@procedure_binders = {}
|
24
23
|
|
25
|
-
|
24
|
+
init
|
26
25
|
end
|
27
26
|
|
28
|
-
|
27
|
+
attr_accessor :start
|
28
|
+
attr_accessor :finish
|
29
|
+
attr_accessor :random
|
30
|
+
attr_accessor :transitions
|
31
|
+
|
32
|
+
private def _init
|
29
33
|
@current = nil
|
30
34
|
@finished = false
|
31
35
|
@history = []
|
32
36
|
end
|
33
37
|
|
34
|
-
def _next_value
|
38
|
+
private def _next_value
|
35
39
|
if @finished
|
36
40
|
@current = nil
|
37
41
|
else
|
38
42
|
if @current.nil?
|
39
43
|
@current = @start
|
40
44
|
else
|
41
|
-
if @transitions.has_key?
|
45
|
+
if @transitions.has_key?(@current)
|
42
46
|
options = @transitions[@current]
|
43
47
|
|
44
48
|
case options
|
@@ -21,7 +21,7 @@ module Musa
|
|
21
21
|
def convert_to_neumas(e)
|
22
22
|
case e
|
23
23
|
when Musa::Neumas::Neuma::Serie then e
|
24
|
-
when Musa::Neumas::Neuma::Parallel then
|
24
|
+
when Musa::Neumas::Neuma::Parallel then S(e).extend(Musa::Neumas::Neuma::Serie)
|
25
25
|
when String then e.to_neumas
|
26
26
|
else
|
27
27
|
raise ArgumentError, "Don't know how to convert to neumas #{e}"
|
@@ -262,11 +262,11 @@ module Musa
|
|
262
262
|
|
263
263
|
when Musa::Series::Serie
|
264
264
|
{ current_operation: :play,
|
265
|
-
current_parameter: element.restart }
|
265
|
+
current_parameter: element.instance.restart }
|
266
266
|
|
267
267
|
when Parallel
|
268
268
|
{ current_operation: :parallel_play,
|
269
|
-
current_parameter: element.tap { |e| e.each(&:restart) } }
|
269
|
+
current_parameter: element.instance.tap { |e| e.each(&:restart) } }
|
270
270
|
|
271
271
|
when Array
|
272
272
|
{ current_operation: :no_eval_play,
|
@@ -17,11 +17,11 @@ module Musa
|
|
17
17
|
:position=,
|
18
18
|
:event_handler
|
19
19
|
|
20
|
-
def_delegators :@
|
21
|
-
def_delegators :@
|
22
|
-
def_delegators :@
|
23
|
-
def_delegators :@
|
24
|
-
def_delegators :@
|
20
|
+
def_delegators :@dsl, :position, :quantize_position, :logger, :debug
|
21
|
+
def_delegators :@dsl, :with, :now, :at, :wait, :play, :play_timed, :every, :move
|
22
|
+
def_delegators :@dsl, :everying, :playing, :moving
|
23
|
+
def_delegators :@dsl, :launch, :on
|
24
|
+
def_delegators :@dsl, :run
|
25
25
|
|
26
26
|
def initialize(beats_per_bar = nil,
|
27
27
|
ticks_per_beat = nil,
|
@@ -37,7 +37,7 @@ module Musa
|
|
37
37
|
do_error_log: do_error_log,
|
38
38
|
log_position_format: log_position_format
|
39
39
|
|
40
|
-
@
|
40
|
+
@dsl = DSLContext.new @sequencer
|
41
41
|
|
42
42
|
with &block if block_given?
|
43
43
|
end
|
@@ -5,209 +5,358 @@ using Musa::Extension::DeepCopy
|
|
5
5
|
|
6
6
|
module Musa
|
7
7
|
module Series
|
8
|
-
module
|
8
|
+
module Constructors; extend self; end
|
9
|
+
module Operations; end
|
9
10
|
|
10
|
-
|
11
|
-
def prototype?
|
12
|
-
@is_instance ? false : true
|
13
|
-
end
|
11
|
+
include Constructors
|
14
12
|
|
15
|
-
|
16
|
-
|
13
|
+
module Serie
|
14
|
+
def self.base
|
15
|
+
SerieImplementation
|
17
16
|
end
|
18
17
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
def self.with(source: false,
|
19
|
+
source_as: nil,
|
20
|
+
private_source: nil,
|
21
|
+
sources: false,
|
22
|
+
sources_as: nil,
|
23
|
+
private_sources: nil,
|
24
|
+
smart_block: false,
|
25
|
+
block: false,
|
26
|
+
block_as: nil)
|
27
|
+
|
28
|
+
source_as ||= :source
|
29
|
+
source_setter = (source_as.to_s + '=').to_sym
|
30
|
+
|
31
|
+
sources_as ||= :sources
|
32
|
+
sources_setter = (sources_as.to_s + '=').to_sym
|
33
|
+
|
34
|
+
block_as ||= :proc
|
35
|
+
block_setter = (block_as.to_s + '=').to_sym
|
36
|
+
|
37
|
+
Module.new do
|
38
|
+
include SerieImplementation
|
39
|
+
|
40
|
+
if source
|
41
|
+
define_method source_as do ||
|
42
|
+
@source
|
43
|
+
end
|
44
|
+
|
45
|
+
define_method source_setter do |serie|
|
46
|
+
raise ArgumentError, "New source should be a #{@get}" unless @source.nil? || @source.prototype? == serie&.prototype?
|
47
|
+
|
48
|
+
serie ||= Musa::Series::Constructors.NIL
|
49
|
+
@get = serie&.instance? ? :instance : :prototype
|
50
|
+
@source = serie
|
51
|
+
mark_regarding! @source
|
52
|
+
end
|
53
|
+
|
54
|
+
if private_source
|
55
|
+
private source_as
|
56
|
+
private source_setter
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if sources
|
61
|
+
define_method sources_as do ||
|
62
|
+
@sources
|
63
|
+
end
|
64
|
+
|
65
|
+
define_method sources_setter do |series|
|
66
|
+
case series
|
67
|
+
when Array
|
68
|
+
getter = @get || ((series.first)&.instance? ? :instance : :prototype)
|
69
|
+
@sources = series.collect(&getter)
|
70
|
+
when Hash
|
71
|
+
getter = @get || ((series.values.first)&.instance? ? :instance : :prototype)
|
72
|
+
@sources = series.transform_values(&getter)
|
73
|
+
else
|
74
|
+
raise ArgumentError, "Only allowed Array or Hash"
|
75
|
+
end
|
76
|
+
|
77
|
+
mark_as! getter
|
78
|
+
end
|
79
|
+
|
80
|
+
if private_sources
|
81
|
+
private sources_as
|
82
|
+
private sources_setter
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if smart_block
|
87
|
+
define_method block_as do |&block|
|
88
|
+
if block
|
89
|
+
@block = Musa::Extension::SmartProcBinder::SmartProcBinder.new(block)
|
90
|
+
else
|
91
|
+
@block.proc
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
define_method block_setter do |block|
|
96
|
+
@block = Musa::Extension::SmartProcBinder::SmartProcBinder.new(block)
|
97
|
+
end
|
98
|
+
|
99
|
+
elsif block
|
100
|
+
define_method block_as do |&block|
|
101
|
+
if block
|
102
|
+
@block = block
|
103
|
+
else
|
104
|
+
@block
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
define_method block_setter do |block|
|
109
|
+
@block = block
|
110
|
+
end
|
111
|
+
end
|
24
112
|
end
|
25
113
|
end
|
26
114
|
|
27
|
-
|
115
|
+
module Prototyping
|
116
|
+
def prototype?
|
117
|
+
@is_instance ? false : true
|
118
|
+
end
|
28
119
|
|
29
|
-
|
30
|
-
|
31
|
-
self
|
32
|
-
else
|
33
|
-
clone(freeze: false).tap(&:_instance!).mark_as_instance!(self).tap(&:restart)
|
120
|
+
def instance?
|
121
|
+
@is_instance ? true : false
|
34
122
|
end
|
35
|
-
end
|
36
123
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
if @sources.is_a?(Array)
|
50
|
-
@sources = @sources.collect(&:prototype).freeze
|
51
|
-
elsif @sources.is_a?(Hash)
|
52
|
-
@sources = @sources.transform_values(&:prototype).freeze
|
124
|
+
def prototype
|
125
|
+
if @is_instance
|
126
|
+
if !@instance_of
|
127
|
+
@instance_of = clone
|
128
|
+
@instance_of._prototype!
|
129
|
+
@instance_of.mark_as_prototype!
|
130
|
+
@instance_of.init if @instance_of.respond_to?(:init)
|
131
|
+
end
|
132
|
+
|
133
|
+
@instance_of
|
134
|
+
else
|
135
|
+
self
|
53
136
|
end
|
54
137
|
end
|
55
|
-
end
|
56
138
|
|
57
|
-
|
58
|
-
@source = @source.instance if @source
|
139
|
+
alias_method :p, :prototype
|
59
140
|
|
60
|
-
|
61
|
-
if @
|
62
|
-
|
63
|
-
|
64
|
-
|
141
|
+
def instance
|
142
|
+
if @is_instance
|
143
|
+
self
|
144
|
+
else
|
145
|
+
new_instance = clone
|
146
|
+
|
147
|
+
new_instance._instance!
|
148
|
+
new_instance.mark_as_instance!(self)
|
149
|
+
new_instance.init if new_instance.respond_to?(:init)
|
150
|
+
|
151
|
+
new_instance
|
65
152
|
end
|
66
153
|
end
|
67
|
-
end
|
68
154
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
155
|
+
alias_method :i, :instance
|
156
|
+
|
157
|
+
# By default, if there is a @source attribute that contains the source of the serie, SeriePrototyping will
|
158
|
+
# handle prototyping/instancing automatically.
|
159
|
+
# If there is a @sources attribute with the eventual several sources, SeriePrototyping will handle them by
|
160
|
+
# default.
|
161
|
+
# If needed the subclasses can override this behaviour to accommodate to real subclass specificities.
|
162
|
+
#
|
163
|
+
protected def _prototype!
|
164
|
+
@source = @source.prototype if @source
|
165
|
+
|
166
|
+
if @sources
|
167
|
+
if @sources.is_a?(Array)
|
168
|
+
@sources = @sources.collect(&:prototype)
|
169
|
+
elsif @sources.is_a?(Hash)
|
170
|
+
@sources = @sources.transform_values(&:prototype)
|
171
|
+
end
|
172
|
+
end
|
74
173
|
end
|
75
|
-
end
|
76
174
|
|
77
|
-
|
78
|
-
|
79
|
-
freeze
|
80
|
-
end
|
175
|
+
protected def _instance!
|
176
|
+
@source = @source.instance if @source
|
81
177
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
178
|
+
if @sources
|
179
|
+
if @sources.is_a?(Array)
|
180
|
+
@sources = @sources.collect(&:instance)
|
181
|
+
elsif @sources.is_a?(Hash)
|
182
|
+
@sources = @sources.transform_values(&:instance)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
87
186
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
187
|
+
protected def mark_as!(getter)
|
188
|
+
case getter
|
189
|
+
when :prototype
|
190
|
+
mark_as_prototype!
|
191
|
+
when :instance
|
192
|
+
mark_as_instance!
|
193
|
+
else
|
194
|
+
raise ArgumentError, "Only can be marked as :prototype or :instance"
|
195
|
+
end
|
92
196
|
end
|
93
|
-
end
|
94
|
-
end
|
95
197
|
|
96
|
-
|
97
|
-
|
98
|
-
|
198
|
+
protected def mark_regarding!(source)
|
199
|
+
if source.prototype?
|
200
|
+
mark_as_prototype!
|
201
|
+
else
|
202
|
+
mark_as_instance!
|
203
|
+
end
|
204
|
+
end
|
99
205
|
|
100
|
-
|
101
|
-
|
206
|
+
protected def mark_as_prototype!
|
207
|
+
@get = :prototype
|
102
208
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@_current_value = nil
|
209
|
+
@is_instance = nil
|
210
|
+
self
|
211
|
+
end
|
107
212
|
|
108
|
-
|
213
|
+
protected def mark_as_instance!(prototype = nil)
|
214
|
+
@get = :instance
|
109
215
|
|
110
|
-
|
216
|
+
@instance_of = prototype
|
217
|
+
@is_instance = true
|
218
|
+
self
|
219
|
+
end
|
220
|
+
|
221
|
+
class PrototypingError < RuntimeError
|
222
|
+
def initialize(message = nil)
|
223
|
+
message ||= 'This serie is a prototype serie: cannot be consumed. To consume the serie use an instance serie via .instance method'
|
224
|
+
super message
|
225
|
+
end
|
226
|
+
end
|
111
227
|
end
|
112
228
|
|
113
|
-
|
114
|
-
|
229
|
+
module SerieImplementation
|
230
|
+
include Serie
|
231
|
+
include Prototyping
|
232
|
+
include Operations
|
115
233
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
234
|
+
def init
|
235
|
+
@_have_peeked_next_value = false
|
236
|
+
@_peeked_next_value = nil
|
237
|
+
@_have_current_value = false
|
238
|
+
@_current_value = nil
|
239
|
+
|
240
|
+
_init
|
241
|
+
|
242
|
+
self
|
243
|
+
end
|
244
|
+
|
245
|
+
private def _init; end
|
246
|
+
|
247
|
+
def restart(...)
|
248
|
+
raise PrototypingError unless @is_instance
|
249
|
+
init
|
250
|
+
_restart(...)
|
251
|
+
|
252
|
+
self
|
253
|
+
end
|
254
|
+
|
255
|
+
private def _restart; end
|
256
|
+
|
257
|
+
def next_value
|
258
|
+
raise PrototypingError unless @is_instance
|
259
|
+
|
260
|
+
unless @_have_current_value && @_current_value.nil?
|
261
|
+
if @_have_peeked_next_value
|
262
|
+
@_have_peeked_next_value = false
|
263
|
+
@_current_value = @_peeked_next_value
|
264
|
+
else
|
265
|
+
@_current_value = _next_value
|
266
|
+
end
|
122
267
|
end
|
268
|
+
|
269
|
+
@_current_value
|
123
270
|
end
|
124
271
|
|
125
|
-
|
126
|
-
end
|
272
|
+
private def _next_value; end
|
127
273
|
|
128
|
-
|
274
|
+
alias_method :v, :next_value
|
129
275
|
|
130
|
-
|
131
|
-
|
276
|
+
def peek_next_value
|
277
|
+
raise PrototypingError unless @is_instance
|
132
278
|
|
133
|
-
|
134
|
-
|
135
|
-
|
279
|
+
if !@_have_peeked_next_value
|
280
|
+
@_have_peeked_next_value = true
|
281
|
+
@_peeked_next_value = _next_value
|
282
|
+
end
|
283
|
+
|
284
|
+
@_peeked_next_value
|
136
285
|
end
|
137
286
|
|
138
|
-
|
139
|
-
|
287
|
+
def current_value
|
288
|
+
raise PrototypingError unless @is_instance
|
140
289
|
|
141
|
-
|
142
|
-
|
290
|
+
@_current_value
|
291
|
+
end
|
143
292
|
|
144
|
-
|
145
|
-
|
293
|
+
def infinite?
|
294
|
+
@source&.infinite? || false
|
295
|
+
end
|
146
296
|
|
147
|
-
|
148
|
-
|
149
|
-
end
|
297
|
+
def to_a(recursive: nil, duplicate: nil, restart: nil, dr: nil)
|
298
|
+
recursive ||= false
|
150
299
|
|
151
|
-
|
152
|
-
recursive ||= false
|
300
|
+
dr = instance? if dr.nil?
|
153
301
|
|
154
|
-
|
302
|
+
duplicate = dr if duplicate.nil?
|
303
|
+
restart = dr if restart.nil?
|
155
304
|
|
156
|
-
|
157
|
-
restart = dr if restart.nil?
|
305
|
+
raise 'Cannot convert to array an infinite serie' if infinite?
|
158
306
|
|
159
|
-
|
307
|
+
array = []
|
160
308
|
|
161
|
-
|
309
|
+
serie = instance
|
162
310
|
|
163
|
-
|
311
|
+
serie = serie.clone(deep: true) if duplicate
|
312
|
+
serie = serie.restart if restart
|
164
313
|
|
165
|
-
|
166
|
-
|
314
|
+
while value = serie.next_value
|
315
|
+
array << if recursive
|
316
|
+
process_for_to_a(value)
|
317
|
+
else
|
318
|
+
value
|
319
|
+
end
|
320
|
+
end
|
167
321
|
|
168
|
-
|
169
|
-
array << if recursive
|
170
|
-
process_for_to_a(value)
|
171
|
-
else
|
172
|
-
value
|
173
|
-
end
|
322
|
+
array
|
174
323
|
end
|
175
324
|
|
176
|
-
|
177
|
-
end
|
325
|
+
alias_method :a, :to_a
|
178
326
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
Nodificator.to_node(self, **attributes)
|
183
|
-
end
|
327
|
+
def to_node(**attributes)
|
328
|
+
Nodificator.to_node(self, **attributes)
|
329
|
+
end
|
184
330
|
|
185
|
-
|
331
|
+
alias_method :node, :to_node
|
186
332
|
|
187
|
-
|
188
|
-
|
333
|
+
class Nodificator
|
334
|
+
extend Musa::GenerativeGrammar
|
189
335
|
|
190
|
-
|
191
|
-
|
336
|
+
def self.to_node(serie, **attributes)
|
337
|
+
N(serie, **attributes)
|
338
|
+
end
|
192
339
|
end
|
193
|
-
end
|
194
340
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
341
|
+
private_constant :Nodificator
|
342
|
+
|
343
|
+
private def process_for_to_a(value)
|
344
|
+
case value
|
345
|
+
when Serie
|
346
|
+
value.to_a(recursive: true, restart: false, duplicate: false)
|
347
|
+
when Array
|
348
|
+
a = value.clone
|
349
|
+
a.collect! { |v| v.is_a?(Serie) ? v.to_a(recursive: true, restart: false, duplicate: false) : process_for_to_a(v) }
|
350
|
+
when Hash
|
351
|
+
h = value.clone
|
352
|
+
h.transform_values! { |v| v.is_a?(Serie) ? v.to_a(recursive: true, restart: false, duplicate: false) : process_for_to_a(v) }
|
353
|
+
else
|
354
|
+
value
|
355
|
+
end
|
209
356
|
end
|
210
357
|
end
|
358
|
+
|
359
|
+
private_constant :SerieImplementation
|
211
360
|
end
|
212
361
|
end
|
213
362
|
end
|