optplus 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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