Class: Pablo

Inherits:
Object
  • Object
show all
Includes:
Helpers
Defined in:
lib/pablo.rb,
lib/pablo/help.rb,
lib/pablo/color.rb,
lib/pablo/token.rb,
lib/pablo/always.rb,
lib/pablo/errors.rb,
lib/pablo/option.rb,
lib/pablo/parser.rb,
lib/pablo/command.rb,
lib/pablo/helpers.rb,
lib/pablo/version.rb,
lib/pablo/arguments.rb,
lib/pablo/expansion.rb

Overview

DON’T PANIC License 1.1 ###########

Don’t panic, this piece of software is free, i.e. you can do with it whatever you like, including, but not limited to:

* using it
* copying it
* (re)distributing it
* burning/burying/shredding it
* eating it
* using it to obtain world domination
* and ignoring it

Under the sole condition that you

* CONSIDER buying the author a strong
  brownian motion producer, say a nice
  hot cup of tea, should you ever meet
  him in person.

Defined Under Namespace

Modules: Helpers Classes: Always, Arguments, Command, HelpCommand, LicenseCommand, MissingArgumentError, Option, Parser, Token, VersionCommand, WrongArgumentError

Constant Summary collapse

VERSION =
'1.0.3'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = Hash.new) ⇒ Pablo

  • opts: options for Pablo

    • :command_consumes_all => true|false Forces toplevel commands (not) to consume all arguments after being applied. I’m still looking for a better name for that option, so it may be subject to change.

    • :expand => true|false|Array Turns on the automatic abbreviation expansion mechanism for

      • all parsers (true)

      • no parsers (false)

      • a specific parser (Array containing :commands and/or :options)

    • :program => String The name of the Program to be displayed by help and version.

    • :version => String The version of the Program to be displayed by version and help.

    • :usage => String The usage information of the Program to be displayed by help, e.g.

      [<command>] [<options>]
      


85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/pablo.rb', line 85

def initialize opts = Hash.new
    @opts = {
        :command_consumes_all => true,
        :expand => false
    }.merge(opts)

    check_options()

    @finalize = @colorize = @cur = nil
    @toplevel, @args, @pending = true, nil, Hash.new
    @expand, @ambiguity, @registered = Array.new, nil, Array.new
    @options, @commands, @tokens = Hash.new, Hash.new, Hash.new
end

Instance Attribute Details

#argsObject

Returns the value of attribute args.



62
63
64
# File 'lib/pablo.rb', line 62

def args
  @args
end

#commandsObject

Returns the value of attribute commands.



63
64
65
# File 'lib/pablo.rb', line 63

def commands
  @commands
end

#expandObject

Returns the value of attribute expand.



64
65
66
# File 'lib/pablo.rb', line 64

def expand
  @expand
end

#optionsObject

Returns the value of attribute options.



63
64
65
# File 'lib/pablo.rb', line 63

def options
  @options
end

#registeredObject

Returns the value of attribute registered.



64
65
66
# File 'lib/pablo.rb', line 64

def registered
  @registered
end

#tokensObject

Returns the value of attribute tokens.



63
64
65
# File 'lib/pablo.rb', line 63

def tokens
  @tokens
end

Instance Method Details

#always(&block) ⇒ Object

Define arbitrary code to always run at that stage of parsing.



196
197
198
# File 'lib/pablo.rb', line 196

def always &block
    exec(Pablo::Always, &block)
end

#ambiguity(&block) ⇒ Object

The block given to this method will be called each time an argument could not be expanded because there were multiple parsers that matched it. The block will be passed the argument and an Array containing the matching parser names as parameters.



52
53
54
55
# File 'lib/pablo/expansion.rb', line 52

def ambiguity &block
    raise "Pablo#ambiguity needs a block to do something sensible" unless block_given?
    @ambiguity = block
end

#ambiguity_commandObject

Creates standard ambiguity output.



91
92
93
94
95
96
97
98
99
100
101
# File 'lib/pablo/help.rb', line 91

def ambiguity_command
    ambiguity do |arg,arr|
        $stdout.puts "Ambiguous argument '#{arg}'. Could be any of:"

        arr.each do |name|
            $stdout.puts "  * #{name}"
        end

        throw :abort
    end
end

#colorize(string, type) ⇒ Object

Colorize the given string of the given type. See Pablo#colorize for valid values of type. If no colorization function is given, the string is returned uncolored.



46
47
48
49
50
51
52
53
54
# File 'lib/pablo/color.rb', line 46

def colorize string, type
    if type == :text
        string.gsub(/\$([^$]+)\$/ )do |match|
            colorize(match[1..-2], :em)
        end
    else
        @colorize.nil? ? string : (@colorize.call(string, type))
    end
end

#colorizing(&block) ⇒ Object

Runs the given block, whenever something needs to be given color. The block will be given the following arguments:

  • The string to colorize

  • A symbol indicating the type of the String. The color should be guessed from that. Can be any of:

    • :h1 - First class heading

    • :h2 - Second class heading

    • :usage - Usage information

    • :em - Highlighted word



36
37
38
39
# File 'lib/pablo/color.rb', line 36

def colorizing &block
    raise "colorize expects a block" unless block_given?
    @colorize = block
end

#command(*args, &block) ⇒ Object Also known as: cmd

Define a command. args must be:

  • at least one name for the command

  • a hash of options:

    • :default The default value to be set if the command is not recognized.

    • :consume_all => true|false Whether (or not) the command should consume_all remaining arguments after it has been recognized

    • :consume => Fixnum|String|Regexp|Array

      • Fixnum: consume the next n arguments

      • String: consume if the next argument equals that String. Else the option will not match.

      • Regexp: consume if the next argument matches that Regexp. Else the option will not match.

      • Array: consume next arr.length arguments if they equal those given in the Array.

    • :expand => Proc A Proc that will be passed a single argument String and a single name of the token as parameters and has to return a Boolean indicating whether or not the String is an abbreviation of that name.



141
142
143
# File 'lib/pablo.rb', line 141

def command *args, &block
    exec(Pablo::Command, *args, &block)
end

#consume_all?Boolean

Whether or not commands should consume all arguments after they parsed something.

Returns:

  • (Boolean)


269
270
271
# File 'lib/pablo.rb', line 269

def consume_all?
    @opts[:command_consumes_all]
end

#desc(d) ⇒ Object

Set the short description of the next parser.



202
203
204
# File 'lib/pablo.rb', line 202

def desc d
    @pending[:desc] = d
end

#expand!(arg) ⇒ Object

Expands the given argument if possible.



34
35
36
37
38
39
40
41
42
43
44
# File 'lib/pablo/expansion.rb', line 34

def expand! arg
    arr = @expand.find_all { |e| e.start_with?(arg) }

    case arr.length
    when 1 then arr[0]
    when 0 then arg
    else
        @ambiguity.call(arg, arr) unless @ambiguity.nil?
        arg
    end
end

#expands?(type) ⇒ Boolean

Whether or not the given type is subject to expansion.

Returns:

  • (Boolean)


28
29
30
# File 'lib/pablo/expansion.rb', line 28

def expands? type
    @opts[:expand] == true or @opts[:expand] == type
end

#help(command = nil) ⇒ Object

Print the help screen.

  • command == nil: program help

  • else: specific command’s help



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/pablo/help.rb', line 107

def help command = nil
    if command.nil?
        put_header

        $stdout.puts "\n   #{colorize(@opts[:program], :em)} #{colorize(@opts[:usage], :usage)}\n" unless @opts[:usage].nil?
        $stdout.puts "\n#{indent colorize(@opts[:longdesc], :text), 3}" unless @opts[:longdesc].nil?

        cmdlen = @registered.collect(&:names_to_user).max_by(&:length).length

        [:commands, :options].each do |type|
            parsers = @registered.find_all { |p| p.klass_to_sym == type }

            unless parsers.empty?
                $stdout.puts
                $stdout.puts ' ' + colorize("#{type.to_s.capitalize}:", :h2)

                parsers.each { |p|
                    p.desc ?
                        $stdout.puts("   #{colorize(p.names_to_user.ljust(cmdlen), :em)}  :  #{colorize(p.desc, :text)}") :
                        $stdout.puts('   ' + colorize(p.names_to_user.ljust(cmdlen), :em))
                }
            end
        end

        tokens = @registered.find_all { |p| p.is_a? Pablo::Token and not p.desc.nil? }
        unless tokens.empty?
            $stdout.puts
            $stdout.puts ' ' + colorize("Tokens:", :h2)

            tokens.each { |t|
                $stdout.puts "   #{colorize(t.names_to_user.ljust(cmdlen), :em)}  :  #{t.desc}"
            }
        end
    else
        command = expand!(command)
        found = @registered.find do |item|
            if not item.names_to_rex.nil? and item.names_to_rex =~ command
                put_header

                $stdout.print "\n   " + colorize(item.names_to_user, :em)
                $stdout.print ' ' + colorize(item.usage, :usage) unless item.usage.nil?
                $stdout.puts "\n\n"
                $stdout.puts indent(colorize(item.longdesc, :text), 1) unless item.longdesc.nil?
                true
            else false
            end
        end

        if found.nil?
            $stdout.puts "Sorry, but I don't know '#{colorize(command, :em)}'."
            $stdout.puts "Try running '#{colorize('help', :em)}' without parameters to get a list of all the commands, options etc."
        end
    end
end

#help_command(options = {}, &block) ⇒ Object

Creates the standard help command.



80
81
82
83
84
85
86
87
# File 'lib/pablo/help.rb', line 80

def help_command options = {}, &block
    desc('Displays this message.') unless @pending[:desc]
    longdesc("Shows a simple help message for this program.\n" +
        "If a command is given as well, a more detailed message about that\n" +
        "particular command is shown (such as this one).") unless @pending[:longdesc]
    usage('[<command>]') unless @pending[:usage]
    exec(Pablo::HelpCommand, :help, options, &block)
end

#licenseObject

Print license and stop processing. Nothing else.



171
172
173
174
175
176
177
# File 'lib/pablo/help.rb', line 171

def license
    if File.exist? @opts[:license].to_s
        open(@opts[:license]) { |f| $stdout.puts f.read }
    else
        $stdout.puts colorize(@opts[:license], :text)
    end
end

#license_command(options = {}, &block) ⇒ Object

Creates the standard license command.



64
65
66
67
68
# File 'lib/pablo/help.rb', line 64

def license_command options = {}, &block
    desc('Shows the license of this program.') unless @pending[:desc]
    longdesc("Displays information about the license of this program.") unless @pending[:longdesc]
    exec(Pablo::LicenseCommand, :license, options, &block)
end

#load_yaml(yml) ⇒ Object

Loads whole parsers and global options from a YAML source. yml can either be a String of YAML or the path of a YAML file.



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/pablo.rb', line 221

def load_yaml yml
    yml = open(yml).read() if File.exist?(yml)
    opts = YAML::load(yml)

    parsers = opts.delete(:parsers)
    @opts.merge!(opts)

    parsers.each do |p|
        type = [:command, :token, :option, :help_command,
            :ambiguity_command, :license_command, :version_command].find { |s| !p[s].nil? }

        unless type.nil?
            args = p.delete(type)

            if args.is_a? Array then args << p
            else args = [p]
            end

            type == :ambiguity_command ?
                self.send(type) : self.send(type, *args)
        end
    end
end

#longdesc(l) ⇒ Object

Set the long description of the next parser.



208
209
210
# File 'lib/pablo.rb', line 208

def longdesc l
    @pending[:longdesc] = l
end

#missing_argument(*args) ⇒ Object

Displays a message about a missing argument and exits parsing.



56
57
58
# File 'lib/pablo/errors.rb', line 56

def missing_argument *args
    raise MissingArgumentError.new @cur.last_match, *args
end

#option(*args, &block) ⇒ Object Also known as: opt

Define an option. args must be:

  • at least one name for the command

  • a hash of options:

    • :default The default value to be set if the option is not recognized.

    • :negative => true|false|String If true, the option will have a counterpart named –no<name>. If a String, the option will have a counterpart named –<string><name>.

    • :consume => Fixnum|String|Regexp|Array

      • Fixnum: consume the next n arguments

      • String: consume if the next argument equals that String. Else the option will not match.

      • Regexp: consume if the next argument matches that Regexp. Else the option will not match.

      • Array: consume next arr.length arguments if they equal those given in the Array.

    • :expand => Proc A Proc that will be passed a single argument String and a single name of the token as parameters and has to return a Boolean indicating whether or not the String is an abbreviation of that name.



169
170
171
# File 'lib/pablo.rb', line 169

def option *args, &block
    exec(Pablo::Option, *args, &block)
end

#parse(args) ⇒ Object

Starts the parsing process on the given args, according to the registered parsers.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/pablo.rb', line 102

def parse args
    @toplevel, @args = false, Pablo::Arguments.new(args)

    catch :abort do
        begin
            @registered.each { |parser|
                @cur = parser
                parser.parse(@args)
            }
            true
        rescue MissingArgumentError, WrongArgumentError => e
            $stderr.puts e.message
            false
        end
    end
end

#put_headerObject

Prints the standard header for most Pablo output.



181
182
183
184
185
186
187
188
# File 'lib/pablo/help.rb', line 181

def put_header
    unless @opts[:program].nil?
        str = "#{@opts[:program]}"
        str << " #{@opts[:version]}" unless @opts[:version].nil?
        str << " - #{@opts[:desc]}" unless @opts[:desc].nil?
        $stdout.puts ' ' + colorize(str, :h1)
    end
end

#run=(object) ⇒ Object

Sets the run object for this pablo instance.



255
256
257
# File 'lib/pablo.rb', line 255

def run= object
    @opts[:run] = object
end

#run_objectObject

Returns the object which keeps all the instance methods corresponding to the defined parsers - or nil if none was given



262
263
264
# File 'lib/pablo.rb', line 262

def run_object
    @opts[:run]
end

#token(*args, &block) ⇒ Object Also known as: tok

Define a token. args must be:

  • at least one name for the command

  • a hash of options:

    • :default The default value to be set if the token is not recognized.

    • :consume => Fixnum|String|Regexp|Array

      • Fixnum: consume the next n arguments

      • String: consume if the next argument equals that String. Else the option will not match.

      • Regexp: consume if the next argument matches that Regexp. Else the option will not match.

      • Array: consume next arr.length arguments if they equal those given in the Array.



189
190
191
# File 'lib/pablo.rb', line 189

def token *args, &block
    exec(Pablo::Token, *args, &block)
end

#toplevel?Boolean

Whether or not we dwell at toplevel currently. Only valid within a Command, Option, Token etc, but NOT within this class.

Returns:

  • (Boolean)


249
250
251
# File 'lib/pablo.rb', line 249

def toplevel?
    @toplevel
end

#usage(u) ⇒ Object

Set the usage information of the next parser.



214
215
216
# File 'lib/pablo.rb', line 214

def usage u
    @pending[:usage] = u
end

#versionObject

Print version screen and stop processing.



164
165
166
167
# File 'lib/pablo/help.rb', line 164

def version
    $stdout.puts colorize("#{@opts[:program]} #{@opts[:version]}", :h1)
    $stdout.puts @opts[:capabilities].join(' ') unless @opts[:capabilities].nil?
end

#version_command(options = {}, &block) ⇒ Object

Creates the standard version command.



72
73
74
75
76
# File 'lib/pablo/help.rb', line 72

def version_command options = {}, &block
    desc('Shows the version of this program.') unless @pending[:desc]
    longdesc("Displays information about the version of this program.") unless @pending[:longdesc]
    exec(Pablo::VersionCommand, :version, options, &block)
end

#wrong_argument(*args) ⇒ Object

Displays a message about a wrong argument (format) and exits parsing.



63
64
65
# File 'lib/pablo/errors.rb', line 63

def wrong_argument *args
    raise WrongArgumentError.new @cur.last_match, *args
end