diff --git a/Gemfile b/Gemfile
index 5026f76..75b364e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,7 +1,7 @@
source "https://rubygems.org"
-# Specify your gem's dependencies in alda-rb-rb.gemspec
gemspec
gem "rake", "~> 12.0"
gem "minitest", "~> 5.0"
+gem "colorize"
diff --git a/alda-rb.gemspec b/alda-rb.gemspec
index 18542e6..7d95bb6 100644
--- a/alda-rb.gemspec
+++ b/alda-rb.gemspec
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
spec.name = "alda-rb"
spec.version = Alda::VERSION
spec.authors = ["Ulysses Zhan"]
- spec.email = ["2938747508@qq.com"]
+ spec.email = ["UlyssesZhan@gmail.com"]
spec.summary = %q{A Ruby library for live-coding music with Alda.}
# spec.description = %q{TODO: Write a longer description or delete this line.}
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
- # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
+ spec.metadata["changelog_uri"] = "https://github.com/UlyssesZh/alda-rb/releases"
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
diff --git a/lib/alda-rb.rb b/lib/alda-rb.rb
index c93b3f8..a02b97f 100644
--- a/lib/alda-rb.rb
+++ b/lib/alda-rb.rb
@@ -3,6 +3,7 @@
require 'stringio'
require 'irb/ruby-lex'
require 'alda-rb/version'
+require 'colorize'
{
Array => -> { "[#{map(&:to_alda_code).join ' '}]" },
@@ -34,6 +35,18 @@ def to_s
end
end
+module Kernel
+ # Runs the alda command.
+ # Does not capture output.
+ # @example
+ # alda 'version'
+ # alda 'play', '-c', 'piano: a'
+ # alda 'repl'
+ def alda *args
+ system Alda.executable, *args
+ end
+end
+
# The module serving as a namespace.
module Alda
@@ -60,15 +73,25 @@ module Alda
].freeze
COMMANDS.each do |command|
- define_method command do |options = {}, **command_options|
- args = []
- block = ->key, val { args.push "--#{key.to_s.tr ?_, ?-}", val.to_s }
- options.each &block
+ define_method command do |*args, **opts|
+ block = ->key, val do
+ next unless val
+ args.push "--#{key.to_s.tr ?_, ?-}"
+ args.push val.to_s unless val == true
+ end
+ # executable
+ args.unshift Alda.executable
+ args.map! &:to_s
+ # options
+ Alda.options.each &block
+ # subcommand
args.push command.to_s
- command_options.each &block
- output = IO.popen [Alda.executable, *args], &:read
- raise CommandLineError.new $?, output if $?.exitstatus.nonzero?
- output
+ # subcommand options
+ opts.each &block
+ # subprocess
+ IO.popen(args, &:read).tap do
+ raise CommandLineError.new $?, _1 if $?.exitstatus.nonzero?
+ end
end
end
@@ -79,6 +102,9 @@ module Alda
singleton_class.attr_accessor :executable
@executable = 'alda'
+ singleton_class.attr_reader :options
+ @options = {}
+
# @return Whether the alda server is up.
def up?
status.include? 'up'
@@ -96,6 +122,18 @@ def self.repl
REPL.new.run
end
+ # Sets the options of alda command.
+ # Not the subcommand options.
+ def self.[] **opts
+ @options.merge! opts
+ self
+ end
+
+ # Clears the command line options.
+ def self.clear_options
+ @options.clear
+ end
+
# Including this module can make your class have the ability
# to have an event list.
# See docs below to get an overview of its functions.
@@ -348,6 +386,10 @@ def history
@session.history.to_s
end
+ def clear_history
+ @session.clear_history
+ end
+
def get_binding
binding
end
@@ -384,7 +426,7 @@ def start
def rb_code
result = ''
begin
- buf = Readline.readline '> ', true
+ buf = Readline.readline '> '.green, true
return unless buf
result.concat buf, ?\n
ltype, indent, continue, block_open = @lex.check_state result
@@ -412,7 +454,7 @@ def process_rb_code code
end
code = @score.events_alda_codes
unless code.empty?
- $stdout.puts code
+ $stdout.puts code.yellow
play_score code
end
true
@@ -423,7 +465,7 @@ def try_command # :block:
begin
yield
rescue CommandLineError => e
- puts e
+ puts e.message.red
end
end
@@ -448,17 +490,32 @@ def clear_history
# The error is raised when one tries to
# run a non-existing subcommand of +alda+.
- class CommandLineError < Exception
+ class CommandLineError < StandardError
# The Process::Status object representing the status of
# the process that runs +alda+ command.
attr_reader :status
+ # The port on which the problematic Alda server runs.
+ # @example
+ # begin
+ # Alda.play({port: 1108}, code: "y")
+ # rescue CommandLineError => e
+ # e.port # => 1108
+ # end
+ attr_reader :port
+
# Create a CommandLineError# object.
# @param status The status of the process running +alda+ command.
# @param msg The exception message.
def initialize status, msg = nil
- super /ERROR\s*(?.*)$/ =~ msg ? message : msg&.lines(chomp: true).first
+ if match = msg&.match(/^\[(?\d+)\]\sERROR\s(?.*)$/)
+ super match[:message]
+ @port = match[:port].to_i
+ else
+ super msg
+ @port = nil
+ end
@status = status
end
end
@@ -469,9 +526,9 @@ def initialize status, msg = nil
# Alda::Score.new do
# motif = f4 f e e d d c2
# g4 f e d c2 # It commented out, error will not occur
- # c4 c g g a a g2 motif # OrderError
+ # c4 c g g a a g2 motif # (OrderError)
# end
- class OrderError < Exception
+ class OrderError < StandardError
# The expected element gotten if it is of the correct order.
# @see #got
diff --git a/test/alda-rb/test.rb b/test/alda-rb/to_alda_code_test.rb
similarity index 100%
rename from test/alda-rb/test.rb
rename to test/alda-rb/to_alda_code_test.rb