ahoward-main 2.9.0
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.
- data/README +1044 -0
- data/README.erb +784 -0
- data/Rakefile +241 -0
- data/TODO +20 -0
- data/lib/main.rb +60 -0
- data/lib/main/base.rb +515 -0
- data/lib/main/cast.rb +100 -0
- data/lib/main/factories.rb +20 -0
- data/lib/main/getoptlong.rb +470 -0
- data/lib/main/logger.rb +51 -0
- data/lib/main/mode.rb +42 -0
- data/lib/main/parameter.rb +699 -0
- data/lib/main/softspoken.rb +12 -0
- data/lib/main/stdext.rb +38 -0
- data/lib/main/usage.rb +208 -0
- data/lib/main/util.rb +91 -0
- data/main.gemspec +28 -0
- data/samples/a.rb +17 -0
- data/samples/b.rb +17 -0
- data/samples/c.rb +21 -0
- data/samples/d.rb +26 -0
- data/samples/e.rb +18 -0
- data/samples/f.rb +27 -0
- data/samples/g.rb +10 -0
- data/samples/h.rb +36 -0
- data/test/main.rb +861 -0
- metadata +83 -0
data/lib/main/stdext.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class Object
|
|
2
|
+
def singleton_class object = self, &block
|
|
3
|
+
sc =
|
|
4
|
+
class << object
|
|
5
|
+
self
|
|
6
|
+
end
|
|
7
|
+
block ? sc.module_eval(&block) : sc
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module SaneAbort
|
|
13
|
+
def abort message = nil
|
|
14
|
+
if message
|
|
15
|
+
message = message.to_s
|
|
16
|
+
message.singleton_class{ fattr 'abort' => true }
|
|
17
|
+
STDERR.puts message
|
|
18
|
+
end
|
|
19
|
+
exit 1
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def abort message = nil
|
|
24
|
+
if message
|
|
25
|
+
message = message.to_s
|
|
26
|
+
message.singleton_class{ fattr 'abort' => true }
|
|
27
|
+
STDERR.puts message
|
|
28
|
+
end
|
|
29
|
+
exit 1
|
|
30
|
+
end
|
|
31
|
+
def Process.abort message = nil
|
|
32
|
+
if message
|
|
33
|
+
message = message.to_s
|
|
34
|
+
message.singleton_class{ fattr 'abort' => true }
|
|
35
|
+
STDERR.puts message
|
|
36
|
+
end
|
|
37
|
+
exit 1
|
|
38
|
+
end
|
data/lib/main/usage.rb
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
module Main
|
|
2
|
+
class Usage < ::Array
|
|
3
|
+
fattr 'chunkname'
|
|
4
|
+
fattr 'upcase'
|
|
5
|
+
fattr 'eos'
|
|
6
|
+
|
|
7
|
+
def initialize opts={}
|
|
8
|
+
self.fields=[]
|
|
9
|
+
self.chunkname = lambda{|chunkname| chunkname.to_s.strip.upcase}
|
|
10
|
+
self.upcase = true
|
|
11
|
+
self.eos = "\n\n"
|
|
12
|
+
if opts.has_key?(:upcase) or opts.has_key?('upcase')
|
|
13
|
+
self.upcase = opts[:upcase] || opts['optcase']
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def clear
|
|
18
|
+
super
|
|
19
|
+
ensure
|
|
20
|
+
fields.clear
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
alias_method 'delete', 'delete_at'
|
|
24
|
+
|
|
25
|
+
def self.default_synopsis main
|
|
26
|
+
# build up synopsis
|
|
27
|
+
s = "#{ main.name }"
|
|
28
|
+
|
|
29
|
+
# mode info
|
|
30
|
+
if main.mode_name != 'main'
|
|
31
|
+
s << " #{ main.fully_qualified_mode.join ' ' }"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
unless main.modes.empty?
|
|
35
|
+
modes = main.modes.keys.join('|')
|
|
36
|
+
s << " (#{ modes })"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# argument info
|
|
40
|
+
main.parameters.each do |p|
|
|
41
|
+
if p.type == :argument
|
|
42
|
+
if(p.required? and p.arity != -1)
|
|
43
|
+
if p.arity > 0
|
|
44
|
+
p.arity.times{ s << " #{ p.name }" }
|
|
45
|
+
else
|
|
46
|
+
(p.arity.abs - 1).times{ s << " #{ p.name }" }
|
|
47
|
+
s << " #{ p.name }*"
|
|
48
|
+
end
|
|
49
|
+
else
|
|
50
|
+
#s << " [#{ p.name }]"
|
|
51
|
+
if p.arity > 0
|
|
52
|
+
a = []
|
|
53
|
+
p.arity.times{ a << "#{ p.name }" }
|
|
54
|
+
s << " [#{ a.join ' ' }]"
|
|
55
|
+
else
|
|
56
|
+
a = []
|
|
57
|
+
(p.arity.abs - 1).times{ a << "#{ p.name }" }
|
|
58
|
+
a << "#{ p.name }*"
|
|
59
|
+
s << " [#{ a.join ' ' }]"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# keyword info
|
|
66
|
+
main.parameters.each do |p|
|
|
67
|
+
if p.type == :keyword
|
|
68
|
+
if p.required?
|
|
69
|
+
s << " #{ p.name }=#{ p.name }"
|
|
70
|
+
else
|
|
71
|
+
s << " [#{ p.name }=#{ p.name }]"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# option info
|
|
77
|
+
n = 0
|
|
78
|
+
main.parameters.each do |p|
|
|
79
|
+
if p.type == :option
|
|
80
|
+
if p.required?
|
|
81
|
+
case p.argument
|
|
82
|
+
when :required
|
|
83
|
+
s << " --#{ p.name }=#{ p.name }"
|
|
84
|
+
when :optional
|
|
85
|
+
s << " --#{ p.name }=[#{ p.name }]"
|
|
86
|
+
else
|
|
87
|
+
s << " --#{ p.name }"
|
|
88
|
+
end
|
|
89
|
+
else
|
|
90
|
+
n += 1
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
if n > 0
|
|
95
|
+
s << " [options]+"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# help info
|
|
99
|
+
=begin
|
|
100
|
+
if main.modes.size > 0
|
|
101
|
+
modes = main.modes.keys.join('|')
|
|
102
|
+
s << "\n#{ main.name } (#{ modes }) help"
|
|
103
|
+
end
|
|
104
|
+
if main.mode_name != 'main'
|
|
105
|
+
s << "\n#{ main.name } #{ main.fully_qualified_mode.join ' ' } help"
|
|
106
|
+
else
|
|
107
|
+
s << "\n#{ main.name } help"
|
|
108
|
+
end
|
|
109
|
+
=end
|
|
110
|
+
|
|
111
|
+
s
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def name_section
|
|
115
|
+
if main.version?
|
|
116
|
+
"#{ main.name } v#{ main.version }"
|
|
117
|
+
else
|
|
118
|
+
"#{ main.name }"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def synopsis_section
|
|
123
|
+
main.synopsis
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def description_section
|
|
127
|
+
main.description if main.description?
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def parameters_section
|
|
131
|
+
arguments = main.parameters.select{|p| p.type == :argument}
|
|
132
|
+
keywords = main.parameters.select{|p| p.type == :keyword}
|
|
133
|
+
options = main.parameters.select{|p| p.type == :option}
|
|
134
|
+
environment = main.parameters.select{|p| p.type == :environment}
|
|
135
|
+
|
|
136
|
+
help, nothelp = options.partition{|p| p.name == 'help'}
|
|
137
|
+
options = nothelp + help
|
|
138
|
+
|
|
139
|
+
parameters = arguments + keywords + options + environment
|
|
140
|
+
|
|
141
|
+
s =
|
|
142
|
+
parameters.map do |p|
|
|
143
|
+
ps = ''
|
|
144
|
+
ps << Util.columnize("#{ p.synopsis }", :indent => 2, :width => 78)
|
|
145
|
+
#ps << Util.columnize("* #{ p.synopsis }", :indent => 2, :width => 78)
|
|
146
|
+
#ps << "\n"
|
|
147
|
+
if p.description?
|
|
148
|
+
ps << "\n"
|
|
149
|
+
ps << Util.columnize("#{ p.description }", :indent => 6, :width => 78)
|
|
150
|
+
#ps << Util.columnize(p.description, :indent => 6, :width => 78)
|
|
151
|
+
#ps << "\n"
|
|
152
|
+
end
|
|
153
|
+
#ps << "\n"
|
|
154
|
+
unless(p.examples.nil? or p.examples.empty?)
|
|
155
|
+
p.examples.each do |example|
|
|
156
|
+
ps << "\n"
|
|
157
|
+
ps << Util.columnize("#{ example }", :indent => 8, :width => 78)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
ps
|
|
161
|
+
end.join("\n")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def author_section
|
|
165
|
+
main.author
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
class << self
|
|
169
|
+
def default_usage main
|
|
170
|
+
usage = new
|
|
171
|
+
usage.main = main
|
|
172
|
+
# HACK
|
|
173
|
+
%w( name synopsis description parameters author ).each do |key|
|
|
174
|
+
usage[key] = nil
|
|
175
|
+
end
|
|
176
|
+
usage
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
alias_method "default", "default_usage"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
fattr "main"
|
|
183
|
+
|
|
184
|
+
def set_defaults!
|
|
185
|
+
usage = self
|
|
186
|
+
usage['name'] ||= name_section
|
|
187
|
+
usage['synopsis'] ||= synopsis_section
|
|
188
|
+
usage['description'] ||= description_section
|
|
189
|
+
usage['parameters'] ||= parameters_section unless main.parameters.empty?
|
|
190
|
+
usage['author'] ||= author_section if main.author?
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def to_s
|
|
194
|
+
set_defaults!
|
|
195
|
+
s = ''
|
|
196
|
+
each_pair do |key, value|
|
|
197
|
+
next unless(key and value)
|
|
198
|
+
up, down = key.to_s.upcase, key.to_s.downcase
|
|
199
|
+
if value
|
|
200
|
+
s << (upcase ? up : down) << "\n"
|
|
201
|
+
s << Util.indent(Util.unindent(value.to_s), 2)
|
|
202
|
+
s << eos
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
s
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
data/lib/main/util.rb
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Main
|
|
2
|
+
module Util
|
|
3
|
+
module ClassMethods
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
module InstanceMethods
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module Methods
|
|
10
|
+
def mcp obj
|
|
11
|
+
Marshal.load(Marshal.dump(obj))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def indent chunk, n = 2
|
|
15
|
+
lines = chunk.split %r/\n/
|
|
16
|
+
re = nil
|
|
17
|
+
s = ' ' * n
|
|
18
|
+
lines.map! do |line|
|
|
19
|
+
unless re
|
|
20
|
+
margin = line[%r/^\s*/]
|
|
21
|
+
re = %r/^#{ margin }/
|
|
22
|
+
end
|
|
23
|
+
line.gsub re, s
|
|
24
|
+
end.join("\n")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def unindent chunk
|
|
28
|
+
lines = chunk.split %r/\n/
|
|
29
|
+
indent = nil
|
|
30
|
+
re = %r/^/
|
|
31
|
+
lines.map! do |line|
|
|
32
|
+
unless indent
|
|
33
|
+
indent = line[%r/^\s*/]
|
|
34
|
+
re = %r/^#{ indent }/
|
|
35
|
+
end
|
|
36
|
+
line.gsub re, ''
|
|
37
|
+
end.join("\n")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def columnize buf, opts = {}
|
|
41
|
+
width = Util.getopt 'width', opts, 80
|
|
42
|
+
indent = Util.getopt 'indent', opts
|
|
43
|
+
indent = Fixnum === indent ? (' ' * indent) : "#{ indent }"
|
|
44
|
+
column = []
|
|
45
|
+
words = buf.split %r/\s+/o
|
|
46
|
+
row = "#{ indent }"
|
|
47
|
+
while((word = words.shift))
|
|
48
|
+
if((row.size + word.size) < (width - 1))
|
|
49
|
+
row << word
|
|
50
|
+
else
|
|
51
|
+
column << row
|
|
52
|
+
row = "#{ indent }"
|
|
53
|
+
row << word
|
|
54
|
+
end
|
|
55
|
+
row << ' ' unless row.size == (width - 1)
|
|
56
|
+
end
|
|
57
|
+
column << row unless row.strip.empty?
|
|
58
|
+
column.join "\n"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def getopt opt, hash, default = nil
|
|
62
|
+
keys = opt.respond_to?('each') ? opt : [opt]
|
|
63
|
+
|
|
64
|
+
keys.each do |key|
|
|
65
|
+
return hash[key] if hash.has_key? key
|
|
66
|
+
key = "#{ key }"
|
|
67
|
+
return hash[key] if hash.has_key? key
|
|
68
|
+
key = key.intern
|
|
69
|
+
return hash[key] if hash.has_key? key
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
return default
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
BLESS = lambda do |other|
|
|
77
|
+
other.module_eval{
|
|
78
|
+
include Methods
|
|
79
|
+
include InstanceMethods
|
|
80
|
+
extend Methods
|
|
81
|
+
extend ClassMethods
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.included other
|
|
86
|
+
BLESS[ other ]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
BLESS[ self ]
|
|
90
|
+
end
|
|
91
|
+
end
|
data/main.gemspec
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
## main.gemspec
|
|
2
|
+
#
|
|
3
|
+
|
|
4
|
+
Gem::Specification::new do |spec|
|
|
5
|
+
spec.name = "main"
|
|
6
|
+
spec.version = "2.9.0"
|
|
7
|
+
spec.platform = Gem::Platform::RUBY
|
|
8
|
+
spec.summary = "main"
|
|
9
|
+
|
|
10
|
+
spec.files = ["lib", "lib/main", "lib/main/base.rb", "lib/main/cast.rb", "lib/main/factories.rb", "lib/main/getoptlong.rb", "lib/main/logger.rb", "lib/main/mode.rb", "lib/main/parameter.rb", "lib/main/softspoken.rb", "lib/main/stdext.rb", "lib/main/usage.rb", "lib/main/util.rb", "lib/main.rb", "main.gemspec", "Rakefile", "README", "README.erb", "samples", "samples/a.rb", "samples/b.rb", "samples/c.rb", "samples/d.rb", "samples/e.rb", "samples/f.rb", "samples/g.rb", "samples/h.rb", "test", "test/main.rb", "TODO"]
|
|
11
|
+
spec.executables = []
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
spec.require_path = "lib"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
spec.has_rdoc = true
|
|
18
|
+
spec.test_files = "test/main.rb"
|
|
19
|
+
#spec.add_dependency 'lib', '>= version'
|
|
20
|
+
#spec.add_dependency 'fattr'
|
|
21
|
+
|
|
22
|
+
spec.extensions.push(*[])
|
|
23
|
+
|
|
24
|
+
spec.rubyforge_project = "codeforpeople"
|
|
25
|
+
spec.author = "Ara T. Howard"
|
|
26
|
+
spec.email = "ara.t.howard@gmail.com"
|
|
27
|
+
spec.homepage = "http://github.com/ahoward/main/tree/master"
|
|
28
|
+
end
|
data/samples/a.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'main'
|
|
2
|
+
|
|
3
|
+
ARGV.replace %w( 42 ) if ARGV.empty?
|
|
4
|
+
|
|
5
|
+
Main {
|
|
6
|
+
argument('foo'){
|
|
7
|
+
required # this is the default
|
|
8
|
+
cast :int # value cast to Fixnum
|
|
9
|
+
validate{|foo| foo == 42} # raises error in failure case
|
|
10
|
+
description 'the foo param' # shown in --help
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
def run
|
|
14
|
+
p params['foo'].given?
|
|
15
|
+
p params['foo'].value
|
|
16
|
+
end
|
|
17
|
+
}
|
data/samples/b.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'main'
|
|
2
|
+
|
|
3
|
+
ARGV.replace %w( 40 1 1 ) if ARGV.empty?
|
|
4
|
+
|
|
5
|
+
Main {
|
|
6
|
+
argument('foo'){
|
|
7
|
+
arity 3 # foo will given three times
|
|
8
|
+
cast :int # value cast to Fixnum
|
|
9
|
+
validate{|foo| [40,1].include? foo} # raises error in failure case
|
|
10
|
+
description 'the foo param' # shown in --help
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
def run
|
|
14
|
+
p params['foo'].given?
|
|
15
|
+
p params['foo'].values
|
|
16
|
+
end
|
|
17
|
+
}
|
data/samples/c.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'main'
|
|
2
|
+
|
|
3
|
+
ARGV.replace %w( foo=40 foo=2 bar=false ) if ARGV.empty?
|
|
4
|
+
|
|
5
|
+
Main {
|
|
6
|
+
keyword('foo'){
|
|
7
|
+
required # by default keywords are not required
|
|
8
|
+
arity 2
|
|
9
|
+
cast :float
|
|
10
|
+
}
|
|
11
|
+
keyword('bar'){
|
|
12
|
+
cast :bool
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
p params['foo'].given?
|
|
17
|
+
p params['foo'].values
|
|
18
|
+
p params['bar'].given?
|
|
19
|
+
p params['bar'].value
|
|
20
|
+
end
|
|
21
|
+
}
|
data/samples/d.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'main'
|
|
2
|
+
|
|
3
|
+
ARGV.replace %w( --foo=40 -f2 ) if ARGV.empty?
|
|
4
|
+
|
|
5
|
+
Main {
|
|
6
|
+
option('foo', 'f'){
|
|
7
|
+
required # by default options are not required, we could use 'foo=foo'
|
|
8
|
+
# above as a shortcut
|
|
9
|
+
argument_required
|
|
10
|
+
arity 2
|
|
11
|
+
cast :float
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
option('bar=[bar]', 'b'){ # note shortcut syntax for optional args
|
|
15
|
+
# argument_optional # we could also use this method
|
|
16
|
+
cast :bool
|
|
17
|
+
default false
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
def run
|
|
21
|
+
p params['foo'].given?
|
|
22
|
+
p params['foo'].values
|
|
23
|
+
p params['bar'].given?
|
|
24
|
+
p params['bar'].value
|
|
25
|
+
end
|
|
26
|
+
}
|