mercenary 0.2.1 → 0.3.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/History.markdown +19 -0
- data/LICENSE.txt +1 -1
- data/README.md +2 -0
- data/examples/help_dialogue.rb +45 -0
- data/examples/logging.rb +39 -0
- data/examples/trace.rb +21 -0
- data/lib/mercenary.rb +4 -2
- data/lib/mercenary/command.rb +85 -22
- data/lib/mercenary/option.rb +80 -0
- data/lib/mercenary/presenter.rb +80 -0
- data/lib/mercenary/program.rb +11 -17
- data/lib/mercenary/version.rb +1 -1
- data/script/cibuild +3 -0
- data/script/console +3 -0
- data/script/examples +17 -0
- data/spec/command_spec.rb +20 -4
- data/spec/option_spec.rb +83 -0
- data/spec/presenter_spec.rb +38 -0
- data/spec/program_spec.rb +19 -0
- metadata +25 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9ebb7e735aa1147cbcc69420fa3a934830c0415
|
4
|
+
data.tar.gz: 120e555e1d05e2af4e674b42cd9143195310b92e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: becf09d037cb3f6475239595c42deba5c89791ccc13f5d2c622d1a88769d91e89c7da2685f1c238f2e41d0308b81cd81679f7ac7352fffa93d36cf7d989a7975
|
7
|
+
data.tar.gz: 2ca3a69a6039ab31343002957dec0c8753181c17817d3d6b59afaa7dc6186e3d78f501979ffae98e3ecb4ba730bdbd46e4d8d5974f653f25fedf603fc9c75cde
|
data/.travis.yml
CHANGED
data/History.markdown
CHANGED
@@ -8,6 +8,25 @@
|
|
8
8
|
|
9
9
|
### Development Fixes
|
10
10
|
|
11
|
+
## 0.3.0 / 2014-02-20
|
12
|
+
|
13
|
+
### Major Enhancements
|
14
|
+
|
15
|
+
* Officially drop 1.8.7 support (#14)
|
16
|
+
* Allow Commands to set their own versions (#17)
|
17
|
+
* Show subcommands, options and usage in help and attach to all commands (#18)
|
18
|
+
* Add `-t, --trace` to allow full exception backtrace to print, otherwise print
|
19
|
+
just the error message (#19)
|
20
|
+
|
21
|
+
### Minor Enhancements
|
22
|
+
|
23
|
+
* Logging state is maintained throughout process (#12)
|
24
|
+
* Tidy up Command#logger output (#21)
|
25
|
+
|
26
|
+
### Development Fixes
|
27
|
+
|
28
|
+
* Added specs for `Program` (#13)
|
29
|
+
|
11
30
|
## 0.2.1 / 2013-12-25
|
12
31
|
|
13
32
|
### Bug Fixes
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })
|
4
|
+
|
5
|
+
require "mercenary"
|
6
|
+
|
7
|
+
# This example sets the logging mode of mercenary to
|
8
|
+
# debug. Logging messages from "p.logger.debug" will
|
9
|
+
# be output to STDOUT.
|
10
|
+
|
11
|
+
Mercenary.program(:help_dialogue) do |p|
|
12
|
+
|
13
|
+
p.version "2.0.1"
|
14
|
+
p.description 'An example of the help dialogue in Mercenary'
|
15
|
+
p.syntax 'help_dialogue <subcommand>'
|
16
|
+
|
17
|
+
p.command(:some_subcommand) do |c|
|
18
|
+
c.version '1.4.2'
|
19
|
+
c.syntax 'some_subcommand <subcommand> [options]'
|
20
|
+
c.description 'Some subcommand to do something'
|
21
|
+
c.option 'an_option', '-o', '--option', 'Some option'
|
22
|
+
|
23
|
+
c.command(:yet_another_sub) do |f|
|
24
|
+
f.syntax 'yet_another_sub [options]'
|
25
|
+
f.description 'Do amazing things'
|
26
|
+
f.option 'blah', '-b', '--blah', 'Trigger blah flag'
|
27
|
+
f.option 'heh', '-H ARG', '--heh ARG', 'Give a heh'
|
28
|
+
|
29
|
+
f.action do |args, options|
|
30
|
+
print "Args: "
|
31
|
+
p args
|
32
|
+
print "Opts: "
|
33
|
+
p options
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
p.command(:another_subcommand) do |c|
|
39
|
+
c.syntax 'another_subcommand <subcommand> [options]'
|
40
|
+
c.description 'Another subcommand to do something different.'
|
41
|
+
c.option 'an_option', '-O', '--option', 'Some option'
|
42
|
+
c.option 'another_options', '--pluginzzz', 'Set where the plugins should be found from'
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/examples/logging.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })
|
4
|
+
|
5
|
+
require "mercenary"
|
6
|
+
|
7
|
+
# This example sets the logging mode of mercenary to
|
8
|
+
# debug. Logging messages from "p.logger.debug" will
|
9
|
+
# be output to STDOUT.
|
10
|
+
|
11
|
+
Mercenary.program(:logger_output) do |p|
|
12
|
+
|
13
|
+
p.version "5.2.6"
|
14
|
+
p.description 'An example of turning on logging for Mercenary.'
|
15
|
+
p.syntax 'logger_output'
|
16
|
+
|
17
|
+
|
18
|
+
p.logger.info "The default log level is INFO. So this will output."
|
19
|
+
p.logger.debug "Since DEBUG is below INFO, this will not output."
|
20
|
+
|
21
|
+
p.logger(Logger::DEBUG)
|
22
|
+
p.logger.debug "Logger level now set to DEBUG. So everything will output."
|
23
|
+
|
24
|
+
p.logger.debug "Example of DEBUG level message."
|
25
|
+
p.logger.info "Example of INFO level message."
|
26
|
+
p.logger.warn "Example of WARN level message."
|
27
|
+
p.logger.error "Example of ERROR level message."
|
28
|
+
p.logger.fatal "Example of FATAL level message."
|
29
|
+
p.logger.unknown "Example of UNKNOWN level message."
|
30
|
+
|
31
|
+
p.action do |args, options|
|
32
|
+
|
33
|
+
p.logger(Logger::INFO)
|
34
|
+
p.logger.debug "Logger level back to INFO. This line will not output."
|
35
|
+
p.logger.info "This INFO message will output."
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/examples/trace.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })
|
4
|
+
|
5
|
+
require "mercenary"
|
6
|
+
|
7
|
+
# This example sets the logging mode of mercenary to
|
8
|
+
# debug. Logging messages from "p.logger.debug" will
|
9
|
+
# be output to STDOUT.
|
10
|
+
|
11
|
+
Mercenary.program(:trace) do |p|
|
12
|
+
|
13
|
+
p.version "2.0.1"
|
14
|
+
p.description 'An example of traces in Mercenary'
|
15
|
+
p.syntax 'trace <subcommand>'
|
16
|
+
|
17
|
+
p.action do |_, _|
|
18
|
+
raise ArgumentError.new("YOU DID SOMETHING TERRIBLE YOU BUFFOON")
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/lib/mercenary.rb
CHANGED
@@ -6,8 +6,10 @@ require "optparse"
|
|
6
6
|
require "logger"
|
7
7
|
|
8
8
|
module Mercenary
|
9
|
-
autoload :Command,
|
10
|
-
autoload :
|
9
|
+
autoload :Command, "mercenary/command"
|
10
|
+
autoload :Option, "mercenary/option"
|
11
|
+
autoload :Presenter, "mercenary/presenter"
|
12
|
+
autoload :Program, "mercenary/program"
|
11
13
|
|
12
14
|
# Public: Instantiate a new program and execute.
|
13
15
|
#
|
data/lib/mercenary/command.rb
CHANGED
@@ -8,6 +8,7 @@ module Mercenary
|
|
8
8
|
attr_accessor :actions
|
9
9
|
attr_reader :map
|
10
10
|
attr_accessor :parent
|
11
|
+
attr_reader :trace
|
11
12
|
|
12
13
|
# Public: Creates a new Command
|
13
14
|
#
|
@@ -17,12 +18,23 @@ module Mercenary
|
|
17
18
|
#
|
18
19
|
# Returns nothing
|
19
20
|
def initialize(name, parent = nil)
|
20
|
-
@name
|
21
|
-
@options
|
22
|
-
@commands =
|
23
|
-
@actions
|
24
|
-
@map
|
25
|
-
@parent
|
21
|
+
@name = name
|
22
|
+
@options = []
|
23
|
+
@commands = {}
|
24
|
+
@actions = []
|
25
|
+
@map = {}
|
26
|
+
@parent = parent
|
27
|
+
@trace = false
|
28
|
+
end
|
29
|
+
|
30
|
+
# Public: Sets or gets the command version
|
31
|
+
#
|
32
|
+
# version - the command version (optional)
|
33
|
+
#
|
34
|
+
# Returns the version and sets it if an argument is non-nil
|
35
|
+
def version(version = nil)
|
36
|
+
@version = version if version
|
37
|
+
@version
|
26
38
|
end
|
27
39
|
|
28
40
|
# Public: Sets or gets the syntax string
|
@@ -32,7 +44,12 @@ module Mercenary
|
|
32
44
|
# Returns the syntax string and sets it if an argument is present
|
33
45
|
def syntax(syntax = nil)
|
34
46
|
@syntax = syntax if syntax
|
35
|
-
|
47
|
+
syntax_list = []
|
48
|
+
if parent
|
49
|
+
syntax_list << parent.syntax.to_s.gsub(/<[\w\s-]+>/, '').gsub(/\[[\w\s-]+\]/, '').strip
|
50
|
+
end
|
51
|
+
syntax_list << (@syntax || name.to_s)
|
52
|
+
syntax_list.join(" ")
|
36
53
|
end
|
37
54
|
|
38
55
|
# Public: Sets or gets the command description
|
@@ -71,8 +88,9 @@ module Mercenary
|
|
71
88
|
#
|
72
89
|
# Returns nothing
|
73
90
|
def option(sym, *options)
|
74
|
-
|
75
|
-
@
|
91
|
+
new_option = Option.new(sym, options)
|
92
|
+
@options << new_option
|
93
|
+
@map[new_option.hash] = sym
|
76
94
|
end
|
77
95
|
|
78
96
|
# Public: Adds a subcommand
|
@@ -112,15 +130,16 @@ module Mercenary
|
|
112
130
|
# level - the logger level (a Logger constant, see docs for more info)
|
113
131
|
#
|
114
132
|
# Returns the instance of Logger
|
115
|
-
def logger(level =
|
133
|
+
def logger(level = nil)
|
116
134
|
unless @logger
|
117
135
|
@logger = Logger.new(STDOUT)
|
136
|
+
@logger.level = level || Logger::INFO
|
118
137
|
@logger.formatter = proc do |severity, datetime, progname, msg|
|
119
|
-
"#{
|
138
|
+
"#{identity} | " << "#{severity.downcase.capitalize}:".ljust(7) << " #{msg}\n"
|
120
139
|
end
|
121
140
|
end
|
122
141
|
|
123
|
-
@logger.level = level
|
142
|
+
@logger.level = level unless level.nil?
|
124
143
|
@logger
|
125
144
|
end
|
126
145
|
|
@@ -134,6 +153,7 @@ module Mercenary
|
|
134
153
|
def go(argv, opts, config)
|
135
154
|
opts.banner = "Usage: #{syntax}"
|
136
155
|
process_options(opts, config)
|
156
|
+
add_default_options(opts)
|
137
157
|
|
138
158
|
if argv[0] && cmd = commands[argv[0].to_sym]
|
139
159
|
logger.debug "Found subcommand '#{cmd.name}'"
|
@@ -153,13 +173,36 @@ module Mercenary
|
|
153
173
|
#
|
154
174
|
# Returns nothing
|
155
175
|
def process_options(opts, config)
|
156
|
-
options.each do |
|
157
|
-
opts.on(*
|
158
|
-
config[map[
|
176
|
+
options.each do |option|
|
177
|
+
opts.on(*option.for_option_parser) do |x|
|
178
|
+
config[map[option.hash]] = x
|
159
179
|
end
|
160
180
|
end
|
161
181
|
end
|
162
182
|
|
183
|
+
# Public: Add version and help options to the command
|
184
|
+
#
|
185
|
+
# opts - instance of OptionParser
|
186
|
+
#
|
187
|
+
# Returns nothing
|
188
|
+
def add_default_options(opts)
|
189
|
+
option 'show_help', '-h', '--help', 'Show this message'
|
190
|
+
option 'show_version', '-v', '--version', 'Print the name and version'
|
191
|
+
opts.on("-v", "--version", "Print the version") do
|
192
|
+
puts "#{name} #{version}"
|
193
|
+
abort
|
194
|
+
end
|
195
|
+
|
196
|
+
opts.on('-t', '--trace', 'Show full backtrace if an error occurs') do
|
197
|
+
@trace = true
|
198
|
+
end
|
199
|
+
|
200
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
201
|
+
puts self
|
202
|
+
exit
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
163
206
|
# Public: Execute all actions given the inputted args and options
|
164
207
|
#
|
165
208
|
# argv - (optional) command-line args (sans opts)
|
@@ -188,19 +231,39 @@ module Mercenary
|
|
188
231
|
#
|
189
232
|
# Returns a string which identifies this command
|
190
233
|
def ident
|
191
|
-
"<Command name=#{
|
234
|
+
"<Command name=#{identity}>"
|
235
|
+
end
|
236
|
+
|
237
|
+
# Public: Get the full identity (name & version) of this command
|
238
|
+
#
|
239
|
+
# Returns a string containing the name and version if it exists
|
240
|
+
def identity
|
241
|
+
"#{full_name} #{version if version}".strip
|
242
|
+
end
|
243
|
+
|
244
|
+
# Public: Get the name of the current command plus that of
|
245
|
+
# its parent commands
|
246
|
+
#
|
247
|
+
# Returns the full name of the command
|
248
|
+
def full_name
|
249
|
+
the_name = []
|
250
|
+
the_name << parent.full_name if parent && parent.full_name
|
251
|
+
the_name << name
|
252
|
+
the_name.join(" ")
|
253
|
+
end
|
254
|
+
|
255
|
+
# Public: Build a string containing a summary of the command
|
256
|
+
#
|
257
|
+
# Returns a one-line summary of the command.
|
258
|
+
def summarize
|
259
|
+
" #{name.to_s.ljust(20)} #{description}"
|
192
260
|
end
|
193
261
|
|
194
262
|
# Public: Build a string containing the command name, options and any subcommands
|
195
263
|
#
|
196
264
|
# Returns the string identifying this command, its options and its subcommands
|
197
265
|
def to_s
|
198
|
-
|
199
|
-
msg += "Command: #{name}\n"
|
200
|
-
options.each { |o| msg += " " + o.inspect + "\n"}
|
201
|
-
msg += "\n"
|
202
|
-
commands.each { |k, v| msg += commands[k].inspect }
|
203
|
-
msg
|
266
|
+
Presenter.new(self).print_command
|
204
267
|
end
|
205
268
|
end
|
206
269
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Mercenary
|
2
|
+
class Option
|
3
|
+
attr_reader :config_key, :description, :switches
|
4
|
+
|
5
|
+
# Public: Create a new Option
|
6
|
+
#
|
7
|
+
# config_key - the key in the config hash to which the value of this option will map
|
8
|
+
# info - an array containing first the switches, then a description of the option
|
9
|
+
#
|
10
|
+
# Returns nothing
|
11
|
+
def initialize(config_key, info)
|
12
|
+
@config_key = config_key
|
13
|
+
@description = info.last unless info.last.start_with?("-")
|
14
|
+
set_switches(info.take(info.size - [@description].reject(&:nil?).size))
|
15
|
+
end
|
16
|
+
|
17
|
+
# Public: Fetch the array containing the info OptionParser is interested in
|
18
|
+
#
|
19
|
+
# Returns the array which OptionParser#on wants
|
20
|
+
def for_option_parser
|
21
|
+
[switches.reject(&:empty?), description].reject{ |o| o.nil? || o.empty? }.flatten
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: Build a string representation of this option including the
|
25
|
+
# switches and description
|
26
|
+
#
|
27
|
+
# Returns a string representation of this option
|
28
|
+
def to_s
|
29
|
+
"#{formatted_switches} #{description}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Build a beautifully-formatted string representation of the switches
|
33
|
+
#
|
34
|
+
# Returns a formatted string representation of the switches
|
35
|
+
def formatted_switches
|
36
|
+
[
|
37
|
+
switches.first.rjust(10),
|
38
|
+
switches.last.ljust(13)
|
39
|
+
].join(", ").gsub(/ , /, ' ').gsub(/, /, ' ')
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Hash based on the hash value of instance variables
|
43
|
+
#
|
44
|
+
# Returns a Fixnum which is unique to this Option based on the instance variables
|
45
|
+
def hash
|
46
|
+
instance_variables.map do |var|
|
47
|
+
instance_variable_get(var).hash
|
48
|
+
end.reduce(:&)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Public: Check equivalence of two Options based on equivalence of their
|
52
|
+
# instance variables
|
53
|
+
#
|
54
|
+
# Returns true if all the instance variables are equal, false otherwise
|
55
|
+
def eql?(other)
|
56
|
+
return false unless self.class.eql?(other.class)
|
57
|
+
instance_variables.map do |var|
|
58
|
+
instance_variable_get(var).eql?(other.instance_variable_get(var))
|
59
|
+
end.all?
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Private: Set the full switches array, ensuring the first element is the
|
65
|
+
# short switch and the second element is the long switch
|
66
|
+
#
|
67
|
+
# Returns the corrected switches array
|
68
|
+
def set_switches(switches)
|
69
|
+
if switches.size < 2
|
70
|
+
if switches.first.start_with?("--")
|
71
|
+
switches.unshift ""
|
72
|
+
else
|
73
|
+
switches << ""
|
74
|
+
end
|
75
|
+
end
|
76
|
+
@switches = switches
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Mercenary
|
2
|
+
class Presenter
|
3
|
+
attr_accessor :command
|
4
|
+
|
5
|
+
# Public: Make a new Presenter
|
6
|
+
#
|
7
|
+
# command - a Mercenary::Command to present
|
8
|
+
#
|
9
|
+
# Returns nothing
|
10
|
+
def initialize(command)
|
11
|
+
@command = command
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: Builds a string representation of the command usage
|
15
|
+
#
|
16
|
+
# Returns the string representation of the command usage
|
17
|
+
def usage_presentation
|
18
|
+
" #{command.syntax}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Builds a string representation of the options
|
22
|
+
#
|
23
|
+
# Returns the string representation of the options
|
24
|
+
def options_presentation
|
25
|
+
return nil unless command.options.size > 0
|
26
|
+
command.options.map(&:to_s).join("\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Builds a string representation of the subcommands
|
30
|
+
#
|
31
|
+
# Returns the string representation of the subcommands
|
32
|
+
def subcommands_presentation
|
33
|
+
return nil unless command.commands.size > 0
|
34
|
+
command.commands.values.map(&:summarize).join("\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Builds the command header, including the command identity and description
|
38
|
+
#
|
39
|
+
# Returns the command header as a String
|
40
|
+
def command_header
|
41
|
+
header = "#{command.identity}"
|
42
|
+
header << " -- #{command.description}" if command.description
|
43
|
+
header
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: Builds a string representation of the whole command
|
47
|
+
#
|
48
|
+
# Returns the string representation of the whole command
|
49
|
+
def command_presentation
|
50
|
+
msg = []
|
51
|
+
msg << command_header
|
52
|
+
msg << "Usage:"
|
53
|
+
msg << usage_presentation
|
54
|
+
|
55
|
+
if opts = options_presentation
|
56
|
+
msg << "Options:\n#{opts}"
|
57
|
+
end
|
58
|
+
if subcommands = subcommands_presentation
|
59
|
+
msg << "Subcommands:\n#{subcommands_presentation}"
|
60
|
+
end
|
61
|
+
msg.join("\n\n")
|
62
|
+
end
|
63
|
+
|
64
|
+
# Public: Turn a print_* into a *_presentation or freak out
|
65
|
+
#
|
66
|
+
# meth - the method being called
|
67
|
+
# args - an array of arguments passed to the missing method
|
68
|
+
# block - the block passed to the missing method
|
69
|
+
#
|
70
|
+
# Returns the value of whatever function is called
|
71
|
+
def method_missing(meth, *args, &block)
|
72
|
+
if meth.to_s =~ /^print_(.+)$/
|
73
|
+
send("#{$1.downcase}_presentation")
|
74
|
+
else
|
75
|
+
super # You *must* call super if you don't handle the method,
|
76
|
+
# otherwise you'll mess up Ruby's method lookup.
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/mercenary/program.rb
CHANGED
@@ -13,19 +13,9 @@ module Mercenary
|
|
13
13
|
super(name)
|
14
14
|
end
|
15
15
|
|
16
|
-
# Public: Sets or gets the program version
|
17
|
-
#
|
18
|
-
# version - the program version (optional)
|
19
|
-
#
|
20
|
-
# Returns the version and sets it if an argument is present
|
21
|
-
def version(version = nil)
|
22
|
-
@version = version if version
|
23
|
-
@version
|
24
|
-
end
|
25
|
-
|
26
16
|
# Public: Run the program
|
27
17
|
#
|
28
|
-
# argv
|
18
|
+
# argv - an array of string args (usually ARGV)
|
29
19
|
#
|
30
20
|
# Returns nothing
|
31
21
|
def go(argv)
|
@@ -35,18 +25,22 @@ module Mercenary
|
|
35
25
|
|
36
26
|
@optparse = OptionParser.new do |opts|
|
37
27
|
cmd = super(argv, opts, @config)
|
38
|
-
|
39
|
-
opts.on('-v', '--version', 'Print the version') do
|
40
|
-
puts "#{name} #{version}"
|
41
|
-
abort
|
42
|
-
end
|
43
28
|
end
|
44
29
|
|
45
30
|
@optparse.parse!(argv)
|
46
31
|
|
47
32
|
logger.debug("Parsed config: #{@config.inspect}")
|
48
33
|
|
49
|
-
|
34
|
+
begin
|
35
|
+
cmd.execute(argv, @config)
|
36
|
+
rescue => e
|
37
|
+
if cmd.trace
|
38
|
+
raise e
|
39
|
+
else
|
40
|
+
logger.error e.message
|
41
|
+
abort
|
42
|
+
end
|
43
|
+
end
|
50
44
|
end
|
51
45
|
end
|
52
46
|
end
|
data/lib/mercenary/version.rb
CHANGED
data/script/cibuild
CHANGED
data/script/console
ADDED
data/script/examples
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#! /bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
function run () {
|
6
|
+
echo "+ ruby ./examples/$@"
|
7
|
+
ruby -e "puts '=' * 79"
|
8
|
+
ruby ./examples/$@
|
9
|
+
ruby -e "puts '=' * 79"
|
10
|
+
}
|
11
|
+
|
12
|
+
run logging.rb
|
13
|
+
run help_dialogue.rb -h
|
14
|
+
run help_dialogue.rb some_subcommand -h
|
15
|
+
run help_dialogue.rb another_subcommand -h
|
16
|
+
run help_dialogue.rb some_subcommand yet_another_sub -h
|
17
|
+
run help_dialogue.rb some_subcommand yet_another_sub -b
|
data/spec/command_spec.rb
CHANGED
@@ -35,6 +35,12 @@ describe(Mercenary::Command) do
|
|
35
35
|
expect(add_sub.call(command).parent).to eq(command)
|
36
36
|
end
|
37
37
|
|
38
|
+
it "can set its version" do
|
39
|
+
version = "1.4.2"
|
40
|
+
command.version version
|
41
|
+
expect(command.version).to eq(version)
|
42
|
+
end
|
43
|
+
|
38
44
|
it "can set its syntax" do
|
39
45
|
syntax_string = "my_name [options]"
|
40
46
|
cmd = described_class.new(:my_name)
|
@@ -50,10 +56,20 @@ describe(Mercenary::Command) do
|
|
50
56
|
|
51
57
|
it "can set its options" do
|
52
58
|
name = "show_drafts"
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
expect(command.
|
59
|
+
opts = ['--drafts', 'Render posts in the _drafts folder']
|
60
|
+
option = Mercenary::Option.new(name, opts)
|
61
|
+
command.option name, *opts
|
62
|
+
expect(command.options).to eql([option])
|
63
|
+
expect(command.map).to include({option.hash => name})
|
64
|
+
end
|
65
|
+
|
66
|
+
it "knows its full name" do
|
67
|
+
expect(command_with_parent.full_name).to eql("my_parent i_have_parent")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "knows its identity" do
|
71
|
+
command_with_parent.version '1.8.7'
|
72
|
+
expect(command_with_parent.identity).to eql("my_parent i_have_parent 1.8.7")
|
57
73
|
end
|
58
74
|
|
59
75
|
it "raises an ArgumentError if I specify a default_command that isn't there" do
|
data/spec/option_spec.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe(Mercenary::Option) do
|
4
|
+
let(:config_key) { "largo" }
|
5
|
+
let(:description) { "This is a description" }
|
6
|
+
let(:switches) { ['-l', '--largo'] }
|
7
|
+
let(:option) { described_class.new(config_key, [switches, description].flatten.reject(&:nil?)) }
|
8
|
+
|
9
|
+
it "knows its config key" do
|
10
|
+
expect(option.config_key).to eql(config_key)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "knows its description" do
|
14
|
+
expect(option.description).to eql(description)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "knows its switches" do
|
18
|
+
expect(option.switches).to eql(switches)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "knows how to present itself" do
|
22
|
+
expect(option.to_s).to eql(" -l, --largo #{description}")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "has an OptionParser representation" do
|
26
|
+
expect(option.for_option_parser).to eql([switches, description].flatten)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "compares itself with other options well" do
|
30
|
+
new_option = described_class.new(config_key, ['-l', '--largo', description])
|
31
|
+
expect(option.eql?(new_option)).to be_true
|
32
|
+
expect(option.hash.eql?(new_option.hash)).to be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "has a custom #hash" do
|
36
|
+
expect(option.hash.to_s).to match(/\d+/)
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with just the long switch" do
|
40
|
+
let(:switches) { ['--largo'] }
|
41
|
+
|
42
|
+
it "adds an empty string in place of the short switch" do
|
43
|
+
expect(option.switches).to eql(['', '--largo'])
|
44
|
+
end
|
45
|
+
|
46
|
+
it "sets its description properly" do
|
47
|
+
expect(option.description).to eql(description)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "knows how to present the switch" do
|
51
|
+
expect(option.formatted_switches).to eql(" --largo ")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "with just the short switch" do
|
56
|
+
let(:switches) { ['-l'] }
|
57
|
+
|
58
|
+
it "adds an empty string in place of the long switch" do
|
59
|
+
expect(option.switches).to eql(['-l', ''])
|
60
|
+
end
|
61
|
+
|
62
|
+
it "sets its description properly" do
|
63
|
+
expect(option.description).to eql(description)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "knows how to present the switch" do
|
67
|
+
expect(option.formatted_switches).to eql(" -l ")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "without a description" do
|
72
|
+
let(:description) { nil }
|
73
|
+
|
74
|
+
it "knows there is no description" do
|
75
|
+
expect(option.description).to be_nil
|
76
|
+
end
|
77
|
+
|
78
|
+
it "knows both inputs are switches" do
|
79
|
+
expect(option.switches).to eql(switches)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe(Mercenary::Presenter) do
|
4
|
+
let(:supercommand) { Mercenary::Command.new(:script_name) }
|
5
|
+
let(:command) { Mercenary::Command.new(:subcommand, supercommand) }
|
6
|
+
let(:presenter) { described_class.new(command) }
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
command.version '1.4.2'
|
10
|
+
command.description 'Do all the things.'
|
11
|
+
command.option 'one', '-1', '--one', 'The first option'
|
12
|
+
command.option 'two', '-2', '--two', 'The second option'
|
13
|
+
supercommand.commands[command.name] = command
|
14
|
+
end
|
15
|
+
|
16
|
+
it "knows how to present the command" do
|
17
|
+
expect(presenter.command_presentation).to eql("script_name subcommand 1.4.2 -- Do all the things.\n\nUsage:\n\n script_name subcommand\n\nOptions:\n -1, --one The first option\n -2, --two The second option")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "knows how to present the subcommands" do
|
21
|
+
expect(described_class.new(supercommand).subcommands_presentation).to eql(" subcommand Do all the things.")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "knows how to present the usage" do
|
25
|
+
expect(presenter.usage_presentation).to eql(" script_name subcommand")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "knows how to present the options" do
|
29
|
+
expect(presenter.options_presentation).to eql(" -1, --one The first option\n -2, --two The second option")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "allows you to say print_* instead of *_presentation" do
|
33
|
+
expect(presenter.print_usage).to eql(presenter.usage_presentation)
|
34
|
+
expect(presenter.print_subcommands).to eql(presenter.subcommands_presentation)
|
35
|
+
expect(presenter.print_options).to eql(presenter.options_presentation)
|
36
|
+
expect(presenter.print_command).to eql(presenter.command_presentation)
|
37
|
+
end
|
38
|
+
end
|
data/spec/program_spec.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe(Mercenary::Program) do
|
4
|
+
|
5
|
+
context "a basic program" do
|
6
|
+
let(:program) { Mercenary::Program.new(:my_name) }
|
7
|
+
|
8
|
+
it "can be created with just a name" do
|
9
|
+
expect(program.name).to eql(:my_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "can set its version" do
|
13
|
+
version = Mercenary::VERSION
|
14
|
+
program.version version
|
15
|
+
expect(program.version).to eq(version)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mercenary
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Preston-Werner
|
@@ -9,48 +9,48 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-02-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - ~>
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '1.3'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - ~>
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '1.3'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - '>='
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '0'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - '>='
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: rspec
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ~>
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: '2.14'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - ~>
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '2.14'
|
56
56
|
description: Lightweight and flexible library for writing command-line apps in Ruby.
|
@@ -61,22 +61,31 @@ executables: []
|
|
61
61
|
extensions: []
|
62
62
|
extra_rdoc_files: []
|
63
63
|
files:
|
64
|
-
-
|
65
|
-
-
|
66
|
-
-
|
64
|
+
- .gitignore
|
65
|
+
- .rspec
|
66
|
+
- .travis.yml
|
67
67
|
- Gemfile
|
68
68
|
- History.markdown
|
69
69
|
- LICENSE.txt
|
70
70
|
- README.md
|
71
71
|
- Rakefile
|
72
|
+
- examples/help_dialogue.rb
|
73
|
+
- examples/logging.rb
|
74
|
+
- examples/trace.rb
|
72
75
|
- lib/mercenary.rb
|
73
76
|
- lib/mercenary/command.rb
|
77
|
+
- lib/mercenary/option.rb
|
78
|
+
- lib/mercenary/presenter.rb
|
74
79
|
- lib/mercenary/program.rb
|
75
80
|
- lib/mercenary/version.rb
|
76
81
|
- mercenary.gemspec
|
77
82
|
- script/bootstrap
|
78
83
|
- script/cibuild
|
84
|
+
- script/console
|
85
|
+
- script/examples
|
79
86
|
- spec/command_spec.rb
|
87
|
+
- spec/option_spec.rb
|
88
|
+
- spec/presenter_spec.rb
|
80
89
|
- spec/program_spec.rb
|
81
90
|
- spec/spec_helper.rb
|
82
91
|
homepage: https://github.com/jekyll/mercenary
|
@@ -89,21 +98,23 @@ require_paths:
|
|
89
98
|
- lib
|
90
99
|
required_ruby_version: !ruby/object:Gem::Requirement
|
91
100
|
requirements:
|
92
|
-
- -
|
101
|
+
- - '>='
|
93
102
|
- !ruby/object:Gem::Version
|
94
103
|
version: '0'
|
95
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
105
|
requirements:
|
97
|
-
- -
|
106
|
+
- - '>='
|
98
107
|
- !ruby/object:Gem::Version
|
99
108
|
version: '0'
|
100
109
|
requirements: []
|
101
110
|
rubyforge_project:
|
102
|
-
rubygems_version: 2.
|
111
|
+
rubygems_version: 2.0.14
|
103
112
|
signing_key:
|
104
113
|
specification_version: 4
|
105
114
|
summary: Lightweight and flexible library for writing command-line apps in Ruby.
|
106
115
|
test_files:
|
107
116
|
- spec/command_spec.rb
|
117
|
+
- spec/option_spec.rb
|
118
|
+
- spec/presenter_spec.rb
|
108
119
|
- spec/program_spec.rb
|
109
120
|
- spec/spec_helper.rb
|