musa-dsl 0.14.32 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/README.md +5 -1
  4. data/lib/musa-dsl.rb +54 -11
  5. data/lib/musa-dsl/core-ext.rb +7 -13
  6. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +15 -23
  7. data/lib/musa-dsl/core-ext/arrayfy.rb +30 -12
  8. data/lib/musa-dsl/core-ext/attribute-builder.rb +194 -0
  9. data/lib/musa-dsl/core-ext/deep-copy.rb +185 -0
  10. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +44 -40
  11. data/lib/musa-dsl/core-ext/inspect-nice.rb +40 -22
  12. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +108 -0
  13. data/lib/musa-dsl/core-ext/with.rb +26 -0
  14. data/lib/musa-dsl/datasets.rb +8 -3
  15. data/lib/musa-dsl/datasets/dataset.rb +3 -0
  16. data/lib/musa-dsl/datasets/delta-d.rb +12 -0
  17. data/lib/musa-dsl/datasets/e.rb +61 -0
  18. data/lib/musa-dsl/datasets/gdv.rb +51 -411
  19. data/lib/musa-dsl/datasets/gdvd.rb +179 -0
  20. data/lib/musa-dsl/datasets/helper.rb +41 -0
  21. data/lib/musa-dsl/datasets/p.rb +68 -0
  22. data/lib/musa-dsl/datasets/packed-v.rb +19 -0
  23. data/lib/musa-dsl/datasets/pdv.rb +22 -15
  24. data/lib/musa-dsl/datasets/ps.rb +113 -0
  25. data/lib/musa-dsl/datasets/score.rb +210 -0
  26. data/lib/musa-dsl/datasets/score/queriable.rb +48 -0
  27. data/lib/musa-dsl/datasets/score/render.rb +31 -0
  28. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +160 -0
  29. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +51 -0
  30. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +153 -0
  31. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +158 -0
  32. data/lib/musa-dsl/datasets/v.rb +23 -0
  33. data/lib/musa-dsl/generative.rb +5 -5
  34. data/lib/musa-dsl/generative/backboner.rb +274 -0
  35. data/lib/musa-dsl/generative/darwin.rb +102 -96
  36. data/lib/musa-dsl/generative/generative-grammar.rb +182 -187
  37. data/lib/musa-dsl/generative/markov.rb +56 -53
  38. data/lib/musa-dsl/generative/variatio.rb +234 -222
  39. data/lib/musa-dsl/logger.rb +1 -0
  40. data/lib/musa-dsl/logger/logger.rb +31 -0
  41. data/lib/musa-dsl/matrix.rb +1 -0
  42. data/lib/musa-dsl/matrix/matrix.rb +210 -0
  43. data/lib/musa-dsl/midi.rb +2 -2
  44. data/lib/musa-dsl/midi/midi-recorder.rb +54 -52
  45. data/lib/musa-dsl/midi/midi-voices.rb +183 -182
  46. data/lib/musa-dsl/music.rb +5 -5
  47. data/lib/musa-dsl/music/chord-definition.rb +54 -50
  48. data/lib/musa-dsl/music/chord-definitions.rb +13 -9
  49. data/lib/musa-dsl/music/chords.rb +236 -238
  50. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +187 -183
  51. data/lib/musa-dsl/music/scales.rb +331 -332
  52. data/lib/musa-dsl/musicxml.rb +1 -0
  53. data/lib/musa-dsl/musicxml/builder/attributes.rb +155 -0
  54. data/lib/musa-dsl/musicxml/builder/backup-forward.rb +45 -0
  55. data/lib/musa-dsl/musicxml/builder/direction.rb +322 -0
  56. data/lib/musa-dsl/musicxml/builder/helper.rb +90 -0
  57. data/lib/musa-dsl/musicxml/builder/measure.rb +137 -0
  58. data/lib/musa-dsl/musicxml/builder/note-complexities.rb +152 -0
  59. data/lib/musa-dsl/musicxml/builder/note.rb +577 -0
  60. data/lib/musa-dsl/musicxml/builder/part-group.rb +44 -0
  61. data/lib/musa-dsl/musicxml/builder/part.rb +67 -0
  62. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +126 -0
  63. data/lib/musa-dsl/musicxml/builder/rest.rb +117 -0
  64. data/lib/musa-dsl/musicxml/builder/score-partwise.rb +120 -0
  65. data/lib/musa-dsl/musicxml/builder/typed-text.rb +43 -0
  66. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +112 -0
  67. data/lib/musa-dsl/neumalang.rb +1 -1
  68. data/lib/musa-dsl/neumalang/datatypes.citrus +79 -0
  69. data/lib/musa-dsl/neumalang/neuma.citrus +165 -0
  70. data/lib/musa-dsl/neumalang/neumalang.citrus +32 -242
  71. data/lib/musa-dsl/neumalang/neumalang.rb +373 -142
  72. data/lib/musa-dsl/neumalang/process.citrus +21 -0
  73. data/lib/musa-dsl/neumalang/terminals.citrus +67 -0
  74. data/lib/musa-dsl/neumalang/vectors.citrus +23 -0
  75. data/lib/musa-dsl/neumas.rb +5 -0
  76. data/lib/musa-dsl/neumas/array-to-neumas.rb +34 -0
  77. data/lib/musa-dsl/neumas/neuma-decoder.rb +63 -0
  78. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +57 -0
  79. data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +15 -0
  80. data/lib/musa-dsl/neumas/neumas.rb +37 -0
  81. data/lib/musa-dsl/neumas/string-to-neumas.rb +33 -0
  82. data/lib/musa-dsl/repl.rb +1 -1
  83. data/lib/musa-dsl/repl/repl.rb +103 -110
  84. data/lib/musa-dsl/sequencer.rb +1 -1
  85. data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +163 -136
  86. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +301 -286
  87. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +548 -321
  88. data/lib/musa-dsl/sequencer/base-sequencer-public.rb +198 -176
  89. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +77 -0
  90. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +75 -0
  91. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +105 -85
  92. data/lib/musa-dsl/sequencer/timeslots.rb +34 -0
  93. data/lib/musa-dsl/series.rb +1 -1
  94. data/lib/musa-dsl/{core-ext → series}/array-to-serie.rb +1 -1
  95. data/lib/musa-dsl/series/base-series.rb +171 -168
  96. data/lib/musa-dsl/series/hash-serie-splitter.rb +134 -132
  97. data/lib/musa-dsl/series/holder-serie.rb +1 -1
  98. data/lib/musa-dsl/series/main-serie-constructors.rb +6 -1
  99. data/lib/musa-dsl/series/main-serie-operations.rb +807 -797
  100. data/lib/musa-dsl/series/proxy-serie.rb +6 -6
  101. data/lib/musa-dsl/series/queue-serie.rb +5 -5
  102. data/lib/musa-dsl/series/series.rb +2 -0
  103. data/lib/musa-dsl/transcription.rb +4 -0
  104. data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +227 -0
  105. data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +36 -0
  106. data/lib/musa-dsl/transcription/from-gdv.rb +17 -0
  107. data/lib/musa-dsl/transcription/transcription.rb +55 -0
  108. data/lib/musa-dsl/transport.rb +6 -6
  109. data/lib/musa-dsl/transport/clock.rb +26 -26
  110. data/lib/musa-dsl/transport/dummy-clock.rb +32 -30
  111. data/lib/musa-dsl/transport/external-tick-clock.rb +21 -20
  112. data/lib/musa-dsl/transport/input-midi-clock.rb +82 -80
  113. data/lib/musa-dsl/transport/timer-clock.rb +72 -71
  114. data/lib/musa-dsl/transport/timer.rb +28 -26
  115. data/lib/musa-dsl/transport/transport.rb +100 -95
  116. data/musa-dsl.gemspec +3 -3
  117. metadata +73 -24
  118. data/lib/musa-dsl/core-ext/array-apply-get.rb +0 -18
  119. data/lib/musa-dsl/core-ext/array-to-neumas.rb +0 -28
  120. data/lib/musa-dsl/core-ext/as-context-run.rb +0 -44
  121. data/lib/musa-dsl/core-ext/duplicate.rb +0 -134
  122. data/lib/musa-dsl/core-ext/key-parameters-procedure-binder.rb +0 -85
  123. data/lib/musa-dsl/core-ext/proc-nice.rb +0 -13
  124. data/lib/musa-dsl/core-ext/send-nice.rb +0 -21
  125. data/lib/musa-dsl/core-ext/string-to-neumas.rb +0 -27
  126. data/lib/musa-dsl/datasets/gdv-decorators.rb +0 -221
  127. data/lib/musa-dsl/generative/rules.rb +0 -282
  128. data/lib/musa-dsl/neuma.rb +0 -1
  129. data/lib/musa-dsl/neuma/neuma.rb +0 -181
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38849caa61ab0d2381beb2b3dfc85686c9b7592b027dfd5a4f1f3aadae60ae7d
4
- data.tar.gz: cdea129555d1bba7a5933cf71a522852463196a27fdca1ac39e10d8d0aca2dcb
3
+ metadata.gz: 41f779e846e1b9c6a9281384feca8f04e5f43e115847c33fa53874418ef332ca
4
+ data.tar.gz: 1d22315101af69d0a49fcee3146328bcf7e979fc6a8d663a54240bec7424667e
5
5
  SHA512:
6
- metadata.gz: 9ae1524d885d66a1d6dc5e1cb4d88e17d069e38cd6a98c3ce8fe4131b2fb2e2df09490e3e2fbcb657a6e35a6ce27f363734e742393d10af5300502262b8d44a1
7
- data.tar.gz: 7b8186dd41f7243f2b1b168929a845efa93a6028c13ff286628b505de7515cb91b4c4861c89c1a1759ab9fb3512bb042b0e20821ff300458e9fec671b9304975
6
+ metadata.gz: 489fd45c150778a288b054cf6a90ed435332606adeeb2804a6c3e6007e5c05339c9f7aeb4e1a905df19668c0108beb8dbe079821f7999d31a288c9dafc6430ff
7
+ data.tar.gz: 38fb6c56f67a8a44f900d60132a7ebae87ea247167f786ef68f1fb8bb8ed6faa6d4ea748ae8751350c0e7408ac9d80b4cbe4b3b93527fb672afe689bb35aa5e7
data/.gitignore CHANGED
@@ -3,8 +3,10 @@
3
3
  .bundle
4
4
  .rspec
5
5
  .idea
6
+ .ruby-gemset
7
+ .ruby-version
6
8
  Gemfile.lock
7
9
  bin
8
10
  *.mindnode
9
-
11
+ test.musicxml
10
12
 
data/README.md CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  Work in progress.
4
4
 
5
- A programming language DSL based on Ruby for musical composition.
5
+ A programming language DSL based on Ruby for sonic and musical composition.
6
6
  Emphasizes the creation of complex temporal structures independently of the audio rendering engine.
7
7
 
8
8
  Some works can be listened on [yeste.studio](https://soundcloud.com/yeste-studio) Soundcloud.
9
+
10
+ ---
11
+
12
+ Coded on [Jetbrains RubyMine IDE](https://www.jetbrains.com/?from=Musa-DSL). Thanks a lot for your support letting me use an Open Source project free license!
@@ -1,17 +1,60 @@
1
- require 'musa-dsl/core-ext'
1
+ module Musa
2
+ VERSION = '0.21.0'
3
+ end
2
4
 
3
- require 'musa-dsl/series'
4
- require 'musa-dsl/neuma'
5
- require 'musa-dsl/datasets'
5
+ require_relative 'musa-dsl/core-ext'
6
6
 
7
- require 'musa-dsl/neumalang'
7
+ require_relative 'musa-dsl/series'
8
+ require_relative 'musa-dsl/datasets'
9
+ require_relative 'musa-dsl/matrix'
8
10
 
9
- require 'musa-dsl/transport'
10
- require 'musa-dsl/sequencer'
11
- require 'musa-dsl/repl'
11
+ require_relative 'musa-dsl/neumalang'
12
+ require_relative 'musa-dsl/neumas'
12
13
 
13
- require 'musa-dsl/midi'
14
+ require_relative 'musa-dsl/logger'
14
15
 
15
- require 'musa-dsl/music'
16
+ require_relative 'musa-dsl/transport'
17
+ require_relative 'musa-dsl/sequencer'
18
+ require_relative 'musa-dsl/repl'
16
19
 
17
- require 'musa-dsl/generative'
20
+ require_relative 'musa-dsl/midi'
21
+ require_relative 'musa-dsl/musicxml'
22
+
23
+ require_relative 'musa-dsl/transcription'
24
+
25
+ require_relative 'musa-dsl/music'
26
+
27
+ require_relative 'musa-dsl/generative'
28
+
29
+ module Musa::All
30
+ include Musa::Logger
31
+
32
+ include Musa::Clock
33
+ include Musa::Transport
34
+ include Musa::Sequencer
35
+
36
+ include Musa::Scales
37
+ include Musa::Chords
38
+ include Musa::Datasets
39
+
40
+ include Musa::Neumalang
41
+ include Musa::Neumas
42
+ include Musa::Matrix
43
+
44
+ include Musa::Series
45
+
46
+ include Musa::Darwin
47
+ include Musa::Markov
48
+ include Musa::Backboner
49
+ include Musa::Variatio
50
+
51
+ include Musa::MIDIRecorder
52
+ include Musa::MIDIVoices
53
+
54
+ include Musa::MusicXML
55
+
56
+ include Musa::Transcription
57
+ include Musa::Transcriptors
58
+
59
+ include Musa::REPL
60
+ end
@@ -1,13 +1,7 @@
1
- require 'musa-dsl/core-ext/array-apply-get'
2
- require 'musa-dsl/core-ext/array-explode-ranges'
3
- require 'musa-dsl/core-ext/array-to-serie'
4
- require 'musa-dsl/core-ext/string-to-neumas'
5
- require 'musa-dsl/core-ext/array-to-neumas'
6
- require 'musa-dsl/core-ext/arrayfy'
7
- require 'musa-dsl/core-ext/duplicate'
8
- require 'musa-dsl/core-ext/key-parameters-procedure-binder'
9
- require 'musa-dsl/core-ext/as-context-run'
10
- require 'musa-dsl/core-ext/inspect-nice'
11
- require 'musa-dsl/core-ext/send-nice'
12
- require 'musa-dsl/core-ext/proc-nice'
13
- require 'musa-dsl/core-ext/dynamic-proxy'
1
+ require_relative 'core-ext/array-explode-ranges'
2
+ require_relative 'core-ext/arrayfy'
3
+ require_relative 'core-ext/deep-copy'
4
+ require_relative 'core-ext/smart-proc-binder'
5
+ require_relative 'core-ext/inspect-nice'
6
+ require_relative 'core-ext/dynamic-proxy'
7
+ require_relative 'core-ext/with'
@@ -1,29 +1,21 @@
1
- class Array
2
- def arrayfy
3
- self
4
- end
5
-
6
- def repeat_to_size(new_size)
7
- pos = -1
8
- new_size -= 1
9
-
10
- new_array = clone
11
- new_array << self[(pos += 1) % size] while (pos + size) < new_size
1
+ module Musa
2
+ module Extension
3
+ module ExplodeRanges
4
+ refine Array do
5
+ def explode_ranges
6
+ array = []
12
7
 
13
- new_array
14
- end
15
-
16
- def explode_ranges
17
- array = []
8
+ each do |element|
9
+ if element.is_a? Range
10
+ element.to_a.each { |element| array << element }
11
+ else
12
+ array << element
13
+ end
14
+ end
18
15
 
19
- each do |element|
20
- if element.is_a? Range
21
- element.to_a.each { |element| array << element }
22
- else
23
- array << element
16
+ array
17
+ end
24
18
  end
25
19
  end
26
-
27
- array
28
20
  end
29
21
  end
@@ -1,15 +1,33 @@
1
- class Object
2
- def arrayfy
3
- if nil?
4
- []
5
- else
6
- [self]
7
- end
8
- end
9
- end
1
+ require_relative 'deep-copy'
10
2
 
11
- class Array
12
- def arrayfy
13
- self
3
+ module Musa
4
+ module Extension
5
+ module Arrayfy
6
+ refine Object do
7
+ def arrayfy(size: nil)
8
+ if size
9
+ size.times.collect { self }
10
+ else
11
+ if nil?
12
+ []
13
+ else
14
+ [self]
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ refine Array do
21
+ def arrayfy(size: nil)
22
+ if size
23
+ DeepCopy::DeepCopy.copy_singleton_class_modules(
24
+ self,
25
+ (self * (size / self.size + ((size % self.size).zero? ? 0 : 1) )).take(size))
26
+ else
27
+ self.clone
28
+ end
29
+ end
30
+ end
31
+ end
14
32
  end
15
33
  end
@@ -0,0 +1,194 @@
1
+ module Musa
2
+ module Extension
3
+ module AttributeBuilder
4
+ # add_thing id, parameter
5
+ # things id1: parameter1, id2: parameter2 -> { id1: Thing(id1, parameter1), id2: Thing(id2, parameter2) }
6
+ # things -> { id1: Thing(id1, parameter1), id2: Thing(id2, parameter2) }
7
+ #
8
+ def attr_tuple_adder_to_hash(name, klass, plural: nil, variable: nil)
9
+
10
+ plural ||= name.to_s + 's'
11
+ variable ||= ('@' + plural.to_s).to_sym
12
+
13
+ adder_method = "add_#{name}".to_sym
14
+
15
+ define_method adder_method do |id, parameter|
16
+ klass.new(id, parameter).tap do |object|
17
+ instance_variable_get(variable)[id] = object
18
+ end
19
+ end
20
+
21
+ define_method plural do |**parameters|
22
+ parameters&.each_pair do |id, value|
23
+ send adder_method, id, value
24
+ end
25
+ instance_variable_get variable
26
+ end
27
+ end
28
+
29
+ # add_thing id, parameter
30
+ # things id1: parameter1, id2: parameter2 -> [ Thing(id1, parameter1), Thing(id2, parameter2) ]
31
+ # things -> [ Thing(id1, parameter1), Thing(id2, parameter2) ]
32
+
33
+ def attr_tuple_adder_to_array(name, klass, plural: nil, variable: nil)
34
+
35
+ plural ||= name.to_s + 's'
36
+ variable ||= ('@' + plural.to_s).to_sym
37
+
38
+ adder_method = "add_#{name}".to_sym
39
+
40
+ define_method adder_method do |id, parameter, &block|
41
+ klass.new(id, parameter, &block).tap do |object|
42
+ instance_variable_get(variable) << object
43
+ end
44
+ end
45
+
46
+ define_method plural do |**parameters, &block|
47
+ parameters.each_pair do |id, value|
48
+ send adder_method, id, value, &block
49
+ end
50
+ instance_variable_get variable
51
+ end
52
+ end
53
+
54
+ # add_thing param1, param2, key1: parameter1, key2: parameter2 -> Thing(...)
55
+ # thing param1, param2, key1: parameter1, key2: parameter2 -> Thing(...)
56
+ # things -> (collection)
57
+
58
+ def attr_complex_adder_to_array(name, klass, plural: nil, variable: nil)
59
+
60
+ plural ||= name.to_s + 's'
61
+ variable ||= ('@' + plural.to_s).to_sym
62
+
63
+ adder_method = "add_#{name}".to_sym
64
+
65
+ define_method adder_method do |*parameters, **key_parameters, &block|
66
+ klass.new(*parameters, **key_parameters, &block).tap do |object|
67
+ instance_variable_get(variable) << object
68
+ end
69
+ end
70
+
71
+ if plural == name
72
+ define_method plural do |*parameters, **key_parameters, &block|
73
+ if parameters.empty? && key_parameters.empty? && block.nil?
74
+ instance_variable_get variable
75
+ else
76
+ send adder_method, *parameters, **key_parameters, &block
77
+ end
78
+ end
79
+ else
80
+ alias_method name, adder_method
81
+
82
+ define_method plural do
83
+ instance_variable_get variable
84
+ end
85
+ end
86
+ end
87
+
88
+
89
+ # add_thing param1, param2, key1: parameter1, key2: parameter2 -> Thing(...)
90
+ # thing param1, param2, key1: parameter1, key2: parameter2 -> Thing(...)
91
+ # things -> (collection)
92
+
93
+ def attr_complex_adder_to_custom(name, plural: nil, variable: nil, &constructor_and_adder)
94
+
95
+ plural ||= name.to_s + 's'
96
+
97
+ adder_method = "add_#{name}".to_sym
98
+
99
+ define_method adder_method do |*parameters, **key_parameters, &block|
100
+ instance_exec(*parameters, **key_parameters, &constructor_and_adder).tap do |object|
101
+ object.with &block if block
102
+ end
103
+ end
104
+
105
+ if plural == name && variable
106
+ define_method plural do |*parameters, **key_parameters, &block|
107
+ if parameters.empty? && key_parameters.empty? && block.nil?
108
+ instance_variable_get variable
109
+ else
110
+ send adder_method, *parameters, **key_parameters, &block
111
+ end
112
+ end
113
+ else
114
+ alias_method name, adder_method
115
+
116
+ if variable
117
+ define_method plural do
118
+ instance_variable_get variable
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ # thing value -> crea Thing(value)
125
+ # thing -> Thing(value)
126
+
127
+ def attr_simple_builder(name, klass = nil, variable: nil)
128
+ variable ||= ('@' + name.to_s).to_sym
129
+
130
+ define_method name do |parameter = nil, &block|
131
+ if parameter.nil?
132
+ instance_variable_get variable
133
+ else
134
+ (klass&.new(parameter, &block) || parameter).tap do |object|
135
+ instance_variable_set variable, object
136
+ end
137
+ end
138
+ end
139
+
140
+ attr_writer name
141
+ end
142
+
143
+ # thing id: value -> crea Thing(id, value)
144
+ # thing -> Thing(id, value)
145
+
146
+ def attr_tuple_builder(name, klass, variable: nil)
147
+ variable ||= ('@' + name.to_s).to_sym
148
+
149
+ define_method name do |**parameters, &block|
150
+ raise ArgumentError, "Method #{name} can only create instances with one id: value arguments pattern" unless parameters.size == 1
151
+
152
+ if parameters.empty?
153
+ instance_variable_get variable
154
+ else
155
+ parameter = parameters.first
156
+ klass.new(*parameter, &block).tap do |object|
157
+ instance_variable_set variable, object
158
+ end
159
+ end
160
+ end
161
+
162
+ attr_writer name
163
+ end
164
+
165
+ # thing value1, value2, key1: value3, key2: value4 -> crea Thing(value1, value2, key1: value3, key2: value3)
166
+ # thing -> Thing(value1, value2, key1: value3, key2: value4)
167
+ # y también...
168
+ # thing value1, value2, key1: value3, key2: value4 -> crea Thing(first_parameter, value1, value2, key1: value3, key2: value3)
169
+ # thing -> Thing(first_parameter, value1, value2, key1: value3, key2: value4)
170
+
171
+ def attr_complex_builder(name, klass, variable: nil, first_parameter: nil)
172
+ variable ||= ('@' + name.to_s).to_sym
173
+
174
+ define_method name do |*parameters, **key_parameters, &block|
175
+ if parameters.empty? && key_parameters.empty? && block.nil?
176
+ instance_variable_get variable
177
+ else
178
+ if first_parameter
179
+ klass.new(first_parameter, *parameters, **key_parameters, &block).tap do |object|
180
+ instance_variable_set variable, object
181
+ end
182
+ else
183
+ klass.new(*parameters, **key_parameters, &block).tap do |object|
184
+ instance_variable_set variable, object
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ attr_writer name
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,185 @@
1
+ # Based on https://github.com/adamluzsi/duplicate.rb/blob/master/lib/duplicate.rb
2
+ # Modifications by Javier Sánchez Yeste
3
+
4
+ module Musa
5
+ module Extension
6
+ module DeepCopy
7
+ module DeepCopy
8
+ extend self
9
+
10
+ def deep_copy(object, method: :dup, freeze: true)
11
+ raise ArgumentError, "deep_copy method can only be :dup or :clone" unless method == :dup || method == :clone
12
+ register = {}
13
+
14
+ _deep_copy(register, object, method, freeze)
15
+ end
16
+
17
+ def copy_singleton_class_modules(source, target)
18
+ source.singleton_class.included_modules.each do |m|
19
+ target.extend m unless target.is_a?(m)
20
+ end
21
+
22
+ target
23
+ end
24
+
25
+ protected
26
+
27
+ def registered(object, register)
28
+ register[object.__id__]
29
+ end
30
+
31
+ def register(register, object, duplicate)
32
+ register[object.__id__] = duplicate
33
+ duplicate
34
+ end
35
+
36
+ def _deep_copy(register, object, method, freeze)
37
+ return registered(object, register) if registered(object, register)
38
+ return register(register, object, object) unless identifiable?(object)
39
+
40
+ case object
41
+
42
+ when Array
43
+ deep_copy_array(register, object, method, freeze)
44
+
45
+ when Hash
46
+ deep_copy_hash(register, object, method, freeze)
47
+
48
+ when Range
49
+ deep_copy_range(register, object, method, freeze)
50
+
51
+ when Struct
52
+ deep_copy_struct(register, object, method, freeze)
53
+
54
+ when Proc
55
+ deep_copy_proc(register, object, method, freeze)
56
+
57
+ when NilClass, Symbol, Numeric, TrueClass, FalseClass, Method
58
+ register(register, object, object)
59
+
60
+ else
61
+ deep_copy_object(register, object, method, freeze)
62
+
63
+ end
64
+ end
65
+
66
+ def identifiable?(object)
67
+ object.class && object.respond_to?(:is_a?)
68
+ rescue NoMethodError
69
+ false
70
+ end
71
+
72
+ def deep_copy_array(register, object, method, freeze)
73
+ deep_copy_object(register, object, method, freeze) do |_, copy|
74
+ copy.map! { |e| _deep_copy(register, e, method, freeze) }
75
+ end
76
+ end
77
+
78
+ def deep_copy_hash(register, object, method, freeze)
79
+ deep_copy_object(register, object, method, freeze) do |object, copy|
80
+ object.reduce(copy) { |hash, (k, v)| hash.merge!(_deep_copy(register, k, method, freeze) => _deep_copy(register, v, method, freeze)) }
81
+ end
82
+ end
83
+
84
+ def deep_copy_range(register, range, method, freeze)
85
+ copy = range.class.new(_deep_copy(register, range.first, method, freeze), _deep_copy(register, range.last, method, freeze))
86
+ copy.freeze if range.frozen?
87
+
88
+ register(register, range, copy)
89
+ rescue StandardError
90
+ register(register, range, range.send(method))
91
+ end
92
+
93
+ def deep_copy_struct(register, struct, method, freeze)
94
+ duplication = register(register, struct, struct.send(method))
95
+
96
+ struct.each_pair do |attr, value|
97
+ duplication.__send__("#{attr}=", _deep_copy(register, value, method, freeze))
98
+ end
99
+
100
+ duplication
101
+ end
102
+
103
+ def deep_copy_object(register, object, method, freeze)
104
+ if method == :clone && object.frozen?
105
+ copy = try_deep_copy(object, :clone, false)
106
+ else
107
+ copy = try_deep_copy(object, method, freeze)
108
+ end
109
+
110
+ register(register, object, copy)
111
+ deep_copy_instance_variables(register, object, register(register, object, copy), method, freeze)
112
+
113
+ yield object, copy if block_given?
114
+
115
+ copy.freeze if method == :clone && object.frozen? && freeze
116
+
117
+ copy
118
+ end
119
+
120
+ def deep_copy_proc(register, object, method, freeze)
121
+ register(register, object, object.dup)
122
+ end
123
+
124
+ def deep_copy_instance_variables(register, object, duplication, method, freeze)
125
+ return duplication unless respond_to_instance_variables?(object)
126
+
127
+ object.instance_variables.each do |instance_variable|
128
+ value = get_instance_variable(object, instance_variable)
129
+
130
+ set_instance_variable(duplication, instance_variable, _deep_copy(register, value, method, freeze))
131
+ end
132
+
133
+ duplication
134
+ end
135
+
136
+ def get_instance_variable(object, instance_variable_name)
137
+ object.instance_variable_get(instance_variable_name)
138
+ rescue NoMethodError
139
+ object.instance_eval(instance_variable_name.to_s)
140
+ end
141
+
142
+ def set_instance_variable(duplicate, instance_variable_name, value_to_set)
143
+ duplicate.instance_variable_set(instance_variable_name, value_to_set)
144
+ rescue NoMethodError
145
+ duplicate.instance_eval("#{instance_variable_name} = Marshal.load(#{Marshal.dump(value_to_set).inspect})")
146
+ end
147
+
148
+ def try_deep_copy(object, method, freeze)
149
+ if method == :dup
150
+ object.dup
151
+ else
152
+ object.clone(freeze: freeze)
153
+ end
154
+ rescue NoMethodError, TypeError
155
+ object
156
+ end
157
+
158
+ def respond_to_instance_variables?(object)
159
+ object.respond_to?(:instance_variables) && object.instance_variables.is_a?(Array)
160
+ rescue NoMethodError
161
+ false
162
+ end
163
+ end
164
+
165
+ refine Object do
166
+ def dup(deep: false)
167
+ if deep
168
+ Musa::Extension::DeepCopy::DeepCopy.deep_copy(self, method: :dup)
169
+ else
170
+ super()
171
+ end
172
+ end
173
+
174
+ def clone(freeze: true, deep: false)
175
+ if deep
176
+ Musa::Extension::DeepCopy::DeepCopy.deep_copy(self, method: :clone, freeze: freeze)
177
+ else
178
+ super(freeze: freeze)
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+