optplus 0.0.8

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/bin/optplus ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env ruby18
2
+ #
3
+ # Author:: Robert Sharp
4
+ # Copyright:: Copyright (c) 2013 Robert Sharp
5
+ # License:: Open Software Licence v3.0
6
+ #
7
+ # This software is licensed for use under the Open Software Licence v. 3.0
8
+ # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
9
+ # and in the file copyright.txt. Under the terms of this licence, all derivative works
10
+ # must themselves be licensed under the Open Software Licence v. 3.0
11
+ #
12
+ #
13
+
14
+
15
+ require 'rubygems'
16
+ require 'thor'
17
+ require 'colored'
18
+
19
+ require 'optplus'
20
+
21
+ # thor-based command line interpreter for the service. Define additional commands etc
22
+ # as required.
23
+
24
+ class OptplusCLI < Thor
25
+
26
+ class_option :config, :aliases=>'-c', :desc=>'use the given config file'
27
+
28
+ default_task :usage
29
+
30
+ desc "usage", "You are looking at it"
31
+ def usage
32
+ puts "What does this do?"
33
+ puts ""
34
+ help
35
+ puts ""
36
+ puts "See Also:"
37
+ puts " README.md: (see optplus readme above)"
38
+ puts " GitHub: https://github.com/osburn-sharp/optplus"
39
+ puts " RubyDoc: http://rubydoc.info/github/osburn-sharp/optplus/frames"
40
+ end
41
+
42
+ desc "readme", "display the readme file for the gem"
43
+ def readme
44
+ gem_spec = Gem::Specification.find_by_name('optplus')
45
+ readme_path = File.join(gem_spec.gem_dir, 'README.md')
46
+
47
+ if FileTest.exists?(readme_path) then
48
+
49
+ File.open(readme_path) do |rfile|
50
+ rfile.each_line do |rline|
51
+ puts rline
52
+ end
53
+ end
54
+
55
+ else
56
+ puts "There is no readme available".red.bold
57
+ end
58
+
59
+ rescue
60
+ puts "There is no readme available".red.bold
61
+ end
62
+
63
+ # put your own commands here
64
+ desc "list", "list things that might be useful"
65
+ def list
66
+
67
+ client_opts = {:local=>false}
68
+ client_opts[:config_file] = options[:config] #if options.has_key?(:config)
69
+ JerbilService::Client.connect(Optplus, client_opts) do |service|
70
+ skey = service.service_key
71
+ # do what you need to here
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+
78
+ OptplusCLI.start
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby18
2
+ #
3
+ # @markup ruby
4
+ # @title Installation Script
5
+ #
6
+ # = Optplus Installation Script
7
+ #
8
+ # == Uses Jeni to install Optplus Files
9
+ #
10
+ #
11
+ # Author:: Robert Sharp
12
+ # Copyright:: Copyright (c) 2013 Robert Sharp
13
+ # License:: Open Software Licence v3.0
14
+ #
15
+ # This software is licensed for use under the Open Software Licence v. 3.0
16
+ # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
17
+ # and in the file copyright.txt. Under the terms of this licence, all derivative works
18
+ # must themselves be licensed under the Open Software Licence v. 3.0
19
+ #
20
+ #
21
+
22
+
23
+ require 'rubygems' # jeni uses it anyway to find the jerbil gem so why not use it here?
24
+ require 'jeni'
25
+
26
+
27
+ Jeni::Installer.new_from_gem('optplus') do |jeni|
28
+ jeni.optparse(ARGV)
29
+
30
+ # add custom installation actions
31
+
32
+ end.run!
data/lib/optplus.rb ADDED
@@ -0,0 +1,416 @@
1
+ #
2
+ # Author:: Robert Sharp
3
+ # Copyright:: Copyright (c) 2013 Robert Sharp
4
+ # License:: Open Software Licence v3.0
5
+ #
6
+ # This software is licensed for use under the Open Software Licence v. 3.0
7
+ # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
8
+ # and in the file copyright.txt. Under the terms of this licence, all derivative works
9
+ # must themselves be licensed under the Open Software Licence v. 3.0
10
+ #
11
+ #
12
+ require 'optparse'
13
+ require 'colored'
14
+ require 'abbrev'
15
+ require 'optplus/errors'
16
+
17
+ module Optplus
18
+
19
+ # == Optplus Parser
20
+ #
21
+ # A wrapper class that adds a little value to writing scipts
22
+ # with optparse. Like Thor but without trying to do too much.
23
+ #
24
+ class Parser
25
+
26
+ class << self
27
+
28
+ # define the usage banner, less "Usage: <prog_name>"!
29
+ #
30
+ # For example: usage "[options] [actions] [filename]" becomes:
31
+ # "Usage: progname [options] [actions] [filename]"
32
+ #
33
+ # @param [String] txt that is the banner
34
+ def usage(txt)
35
+ @_banner = txt
36
+ end
37
+
38
+ # Adds a description to the help/usage
39
+ #
40
+ # This takes any number of string arguments and displays them as separate lines.
41
+ #
42
+ # @param [Array] lines of description text as variable arguments
43
+ def description(*lines)
44
+ @_description = lines
45
+ end
46
+
47
+
48
+ # Add a brief description for a specific action
49
+ #
50
+ # Add a little Thor-like description before each method. Unlike Thor,
51
+ # you will not get told off if there is no corresponding method but
52
+ # its probably a good idea if you add one.
53
+ #
54
+ # @param [Symbol] action to be described
55
+ # @param [String] description of the action
56
+ def describe(action, description)
57
+ @_actions ||= Array.new
58
+ @_actions << action.to_s
59
+ @_descriptions ||= Hash.new
60
+ @_descriptions[action] = description
61
+ end
62
+
63
+
64
+ # add a block of helpful text for an action
65
+ #
66
+ # Adds all of the arguments as lines to display when you use the help
67
+ # switch with the given argument, instead of the general help.
68
+ # Note that optplus does not allow options specific to actions so this is
69
+ # just text.
70
+ #
71
+ # @param [String] action to describe with helpful text
72
+ # @param [Array] lines of helpful text to display as arguments
73
+ def help(action, *lines)
74
+ @_help ||= Hash.new
75
+ @_help[action] = lines
76
+ end
77
+
78
+ # @!visibility private
79
+ attr_reader :_banner
80
+ # @!visibility private
81
+ attr_reader :_description
82
+ # @!visibility private
83
+ attr_reader :_actions
84
+ # @!visibility private
85
+ attr_reader :_descriptions
86
+ # @!visibility private
87
+ attr_accessor :_help
88
+ # end of private stuff
89
+
90
+ # @!visibility public
91
+
92
+ # Do the option parsing and actioning stuff
93
+ #
94
+ # If you write an optplus class, run the script and nothing happens it is because
95
+ # you forgot to add MyClass.run! Simple and easily done.
96
+ #
97
+ def run!
98
+
99
+ @_parent ||= nil
100
+
101
+ begin
102
+ me = self.new
103
+
104
+ if me._needs_help? then
105
+ me._help_me
106
+ elsif me._args.length > 0 then
107
+ action = me.next_argument
108
+ alup = @_actions.abbrev(action)
109
+ if alup.has_key?(action) then
110
+
111
+ me.before_actions if me.respond_to?(:before_actions)
112
+
113
+ begin
114
+ me.send(alup[action].to_sym)
115
+
116
+ # trap a deliberate exit and tidy up
117
+ # if required
118
+ rescue Optplus::ExitOnError => err
119
+ puts err.message.red.bold unless err.message == ''
120
+ me.after_actions if me.respond_to?(:after_actions)
121
+ raise Optplus::ExitOnError, '' # with no message
122
+ end
123
+
124
+ me.after_actions if me.respond_to?(:after_actions)
125
+
126
+ else
127
+ puts "Sorry, What?"
128
+ puts ""
129
+ me._get_help
130
+ end
131
+ else
132
+ me._get_help
133
+ end
134
+
135
+ return true
136
+
137
+ rescue OptionParser::InvalidOption => opterr
138
+ puts "Error: Invalid Option".red.bold
139
+ puts "I do not understand the option: #{opterr.args.join}"
140
+ rescue OptionParser::InvalidArgument => opterr
141
+ puts "Error: You have entered an invalid argument to an option".red.bold
142
+ puts "The option in question is: #{opterr.args.join(' ')}"
143
+ rescue OptionParser::AmbiguousOption => opterr
144
+ puts "Error: You need to be clearer than that".red.bold
145
+ puts "I am not be sure what option you mean: #{opterr.args.join}"
146
+ rescue OptionParser::AmbiguousArgument => opterr
147
+ puts "Error: You need to be clearer than that".red.bold
148
+ puts "I am not sure what argument you mean: #{opterr.args.join(' ')}"
149
+ rescue OptionParser::MissingArgument => opterr
150
+ puts "Error: You need to provide an argument with that option".red.bold
151
+ puts "This is the option in question: #{opterr.args.join}"
152
+ rescue OptionParser::ParseError => opterr
153
+ puts "Error: the command line is not as expected".red.bold
154
+ puts opterr.to_s
155
+ rescue Optplus::ParseError => err
156
+ puts "Error: #{err.message}".red.bold
157
+ rescue Optplus::ExitOnError => err
158
+ puts err.message.red.bold unless err.message == ''
159
+ raise Optplus::ExitOnError, '' unless @_parent.nil?
160
+ end
161
+
162
+ # only rescued exceptions will reach here
163
+ exit 1 if @_parent.nil?
164
+
165
+ end
166
+
167
+ end # class << self
168
+
169
+ # @!method self.nest_parser(name, klass, description)
170
+ # nest a parser for subcommands
171
+ # This will add the given name to the actions list
172
+ # and then parse the next argument as a subcommand
173
+ # The klass must inherit {Optplus::NestedParser}
174
+ # @param [Symbol] name of action to nest
175
+ # @param [Class] klass of Nested Parser
176
+ # @param [String] description of action
177
+ instance_eval do
178
+ def nest_parser(name, klass, description)
179
+ self.describe(name, description)
180
+ self._help[name] = klass
181
+ class_eval %Q{
182
+ def #{name}
183
+ #{klass}.run!(self)
184
+ end
185
+ }
186
+ end
187
+ end
188
+
189
+ # create an Optplus instance, define the options and parse the command line
190
+ #
191
+ # This method will call the following if they have been defined:
192
+ #
193
+ # * before_all - any setting up needed right at the start
194
+ # * options - to add options
195
+ # * before_actions - after options have been parsed but before actions are
196
+ # implemented
197
+ #
198
+ # @param [Class] klass for internal use in the instance itself
199
+ def initialize
200
+
201
+ @klass = self.class
202
+ @klass._help ||= Hash.new
203
+ @_help = false
204
+ @options = Hash.new
205
+
206
+ self.before_all if self.respond_to?(:before_all)
207
+
208
+ begin
209
+ @_optparse = OptionParser.new do |opts|
210
+ @program_name = opts.program_name
211
+ opts.banner = "Usage: #{@program_name} #{@klass._banner}"
212
+ opts.separator ""
213
+
214
+ @klass._description.each do |dline|
215
+ opts.separator " " + dline
216
+ end
217
+
218
+ opts.separator ""
219
+ opts.separator "Actions:"
220
+ opts.separator ""
221
+ flags = 0
222
+ @klass._descriptions.each do |key, value|
223
+ flag = @klass._help.has_key?(key.to_sym) ? '(-h)' : ''
224
+ flags += 1 unless flag == ''
225
+ opts.separator " #{key} - #{value} #{flag}"
226
+ end
227
+
228
+ if flags > 0 then
229
+ opts.separator ""
230
+ opts.separator " (-h indicates actions with additional help)"
231
+ opts.separator ""
232
+ end
233
+
234
+ opts.separator ""
235
+ opts.separator "Options:"
236
+ opts.separator ""
237
+
238
+ if @klass._help.length > 0 then
239
+ help_string = 'use with an action for further help'
240
+ else
241
+ help_string = 'you are looking at it'
242
+ end
243
+ options(opts) if self.respond_to?(:options)
244
+ opts.on_tail('-h', '--help', help_string) do
245
+ @_help = true
246
+ end
247
+
248
+ end
249
+
250
+ @_args = @_optparse.permute(ARGV)
251
+
252
+ # trap a deliberate exit and force exit before
253
+ # executing before_actions
254
+ rescue ExitOnError => err
255
+ puts err.message.red.bold unless err.message == ''
256
+ exit 1
257
+ end
258
+
259
+
260
+ end
261
+
262
+ # provides convenient access to the name of the program
263
+ attr_reader :program_name
264
+
265
+ # add optparse option for debug mode
266
+ #
267
+ # @param [Optparse] opts being the optparse instance
268
+ # @param [String] switch being the short-form option on the command line
269
+ def debug_option(opts, switch='-D')
270
+ opts.on_tail(switch, '--debug', 'show debug information') do |d|
271
+ @options[:debug] = d
272
+ end
273
+ end
274
+
275
+ # add optparse option for verbose mode
276
+ #
277
+ # @param [Optparse] opts being the optparse instance
278
+ # @param [String] switch being the short-form option on the command line
279
+ def verbose_option(opts, switch='-V')
280
+ opts.on_tail(switch, '--verbose', 'show verbose information') do |v|
281
+ @options[:verbose] = v
282
+ end
283
+ end
284
+
285
+
286
+ # @!visibility private
287
+ attr_reader :_args
288
+
289
+
290
+ # return the next argument, if there is one or nil otherwise
291
+ #
292
+ # @return [String] being the next argument
293
+ def next_argument
294
+ @_args.shift
295
+ end
296
+
297
+ # return the next argument or the given default
298
+ #
299
+ # @param [Object] default to return if no argument
300
+ # @return [String] being the next argument or the default
301
+ def next_argument_or(default)
302
+ next_argument || default
303
+ end
304
+
305
+ # return the next argument or raise exception with the given message
306
+ #
307
+ # The exception does not need to be handled because {Optplus::Parser.run!}
308
+ # will rescue it and display an error message.
309
+ #
310
+ # @param [String] msg to attach to exception
311
+ # @return [String] being the next argument
312
+ # @raise [Optplus::ParseError] if there is no argument
313
+ def next_argument_or_error(msg)
314
+ next_argument || raise(Optplus::ParseError, msg)
315
+ end
316
+
317
+ # return all of the remaining args, or an empty array
318
+ #
319
+ # This clears all remaining arguments so that subsequent
320
+ # calls e.g. to {Optplus::Parser#next_argument} return nil
321
+ #
322
+ # @return [Array] of arguments
323
+ def all_arguments
324
+ args = @_args.dup
325
+ @_args = Array.new
326
+ return args
327
+ end
328
+
329
+
330
+ # @!visibility private
331
+ def _get_help
332
+ puts @_optparse.help
333
+ puts ""
334
+ end
335
+
336
+ # @!visibility private
337
+ def _needs_help?
338
+ @_help
339
+ end
340
+
341
+ # set the value of the given option, which defaults to true
342
+ #
343
+ # If a value is omitted then the option is set to be true
344
+ #
345
+ # @param [Symbol] key to use in getting the option
346
+ # @param [Object] value to set the option to
347
+ def set_option(key, value=true)
348
+ @options[key] = value
349
+ end
350
+
351
+ # get the value of the option
352
+ #
353
+ # Returns nil if there is no option with the given key
354
+ #
355
+ # @param [Symbol] key to the option to get
356
+ # @return [Object] or nil if no option set
357
+ def get_option(key)
358
+ @options[key]
359
+ end
360
+
361
+ # check if the option has been set
362
+ #
363
+ # @param [Symbol] key for the option to test
364
+ # @return [Boolean] true if option has been set
365
+ def option?(key)
366
+ @options.has_key?(key)
367
+ end
368
+
369
+ # call this to exit the script in case of an error
370
+ # and ensure any tidying up has been done
371
+ def exit_on_error(msg='')
372
+ raise Optplus::ExitOnError, msg
373
+ end
374
+
375
+ # @!visibility private
376
+ def _help_me
377
+ # is there an action on the line?
378
+ if _args.length > 0 then
379
+ # yes, but is it legit?
380
+ action = next_argument
381
+ alup = @klass._actions.abbrev(action)
382
+ action = alup[action].to_sym if alup.has_key?(action)
383
+ if @klass._help.has_key?(action) then
384
+ # valid help so use it
385
+ if @klass._help[action].kind_of?(Array) then
386
+ # its an array of strings, so print them
387
+ puts "Help for #{action}"
388
+ puts ""
389
+ @klass._help[action].each do |aline|
390
+ puts aline
391
+ end
392
+ puts ""
393
+ else
394
+ # its a nested parser so call its _help_me method
395
+ nested_klass = @klass._help[action]
396
+ nested_parser = nested_klass.new(self)
397
+ nested_parser._help_me
398
+ end
399
+ return
400
+ elsif @klass._actions.include?(action.to_s)
401
+ # valid action but no help
402
+ puts "Sorry, there is no specific help for action: #{action}".yellow
403
+ puts ""
404
+ else
405
+ # invalid action
406
+ puts "Sorry, but I do not understand the action: #{action}".red.bold
407
+ puts ""
408
+ end
409
+ end
410
+ _get_help
411
+
412
+ end
413
+
414
+ end
415
+
416
+ end