alda-rb 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/alda-rb/repl.rb CHANGED
@@ -1,17 +1,10 @@
1
1
  require 'colorize'
2
2
  require 'irb/ruby-lex'
3
3
  require 'json'
4
- require 'readline'
4
+ require 'reline'
5
5
  require 'stringio'
6
-
7
- ##
8
- # :call-seq:
9
- # repl() -> nil
10
- #
11
- # Start a REPL session.
12
- def Alda.repl
13
- Alda::REPL.new.run
14
- end
6
+ require 'bencode'
7
+ require 'socket'
15
8
 
16
9
  ##
17
10
  # An instance of this class is an \REPL session.
@@ -32,101 +25,303 @@ end
32
25
  # your previous input, run <tt>puts history</tt>.
33
26
  #
34
27
  # Unlike \IRB, this \REPL does not print the result of
35
- # the executed codes. Use +p+ if you want.
28
+ # the executed codes. Use +p+ or +puts+ if you want.
36
29
  #
37
30
  # +Interrupt+ and +SystemExit+ exceptions are rescued and
38
31
  # will not cause the process terminating.
39
32
  # +exit+ terminates the \REPL session instead of the process.
40
33
  #
41
- # To start an \REPL session in a ruby program, use Alda::repl.
34
+ # To start an \REPL session in a ruby program, use #run.
42
35
  # To start an \REPL session conveniently from command line,
43
- # run command <tt>ruby -ralda-rb -e "Alda.repl"</tt>.
36
+ # run command <tt>alda-irb</tt>.
37
+ # For details about this command line tool, run <tt>alda-irb --help</tt>.
44
38
  #
45
- # $ ruby -ralda-rb -e "Alda.repl"
46
- # > puts status
47
- # [27713] Server up (2/2 workers available, backend port: 33245)
48
- # > piano_ c d e f
49
- # [piano: c d e f]
39
+ # $ alda-irb
40
+ # > p processes.last
41
+ # {:id=>"dus", :port=>34317, :state=>nil, :expiry=>nil, :type=>:repl_server}
42
+ # > piano_; c d e f
43
+ # piano: [c d e f]
50
44
  # > 5.times do
51
- # > c
52
- # > end
45
+ # . c
46
+ # > end
53
47
  # c c c c c
54
- # > puts history
55
- # [piano: c d e f]
48
+ # > score_text
49
+ # piano: [c d e f]
56
50
  # c c c c c
57
51
  # > play
52
+ # Playing...
58
53
  # > save 'temp.alda'
59
54
  # > puts `cat temp.alda`
60
- # [piano: c d e f]
55
+ # piano: [c d e f]
61
56
  # c c c c c
62
57
  # > system 'rm temp.alda'
63
58
  # > exit
59
+ #
60
+ # Notice that there is a significant difference between \Alda 1 \REPL and \Alda 2 \REPL.
61
+ # In short, \Alda 2 has a much more powerful \REPL than \Alda 1,
62
+ # so it dropped the <tt>--history</tt> option in the <tt>alda play</tt> command line interface
63
+ # ({alda-lang/alda#367}[https://github.com/alda-lang/alda/issues/367]).
64
+ # It has an nREPL server, and this class simply functions by sending messages to the nREPL server.
65
+ # However, for \Alda 1, this class maintains necessary information
66
+ # in the memory of the Ruby program,
67
+ # and the \REPL is implemented by repeatedly running <tt>alda play</tt> in command line.
68
+ # Therefore, this class functions differently for \Alda 1 and \Alda 2
69
+ # and you thus should not modify Alda::generation during an \REPL session.
70
+ #
71
+ # It is also possible to use this class as a Ruby wrapper of APIs of the \Alda nREPL server
72
+ # in \Alda 2.
73
+ # In this usage, you never need to call #run, and you call #message or #raw_message instead.
74
+ #
75
+ # repl = Alda::REPL.new
76
+ # repl.message :eval_and_play, code: 'piano: c d e f' # => nil
77
+ # repl.message :eval_and_play, code: 'g a b > c' # => nil
78
+ # repl.message :score_text # => "piano: [c d e f]\ng a b > c\n"
79
+ # repl.message :eval_and_play, code: 'this will cause an error' # (raises Alda::NREPLServerError)
64
80
  class Alda::REPL
65
81
 
66
82
  ##
67
83
  # The score object used in Alda::REPL.
68
84
  #
69
85
  # Includes Alda, so it can refer to alda commandline.
86
+ # However, the methods Alda::Score#play, Alda::Score#parse and Alda::Score#export
87
+ # are still retained instead of being overridden by the included module.
70
88
  #
71
89
  # When you are in an \REPL session, you are actually
72
90
  # in an instance of this class,
73
91
  # so you can call the instance methods down here
74
92
  # when you play with an \REPL.
75
- class TempScore < Alda::Score
93
+ class TempScore < ::Alda::Score
76
94
  include Alda
77
95
 
78
- Score.instance_methods(false).each do |meth|
79
- define_method meth, Score.instance_method(meth)
96
+ %i[play parse export].each do |meth|
97
+ define_method meth, Alda::Score.instance_method(meth)
80
98
  end
81
99
 
100
+ ##
101
+ # :call-seq:
102
+ # new(session) -> TempScore
103
+ #
104
+ # Creates a new TempScore for the given \REPL session specified by +session+.
105
+ # It is called in Alda::REPL::new.
82
106
  def initialize session
83
107
  super()
84
108
  @session = session
85
109
  end
86
110
 
111
+ ##
112
+ # :call-seq:
113
+ # to_s -> String
114
+ #
115
+ # Overrides Alda::Score#to_s.
116
+ # Returns the history.
117
+ #
118
+ # $ alda-irb
119
+ # > harmonica_; a b c
120
+ # harmonica: [a b c]
121
+ # > guitar_; c g e
122
+ # guitar: [c g e]
123
+ # > p to_s
124
+ # "harmonica: [a b c]\nguitar: [c g e]\n"
87
125
  def to_s
88
- history
89
- end
90
-
91
- def history
92
- @session.history.to_s
126
+ @session.history
93
127
  end
94
128
 
129
+ ##
130
+ # :call-seq:
131
+ # clear_history() -> nil
132
+ #
133
+ # Clears all the modifications that have been made to the score
134
+ # and start a new one.
135
+ # See #score for an example.
95
136
  def clear_history
96
137
  @session.clear_history
97
138
  end
139
+ alias new clear_history
140
+ alias new_score clear_history
98
141
 
142
+ ##
143
+ # :call-seq:
144
+ # get_binding() -> Binding
145
+ #
146
+ # Returns a Binding for the instance eval local environment of this score.
147
+ # Different callings of this method will return different bindings,
148
+ # and they do not share local variables.
149
+ # This method is called in Alda::REPL::new.
150
+ #
151
+ # $ alda-irb
152
+ # > p get_binding.receiver == self
153
+ # true
99
154
  def get_binding
100
155
  binding
101
156
  end
102
157
 
158
+ ##
159
+ # :call-seq:
160
+ # score() -> nil
161
+ #
162
+ # Print the history (all \Alda code of the score).
163
+ #
164
+ # $ alda-irb
165
+ # > violin_; a b
166
+ # violin: [a b]
167
+ # > score
168
+ # violin: [a b]
169
+ # > clear_history
170
+ # > score
171
+ # > viola_; c
172
+ # viola: c
173
+ # > score
174
+ # viola: c
103
175
  def score
104
- puts history
176
+ print @session.color ? @session.history.blue : @session.history
177
+ nil
105
178
  end
179
+ alias score_text score
106
180
 
181
+ ##
182
+ # :call-seq:
183
+ # map() -> nil
184
+ #
185
+ # Prints a data representation of the score.
186
+ # This is the output that you get when you call Alda::Score#parse.
107
187
  def map
108
- puts JSON.generate JSON.parse(parse),
109
- indent: ' ', space: ' ', object_nl: ?\n, array_nl: ?\n
188
+ json = Alda.v1? ? parse : @session.message(:score_data)
189
+ json = JSON.generate JSON.parse(json), indent: ' ', space: ' ', object_nl: ?\n, array_nl: ?\n
190
+ puts @session.color ? json.blue : json
191
+ end
192
+ alias score_data map
193
+
194
+ ##
195
+ # :call-seq:
196
+ # score_events() -> nil
197
+ #
198
+ # Prints the parsed events output of the score.
199
+ # This is the output that you get when you call Alda::Score#parse with <tt>output: :events</tt>.
200
+ def score_events
201
+ json = Alda.v1? ? parse(output: :events) : @session.message(:score_events)
202
+ json = JSON.generate JSON.parse(json), indent: ' ', space: ' ', object_nl: ?\n, array_nl: ?\n
203
+ puts @session.color ? json.blue : json
110
204
  end
111
205
 
112
206
  alias quit exit
113
- alias new clear_history
114
207
  end
115
208
 
116
209
  ##
117
- # The history.
118
- attr_reader :history
210
+ # The host of the nREPL server. Only useful in \Alda 2.
211
+ attr_reader :host
212
+
213
+ ##
214
+ # The port of the nREPL server. Only useful in \Alda 2.
215
+ attr_reader :port
216
+
217
+ ##
218
+ # Whether the output should be colored.
219
+ attr_accessor :color
220
+
221
+ ##
222
+ # Whether a preview of what \Alda code will be played everytime you input ruby codes.
223
+ attr_accessor :preview
119
224
 
120
225
  ##
121
226
  # :call-seq:
122
- # new() -> Alda::REPL
227
+ # new(**opts) -> Alda::REPL
123
228
  #
124
229
  # Creates a new Alda::REPL.
125
- def initialize
230
+ # The parameter +color+ specifies whether the output should be colored (sets #color).
231
+ # The parameter +preview+ specifies whether a preview of what \Alda code will be played
232
+ # everytime you input ruby codes (sets #preview).
233
+ #
234
+ # The +opts+ are passed to the command line of <tt>alda repl</tt>.
235
+ # Available options are +host+, +port+, etc.
236
+ # Run <tt>alda repl --help</tt> for more info.
237
+ # If +port+ is specified and +host+ is not or is specified to be <tt>"localhost"</tt>
238
+ # or <tt>"127.0.0.1"</tt>, then this method will try to connect to an existing
239
+ # \Alda REPL server.
240
+ # A new one will be started only if no existing server is found.
241
+ #
242
+ # The +opts+ are ignored in \Alda 1.
243
+ def initialize color: true, preview: true, **opts
126
244
  @score = TempScore.new self
127
245
  @binding = @score.get_binding
128
246
  @lex = RubyLex.new
129
- @history = StringIO.new
247
+ @color = color
248
+ @preview = preview
249
+ setup_repl opts
250
+ end
251
+
252
+ ##
253
+ # :call-seq:
254
+ # setup_repl(opts) -> nil
255
+ #
256
+ # Sets up the \REPL session.
257
+ # This method is called in ::new.
258
+ # After you #terminate the session,
259
+ # you cannot use the \REPL anymore unless you call this method again.
260
+ def setup_repl opts
261
+ if Alda.v1?
262
+ @history = StringIO.new
263
+ else
264
+ if opts[:port] && [nil, 'localhost', '127.0.0.1'].include?(opts[:host]) &&
265
+ Alda.processes.any? { _1[:port] == opts[:port].to_i && _1[:type] == :repl_server }
266
+ @port = opts[:port].to_i
267
+ @host = opts[:host] || 'localhost'
268
+ else
269
+ @nrepl_pipe = Alda.pipe :repl, **opts, server: true
270
+ /nrepl:\/\/(?<host>[a-zA-Z0-9._\-]+):(?<port>\d+)/ =~ @nrepl_pipe.gets
271
+ @host = host
272
+ @port = port.to_i
273
+ Process.detach @nrepl_pipe.pid
274
+ end
275
+ @socket = TCPSocket.new @host, @port
276
+ @bencode_parser = BEncode::Parser.new @socket
277
+ end
278
+ nil
279
+ end
280
+
281
+ ##
282
+ # :call-seq:
283
+ # raw_message(contents) -> Hash
284
+ #
285
+ # Sends a message to the nREPL server and returns the response.
286
+ # The parameter +contents+ is a Hash or a JSON string.
287
+ #
288
+ # repl = Alda::REPL.new
289
+ # repl.raw_message op: 'describe' # => {"ops"=>...}
290
+ def raw_message contents
291
+ Alda::GenerationError.assert_generation [:v2]
292
+ contents = JSON.parse contents if contents.is_a? String
293
+ @socket.write contents.bencode
294
+ @bencode_parser.parse!
295
+ end
296
+
297
+ ##
298
+ # :call-seq:
299
+ # message(op, **params) -> String or Hash
300
+ #
301
+ # Sends a message to the nREPL server with the following format,
302
+ # with +op+ being the operation name (the +op+ field in the message),
303
+ # and +params+ being the parameters (other fields in the message).
304
+ # Then, this method analyzes the response.
305
+ # If there is an error, raises Alda::NREPLServerError.
306
+ # Otherwise, if the response contains only one field, return the content of that field (a String).
307
+ # Otherwise, return the whole response as a Hash.
308
+ #
309
+ # repl = Alda::REPL.new
310
+ # repl.message :eval_and_play, code: 'piano: c d e f' # => nil
311
+ # repl.message :eval_and_play, code: 'g a b > c' # => nil
312
+ # repl.message :score_text # => "piano: [c d e f]\ng a b > c\n"
313
+ # repl.message :eval_and_play, code: 'this will cause an error' # (raises Alda::NREPLServerError)
314
+ def message op, **params
315
+ result = raw_message op: Alda::Utils.snake_to_slug(op), **params
316
+ result.transform_keys! { Alda::Utils.slug_to_snake _1 }
317
+ if result.delete(:status).include? 'error'
318
+ raise Alda::NREPLServerError.new @host, @port, result.delete(:problems)
319
+ end
320
+ case result.size
321
+ when 0 then nil
322
+ when 1 then result.values.first
323
+ else result
324
+ end
130
325
  end
131
326
 
132
327
  ##
@@ -134,10 +329,11 @@ class Alda::REPL
134
329
  # run() -> nil
135
330
  #
136
331
  # Runs the session.
137
- # Includes the start, the main loop, and the termination.
332
+ # Includes the start (#start), the main loop, and the termination (#terminate).
138
333
  def run
139
334
  start
140
335
  while code = rb_code
336
+ next if code.empty?
141
337
  break unless process_rb_code code
142
338
  end
143
339
  terminate
@@ -159,18 +355,37 @@ class Alda::REPL
159
355
  # It can intelligently continue reading if the code is not complete yet.
160
356
  def rb_code
161
357
  result = ''
358
+ indent = 0
162
359
  begin
163
- buf = Readline.readline '> '.green, true
164
- return unless buf
165
- result.concat buf, ?\n
360
+ result.concat readline(indent).tap { return unless _1 }, ?\n
166
361
  ltype, indent, continue, block_open = @lex.check_state result
167
362
  rescue Interrupt
168
363
  $stdout.puts
169
- retry
364
+ return ''
170
365
  end while ltype || indent.nonzero? || continue || block_open
171
366
  result
172
367
  end
173
368
 
369
+ ##
370
+ # :call-seq:
371
+ # readline(indent = 0) -> String
372
+ #
373
+ # Prompts the user to input a line.
374
+ # The parameter +indent+ is the indentation level.
375
+ # Twice the number of spaces is already in the input field before the user fills in.
376
+ # The prompt hint is different for zero +indent+ and nonzero +indent+.
377
+ # Returns the user input.
378
+ def readline indent = 0
379
+ prompt = indent.nonzero? ? '. ' : '> '
380
+ prompt = prompt.green if @color
381
+ Reline.pre_input_hook = -> do
382
+ Reline.insert_text ' ' * indent
383
+ Reline.redisplay
384
+ Reline.pre_input_hook = nil
385
+ end
386
+ Reline.readline prompt, true
387
+ end
388
+
174
389
  ##
175
390
  # :call-seq:
176
391
  # process_rb_code(code) -> true or false
@@ -182,18 +397,16 @@ class Alda::REPL
182
397
  @score.clear
183
398
  begin
184
399
  @binding.eval code
185
- rescue StandardError, ScriptError => e
400
+ rescue StandardError, ScriptError, Interrupt => e
186
401
  $stderr.print e.full_message
187
402
  return true
188
- rescue Interrupt
189
- return true
190
403
  rescue SystemExit
191
404
  return false
192
405
  end
193
406
  code = @score.events_alda_codes
194
407
  unless code.empty?
195
- $stdout.puts code.yellow
196
- play_score code
408
+ $stdout.puts @color ? code.yellow : code
409
+ try_command { play_score code }
197
410
  end
198
411
  true
199
412
  end
@@ -202,12 +415,15 @@ class Alda::REPL
202
415
  # :call-seq:
203
416
  # try_command() { ... } -> obj
204
417
  #
205
- # Tries to run the block and rescue Alda::CommandLineError.
418
+ # Run the block.
419
+ # In \Alda 1, catches Alda::CommandLineError.
420
+ # In \Alda 2, catches Alda::NREPLServerError.
421
+ # If an error is caught, prints the error message (in red if #color is true).
206
422
  def try_command
207
423
  begin
208
424
  yield
209
- rescue Alda::CommandLineError => e
210
- puts e.message.red
425
+ rescue Alda.v1? ? Alda::CommandLineError : Alda::NREPLServerError => e
426
+ puts @color ? e.message.red : e.message
211
427
  end
212
428
  end
213
429
 
@@ -215,11 +431,15 @@ class Alda::REPL
215
431
  # :call-seq:
216
432
  # play_score(code) -> nil
217
433
  #
218
- # Plays the score by sending +code+ to command line alda.
434
+ # Appends +code+ to the history and plays the +code+ as \Alda code.
435
+ # In \Alda 1, plays the score by sending +code+ to command line alda.
436
+ # In \Alda 2, sends +code+ to the nREPL server for evaluating and playing.
219
437
  def play_score code
220
- try_command do
438
+ if Alda.v1?
221
439
  Alda.play code: code, history: @history
222
440
  @history.puts code
441
+ else
442
+ message :eval_and_play, code: code
223
443
  end
224
444
  end
225
445
 
@@ -228,18 +448,50 @@ class Alda::REPL
228
448
  # terminate() -> nil
229
449
  #
230
450
  # Terminates the REPL session.
231
- # Currently just clears #history.
451
+ # In \Alda 1, just calls #clear_history.
452
+ # In \Alda 2, sends a SIGINT to the nREPL server if it was spawned by the Ruby program.
232
453
  def terminate
233
- clear_history
454
+ if Alda.v1?
455
+ clear_history
456
+ else
457
+ if @nrepl_pipe
458
+ if Alda::Utils.win_platform?
459
+ system 'taskkill', '/f', '/pid', @nrepl_pipe.pid.to_s
460
+ else
461
+ Process.kill :INT, @nrepl_pipe.pid
462
+ end
463
+ @nrepl_pipe.close
464
+ end
465
+ @socket.close
466
+ end
467
+ end
468
+
469
+ ##
470
+ # :call-seq:
471
+ # history() -> String
472
+ #
473
+ # In \Alda 1, it is the same as an attribute reader.
474
+ # In \Alda 2, it asks the nREPL server for its score text and returns it.
475
+ def history
476
+ if Alda.v1?
477
+ @history
478
+ else
479
+ try_command { message :score_text }
480
+ end
234
481
  end
235
482
 
236
483
  ##
237
484
  # :call-seq:
238
485
  # clear_history() -> nil
239
486
  #
240
- # Clears #history.
487
+ # In \Alda 1, clears #history.
488
+ # In \Alda 2, askes the nREPL server to clear its history (start a new score).
241
489
  def clear_history
242
- @history = StringIO.new
490
+ if Alda.v1?
491
+ @history = StringIO.new
492
+ else
493
+ try_command { message :new_score }
494
+ end
243
495
  nil
244
496
  end
245
497
  end
@@ -0,0 +1,47 @@
1
+ ##
2
+ # Some useful functions.
3
+ module Alda::Utils
4
+
5
+ ##
6
+ # :call-seq:
7
+ # warn(message) -> nil
8
+ #
9
+ # Prints a warning message to standard error, appended by a newline.
10
+ # The message is prefixed with the filename and lineno of the caller
11
+ # (the lowest level where the file is not an alda-rb source file).
12
+ def warn message
13
+ location = caller_locations.find { !_1.path.start_with? __dir__ }
14
+ Warning.warn "#{location.path}:#{location.lineno}: #{message}\n"
15
+ end
16
+
17
+ ##
18
+ # :call-seq:
19
+ # win_platform? -> true or false
20
+ #
21
+ # Returns whether the current platform is Windows.
22
+ def win_platform?
23
+ Gem.win_platform?
24
+ end
25
+
26
+ ##
27
+ # :call-seq:
28
+ # snake_to_slug(sym) -> String
29
+ #
30
+ # Converts a snake_case Symbol to a slug-case String.
31
+ # The inverse of ::slug_to_snake.
32
+ def snake_to_slug sym
33
+ sym.to_s.gsub ?_, ?-
34
+ end
35
+
36
+ ##
37
+ # :call-seq:
38
+ # slug_to_snake(str) -> Symbol
39
+ #
40
+ # Converts a slug-case String to a snake_case Symbol.
41
+ # The inverse of ::snake_to_slug.
42
+ def slug_to_snake str
43
+ str.to_s.gsub(?-, ?_).to_sym
44
+ end
45
+
46
+ module_function :warn, :win_platform?, :snake_to_slug, :slug_to_snake
47
+ end
@@ -7,5 +7,5 @@ module Alda
7
7
  # The version number of alda-rb.
8
8
  #
9
9
  # The same as that in alda-rb gem spec.
10
- VERSION = '0.2.0'
10
+ VERSION = '0.3.0'
11
11
  end
data/lib/alda-rb.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'alda-rb/version'
4
4
  require 'alda-rb/patches'
5
+ require 'alda-rb/utils'
5
6
  require 'alda-rb/error'
6
7
  require 'alda-rb/commandline'
7
8
  require 'alda-rb/event_list'
metadata CHANGED
@@ -1,26 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alda-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ulysses Zhan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-13 00:00:00.000000000 Z
11
+ date: 2023-05-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
15
15
  - UlyssesZhan@gmail.com
16
- executables: []
16
+ executables:
17
+ - alda-irb
17
18
  extensions: []
18
19
  extra_rdoc_files: []
19
20
  files:
21
+ - ".github/workflows/main.yml"
20
22
  - ".gitignore"
21
23
  - ".travis.yml"
24
+ - CHANGELOG.md
22
25
  - CODE_OF_CONDUCT.md
23
26
  - Gemfile
27
+ - Gemfile.lock
24
28
  - LICENSE.txt
25
29
  - README.md
26
30
  - Rakefile
@@ -31,10 +35,12 @@ files:
31
35
  - examples/bwv846_prelude.rb
32
36
  - examples/clapping_music.rb
33
37
  - examples/dot_accessor.rb
38
+ - examples/dynamics.rb
34
39
  - examples/entropy.rb
35
40
  - examples/hanon.rb
36
41
  - examples/hello_world.rb
37
42
  - examples/key_signature.rb
43
+ - examples/marriage_d_amour.rb
38
44
  - examples/midi_note_numbers.rb
39
45
  - examples/modes2.rb
40
46
  - examples/multi_poly.rb
@@ -42,7 +48,10 @@ files:
42
48
  - examples/phase.rb
43
49
  - examples/seconds_and_milliseconds.rb
44
50
  - examples/showcase.rb
51
+ - examples/track-volume.rb
52
+ - examples/variables-2.rb
45
53
  - examples/variables.rb
54
+ - exe/alda-irb
46
55
  - lib/alda-rb.rb
47
56
  - lib/alda-rb/commandline.rb
48
57
  - lib/alda-rb/error.rb
@@ -50,14 +59,15 @@ files:
50
59
  - lib/alda-rb/event_list.rb
51
60
  - lib/alda-rb/patches.rb
52
61
  - lib/alda-rb/repl.rb
62
+ - lib/alda-rb/utils.rb
53
63
  - lib/alda-rb/version.rb
54
- homepage: https://github.com/UlyssesZh/alda-rb
64
+ homepage: https://ulysseszh.github.io/doc/alda-rb
55
65
  licenses:
56
66
  - MIT
57
67
  metadata:
58
- homepage_uri: https://github.com/UlyssesZh/alda-rb
68
+ homepage_uri: https://ulysseszh.github.io/doc/alda-rb
59
69
  source_code_uri: https://github.com/UlyssesZh/alda-rb
60
- changelog_uri: https://github.com/UlyssesZh/alda-rb/releases
70
+ changelog_uri: https://ulysseszh.github.io/doc/alda-rb/CHANGELOG_md.html
61
71
  post_install_message:
62
72
  rdoc_options: []
63
73
  require_paths:
@@ -73,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
83
  - !ruby/object:Gem::Version
74
84
  version: '0'
75
85
  requirements: []
76
- rubygems_version: 3.1.2
86
+ rubygems_version: 3.1.6
77
87
  signing_key:
78
88
  specification_version: 4
79
89
  summary: A Ruby library for live-coding music with Alda.