pablo 1.0.3
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.tar.gz.sig +0 -0
- data/History.txt +4 -0
- data/LICENSE.txt +22 -0
- data/Manifest.txt +26 -0
- data/README.txt +61 -0
- data/lib/pablo.rb +298 -0
- data/lib/pablo/always.rb +44 -0
- data/lib/pablo/arguments.rb +76 -0
- data/lib/pablo/color.rb +57 -0
- data/lib/pablo/command.rb +71 -0
- data/lib/pablo/errors.rb +68 -0
- data/lib/pablo/expansion.rb +58 -0
- data/lib/pablo/help.rb +191 -0
- data/lib/pablo/helpers.rb +46 -0
- data/lib/pablo/option.rb +89 -0
- data/lib/pablo/parser.rb +192 -0
- data/lib/pablo/token.rb +66 -0
- data/lib/pablo/version.rb +29 -0
- data/test/fixtures/license.txt +22 -0
- data/test/fixtures/load_yaml.yml +37 -0
- data/test/fixtures/output.yml +13 -0
- data/test/help_fixtures.rb +47 -0
- data/test/test_arguments.rb +56 -0
- data/test/test_expansion.rb +62 -0
- data/test/test_output.rb +202 -0
- data/test/test_pablo.rb +115 -0
- data/test/test_parsers.rb +470 -0
- metadata +117 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
###### DON'T PANIC License 1.1 ###########
|
2
|
+
#
|
3
|
+
# Don't panic, this piece of software is
|
4
|
+
# free, i.e. you can do with it whatever
|
5
|
+
# you like, including, but not limited to:
|
6
|
+
#
|
7
|
+
# * using it
|
8
|
+
# * copying it
|
9
|
+
# * (re)distributing it
|
10
|
+
# * burning/burying/shredding it
|
11
|
+
# * eating it
|
12
|
+
# * using it to obtain world domination
|
13
|
+
# * and ignoring it
|
14
|
+
#
|
15
|
+
# Under the sole condition that you
|
16
|
+
#
|
17
|
+
# * CONSIDER buying the author a strong
|
18
|
+
# brownian motion producer, say a nice
|
19
|
+
# hot cup of tea, should you ever meet
|
20
|
+
# him in person.
|
21
|
+
#
|
22
|
+
##########################################
|
23
|
+
|
24
|
+
class Pablo
|
25
|
+
|
26
|
+
#
|
27
|
+
# General purpose helpers.
|
28
|
+
module Helpers
|
29
|
+
private
|
30
|
+
|
31
|
+
#
|
32
|
+
# Indent text by n spaces.
|
33
|
+
def indent text, n
|
34
|
+
(' ' * n) + text.split(/\n/, -1).join("\n" + (' ' * n))
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Little rails-style helper.
|
39
|
+
def returning val
|
40
|
+
yield val
|
41
|
+
val
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
data/lib/pablo/option.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
###### DON'T PANIC License 1.1 ###########
|
2
|
+
#
|
3
|
+
# Don't panic, this piece of software is
|
4
|
+
# free, i.e. you can do with it whatever
|
5
|
+
# you like, including, but not limited to:
|
6
|
+
#
|
7
|
+
# * using it
|
8
|
+
# * copying it
|
9
|
+
# * (re)distributing it
|
10
|
+
# * burning/burying/shredding it
|
11
|
+
# * eating it
|
12
|
+
# * using it to obtain world domination
|
13
|
+
# * and ignoring it
|
14
|
+
#
|
15
|
+
# Under the sole condition that you
|
16
|
+
#
|
17
|
+
# * CONSIDER buying the author a strong
|
18
|
+
# brownian motion producer, say a nice
|
19
|
+
# hot cup of tea, should you ever meet
|
20
|
+
# him in person.
|
21
|
+
#
|
22
|
+
##########################################
|
23
|
+
|
24
|
+
class Pablo
|
25
|
+
|
26
|
+
class Option < Pablo::Parser
|
27
|
+
|
28
|
+
include Pablo::Helpers
|
29
|
+
|
30
|
+
def initialize *args, &block
|
31
|
+
super(*args, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse args
|
35
|
+
name = nil
|
36
|
+
idx = args.zip((0...args.length).to_a).find_index { |(a,i)|
|
37
|
+
verifies?(a,i) and (name = matches?(a))
|
38
|
+
}
|
39
|
+
|
40
|
+
unless idx.nil?
|
41
|
+
args.consume idx
|
42
|
+
args.slice idx
|
43
|
+
|
44
|
+
unless @opts[:negates].nil?
|
45
|
+
@pablo.options[@opts[:negates].to_sym] = false
|
46
|
+
else
|
47
|
+
@pablo.options[name] = true
|
48
|
+
end
|
49
|
+
|
50
|
+
consume?(name, args)
|
51
|
+
run?(args)
|
52
|
+
|
53
|
+
args.unslice
|
54
|
+
true
|
55
|
+
else
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Format an option's names to be displayed to the user.
|
62
|
+
def names_to_user
|
63
|
+
@names.collect { |n| format(n) }.join('|')
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Format an option's names to be used in a Regexp
|
68
|
+
def names_to_rex
|
69
|
+
Regexp.new(@names.collect { |n| format(n) }.join('|'))
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def format name
|
75
|
+
@toplevel ?
|
76
|
+
'--' + name.to_s :
|
77
|
+
'-' + name.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
def check_options
|
81
|
+
super()
|
82
|
+
raise ":negates expected to be a Symbol" unless
|
83
|
+
@opts[:negates].respond_to? :to_sym or @opts[:negates].nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
data/lib/pablo/parser.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
###### DON'T PANIC License 1.1 ###########
|
2
|
+
#
|
3
|
+
# Don't panic, this piece of software is
|
4
|
+
# free, i.e. you can do with it whatever
|
5
|
+
# you like, including, but not limited to:
|
6
|
+
#
|
7
|
+
# * using it
|
8
|
+
# * copying it
|
9
|
+
# * (re)distributing it
|
10
|
+
# * burning/burying/shredding it
|
11
|
+
# * eating it
|
12
|
+
# * using it to obtain world domination
|
13
|
+
# * and ignoring it
|
14
|
+
#
|
15
|
+
# Under the sole condition that you
|
16
|
+
#
|
17
|
+
# * CONSIDER buying the author a strong
|
18
|
+
# brownian motion producer, say a nice
|
19
|
+
# hot cup of tea, should you ever meet
|
20
|
+
# him in person.
|
21
|
+
#
|
22
|
+
##########################################
|
23
|
+
|
24
|
+
class Pablo
|
25
|
+
|
26
|
+
#
|
27
|
+
# Superclass for Command, Option, Token etc.
|
28
|
+
# Handles common initialization tasks and provides helpers.
|
29
|
+
class Parser
|
30
|
+
|
31
|
+
include Pablo::Helpers
|
32
|
+
|
33
|
+
attr_accessor :names, :desc, :longdesc, :usage, :last_match
|
34
|
+
|
35
|
+
def initialize pablo, *args, &block
|
36
|
+
# set names, opts
|
37
|
+
@pablo, @toplevel, @block = pablo, pablo.toplevel?, block
|
38
|
+
@opts = Hash.new
|
39
|
+
@opts.merge!(args.delete_at(-1).to_hash) if args[-1].respond_to?(:to_hash)
|
40
|
+
check_options()
|
41
|
+
|
42
|
+
@file_methods =
|
43
|
+
%w{file? directory? exists? exist? executable? readable? writable?}.
|
44
|
+
collect(&:to_sym)
|
45
|
+
@verify, args = args.partition { |x|
|
46
|
+
[String, Array, Regexp, Proc, Fixnum, Range, Proc].include? x.class or @file_methods.include? x
|
47
|
+
}
|
48
|
+
|
49
|
+
[:position, :matches, :is].each do |sym|
|
50
|
+
@verify << @opts.delete(sym) unless @opts[sym].nil?
|
51
|
+
end
|
52
|
+
|
53
|
+
@names = args
|
54
|
+
check_names()
|
55
|
+
|
56
|
+
# build expansion table
|
57
|
+
names.each do |name|
|
58
|
+
name = format(name)
|
59
|
+
@pablo.expand << name unless @pablo.expand.include? name
|
60
|
+
end if @pablo.expands?(klass_to_sym())
|
61
|
+
|
62
|
+
# get desc, longdesc, usage
|
63
|
+
@desc, @longdesc, @usage = @opts[:desc], @opts[:longdesc], @opts[:usage]
|
64
|
+
%w{desc longdesc usage}.each { |sym| @opts.delete(sym.to_sym) }
|
65
|
+
|
66
|
+
# register
|
67
|
+
@pablo.registered << self
|
68
|
+
# set default
|
69
|
+
@names.each { |n| @pablo.send(klass_to_sym())[n] = @opts[:default] } unless @opts[:default].nil?
|
70
|
+
end
|
71
|
+
|
72
|
+
def klass_to_sym
|
73
|
+
case self
|
74
|
+
when Pablo::Command then :commands
|
75
|
+
when Pablo::Option then :options
|
76
|
+
when Pablo::Token then :tokens
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def format name
|
83
|
+
name.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# If a block was given, this executes it.
|
88
|
+
# If a run object is specified, this executes the corresponding
|
89
|
+
# instance method.
|
90
|
+
def run? args
|
91
|
+
unless @block.nil?
|
92
|
+
@block.call(args, @pablo)
|
93
|
+
end
|
94
|
+
|
95
|
+
if @opts[:run] != false and not @pablo.run_object.nil?
|
96
|
+
meth = @names.find { |n|
|
97
|
+
@pablo.run_object.public_methods.any? { |m|
|
98
|
+
n == m.to_sym
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
unless meth.nil?
|
103
|
+
@pablo.run_object.send(meth, args, @pablo)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Tests all the +@verify+ parameters against the given argument
|
110
|
+
# and index and returns +true+ if they all match and +false+ otherwise.
|
111
|
+
def verifies? arg, idx
|
112
|
+
@verify.all? do |ver|
|
113
|
+
case ver
|
114
|
+
when Array then ver.include? arg
|
115
|
+
when Regexp then ver =~ arg
|
116
|
+
when String then ver == arg
|
117
|
+
when Proc then ver.call(arg)
|
118
|
+
when Fixnum then ver == idx
|
119
|
+
when Range then ver.include? idx
|
120
|
+
when Proc then ver.call(arg, idx)
|
121
|
+
else # File methods
|
122
|
+
@file_methods.include?(ver) ?
|
123
|
+
File.send(ver, arg) :
|
124
|
+
true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Returns true if +@opts[:consume]+ is not +nil+ and
|
131
|
+
# consumes whatever was specified in that case.
|
132
|
+
def consume? name, args
|
133
|
+
unless @opts[:consume].nil?
|
134
|
+
@pablo.send(klass_to_sym())[name] =
|
135
|
+
case @opts[:consume]
|
136
|
+
when String
|
137
|
+
args[0] == @opts[:consume] ?
|
138
|
+
returning(args[0]) { args.consume 0 } :
|
139
|
+
nil
|
140
|
+
when Array
|
141
|
+
returning(ret = @opts[:consume].find { |c| c == args[0] }) {
|
142
|
+
args.consume 0 unless ret.nil? }
|
143
|
+
when Regexp
|
144
|
+
args[0] =~ @opts[:consume] ?
|
145
|
+
returning(args[0]) { args.consume 0 } :
|
146
|
+
nil
|
147
|
+
when 1
|
148
|
+
returning(args[0]) { args.consume 0 }
|
149
|
+
when Fixnum
|
150
|
+
@opts[:consume] > 1 ?
|
151
|
+
returning(args[0...@opts[:consume]]) { args.consume 0 } :
|
152
|
+
nil
|
153
|
+
end
|
154
|
+
|
155
|
+
true
|
156
|
+
else false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
#
|
161
|
+
# Expands the given +name+ if expansion is turned on for this
|
162
|
+
# type of Parser and the +name+ matches one of this parser's
|
163
|
+
# names - optionally with a prefix assigned.
|
164
|
+
# If not, the name is matched unexpanded.
|
165
|
+
# If no match is found, nil is returned.
|
166
|
+
def matches? arg
|
167
|
+
arg = @pablo.expand!(arg)
|
168
|
+
|
169
|
+
@last_match = @names.find do |n|
|
170
|
+
format(n.to_s) == arg
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def check_names
|
175
|
+
raise ArgumentError.new("names must be Symbols") if @names.any? { |n| !n.is_a?(Symbol) }
|
176
|
+
end
|
177
|
+
|
178
|
+
def check_options
|
179
|
+
raise ":consume expected to be any of Fixnum, Array, String, Regexp" unless
|
180
|
+
[Fixnum, Array, String, Regexp].include? @opts[:consume].class or @opts[:consume].nil?
|
181
|
+
raise ":position expected to be any of Fixnum, Range" unless
|
182
|
+
[Fixnum, Range].include? @opts[:position].class or @opts[:position].nil?
|
183
|
+
raise ":is expected to be any of Array, String" unless
|
184
|
+
[Array, String].include? @opts[:is].class or @opts[:is].nil?
|
185
|
+
raise ":matches expected to be a Regexp" unless
|
186
|
+
@opts[:matches].is_a? Regexp or @opts[:matches].nil?
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
data/lib/pablo/token.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
###### DON'T PANIC License 1.1 ###########
|
2
|
+
#
|
3
|
+
# Don't panic, this piece of software is
|
4
|
+
# free, i.e. you can do with it whatever
|
5
|
+
# you like, including, but not limited to:
|
6
|
+
#
|
7
|
+
# * using it
|
8
|
+
# * copying it
|
9
|
+
# * (re)distributing it
|
10
|
+
# * burning/burying/shredding it
|
11
|
+
# * eating it
|
12
|
+
# * using it to obtain world domination
|
13
|
+
# * and ignoring it
|
14
|
+
#
|
15
|
+
# Under the sole condition that you
|
16
|
+
#
|
17
|
+
# * CONSIDER buying the author a strong
|
18
|
+
# brownian motion producer, say a nice
|
19
|
+
# hot cup of tea, should you ever meet
|
20
|
+
# him in person.
|
21
|
+
#
|
22
|
+
##########################################
|
23
|
+
|
24
|
+
class Pablo
|
25
|
+
|
26
|
+
class Token < Pablo::Parser
|
27
|
+
|
28
|
+
include Pablo::Helpers
|
29
|
+
|
30
|
+
def parse args
|
31
|
+
idx = args.zip((0...args.length).to_a).find_index { |(a,i)|
|
32
|
+
verifies?(a,i)
|
33
|
+
}
|
34
|
+
|
35
|
+
unless idx.nil?
|
36
|
+
name = @names[0]
|
37
|
+
args.slice idx
|
38
|
+
@names.each { |n| @pablo.tokens[n] = true } unless name.nil?
|
39
|
+
|
40
|
+
if returning(consume?(name, args)) { run?(args) }
|
41
|
+
@names[1..-1].each { |n| @pablo.tokens[n] = @pablo.tokens[@names[0]] }
|
42
|
+
end
|
43
|
+
|
44
|
+
args.unslice
|
45
|
+
true
|
46
|
+
else
|
47
|
+
false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Format a token's names to be displayed to the user.
|
53
|
+
def names_to_user
|
54
|
+
@names.collect(&:to_s).join('|')
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Format a token's names to be used in a Regexp
|
59
|
+
def names_to_rex
|
60
|
+
Regexp.new(names_to_user)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
###### DON'T PANIC License 1.1 ###########
|
2
|
+
#
|
3
|
+
# Don't panic, this piece of software is
|
4
|
+
# free, i.e. you can do with it whatever
|
5
|
+
# you like, including, but not limited to:
|
6
|
+
#
|
7
|
+
# * using it
|
8
|
+
# * copying it
|
9
|
+
# * (re)distributing it
|
10
|
+
# * burning/burying/shredding it
|
11
|
+
# * eating it
|
12
|
+
# * using it to obtain world domination
|
13
|
+
# * and ignoring it
|
14
|
+
#
|
15
|
+
# Under the sole condition that you
|
16
|
+
#
|
17
|
+
# * CONSIDER buying the author a strong
|
18
|
+
# brownian motion producer, say a nice
|
19
|
+
# hot cup of tea, should you ever meet
|
20
|
+
# him in person.
|
21
|
+
#
|
22
|
+
##########################################
|
23
|
+
|
24
|
+
class Pablo
|
25
|
+
|
26
|
+
VERSION = '1.0.3'
|
27
|
+
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
/*---- DON'T PANIC License 1.1 -----------
|
2
|
+
|
3
|
+
Don't panic, this piece of software is
|
4
|
+
free, i.e. you can do with it whatever
|
5
|
+
you like, including, but not limited to:
|
6
|
+
|
7
|
+
* using it
|
8
|
+
* copying it
|
9
|
+
* (re)distributing it
|
10
|
+
* burning/burying/shredding it
|
11
|
+
* eating it
|
12
|
+
* using it to obtain world domination
|
13
|
+
* and ignoring it
|
14
|
+
|
15
|
+
Under the sole condition that you
|
16
|
+
|
17
|
+
* CONSIDER buying the author a strong
|
18
|
+
brownian motion producer, say a nice
|
19
|
+
hot cup of tea, should you ever meet
|
20
|
+
him in person.
|
21
|
+
|
22
|
+
----------------------------------------*/
|