scout-gear 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitmodules +4 -0
- data/.vimproject +604 -2
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/bin/scout +28 -0
- data/lib/scout/indiferent_hash.rb +0 -30
- data/lib/scout/misc/format.rb +227 -0
- data/lib/scout/misc.rb +1 -0
- data/lib/scout/path/find.rb +15 -9
- data/lib/scout/path/util.rb +4 -5
- data/lib/scout/path.rb +13 -7
- data/lib/scout/simple_opt/accessor.rb +54 -0
- data/lib/scout/simple_opt/doc.rb +120 -0
- data/lib/scout/simple_opt/get.rb +57 -0
- data/lib/scout/simple_opt/parse.rb +66 -0
- data/lib/scout/simple_opt/setup.rb +26 -0
- data/lib/scout/simple_opt.rb +5 -0
- data/lib/scout-gear.rb +1 -0
- data/scout-gear.gemspec +15 -3
- data/test/scout/indiferent_hash/test_case_insensitive.rb +16 -0
- data/test/scout/path/test_find.rb +9 -1
- data/test/scout/simple_opt/test_get.rb +11 -0
- data/test/scout/simple_opt/test_parse.rb +10 -0
- data/test/scout/simple_opt/test_setup.rb +77 -0
- metadata +14 -2
@@ -0,0 +1,227 @@
|
|
1
|
+
module Misc
|
2
|
+
COLOR_LIST = %w(#BC80BD #CCEBC5 #FFED6F #8DD3C7 #FFFFB3 #BEBADA #FB8072 #80B1D3 #FDB462 #B3DE69 #FCCDE5 #D9D9D9)
|
3
|
+
|
4
|
+
def self.colors_for(list)
|
5
|
+
unused = COLOR_LIST.dup
|
6
|
+
|
7
|
+
used = {}
|
8
|
+
colors = list.collect do |elem|
|
9
|
+
if used.include? elem
|
10
|
+
used[elem]
|
11
|
+
else
|
12
|
+
color = unused.shift
|
13
|
+
used[elem]=color
|
14
|
+
color
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
[colors, used]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.format_seconds(time, extended = false)
|
22
|
+
seconds = time.to_i
|
23
|
+
str = [seconds/3600, seconds/60 % 60, seconds % 60].map{|t| "%02i" % t }.join(':')
|
24
|
+
str << ".%02i" % ((time - seconds) * 100) if extended
|
25
|
+
str
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.format_paragraph(text, size = 80, indent = 0, offset = 0)
|
29
|
+
i = 0
|
30
|
+
size = size + offset + indent
|
31
|
+
re = /((?:\n\s*\n\s*)|(?:\n\s*(?=\*)))/
|
32
|
+
text.split(re).collect do |paragraph|
|
33
|
+
i += 1
|
34
|
+
str = if i % 2 == 1
|
35
|
+
words = paragraph.gsub(/\s+/, "\s").split(" ")
|
36
|
+
lines = []
|
37
|
+
line = " "*offset
|
38
|
+
word = words.shift
|
39
|
+
while word
|
40
|
+
word = word[0..size-indent-offset-4] + '...' if word.length >= size - indent - offset
|
41
|
+
while word and Log.uncolor(line).length + Log.uncolor(word).length <= size - indent
|
42
|
+
line << word << " "
|
43
|
+
word = words.shift
|
44
|
+
end
|
45
|
+
offset = 0
|
46
|
+
lines << ((" " * indent) << line[0..-2])
|
47
|
+
line = ""
|
48
|
+
end
|
49
|
+
(lines * "\n")
|
50
|
+
else
|
51
|
+
paragraph
|
52
|
+
end
|
53
|
+
offset = 0
|
54
|
+
str
|
55
|
+
end*""
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.format_definition_list_item(dt, dd, size = 80, indent = 20, color = :yellow)
|
59
|
+
dd = "" if dd.nil?
|
60
|
+
dt = Log.color color, dt if color
|
61
|
+
dt = dt.to_s unless dd.empty?
|
62
|
+
len = Log.uncolor(dt).length
|
63
|
+
|
64
|
+
if indent < 0
|
65
|
+
text = format_paragraph(dd, size, indent.abs-1, 0)
|
66
|
+
text = dt << "\n" << text
|
67
|
+
else
|
68
|
+
offset = len - indent
|
69
|
+
offset = 0 if offset < 0
|
70
|
+
text = format_paragraph(dd, size, indent.abs+1, offset)
|
71
|
+
text[0..len-1] = dt
|
72
|
+
end
|
73
|
+
text
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.format_definition_list(defs, size = 80, indent = 20, color = :yellow, sep = "\n\n")
|
77
|
+
entries = []
|
78
|
+
defs.each do |dt,dd|
|
79
|
+
text = format_definition_list_item(dt,dd,size,indent,color)
|
80
|
+
entries << text
|
81
|
+
end
|
82
|
+
entries * sep
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.camel_case(string)
|
86
|
+
return string if string !~ /_/ && string =~ /[A-Z]+.*/
|
87
|
+
string.split(/_|(\d+)/).map{|e|
|
88
|
+
(e =~ /^[A-Z]{2,}$/ ? e : e.capitalize)
|
89
|
+
}.join
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.camel_case_lower(string)
|
93
|
+
string.split('_').inject([]){ |buffer,e|
|
94
|
+
buffer.push(buffer.empty? ? e.downcase : (e =~ /^[A-Z]{2,}$/ ? e : e.capitalize))
|
95
|
+
}.join
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.snake_case(string)
|
99
|
+
return nil if string.nil?
|
100
|
+
string = string.to_s if Symbol === string
|
101
|
+
string.
|
102
|
+
gsub(/([A-Z]{2,})([A-Z][a-z])/,'\1_\2').
|
103
|
+
gsub(/([a-z])([A-Z])/,'\1_\2').
|
104
|
+
gsub(/\s/,'_').gsub(/[^\w_]/, '').
|
105
|
+
split("_").collect{|p| p.match(/[A-Z]{2,}/) ? p : p.downcase } * "_"
|
106
|
+
end
|
107
|
+
|
108
|
+
# source: https://gist.github.com/ekdevdes/2450285
|
109
|
+
# author: Ethan Kramer (https://github.com/ekdevdes)
|
110
|
+
def self.humanize(value, options = {})
|
111
|
+
if options.empty?
|
112
|
+
options[:format] = :sentence
|
113
|
+
end
|
114
|
+
|
115
|
+
values = value.to_s.split('_')
|
116
|
+
values.each_index do |index|
|
117
|
+
# lower case each item in array
|
118
|
+
# Miguel Vazquez edit: Except for acronyms
|
119
|
+
values[index].downcase! unless values[index].match(/[a-zA-Z][A-Z]/)
|
120
|
+
end
|
121
|
+
if options[:format] == :allcaps
|
122
|
+
values.each do |value|
|
123
|
+
value.capitalize!
|
124
|
+
end
|
125
|
+
|
126
|
+
if options.empty?
|
127
|
+
options[:seperator] = " "
|
128
|
+
end
|
129
|
+
|
130
|
+
return values.join " "
|
131
|
+
end
|
132
|
+
|
133
|
+
if options[:format] == :class
|
134
|
+
values.each do |value|
|
135
|
+
value.capitalize!
|
136
|
+
end
|
137
|
+
|
138
|
+
return values.join ""
|
139
|
+
end
|
140
|
+
|
141
|
+
if options[:format] == :sentence
|
142
|
+
values[0].capitalize! unless values[0].match(/[a-zA-Z][A-Z]/)
|
143
|
+
|
144
|
+
return values.join " "
|
145
|
+
end
|
146
|
+
|
147
|
+
if options[:format] == :nocaps
|
148
|
+
return values.join " "
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.fixascii(string)
|
153
|
+
if string.respond_to?(:encode)
|
154
|
+
self.fixutf8(string).encode("ASCII-8BIT")
|
155
|
+
else
|
156
|
+
string
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.to_utf8(string)
|
161
|
+
string.encode("UTF-16BE", :invalid => :replace, :undef => :replace, :replace => "?").encode('UTF-8')
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.fixutf8(string)
|
165
|
+
return nil if string.nil?
|
166
|
+
return string if string.respond_to?(:encoding) && string.encoding.to_s == "UTF-8" && (string.respond_to?(:valid_encoding?) && string.valid_encoding?) ||
|
167
|
+
(string.respond_to?(:valid_encoding) && string.valid_encoding)
|
168
|
+
|
169
|
+
if string.respond_to?(:encode)
|
170
|
+
string.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
|
171
|
+
else
|
172
|
+
require 'iconv'
|
173
|
+
@@ic ||= Iconv.new('UTF-8//IGNORE', 'UTF-8')
|
174
|
+
@@ic.iconv(string)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.humanize_list(list)
|
179
|
+
return "" if list.empty?
|
180
|
+
if list.length == 1
|
181
|
+
list.first
|
182
|
+
else
|
183
|
+
list[0..-2].collect{|e| e.to_s} * ", " << " and " << list[-1].to_s
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.parse_sql_values(txt)
|
188
|
+
io = StringIO.new txt.strip
|
189
|
+
|
190
|
+
values = []
|
191
|
+
fields = []
|
192
|
+
current = nil
|
193
|
+
quoted = false
|
194
|
+
while c = io.getc
|
195
|
+
if quoted
|
196
|
+
if c == "'"
|
197
|
+
quoted = false
|
198
|
+
else
|
199
|
+
current << c
|
200
|
+
end
|
201
|
+
else
|
202
|
+
case c
|
203
|
+
when "("
|
204
|
+
current = ""
|
205
|
+
when ")"
|
206
|
+
fields << current
|
207
|
+
values << fields
|
208
|
+
fields = []
|
209
|
+
current = nil
|
210
|
+
when ','
|
211
|
+
if not current.nil?
|
212
|
+
fields << current
|
213
|
+
current = ""
|
214
|
+
end
|
215
|
+
when "'"
|
216
|
+
quoted = true
|
217
|
+
when ";"
|
218
|
+
break
|
219
|
+
else
|
220
|
+
current << c
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
values
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
data/lib/scout/misc.rb
CHANGED
data/lib/scout/path/find.rb
CHANGED
@@ -1,25 +1,27 @@
|
|
1
|
+
require_relative '../indiferent_hash'
|
1
2
|
module Path
|
3
|
+
|
2
4
|
def _parts
|
3
5
|
@_parts ||= self.split("/")
|
4
6
|
end
|
5
7
|
|
6
8
|
def _subpath
|
7
|
-
@subpath ||= _parts.length > 1 ? _parts[1..-1] * "/" : _parts[0]
|
9
|
+
@subpath ||= _parts.length > 1 ? _parts[1..-1] * "/" : _parts[0] || ""
|
8
10
|
end
|
9
11
|
|
10
12
|
def _toplevel
|
11
|
-
@toplevel ||= _parts.length > 1 ? _parts[0] :
|
13
|
+
@toplevel ||= _parts.length > 1 ? _parts[0] : ""
|
12
14
|
end
|
13
15
|
|
14
16
|
def self.follow(path, map)
|
15
|
-
map.sub('{PKGDIR}', path.pkgdir || Path.default_pkgdir).
|
16
|
-
sub('{RESOURCE}', path.to_s).
|
17
|
+
map.sub('{PKGDIR}', path.pkgdir.respond_to?(:subdir) ? path.pkgdir.pkgdir : path.pkgdir || Path.default_pkgdir).
|
18
|
+
sub('{RESOURCE}', path.pkgdir.to_s).
|
17
19
|
sub('{PWD}', FileUtils.pwd).
|
18
20
|
sub('{TOPLEVEL}', path._toplevel).
|
19
21
|
sub('{SUBPATH}', path._subpath).
|
20
22
|
sub('{BASENAME}', File.basename(path)).
|
21
23
|
sub('{PATH}', path).
|
22
|
-
sub('{LIBDIR}', path.libdir || Path.caller_lib_dir).
|
24
|
+
sub('{LIBDIR}', path.libdir || (path.pkgdir.respond_to?(:libdir) && path.pkgdir.libdir) || Path.caller_lib_dir).
|
23
25
|
sub('{REMOVE}/', '').
|
24
26
|
sub('{REMOVE}', '').gsub(/\/+/,'/')
|
25
27
|
end
|
@@ -48,11 +50,16 @@ module Path
|
|
48
50
|
@@search_order ||= (path_maps.keys & map_search) + (path_maps.keys - map_search)
|
49
51
|
end
|
50
52
|
|
53
|
+
def self.add_path(name, map)
|
54
|
+
@@path_maps[name] = map
|
55
|
+
@@search_order = nil
|
56
|
+
end
|
57
|
+
|
51
58
|
SLASH = "/"[0]
|
52
59
|
DOT = "."[0]
|
53
60
|
def located?
|
54
61
|
# OPEN RESOURCE
|
55
|
-
self.slice(0,1) == SLASH || (self.
|
62
|
+
self.slice(0,1) == SLASH || (self.slice(0,1) == DOT && self.slice(1,2) == SLASH) # || (resource != Rbbt && (Open.remote?(self) || Open.ssh?(self)))
|
56
63
|
end
|
57
64
|
|
58
65
|
def annotate_found_where(found, where)
|
@@ -85,13 +92,13 @@ module Path
|
|
85
92
|
|
86
93
|
def find(where = nil)
|
87
94
|
return self if located?
|
95
|
+
return find_all if where == 'all' || where == :all
|
88
96
|
return follow(where) if where
|
89
97
|
|
90
|
-
|
91
98
|
Path.search_order.each do |map_name|
|
92
99
|
found = follow(map_name, false)
|
93
100
|
|
94
|
-
return annotate_found_where(found, map_name) if File.exist?(found) || File.directory?(
|
101
|
+
return annotate_found_where(found, map_name) if File.exist?(found) || File.directory?(found)
|
95
102
|
end
|
96
103
|
|
97
104
|
return follow(:default)
|
@@ -110,5 +117,4 @@ module Path
|
|
110
117
|
.collect{|where| find(where) }
|
111
118
|
.select{|file| file.exist? }.uniq
|
112
119
|
end
|
113
|
-
|
114
120
|
end
|
data/lib/scout/path/util.rb
CHANGED
@@ -31,12 +31,11 @@ module Path
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def glob_all(pattern = nil, caller_lib = nil, search_paths = nil)
|
34
|
-
search_paths ||= Path.
|
34
|
+
search_paths ||= Path.path_maps
|
35
35
|
search_paths = search_paths.dup
|
36
36
|
|
37
|
-
location_paths = {}
|
38
37
|
search_paths.keys.collect do |where|
|
39
|
-
found = find(where
|
38
|
+
found = find(where)
|
40
39
|
paths = pattern ? Dir.glob(File.join(found, pattern)) : Dir.glob(found)
|
41
40
|
|
42
41
|
paths = paths.collect{|p| self.annotate p }
|
@@ -46,7 +45,7 @@ module Path
|
|
46
45
|
p.where = where
|
47
46
|
end if found.original and pattern
|
48
47
|
|
49
|
-
|
50
|
-
end
|
48
|
+
paths
|
49
|
+
end.flatten.uniq
|
51
50
|
end
|
52
51
|
end
|
data/lib/scout/path.rb
CHANGED
@@ -4,16 +4,17 @@ require_relative 'path/util'
|
|
4
4
|
|
5
5
|
module Path
|
6
6
|
extend MetaExtension
|
7
|
-
extension_attr :pkgdir, :libdir
|
7
|
+
extension_attr :pkgdir, :libdir, :path_maps
|
8
8
|
|
9
9
|
def self.caller_lib_dir(file = nil, relative_to = ['lib', 'bin'])
|
10
10
|
|
11
11
|
if file.nil?
|
12
12
|
caller_dup = caller.dup
|
13
13
|
while file = caller_dup.shift
|
14
|
-
break unless file =~ /scout\/(?:resource\.rb|workflow\.rb)/ or
|
15
|
-
file =~ /scout\/path\.rb/ or
|
16
|
-
file =~ /scout\/
|
14
|
+
break unless file =~ /(?:scout|rbbt)\/(?:resource\.rb|workflow\.rb)/ or
|
15
|
+
file =~ /(?:scout|rbbt)\/(?:.*\/)?path\.rb/ or
|
16
|
+
file =~ /(?:scout|rbbt)\/(?:.*\/)?path\/(?:find|refactor|util)\.rb/ or
|
17
|
+
file =~ /(?:scout|rbbt)\/persist.rb/
|
17
18
|
end
|
18
19
|
file = file.sub(/\.rb[^\w].*/,'.rb')
|
19
20
|
end
|
@@ -34,15 +35,20 @@ module Path
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def self.default_pkgdir
|
37
|
-
@@default_pkgdir
|
38
|
+
@@default_pkgdir ||= 'scout'
|
38
39
|
end
|
40
|
+
|
41
|
+
def self.default_pkgdir=(pkgdir)
|
42
|
+
@@default_pkgdir = pkgdir
|
43
|
+
end
|
44
|
+
|
39
45
|
|
40
46
|
def pkgdir
|
41
47
|
@pkgdir ||= Path.default_pkgdir
|
42
48
|
end
|
43
49
|
|
44
50
|
def libdir
|
45
|
-
@libdir
|
51
|
+
@libdir
|
46
52
|
end
|
47
53
|
|
48
54
|
def join(subpath, prevpath = nil)
|
@@ -50,7 +56,7 @@ module Path
|
|
50
56
|
prevpath = prevpath.to_s if Symbol === prevpath
|
51
57
|
|
52
58
|
subpath = File.join(prevpath.to_s, subpath) if prevpath
|
53
|
-
new = File.join(self, subpath)
|
59
|
+
new = self.empty? ? subpath : File.join(self, subpath)
|
54
60
|
self.annotate(new)
|
55
61
|
new
|
56
62
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module SOPT
|
2
|
+
class << self
|
3
|
+
attr_accessor :inputs, :input_shortcuts, :input_types, :input_descriptions, :input_defaults
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.all
|
7
|
+
@all ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.shortcuts
|
11
|
+
@shortcuts ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.inputs
|
15
|
+
@inputs ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.input_shortcuts
|
19
|
+
@input_shortcuts ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.input_types
|
23
|
+
@input_types ||= {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.input_descriptions
|
27
|
+
@input_descriptions ||= {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.input_defaults
|
31
|
+
@input_defaults ||= {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.reset
|
35
|
+
@shortcuts = {}
|
36
|
+
@all = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.delete_inputs(inputs)
|
40
|
+
inputs.each do |input|
|
41
|
+
input = input.to_s
|
42
|
+
self.shortcuts.delete self.input_shortcuts.delete(input)
|
43
|
+
self.inputs.delete input
|
44
|
+
self.input_types.delete input
|
45
|
+
self.input_defaults.delete input
|
46
|
+
self.input_descriptions.delete input
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.usage
|
51
|
+
puts SOPT.doc
|
52
|
+
exit 0
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require_relative '../log'
|
2
|
+
module SOPT
|
3
|
+
|
4
|
+
class << self
|
5
|
+
attr_accessor :command, :summary, :synopsys, :description
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.command
|
9
|
+
@command ||= File.basename($0)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.summary
|
13
|
+
@summary ||= ""
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.synopsys
|
17
|
+
@synopsys ||= begin
|
18
|
+
"#{command} " <<
|
19
|
+
inputs.collect{|name|
|
20
|
+
"[" << input_format(name, input_types[name] || :string, input_defaults[name], input_shortcuts[name]).sub(/:$/,'') << "]"
|
21
|
+
} * " "
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.description
|
26
|
+
@description ||= "Missing"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.input_format(name, type = nil, default = nil, short = nil)
|
30
|
+
input_str = (short.nil? or short.empty?) ? "--#{name}" : "-#{short},--#{name}"
|
31
|
+
input_str = Log.color(:blue, input_str)
|
32
|
+
extra = case type
|
33
|
+
when nil
|
34
|
+
""
|
35
|
+
when :boolean
|
36
|
+
"[=false]"
|
37
|
+
when :tsv, :text
|
38
|
+
"=<file|->"
|
39
|
+
when :array
|
40
|
+
"=<list|file|->"
|
41
|
+
else
|
42
|
+
"=<#{ type }>"
|
43
|
+
end
|
44
|
+
#extra << " (default: #{Array === default ? (default.length > 3 ? default[0..2]*", " + ', ...' : default*", " ): default})" if default != nil
|
45
|
+
extra << " (default: #{Misc.fingerprint(default)})" if default != nil
|
46
|
+
input_str << Log.color(:green, extra)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.input_doc(inputs, input_types = nil, input_descriptions = nil, input_defaults = nil, input_shortcuts = nil)
|
50
|
+
type = description = default = nil
|
51
|
+
shortcut = ""
|
52
|
+
seen = []
|
53
|
+
inputs.collect do |name|
|
54
|
+
next if seen.include? name
|
55
|
+
seen << name
|
56
|
+
|
57
|
+
type = input_types[name] unless input_types.nil?
|
58
|
+
description = input_descriptions[name] unless input_descriptions.nil?
|
59
|
+
default = input_defaults[name] unless input_defaults.nil?
|
60
|
+
|
61
|
+
name = name.to_s
|
62
|
+
|
63
|
+
case input_shortcuts
|
64
|
+
when nil, FalseClass
|
65
|
+
shortcut = nil
|
66
|
+
when Hash
|
67
|
+
shortcut = input_shortcuts[name]
|
68
|
+
when TrueClass
|
69
|
+
shortcut = fix_shortcut(name[0], name)
|
70
|
+
end
|
71
|
+
|
72
|
+
type = :string if type.nil?
|
73
|
+
register(shortcut, name, type, description) unless self.inputs.include? name
|
74
|
+
|
75
|
+
name = SOPT.input_format(name, type.to_sym, default, shortcut)
|
76
|
+
description
|
77
|
+
Misc.format_definition_list_item(name, description, 80, 31, nil)
|
78
|
+
end * "\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.doc
|
82
|
+
doc = <<-EOF
|
83
|
+
#{Log.color :magenta}#{command}(1) -- #{summary}
|
84
|
+
#{"=" * (command.length + summary.length + 7)}#{Log.color :reset}
|
85
|
+
|
86
|
+
#{ Log.color :magenta, "## SYNOPSYS"}
|
87
|
+
|
88
|
+
#{Log.color :blue, synopsys}
|
89
|
+
|
90
|
+
#{ Log.color :magenta, "## DESCRIPTION"}
|
91
|
+
|
92
|
+
#{Misc.format_paragraph description}
|
93
|
+
|
94
|
+
#{ Log.color :magenta, "## OPTIONS"}
|
95
|
+
|
96
|
+
#{input_doc(inputs, input_types, input_descriptions, input_defaults, input_shortcuts)}
|
97
|
+
EOF
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.doc
|
101
|
+
doc = <<-EOF
|
102
|
+
#{Log.color :magenta}#{command}(1) -- #{summary}
|
103
|
+
#{"=" * (command.length + summary.length + 7)}#{Log.color :reset}
|
104
|
+
|
105
|
+
EOF
|
106
|
+
|
107
|
+
if synopsys and not synopsys.empty?
|
108
|
+
doc << Log.color(:magenta, "## SYNOPSYS") << "\n\n"
|
109
|
+
doc << Log.color(:blue, synopsys) << "\n\n"
|
110
|
+
end
|
111
|
+
|
112
|
+
if description and not description.empty?
|
113
|
+
doc << Log.color(:magenta, "## DESCRIPTION") << "\n\n"
|
114
|
+
doc << Misc.format_paragraph(description) << "\n\n"
|
115
|
+
end
|
116
|
+
|
117
|
+
doc << Log.color(:magenta, "## OPTIONS") << "\n\n"
|
118
|
+
doc << input_doc(inputs, input_types, input_descriptions, input_defaults, input_shortcuts)
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module SOPT
|
2
|
+
GOT_OPTIONS= IndiferentHash.setup({})
|
3
|
+
def self.current_options=(options)
|
4
|
+
@@current_options = options
|
5
|
+
end
|
6
|
+
def self.consume(args = ARGV)
|
7
|
+
i = 0
|
8
|
+
@@current_options ||= {}
|
9
|
+
while i < args.length do
|
10
|
+
current = args[i]
|
11
|
+
break if current == "--"
|
12
|
+
if m = current.match(/--?(.+?)(?:=(.+))?$/)
|
13
|
+
key = $1
|
14
|
+
value = $2
|
15
|
+
|
16
|
+
input = inputs.include?(key)? key : shortcuts[key]
|
17
|
+
|
18
|
+
if input.nil?
|
19
|
+
i += 1
|
20
|
+
next
|
21
|
+
else
|
22
|
+
args.delete_at i
|
23
|
+
end
|
24
|
+
else
|
25
|
+
i += 1
|
26
|
+
next
|
27
|
+
end
|
28
|
+
|
29
|
+
if input_types[input] == :string
|
30
|
+
value = args.delete_at(i) if value.nil?
|
31
|
+
@@current_options[input] = value
|
32
|
+
else
|
33
|
+
if value.nil? and %w(F false FALSE no).include?(args[i])
|
34
|
+
Log.warn "Boolean values are best specified as #{current}=[true|false], not #{ current } [true|false]. Token '#{args[i]}' following '#{current}' automatically assigned as value"
|
35
|
+
value = args.delete_at(i)
|
36
|
+
end
|
37
|
+
@@current_options[input] = %w(F false FALSE no).include?(value)? false : true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
IndiferentHash.setup @@current_options
|
42
|
+
GOT_OPTIONS.merge! @@current_options
|
43
|
+
|
44
|
+
@@current_options
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.get(opt_str)
|
48
|
+
SOPT.parse(opt_str)
|
49
|
+
SOPT.consume(ARGV)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.require(options, *parameters)
|
53
|
+
parameters.flatten.each do |parameter|
|
54
|
+
raise ParameterException, "Parameter '#{ Log.color :blue, parameter }' not given" if options[parameter].nil?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module SOPT
|
2
|
+
def self.fix_shortcut(short, long)
|
3
|
+
return short unless short and shortcuts.include?(short)
|
4
|
+
|
5
|
+
current = shortcuts.select{|s,l| l == long}.collect{|s,l| s }.first
|
6
|
+
return current if current
|
7
|
+
|
8
|
+
chars = long.chars.to_a
|
9
|
+
current = [chars.shift]
|
10
|
+
short = current * ""
|
11
|
+
|
12
|
+
if (shortcuts.include?(short) and not shortcuts[short] == long)
|
13
|
+
if long.index "-" or long.index "_"
|
14
|
+
parts = long.split(/[_-]/)
|
15
|
+
acc = parts.collect{|s| s[0] } * ""
|
16
|
+
return acc unless shortcuts.include? acc
|
17
|
+
elsif m = long.match(/(\d+)/)
|
18
|
+
n = m[0]
|
19
|
+
acc = long[0] + n
|
20
|
+
return acc unless shortcuts.include? acc
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
while shortcuts.include?(short) && shortcuts[short] != long
|
25
|
+
next_letter = chars.shift
|
26
|
+
next_letter = chars.shift while %w(. - _).include?(next_letter)
|
27
|
+
return nil if next_letter.nil?
|
28
|
+
current << next_letter
|
29
|
+
short = current * ""
|
30
|
+
end
|
31
|
+
|
32
|
+
return nil if shortcuts.include? short
|
33
|
+
|
34
|
+
short
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.register(short, long, asterisk, description)
|
38
|
+
short = fix_shortcut(short, long)
|
39
|
+
shortcuts[short] = long if short
|
40
|
+
inputs << long
|
41
|
+
input_shortcuts[long] = short
|
42
|
+
input_descriptions[long] = description
|
43
|
+
input_types[long] = asterisk ? :string : :boolean
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.parse(opt_str)
|
47
|
+
info = {}
|
48
|
+
|
49
|
+
inputs = []
|
50
|
+
if opt_str.include? "\n"
|
51
|
+
re = /\n+/
|
52
|
+
else
|
53
|
+
re = /:/
|
54
|
+
end
|
55
|
+
opt_str.split(re).each do |entry|
|
56
|
+
entry.strip!
|
57
|
+
next if entry.empty?
|
58
|
+
names, _sep, description = entry.partition /\s+/
|
59
|
+
short, long, asterisk = names.match(/\s*(?:-(.+))?(?:--(.+?))([*])?$/).values_at 1,2,3
|
60
|
+
|
61
|
+
inputs << long
|
62
|
+
register short, long, asterisk, description
|
63
|
+
end
|
64
|
+
inputs
|
65
|
+
end
|
66
|
+
end
|