gly 0.0.3 → 0.0.4
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/lib/gly.rb +4 -1
- data/lib/gly/cli.rb +71 -8
- data/lib/gly/document.rb +20 -8
- data/lib/gly/document_gabc_convertor.rb +10 -2
- data/lib/gly/exceptions.rb +16 -0
- data/lib/gly/exceptions.rb~ +3 -0
- data/lib/gly/gabc_convertor.rb +29 -11
- data/lib/gly/gly_convertor.rb +52 -0
- data/lib/gly/gly_convertor.rb~ +11 -0
- data/lib/gly/glyfy_cli.rb~ +92 -0
- data/lib/gly/headers.rb +3 -1
- data/lib/gly/lister.rb +41 -6
- data/lib/gly/lyrics.rb +7 -7
- data/lib/gly/markup.rb +15 -0
- data/lib/gly/markup.rb~ +11 -0
- data/lib/gly/parser.rb +65 -23
- data/lib/gly/preview_builder.rb +13 -4
- data/lib/gly/preview_generator.rb +44 -23
- data/lib/gly/{parsed_score.rb → score.rb} +1 -1
- data/lib/gly/templates/lualatex_document.tex +11 -11
- data/tests/examples.rb +27 -7
- data/tests/examples/gly/expected/block_lyrics.gabc +1 -1
- data/tests/examples/gly/expected/block_lyrics.gabc~ +2 -0
- data/tests/examples/gly/expected/block_music.gabc +2 -0
- data/tests/examples/gly/expected/block_music.gabc~ +4 -0
- data/tests/examples/gly/expected/blocks.gabc +3 -0
- data/tests/examples/gly/expected/differentia_final.gabc +2 -0
- data/tests/examples/gly/expected/empty_lyric_syllable.gabc +2 -0
- data/tests/examples/gly/expected/empty_lyric_syllable.gabc~ +1 -0
- data/tests/examples/gly/expected/explicit_lyrics.gabc +1 -1
- data/tests/examples/gly/expected/header_colon.gabc +3 -0
- data/tests/examples/gly/expected/header_colon.gabc~ +3 -0
- data/tests/examples/gly/expected/header_empty.gabc +2 -0
- data/tests/examples/gly/expected/header_empty.gabc~ +2 -0
- data/tests/examples/gly/expected/nabc.gabc +3 -0
- data/tests/examples/gly/expected/no_clef.gabc +2 -0
- data/tests/examples/gly/expected/no_clef.gabc~ +1 -0
- data/tests/examples/gly/expected/real1.gabc~ +15 -0
- data/tests/examples/gly/expected/unsingables.gabc +2 -0
- data/tests/examples/gly/expected/unsingables.gabc~ +2 -0
- data/tests/examples/gly/given/block_lyrics.gly~ +3 -0
- data/tests/examples/gly/given/block_music.gly +4 -0
- data/tests/examples/gly/given/blocks.gly +6 -0
- data/tests/examples/gly/given/differentia_final.gly +5 -0
- data/tests/examples/gly/given/differentia_final.gly~ +2 -0
- data/tests/examples/gly/given/empty_lyric_syllable.gly +2 -0
- data/tests/examples/gly/given/empty_lyric_syllable.gly~ +2 -0
- data/tests/examples/gly/given/header_colon.gly +3 -0
- data/tests/examples/gly/given/header_colon.gly~ +4 -0
- data/tests/examples/gly/given/header_empty.gly +1 -0
- data/tests/examples/gly/given/header_empty.gly~ +2 -0
- data/tests/examples/gly/given/nabc.gly +3 -0
- data/tests/examples/gly/given/nabc.gly~ +2 -0
- data/tests/examples/gly/given/no_clef.gly +2 -0
- data/tests/examples/gly/given/no_clef.gly~ +1 -0
- data/tests/examples/gly/given/real1.gly~ +18 -0
- data/tests/examples/gly/given/unsingables.gly +2 -0
- data/tests/examples/gly/given/unsingables.gly~ +2 -0
- data/tests/examples/glyfy/expected/header.gly +3 -0
- data/tests/examples/glyfy/expected/header.gly~ +2 -0
- data/tests/examples/glyfy/expected/simple.gly +3 -0
- data/tests/examples/glyfy/expected/simple.gly~ +2 -0
- data/tests/examples/glyfy/given/header.gabc +3 -0
- data/tests/examples/glyfy/given/simple.gabc +2 -0
- data/tests/examples/parser/markup_after_header.gly +4 -0
- data/tests/examples/parser/markups.gly +10 -0
- data/tests/examples/parser/markups.gly~ +5 -0
- data/tests/examples/parser/score_markup_order.gly +19 -0
- data/tests/examples/parser/score_markup_order.gly~ +13 -0
- data/tests/parser.rb +51 -0
- data/tests/parser.rb~ +12 -0
- data/tests/run.rb +1 -0
- data/tests/test_helper.rb +19 -0
- metadata +59 -10
- data/bin/glyfy +0 -4
- data/tests/examples.rb~ +0 -9
- data/tests/programmed_examples.rb~ +0 -14
- data/tests/run.rb~ +0 -3
- data/tests/string_helpers_test.rb~ +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a2ffe57b4f0e19ec4b23c18288c6ebc3f72409d
|
4
|
+
data.tar.gz: 0f12ea9c6bb6d325e3b988c78229ffad8bc38a03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea90186703af9c3aa87388f876ef6e7332d7b78ef9babd564a28fa04318051a0b418b705eb5a5f7cda2bd8e8ea22a6e8cda7162b9be7d4f3baccf72a67b3c20e
|
7
|
+
data.tar.gz: 03ebc311afc420dfbebe8bd808c72be8884c3140fde9d802ccfdb8c33525efae27f3fa53203d959963b6c78cad331f56948bd422624953a332b8d3911f1a8a04
|
data/lib/gly.rb
CHANGED
data/lib/gly/cli.rb
CHANGED
@@ -15,6 +15,8 @@ module Gly
|
|
15
15
|
files.each do |f|
|
16
16
|
DocumentGabcConvertor.new(parser.parse(f)).convert
|
17
17
|
end
|
18
|
+
rescue Gly::Exception => ex
|
19
|
+
error_exit! ex
|
18
20
|
end
|
19
21
|
|
20
22
|
desc 'preview FILE ...', 'convert to gabc AND generate pdf preview'
|
@@ -24,19 +26,42 @@ module Gly
|
|
24
26
|
option :template, aliases: :t, banner: 'use custom document template'
|
25
27
|
def preview(*files)
|
26
28
|
tpl = nil
|
27
|
-
|
29
|
+
if options[:template]
|
30
|
+
begin
|
31
|
+
tpl = File.read(options[:template])
|
32
|
+
rescue Errno::ENOENT
|
33
|
+
raise Gly::Exception.new("File not found: '#{options[:template]}'")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# convert HashWithIndifferentAccess to Hash
|
38
|
+
# with Symbol keys - options.to_h would make Hash
|
39
|
+
# with String keys
|
40
|
+
opts = options.each_pair.collect {|k,v| [k.to_sym,v]}.to_h
|
28
41
|
|
29
|
-
opts = options.to_h
|
30
42
|
opts[:suffix_always] = true
|
31
43
|
|
32
44
|
files.each do |f|
|
33
45
|
gen = PreviewGenerator.new template: tpl, options: opts
|
34
|
-
|
46
|
+
begin
|
47
|
+
gen.process(parser.parse(f))
|
48
|
+
rescue Errno::ENOENT
|
49
|
+
with_extension = f + '.gly'
|
50
|
+
if File.exist? with_extension
|
51
|
+
gen.process(parser.parse(with_extension))
|
52
|
+
else
|
53
|
+
raise Gly::Exception.new("File not found: '#{f}'")
|
54
|
+
end
|
55
|
+
end
|
35
56
|
end
|
57
|
+
|
58
|
+
rescue Gly::Exception => ex
|
59
|
+
error_exit! ex
|
36
60
|
end
|
37
61
|
|
38
62
|
desc 'list FILE ...', 'list scores contained in files'
|
39
63
|
option :recursive, type: :boolean, aliases: :r, banner: 'recursively traverse directories', default: false
|
64
|
+
option :format, type: :string, aliases: :f, banner: 'grep|overview'
|
40
65
|
def list(*files)
|
41
66
|
if files.empty?
|
42
67
|
STDERR.puts 'No file specified.'
|
@@ -54,22 +79,48 @@ module Gly
|
|
54
79
|
files.flatten!
|
55
80
|
end
|
56
81
|
|
57
|
-
lister = Lister.new(files)
|
82
|
+
lister = Lister.new(files, options[:format])
|
58
83
|
lister.list(STDOUT, STDERR)
|
59
84
|
|
60
85
|
exit(lister.error? ? 1 : 0)
|
86
|
+
rescue Gly::Exception => ex
|
87
|
+
error_exit! ex
|
61
88
|
end
|
62
89
|
|
63
90
|
desc 'ly FILE ...', 'transform gly document to lilypond document'
|
64
91
|
def ly(*files)
|
65
|
-
|
66
|
-
STDERR.puts "'lygre' gem not found. Please, install lygre in order to run 'gly ly'."
|
67
|
-
exit 1
|
68
|
-
end
|
92
|
+
check_lygre_available!
|
69
93
|
|
70
94
|
files.each do |f|
|
71
95
|
DocumentLyConvertor.new(parser.parse(f)).convert
|
72
96
|
end
|
97
|
+
|
98
|
+
rescue Gly::Exception => ex
|
99
|
+
error_exit! ex
|
100
|
+
end
|
101
|
+
|
102
|
+
desc 'fy FILE ...', 'transform gabc to gly'
|
103
|
+
def fy(*files)
|
104
|
+
check_lygre_available!
|
105
|
+
|
106
|
+
files.each_with_index do |f,i|
|
107
|
+
input = File.read f
|
108
|
+
|
109
|
+
parser = GabcParser.new
|
110
|
+
result = parser.parse(input)
|
111
|
+
|
112
|
+
if result then
|
113
|
+
puts if i >= 1
|
114
|
+
GlyConvertor.new.convert result.create_score, STDOUT
|
115
|
+
else
|
116
|
+
STDERR.puts 'glyfy considers the input invalid gabc:'
|
117
|
+
STDERR.puts
|
118
|
+
STDERR.puts "'#{parser.failure_reason}' on line #{parser.failure_line} column #{parser.failure_column}:"
|
119
|
+
STDERR.puts input.split("\n")[parser.failure_line-1]
|
120
|
+
STDERR.puts (" " * parser.failure_column) + "^"
|
121
|
+
return false
|
122
|
+
end
|
123
|
+
end
|
73
124
|
end
|
74
125
|
|
75
126
|
private
|
@@ -78,6 +129,13 @@ module Gly
|
|
78
129
|
Parser.new options[:separator]
|
79
130
|
end
|
80
131
|
|
132
|
+
def check_lygre_available!
|
133
|
+
unless defined? LilypondConvertor
|
134
|
+
STDERR.puts "'lygre' gem not found. Please, install lygre in order to run 'gly ly'."
|
135
|
+
exit 1
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
81
139
|
class << self
|
82
140
|
# override Thor's default handler
|
83
141
|
def handle_no_command_error(command, has_namespace=$thor_runner)
|
@@ -88,5 +146,10 @@ module Gly
|
|
88
146
|
end
|
89
147
|
end
|
90
148
|
end
|
149
|
+
|
150
|
+
def error_exit!(exception)
|
151
|
+
STDERR.puts 'ERROR: ' + exception.message
|
152
|
+
exit 1
|
153
|
+
end
|
91
154
|
end
|
92
155
|
end
|
data/lib/gly/document.rb
CHANGED
@@ -5,24 +5,36 @@ module Gly
|
|
5
5
|
class Document
|
6
6
|
def initialize
|
7
7
|
@scores = []
|
8
|
+
@content = []
|
8
9
|
@scores_by_id = {}
|
9
10
|
@header = Headers.new
|
10
11
|
@path = nil
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
+
# only Scores
|
15
|
+
attr_reader :scores
|
16
|
+
|
17
|
+
# Scores and Markups
|
18
|
+
attr_reader :content
|
19
|
+
|
20
|
+
attr_reader :header
|
14
21
|
attr_accessor :path
|
15
22
|
|
23
|
+
# append a new Score (or Markup)
|
16
24
|
def <<(score)
|
17
|
-
@
|
25
|
+
@content << score
|
18
26
|
|
19
|
-
|
20
|
-
|
21
|
-
if @scores_by_id.has_key? sid
|
22
|
-
raise ArgumentError.new("More than one score with id '#{sid}'.")
|
23
|
-
end
|
27
|
+
if score.is_a? Score
|
28
|
+
@scores << score
|
24
29
|
|
25
|
-
|
30
|
+
sid = score.headers['id']
|
31
|
+
if sid
|
32
|
+
if @scores_by_id.has_key? sid
|
33
|
+
raise ArgumentError.new("More than one score with id '#{sid}'.")
|
34
|
+
end
|
35
|
+
|
36
|
+
@scores_by_id[sid] = score
|
37
|
+
end
|
26
38
|
end
|
27
39
|
|
28
40
|
self
|
@@ -17,14 +17,18 @@ module Gly
|
|
17
17
|
# iterates over document scores,
|
18
18
|
# yields score and filename of it's generated gabc file
|
19
19
|
def each_score_with_gabcname
|
20
|
+
return to_enum(:each_score_with_gabcname) unless block_given?
|
21
|
+
|
20
22
|
@doc.scores.each_with_index do |score, si|
|
21
|
-
|
23
|
+
gabc = gabc_fname(score, si)
|
24
|
+
gtex = gtex_fname(score, si)
|
25
|
+
yield score, gabc, gtex
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
25
29
|
private
|
26
30
|
|
27
|
-
def
|
31
|
+
def gabc_fname(score, score_index=nil)
|
28
32
|
if @doc.scores.size == 1 && !@options[:suffix_always]
|
29
33
|
score_id = ''
|
30
34
|
else
|
@@ -34,5 +38,9 @@ module Gly
|
|
34
38
|
File.basename(@doc.path)
|
35
39
|
.sub(/\.gly\Z/i, "#{score_id}.gabc")
|
36
40
|
end
|
41
|
+
|
42
|
+
def gtex_fname(score, score_index=nil)
|
43
|
+
gabc_fname(score, score_index).sub('.gabc', '.gtex')
|
44
|
+
end
|
37
45
|
end
|
38
46
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Gly
|
2
|
+
# general application-specific error
|
3
|
+
class Exception < StandardError
|
4
|
+
def self.wrap(exception)
|
5
|
+
e = new exception.message
|
6
|
+
e.wrapped_exception = exception
|
7
|
+
e
|
8
|
+
end
|
9
|
+
|
10
|
+
# may be nil
|
11
|
+
attr_accessor :wrapped_exception
|
12
|
+
end
|
13
|
+
|
14
|
+
# error when parsing gly input file
|
15
|
+
class ParseError < Exception; end
|
16
|
+
end
|
data/lib/gly/gabc_convertor.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'stringio'
|
2
2
|
|
3
3
|
module Gly
|
4
|
-
# takes
|
4
|
+
# takes Score, translates it to gabc
|
5
5
|
class GabcConvertor
|
6
6
|
def convert(score, out=StringIO.new)
|
7
7
|
score.headers.each_pair do |key,value|
|
@@ -12,43 +12,61 @@ module Gly
|
|
12
12
|
out.puts '%%'
|
13
13
|
|
14
14
|
lyric_enum = score.lyrics.each_syllable.to_enum
|
15
|
+
|
15
16
|
score.music.each_with_index do |mus_chunk,i|
|
16
17
|
begin
|
17
18
|
next_syl = lyric_enum.peek
|
19
|
+
out.print lyric_enum.next if next_syl == ' '
|
18
20
|
rescue StopIteration
|
19
21
|
next_syl = ''
|
20
|
-
end
|
22
|
+
end until next_syl != ' '
|
21
23
|
|
22
|
-
if
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
if no_lyrics? mus_chunk, next_syl
|
25
|
+
if i == score.music.size - 1
|
26
|
+
out.print ' '
|
27
|
+
end
|
26
28
|
else
|
27
29
|
# regular music chunk
|
28
30
|
begin
|
29
31
|
out.print strip_directives lyric_enum.next
|
30
32
|
rescue StopIteration
|
31
|
-
out.print ' ' if i
|
33
|
+
out.print ' ' if i > 0 # don't add space at the very beginning
|
32
34
|
end
|
33
35
|
end
|
36
|
+
|
34
37
|
out.print "(#{mus_chunk})"
|
35
|
-
|
38
|
+
if no_lyrics?(mus_chunk, next_syl) &&
|
39
|
+
i != (score.music.size - 1) &&
|
40
|
+
! score.lyrics.empty?
|
41
|
+
out.print ' '
|
42
|
+
end
|
36
43
|
end
|
37
44
|
|
38
45
|
return out
|
39
46
|
end
|
40
47
|
|
48
|
+
def no_lyrics?(music_chunk, syllable)
|
49
|
+
clef?(music_chunk) ||
|
50
|
+
(nonlyrical_chunk?(music_chunk) &&
|
51
|
+
! nonlyrical_lyrics?(syllable))
|
52
|
+
end
|
53
|
+
|
41
54
|
def clef?(chunk)
|
42
55
|
chunk =~ /\A[cf][1-4]\Z/
|
43
56
|
end
|
44
57
|
|
45
|
-
def
|
46
|
-
chunk
|
58
|
+
def without_differentiae(chunk)
|
59
|
+
chunk.gsub /(([,`])|(:[:']?)|(;[1-6]?))/, ''
|
60
|
+
end
|
61
|
+
|
62
|
+
def without_breaks(chunk)
|
63
|
+
chunk.gsub /[zZ]/, ''
|
47
64
|
end
|
48
65
|
|
49
66
|
# is the given music chunk capable of bearing lyrics?
|
50
67
|
def nonlyrical_chunk?(chunk)
|
51
|
-
|
68
|
+
chunk.size > 0 &&
|
69
|
+
without_breaks(without_differentiae(chunk)).empty?
|
52
70
|
end
|
53
71
|
|
54
72
|
def nonlyrical_lyrics?(syl)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Gly
|
4
|
+
# converts gabc to gly
|
5
|
+
class GlyConvertor
|
6
|
+
# score - GabcScore (from the lygre gem)
|
7
|
+
def convert(score, output_stream=StringIO.new)
|
8
|
+
out = NonemptyPrinter.new output_stream
|
9
|
+
out.puts '\score'
|
10
|
+
out.puts header score
|
11
|
+
out.puts music score
|
12
|
+
out.puts lyrics score
|
13
|
+
out
|
14
|
+
end
|
15
|
+
|
16
|
+
def header(score)
|
17
|
+
score.header.each_pair.collect do |key,value|
|
18
|
+
"#{key}: #{value}"
|
19
|
+
end.join "\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
def music(score)
|
23
|
+
score.music.words.collect do |word|
|
24
|
+
word.each_syllable.collect do |syl|
|
25
|
+
syl = syl.notes.collect(&:text_value).join ''
|
26
|
+
if syl.include? ' ' || syl.empty?
|
27
|
+
syl = "(#{syl})"
|
28
|
+
end
|
29
|
+
syl
|
30
|
+
end.join ' '
|
31
|
+
end.flatten.join ' '
|
32
|
+
end
|
33
|
+
|
34
|
+
def lyrics(score)
|
35
|
+
score.music.words
|
36
|
+
.collect {|w| word w }
|
37
|
+
.select {|w_str| ! w_str.empty? }
|
38
|
+
.join(' ')
|
39
|
+
end
|
40
|
+
|
41
|
+
def word(word)
|
42
|
+
word.each_syllable.collect {|s| s.lyrics }.join ' -- '
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class NonemptyPrinter < SimpleDelegator
|
47
|
+
def puts(what)
|
48
|
+
return if what.nil? || what.empty?
|
49
|
+
super(what)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'grely'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
8
|
+
module Gly
|
9
|
+
# implements the 'gly' executable
|
10
|
+
class CLI < Thor
|
11
|
+
class_option :separator, aliases: :s, banner: 'syllable separator (default is double dash "--")'
|
12
|
+
|
13
|
+
desc 'gabc FILE ...', 'convert gly to gabc'
|
14
|
+
def gabc(*files)
|
15
|
+
files.each do |f|
|
16
|
+
DocumentGabcConvertor.new(parser.parse(f)).convert
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'preview FILE ...', 'convert to gabc AND generate pdf preview'
|
21
|
+
option :no_build, type: :boolean, aliases: :B, banner: 'only generate preview assets, don\'t compile them'
|
22
|
+
option :no_document, type: :boolean, aliases: :D, banner: 'produce main LaTeX file without document definition; in this case --no-build is applied automatically'
|
23
|
+
option :full_headers, type: :boolean, aliases: :h, banner: 'include full document and score headers'
|
24
|
+
option :template, aliases: :t, banner: 'use custom document template'
|
25
|
+
def preview(*files)
|
26
|
+
tpl = nil
|
27
|
+
tpl = File.read(options[:template]) if options[:template]
|
28
|
+
|
29
|
+
opts = options.to_h
|
30
|
+
opts[:suffix_always] = true
|
31
|
+
|
32
|
+
files.each do |f|
|
33
|
+
gen = PreviewGenerator.new template: tpl, options: opts
|
34
|
+
gen.process(parser.parse(f))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'list FILE ...', 'list scores contained in files'
|
39
|
+
option :recursive, type: :boolean, aliases: :r, banner: 'recursively traverse directories', default: false
|
40
|
+
def list(*files)
|
41
|
+
if files.empty?
|
42
|
+
STDERR.puts 'No file specified.'
|
43
|
+
exit 1
|
44
|
+
end
|
45
|
+
|
46
|
+
if options[:recursive]
|
47
|
+
files = files.collect do |f|
|
48
|
+
if File.directory?(f)
|
49
|
+
Dir[File.join(f, '**/*.gly')]
|
50
|
+
else
|
51
|
+
f
|
52
|
+
end
|
53
|
+
end
|
54
|
+
files.flatten!
|
55
|
+
end
|
56
|
+
|
57
|
+
lister = Lister.new(files)
|
58
|
+
lister.list(STDOUT, STDERR)
|
59
|
+
|
60
|
+
exit(lister.error? ? 1 : 0)
|
61
|
+
end
|
62
|
+
|
63
|
+
desc 'ly FILE ...', 'transform gly document to lilypond document'
|
64
|
+
def ly(*files)
|
65
|
+
unless defined? LilypondConvertor
|
66
|
+
STDERR.puts "'lygre' gem not found. Please, install lygre in order to run 'gly ly'."
|
67
|
+
exit 1
|
68
|
+
end
|
69
|
+
|
70
|
+
files.each do |f|
|
71
|
+
DocumentLyConvertor.new(parser.parse(f)).convert
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def parser
|
78
|
+
Parser.new options[:separator]
|
79
|
+
end
|
80
|
+
|
81
|
+
class << self
|
82
|
+
# override Thor's default handler
|
83
|
+
def handle_no_command_error(command, has_namespace=$thor_runner)
|
84
|
+
if has_namespace
|
85
|
+
fail Thor::UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace."
|
86
|
+
else
|
87
|
+
fail Thor::UndefinedCommandError, "Could not find command #{command.inspect}. Did you mean 'gly preview #{command}' ?"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|