commander-openflighthpc 1.0.0.pre.alpha1

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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +44 -0
  5. data/.rubocop_todo.yml +77 -0
  6. data/.travis.yml +14 -0
  7. data/CHANGELOG.md +5 -0
  8. data/Gemfile +3 -0
  9. data/LICENSE +51 -0
  10. data/Manifest +38 -0
  11. data/README.md +492 -0
  12. data/Rakefile +13 -0
  13. data/bin/commander +104 -0
  14. data/commander-openflighthpc.gemspec +32 -0
  15. data/lib/commander.rb +36 -0
  16. data/lib/commander/blank.rb +7 -0
  17. data/lib/commander/command.rb +263 -0
  18. data/lib/commander/configure.rb +14 -0
  19. data/lib/commander/core_ext.rb +2 -0
  20. data/lib/commander/core_ext/array.rb +24 -0
  21. data/lib/commander/core_ext/object.rb +8 -0
  22. data/lib/commander/delegates.rb +27 -0
  23. data/lib/commander/help_formatters.rb +52 -0
  24. data/lib/commander/help_formatters/base.rb +24 -0
  25. data/lib/commander/help_formatters/terminal.rb +24 -0
  26. data/lib/commander/help_formatters/terminal/command_help.erb +35 -0
  27. data/lib/commander/help_formatters/terminal/help.erb +36 -0
  28. data/lib/commander/help_formatters/terminal/subcommand_help.erb +23 -0
  29. data/lib/commander/help_formatters/terminal_compact.rb +11 -0
  30. data/lib/commander/help_formatters/terminal_compact/command_help.erb +26 -0
  31. data/lib/commander/help_formatters/terminal_compact/help.erb +29 -0
  32. data/lib/commander/help_formatters/terminal_compact/subcommand_help.erb +15 -0
  33. data/lib/commander/import.rb +5 -0
  34. data/lib/commander/methods.rb +11 -0
  35. data/lib/commander/patches/decimal-integer.rb +17 -0
  36. data/lib/commander/patches/help_formatter_binding.rb +15 -0
  37. data/lib/commander/patches/implicit-short-tags.rb +75 -0
  38. data/lib/commander/patches/option_defaults.rb +23 -0
  39. data/lib/commander/patches/validate_inputs.rb +76 -0
  40. data/lib/commander/platform.rb +7 -0
  41. data/lib/commander/runner.rb +493 -0
  42. data/lib/commander/user_interaction.rb +551 -0
  43. data/lib/commander/version.rb +3 -0
  44. data/spec/command_spec.rb +157 -0
  45. data/spec/configure_spec.rb +37 -0
  46. data/spec/core_ext/array_spec.rb +18 -0
  47. data/spec/core_ext/object_spec.rb +19 -0
  48. data/spec/help_formatters/terminal_compact_spec.rb +69 -0
  49. data/spec/help_formatters/terminal_spec.rb +67 -0
  50. data/spec/methods_spec.rb +61 -0
  51. data/spec/patches/validate_inputs_spec.rb +84 -0
  52. data/spec/runner_spec.rb +672 -0
  53. data/spec/spec_helper.rb +79 -0
  54. data/spec/ui_spec.rb +30 -0
  55. metadata +183 -0
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+
5
+ desc 'Run specs'
6
+ RSpec::Core::RakeTask.new do |t|
7
+ t.verbose = false
8
+ t.rspec_opts = '--color --order random'
9
+ end
10
+
11
+ RuboCop::RakeTask.new
12
+
13
+ task default: [:spec, :rubocop]
data/bin/commander ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'commander/import'
5
+
6
+ program :name, 'commander'
7
+ program :version, Commander::VERSION
8
+ program :description, 'Commander utility program.'
9
+
10
+ command :init do |c|
11
+ c.syntax = 'commander init [option] <file>'
12
+ c.summary = 'Initialize a commander template'
13
+ c.description = 'Initialize an empty <file> with a commander template,
14
+ allowing very quick creation of commander executables.'
15
+ c.example 'Create a new classic style template file.', 'commander init bin/my_executable'
16
+ c.example 'Create a new modular style template file.', 'commander init --modular bin/my_executable'
17
+ c.option '-m', '--modular', 'Initialize a modular style template'
18
+ c.action do |args, options|
19
+ file = args.shift || abort('file argument required.')
20
+ name = ask 'Machine name of program: '
21
+ description = ask 'Describe your program: '
22
+ commands = ask_for_array 'List the commands you wish to create: '
23
+ begin
24
+ if options.modular
25
+ File.open(file, 'w') do |f|
26
+ f.write <<-"...".gsub!(/^ {10}/, '')
27
+ #!/usr/bin/env ruby
28
+
29
+ require 'rubygems'
30
+ require 'commander'
31
+
32
+ class MyApplication
33
+ include Commander::Methods
34
+ # include whatever modules you need
35
+
36
+ def run
37
+ program :name, '#{name}'
38
+ program :version, '0.0.1'
39
+ program :description, '#{description}'
40
+
41
+ ...
42
+ commands.each do |command|
43
+ f.write <<-"...".gsub!(/^ {12}/, '')
44
+ command :#{command} do |c|
45
+ c.syntax = '#{name} #{command} [options]'
46
+ c.summary = ''
47
+ c.description = ''
48
+ c.example 'description', 'command example'
49
+ c.option '--some-switch', 'Some switch that does something'
50
+ c.action do |args, options|
51
+ # Do something or c.when_called #{name.capitalize}::Commands::#{command.capitalize}
52
+ end
53
+ end
54
+
55
+ ...
56
+ end
57
+ f.write <<-"...".gsub!(/^ {12}/, '')
58
+ run!
59
+ end
60
+ end
61
+
62
+ MyApplication.new.run if $0 == __FILE__
63
+ ...
64
+ end
65
+
66
+ File.chmod(0755, file)
67
+ say "Initialized modular template in #{file}"
68
+ else
69
+ File.open(file, 'w') do |f|
70
+ f.write <<-"...".gsub!(/^ {10}/, '')
71
+ #!/usr/bin/env ruby
72
+
73
+ require 'rubygems'
74
+ require 'commander/import'
75
+
76
+ program :name, '#{name}'
77
+ program :version, '0.0.1'
78
+ program :description, '#{description}'
79
+
80
+ ...
81
+ commands.each do |command|
82
+ f.write <<-"...".gsub!(/^ {12}/, '')
83
+ command :#{command} do |c|
84
+ c.syntax = '#{name} #{command} [options]'
85
+ c.summary = ''
86
+ c.description = ''
87
+ c.example 'description', 'command example'
88
+ c.option '--some-switch', 'Some switch that does something'
89
+ c.action do |args, options|
90
+ # Do something or c.when_called #{name.capitalize}::Commands::#{command.capitalize}
91
+ end
92
+ end
93
+
94
+ ...
95
+ end
96
+ end
97
+ File.chmod 0755, file
98
+ say "Initialized template in #{file}"
99
+ end
100
+ rescue => e
101
+ abort e
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
4
+ require 'commander/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'commander-openflighthpc'
8
+ s.version = Commander::VERSION
9
+ s.authors = ['Alces Flight Ltd', 'TJ Holowaychuk', 'Gabriel Gilder']
10
+ s.email = ['flight@openflighthpc.org']
11
+ s.license = 'MIT'
12
+ s.homepage = 'https://github.com/openflighthpc/commander-openflighthpc'
13
+ s.summary = 'The complete solution for Ruby command-line executables'
14
+ s.description = 'The complete solution for Ruby command-line executables. Commander bridges the gap between other terminal related libraries you know and love (OptionParser, HighLine), while providing many new features, and an elegant API.'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
19
+ s.require_paths = ['lib']
20
+
21
+ s.add_runtime_dependency('highline', '~> 1.7.2')
22
+
23
+ s.add_development_dependency('rspec', '~> 3.2')
24
+ s.add_development_dependency('rake')
25
+ s.add_development_dependency('simplecov')
26
+ if RUBY_VERSION < '2.0'
27
+ s.add_development_dependency('rubocop', '~> 0.41.1')
28
+ s.add_development_dependency('json', '< 2.0')
29
+ else
30
+ s.add_development_dependency('rubocop', '~> 0.49.1')
31
+ end
32
+ end
data/lib/commander.rb ADDED
@@ -0,0 +1,36 @@
1
+ #--
2
+ # Copyright (c) 2008-2009 TJ Holowaychuk <tj@vision-media.ca>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'highline'
25
+ $terminal = HighLine.new
26
+ require 'commander/version'
27
+ require 'commander/blank'
28
+ require 'commander/user_interaction'
29
+ require 'commander/core_ext'
30
+ require 'commander/runner'
31
+ require 'commander/command'
32
+ require 'commander/help_formatters'
33
+ require 'commander/platform'
34
+ require 'commander/delegates'
35
+ require 'commander/methods'
36
+ require 'commander/configure'
@@ -0,0 +1,7 @@
1
+ module Blank
2
+ def self.included(base)
3
+ base.class_eval do
4
+ instance_methods.each { |m| undef_method m unless m =~ /^__|object_id/ }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,263 @@
1
+ require 'optparse'
2
+ require 'commander/patches/implicit-short-tags'
3
+ require 'commander/patches/decimal-integer'
4
+ require 'commander/patches/validate_inputs'
5
+ require 'commander/patches/option_defaults'
6
+ require 'commander/patches/help_formatter_binding'
7
+
8
+ OptionParser.prepend Commander::Patches::ImplicitShortTags
9
+ OptionParser.prepend Commander::Patches::DecimalInteger
10
+
11
+ module Commander
12
+ class Command
13
+ prepend Patches::ValidateInputs
14
+
15
+ attr_accessor :name, :examples, :syntax, :description
16
+ attr_accessor :summary, :proxy_options, :options, :hidden
17
+ attr_reader :sub_command_group
18
+
19
+ alias sub_command= hidden=
20
+ alias sub_command hidden
21
+
22
+ ##
23
+ # Options struct.
24
+
25
+ class Options
26
+ include Blank
27
+
28
+ def initialize
29
+ @table = {}
30
+ end
31
+
32
+ def __hash__
33
+ @table
34
+ end
35
+
36
+ def method_missing(meth, *args)
37
+ meth.to_s =~ /=$/ ? @table[meth.to_s.chop.to_sym] = args.first : @table[meth]
38
+ end
39
+
40
+ def default(defaults = {})
41
+ @table = defaults.merge! @table
42
+ end
43
+
44
+ def inspect
45
+ "<Commander::Command::Options #{ __hash__.map { |k, v| "#{k}=#{v.inspect}" }.join(', ') }>"
46
+ end
47
+ end
48
+
49
+ ##
50
+ # Initialize new command with specified _name_.
51
+
52
+ def initialize(name)
53
+ @name, @examples, @when_called = name.to_s, [], []
54
+ @options, @proxy_options = [], []
55
+ end
56
+
57
+ ##
58
+ # Add a usage example for this command.
59
+ #
60
+ # Usage examples are later displayed in help documentation
61
+ # created by the help formatters.
62
+ #
63
+ # === Examples
64
+ #
65
+ # command :something do |c|
66
+ # c.example "Should do something", "my_command something"
67
+ # end
68
+ #
69
+
70
+ def example(description, command)
71
+ @examples << [description, command]
72
+ end
73
+
74
+ ##
75
+ # Add an option.
76
+ #
77
+ # Options are parsed via OptionParser so view it
78
+ # for additional usage documentation. A block may optionally be
79
+ # passed to handle the option, otherwise the _options_ struct seen below
80
+ # contains the results of this option. This handles common formats such as:
81
+ #
82
+ # -h, --help options.help # => bool
83
+ # --[no-]feature options.feature # => bool
84
+ # --large-switch options.large_switch # => bool
85
+ # --file FILE options.file # => file passed
86
+ # --list WORDS options.list # => array
87
+ # --date [DATE] options.date # => date or nil when optional argument not set
88
+ #
89
+ # === Examples
90
+ #
91
+ # command :something do |c|
92
+ # c.option '--recursive', 'Do something recursively'
93
+ # c.option '--file FILE', 'Specify a file'
94
+ # c.option('--info', 'Display info') { puts "handle with block" }
95
+ # c.option '--[no-]feature', 'With or without feature'
96
+ # c.option '--list FILES', Array, 'List the files specified'
97
+ #
98
+ # c.when_called do |args, options|
99
+ # do_something_recursively if options.recursive
100
+ # do_something_with_file options.file if options.file
101
+ # end
102
+ # end
103
+ #
104
+ # === Help Formatters
105
+ #
106
+ # This method also parses the arguments passed in order to determine
107
+ # which were switches, and which were descriptions for the
108
+ # option which can later be used within help formatters
109
+ # using option[:switches] and option[:description].
110
+ #
111
+ # === Input Parsing
112
+ #
113
+ # Since Commander utilizes OptionParser you can pre-parse and evaluate
114
+ # option arguments. Simply require 'optparse/time', or 'optparse/date', as these
115
+ # objects must respond to #parse.
116
+ #
117
+ # c.option '--time TIME', Time
118
+ # c.option '--date [DATE]', Date
119
+ #
120
+
121
+ # NOTE: This method is being patched to handle defaults differently
122
+ prepend Patches::OptionDefaults
123
+ def option(*args, &block)
124
+ switches, description = Runner.separate_switches_from_description(*args)
125
+ proc = block || option_proc(switches)
126
+ @options << {
127
+ args: args,
128
+ proc: proc,
129
+ switches: switches,
130
+ description: description,
131
+ }
132
+ end
133
+
134
+ ##
135
+ # Handle execution of command. The handler may be a class,
136
+ # object, or block (see examples below).
137
+ #
138
+ # === Examples
139
+ #
140
+ # # Simple block handling
141
+ # c.when_called do |args, options|
142
+ # # do something
143
+ # end
144
+ #
145
+ # # Create inst of Something and pass args / options
146
+ # c.when_called MyLib::Command::Something
147
+ #
148
+ # # Create inst of Something and use arbitrary method
149
+ # c.when_called MyLib::Command::Something, :some_method
150
+ #
151
+ # # Pass an object to handle callback (requires method symbol)
152
+ # c.when_called SomeObject, :some_method
153
+ #
154
+
155
+ def when_called(*args, &block)
156
+ fail ArgumentError, 'must pass an object, class, or block.' if args.empty? && !block
157
+ @when_called = block ? [block] : args
158
+ end
159
+ alias action when_called
160
+
161
+ ##
162
+ # Handles displaying subcommand help. By default it will set the action to
163
+ # display the subcommand if the action hasn't already been set
164
+
165
+ def sub_command_group?
166
+ !!@sub_command_group
167
+ end
168
+
169
+ def sub_command_group=(value)
170
+ @sub_command_group = value
171
+ if @when_called.empty?
172
+ self.action {
173
+ exec("#{$0} #{ARGV.join(" ")} --help")
174
+ }
175
+ end
176
+ end
177
+
178
+ def configure_sub_command(runner)
179
+ @sub_command_group = true
180
+ if @when_called.empty?
181
+ action do |args, opts|
182
+ unless args.empty?
183
+ raise Commander::Runner::InvalidCommandError,
184
+ "unrecognized subcommand '#{args[0]}'"
185
+ end
186
+ runner.command('help').run(ARGV[0])
187
+ end
188
+ end
189
+ end
190
+
191
+ ##
192
+ # Run the command with _args_.
193
+ #
194
+ # * parses options, call option blocks
195
+ # * invokes when_called proc
196
+ #
197
+
198
+ def run(*args)
199
+ call parse_options_and_call_procs(*args)
200
+ end
201
+
202
+ #:stopdoc:
203
+
204
+ ##
205
+ # Parses options and calls associated procs,
206
+ # returning the arguments remaining.
207
+
208
+ def parse_options_and_call_procs(*args)
209
+ opt = @options.each_with_object(OptionParser.new) do |option, opts|
210
+ opts.on(*option[:args], &option[:proc])
211
+ opts
212
+ end
213
+ default_opt = @options.each_with_object([]) do |h, arr|
214
+ if h.key?(:default)
215
+ arr.push(h[:switches][0].split[0])
216
+ arr.push(h[:default].to_s)
217
+ end
218
+ end
219
+ opt.parse! default_opt
220
+ opt.parse! args
221
+ end
222
+
223
+ ##
224
+ # Call the commands when_called block with _args_.
225
+
226
+ def call(args = [])
227
+ object = @when_called.shift
228
+ meth = @when_called.shift || :call
229
+ options = proxy_option_struct
230
+ case object
231
+ when Proc then object.call(args, options)
232
+ when Class then meth != :call ? object.new.send(meth, args, options) : object.new(args, options)
233
+ else object.send(meth, args, options) if object
234
+ end
235
+ end
236
+
237
+ ##
238
+ # Creates an Options instance populated with the option values
239
+ # collected by the #option_proc.
240
+
241
+ def proxy_option_struct
242
+ proxy_options.each_with_object(Options.new) do |(option, value), options|
243
+ # options that are present will evaluate to true
244
+ value = true if value.nil?
245
+ options.__send__ :"#{option}=", value
246
+ options
247
+ end
248
+ end
249
+
250
+ ##
251
+ # Option proxy proc used when a block is not explicitly passed
252
+ # via the #option method. This allows commander to auto-populate
253
+ # and work with option values.
254
+
255
+ def option_proc(switches)
256
+ ->(value) { proxy_options << [Runner.switch_to_sym(switches.last), value] }
257
+ end
258
+
259
+ def inspect
260
+ "<Commander::Command:#{name}>"
261
+ end
262
+ end
263
+ end