gly 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|