musa-dsl 0.22.6 → 0.23.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|