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.
@@ -0,0 +1,76 @@
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
+ # Safely encapsules the arguments and provides means to
27
+ # manipulate them
28
+ class Arguments
29
+
30
+ # undef all instance methods so we can use this as
31
+ # a proxy to the underlying Array
32
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
33
+
34
+ def method_missing sym, *args, &block
35
+ @slices[-1][:arr].send(sym, *args, &block)
36
+ end
37
+
38
+ def initialize arr
39
+ @slices = [{:idx => 0, :arr => arr}]
40
+ end
41
+
42
+ def consume idx
43
+ @slices[-1][:arr].delete_at idx
44
+ end
45
+
46
+ def consumed?
47
+ @slices[-1][:arr].empty?
48
+ end
49
+
50
+ def consume_all
51
+ @slices[-1][:arr].clear
52
+ end
53
+
54
+ #
55
+ # Constrain arguments on the subset +idx..-1+.
56
+ def slice idx
57
+ @slices << {
58
+ :idx => idx,
59
+ :arr => @slices[-1][:arr][idx..-1]
60
+ }
61
+ end
62
+
63
+ #
64
+ # Remove arguments constrain and reconcile arguments
65
+ # with slice.
66
+ def unslice
67
+ raise "cannot unslice fully unsliced Arguments" unless @slices.length > 1
68
+ idx, arr = @slices[-1][:idx], @slices[-1][:arr]
69
+ @slices[-2][:arr][idx..-1] = arr
70
+ @slices.delete_at(-1)
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
@@ -0,0 +1,57 @@
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
+ # Runs the given +block+, whenever something needs to be given color.
28
+ # The block will be given the following arguments:
29
+ # * The string to colorize
30
+ # * A symbol indicating the type of the String. The color should be
31
+ # guessed from that. Can be any of:
32
+ # * :h1 - First class heading
33
+ # * :h2 - Second class heading
34
+ # * :usage - Usage information
35
+ # * :em - Highlighted word
36
+ def colorizing &block
37
+ raise "colorize expects a block" unless block_given?
38
+ @colorize = block
39
+ end
40
+
41
+ #
42
+ # Colorize the given +string+ of the given +type+.
43
+ # See Pablo#colorize for valid values of +type+.
44
+ # If no colorization function is given, the +string+ is returned
45
+ # uncolored.
46
+ def colorize string, type
47
+ if type == :text
48
+ string.gsub(/\$([^$]+)\$/ )do |match|
49
+ colorize(match[1..-2], :em)
50
+ end
51
+ else
52
+ @colorize.nil? ? string : (@colorize.call(string, type))
53
+ end
54
+ end
55
+
56
+ end
57
+
@@ -0,0 +1,71 @@
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 Command < Pablo::Parser
27
+
28
+ def initialize *args, &block
29
+ super(*args, &block)
30
+ @opts[:consume_all] = @pablo.consume_all? if @opts[:consume_all].nil?
31
+ end
32
+
33
+ def parse args
34
+ name = nil
35
+ idx = args.zip((0...args.length).to_a).find_index { |(a,i)|
36
+ verifies?(a,i) and (name = matches?(a))
37
+ }
38
+
39
+ unless idx.nil?
40
+ args.consume idx
41
+ args.slice idx
42
+
43
+ @pablo.commands[name] = true
44
+
45
+ unless returning(consume?(name, args)) { run?(args) }
46
+ args.consume_all if @toplevel and @opts[:consume_all]
47
+ end
48
+
49
+ args.unslice
50
+ true
51
+ else
52
+ false
53
+ end
54
+ end
55
+
56
+ #
57
+ # Format a command's names to be displayed to the user.
58
+ def names_to_user
59
+ @names.collect(&:to_s).join('|')
60
+ end
61
+
62
+ #
63
+ # Format a command's names to be used in a Regexp.
64
+ def names_to_rex
65
+ Regexp.new(names_to_user)
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+
@@ -0,0 +1,68 @@
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 MissingArgumentError < RuntimeError
27
+ def initialize name, arg = nil
28
+ @name, @arg = name, arg
29
+ end
30
+
31
+ def to_s
32
+ @arg ?
33
+ "Missing argument '#{@arg}' for '#{@name}'." :
34
+ "Missing argument for '#{@name}'."
35
+ end
36
+ end
37
+
38
+ class WrongArgumentError < RuntimeError
39
+ def initialize name, arg = nil, type = nil
40
+ @name, @arg, @type = name, arg, type
41
+ end
42
+
43
+ def to_s
44
+ (@arg ?
45
+ "Wrong argument '#{@arg}' for '#{@name}'." :
46
+ "Wrong argument for '#{@name}'.") +
47
+ (@type ?
48
+ " Expected #{@type}." :
49
+ '')
50
+ end
51
+ end
52
+
53
+ #
54
+ # Displays a message about a missing argument
55
+ # and exits parsing.
56
+ def missing_argument *args
57
+ raise MissingArgumentError.new @cur.last_match, *args
58
+ end
59
+
60
+ #
61
+ # Displays a message about a wrong argument (format)
62
+ # and exits parsing.
63
+ def wrong_argument *args
64
+ raise WrongArgumentError.new @cur.last_match, *args
65
+ end
66
+
67
+ end
68
+
@@ -0,0 +1,58 @@
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
+ # Whether or not the given +type+ is subject to expansion.
28
+ def expands? type
29
+ @opts[:expand] == true or @opts[:expand] == type
30
+ end
31
+
32
+ #
33
+ # Expands the given +arg+ument if possible.
34
+ def expand! arg
35
+ arr = @expand.find_all { |e| e.start_with?(arg) }
36
+
37
+ case arr.length
38
+ when 1 then arr[0]
39
+ when 0 then arg
40
+ else
41
+ @ambiguity.call(arg, arr) unless @ambiguity.nil?
42
+ arg
43
+ end
44
+ end
45
+
46
+ #
47
+ # The block given to this method will be called each time an
48
+ # argument could not be expanded because there were multiple
49
+ # parsers that matched it.
50
+ # The block will be passed the argument and an Array containing
51
+ # the matching parser names as parameters.
52
+ def ambiguity &block
53
+ raise "Pablo#ambiguity needs a block to do something sensible" unless block_given?
54
+ @ambiguity = block
55
+ end
56
+
57
+ end
58
+
data/lib/pablo/help.rb ADDED
@@ -0,0 +1,191 @@
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
+ # The class that handles standard help displaying.
28
+ class HelpCommand < Pablo::Command
29
+ private
30
+
31
+ def run? args
32
+ super(args)
33
+ args.empty? ? @pablo.help() : @pablo.help(args[0])
34
+ throw :abort
35
+ end
36
+ end
37
+
38
+ #
39
+ # The class that handles standard version displaying.
40
+ class VersionCommand < Pablo::Command
41
+ private
42
+
43
+ def run? args
44
+ super(args)
45
+ @pablo.version()
46
+ throw :abort
47
+ end
48
+ end
49
+
50
+ #
51
+ # The class that handles standard License displaying.
52
+ class LicenseCommand < Pablo::Command
53
+ private
54
+
55
+ def run? args
56
+ super(args)
57
+ @pablo.license()
58
+ throw :abort
59
+ end
60
+ end
61
+
62
+ #
63
+ # Creates the standard license command.
64
+ def license_command options = {}, &block
65
+ desc('Shows the license of this program.') unless @pending[:desc]
66
+ longdesc("Displays information about the license of this program.") unless @pending[:longdesc]
67
+ exec(Pablo::LicenseCommand, :license, options, &block)
68
+ end
69
+
70
+ #
71
+ # Creates the standard version command.
72
+ def version_command options = {}, &block
73
+ desc('Shows the version of this program.') unless @pending[:desc]
74
+ longdesc("Displays information about the version of this program.") unless @pending[:longdesc]
75
+ exec(Pablo::VersionCommand, :version, options, &block)
76
+ end
77
+
78
+ #
79
+ # Creates the standard help command.
80
+ def help_command options = {}, &block
81
+ desc('Displays this message.') unless @pending[:desc]
82
+ longdesc("Shows a simple help message for this program.\n" +
83
+ "If a command is given as well, a more detailed message about that\n" +
84
+ "particular command is shown (such as this one).") unless @pending[:longdesc]
85
+ usage('[<command>]') unless @pending[:usage]
86
+ exec(Pablo::HelpCommand, :help, options, &block)
87
+ end
88
+
89
+ #
90
+ # Creates standard ambiguity output.
91
+ def ambiguity_command
92
+ ambiguity do |arg,arr|
93
+ $stdout.puts "Ambiguous argument '#{arg}'. Could be any of:"
94
+
95
+ arr.each do |name|
96
+ $stdout.puts " * #{name}"
97
+ end
98
+
99
+ throw :abort
100
+ end
101
+ end
102
+
103
+ #
104
+ # Print the help screen.
105
+ # * command == nil: program help
106
+ # * else: specific command's help
107
+ def help command = nil
108
+ if command.nil?
109
+ put_header
110
+
111
+ $stdout.puts "\n #{colorize(@opts[:program], :em)} #{colorize(@opts[:usage], :usage)}\n" unless @opts[:usage].nil?
112
+ $stdout.puts "\n#{indent colorize(@opts[:longdesc], :text), 3}" unless @opts[:longdesc].nil?
113
+
114
+ cmdlen = @registered.collect(&:names_to_user).max_by(&:length).length
115
+
116
+ [:commands, :options].each do |type|
117
+ parsers = @registered.find_all { |p| p.klass_to_sym == type }
118
+
119
+ unless parsers.empty?
120
+ $stdout.puts
121
+ $stdout.puts ' ' + colorize("#{type.to_s.capitalize}:", :h2)
122
+
123
+ parsers.each { |p|
124
+ p.desc ?
125
+ $stdout.puts(" #{colorize(p.names_to_user.ljust(cmdlen), :em)} : #{colorize(p.desc, :text)}") :
126
+ $stdout.puts(' ' + colorize(p.names_to_user.ljust(cmdlen), :em))
127
+ }
128
+ end
129
+ end
130
+
131
+ tokens = @registered.find_all { |p| p.is_a? Pablo::Token and not p.desc.nil? }
132
+ unless tokens.empty?
133
+ $stdout.puts
134
+ $stdout.puts ' ' + colorize("Tokens:", :h2)
135
+
136
+ tokens.each { |t|
137
+ $stdout.puts " #{colorize(t.names_to_user.ljust(cmdlen), :em)} : #{t.desc}"
138
+ }
139
+ end
140
+ else
141
+ command = expand!(command)
142
+ found = @registered.find do |item|
143
+ if not item.names_to_rex.nil? and item.names_to_rex =~ command
144
+ put_header
145
+
146
+ $stdout.print "\n " + colorize(item.names_to_user, :em)
147
+ $stdout.print ' ' + colorize(item.usage, :usage) unless item.usage.nil?
148
+ $stdout.puts "\n\n"
149
+ $stdout.puts indent(colorize(item.longdesc, :text), 1) unless item.longdesc.nil?
150
+ true
151
+ else false
152
+ end
153
+ end
154
+
155
+ if found.nil?
156
+ $stdout.puts "Sorry, but I don't know '#{colorize(command, :em)}'."
157
+ $stdout.puts "Try running '#{colorize('help', :em)}' without parameters to get a list of all the commands, options etc."
158
+ end
159
+ end
160
+ end
161
+
162
+ #
163
+ # Print version screen and stop processing.
164
+ def version
165
+ $stdout.puts colorize("#{@opts[:program]} #{@opts[:version]}", :h1)
166
+ $stdout.puts @opts[:capabilities].join(' ') unless @opts[:capabilities].nil?
167
+ end
168
+
169
+ #
170
+ # Print license and stop processing. Nothing else.
171
+ def license
172
+ if File.exist? @opts[:license].to_s
173
+ open(@opts[:license]) { |f| $stdout.puts f.read }
174
+ else
175
+ $stdout.puts colorize(@opts[:license], :text)
176
+ end
177
+ end
178
+
179
+ #
180
+ # Prints the standard header for most Pablo output.
181
+ def put_header
182
+ unless @opts[:program].nil?
183
+ str = "#{@opts[:program]}"
184
+ str << " #{@opts[:version]}" unless @opts[:version].nil?
185
+ str << " - #{@opts[:desc]}" unless @opts[:desc].nil?
186
+ $stdout.puts ' ' + colorize(str, :h1)
187
+ end
188
+ end
189
+
190
+ end
191
+