methadone 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -6,9 +6,12 @@ License:: Distributes under the Apache License, see LICENSE.txt in the source di
6
6
 
7
7
  A smattering of tools to make your command-line apps easily awesome; kick the bash habit without sacrificing any of the power.
8
8
 
9
- Currently, this is under development and has the following to offer:
9
+ The overall goal of this project is to allow you to write a command-line app in Ruby that is as close as possible to the expedience and concisenes of bash, but with all the power of Ruby available when you need it.
10
+
11
+ Currently, this library is under development and has the following to offer:
10
12
 
11
13
  * Bootstrapping a new CLI app
14
+ * Lightweight DSL to structure your bin file
12
15
  * Utility Classes
13
16
  * Methadone::CLILogger - a logger subclass that sends messages to standard error standard out as appropriate
14
17
  * Methadone::CLILogging - a module that, when included in any class, provides easy access to a shared logger
@@ -48,6 +51,62 @@ The +methadone+ command-line app will bootstrap a new command-line app, setting
48
51
 
49
52
  Basically, this sets you up with all the boilerplate that you *should* be using to write a command-line app.
50
53
 
54
+ == DSL for your `bin` file
55
+
56
+ A canonical `OptionParser` driven app has a few problems with it structurally that methadone can solve
57
+
58
+ * Backwards organization - main logic is at the bottom of the file, not the top
59
+ * Verbose to use +opts.on+ just to set a value in a +Hash+
60
+ * No exception handling
61
+
62
+ Methadone gives you a simple,lightweight DSL to help. It's important to note that we're taking a light touch here; this is all a thin wrapper around +OptionParser+ and you still have complete access to it if you'd like. We're basically wrapping up some canonical boilerplate into more expedient code
63
+
64
+ #!/usr/bin/env ruby
65
+
66
+ require 'optparse'
67
+ require 'methadone'
68
+
69
+ include Methadone::Main
70
+
71
+ main do |name,password|
72
+ name # => guaranteed to be non-nil
73
+ password # => nil if user omitted on command line
74
+ options[:switch] # => true if user used --switch or -s
75
+ options[:s] # => ALSO true if user used --switch or -s
76
+ options[:f] # => value of FILE if used on command-line
77
+ options[:flag] # => ALSO value of FILE if used on command-line
78
+
79
+ # If something goes wrong, you can just raise an exception
80
+ # or call exit_now! if you want to control the exit status
81
+ end
82
+
83
+ description "One line summary of your awesome app"
84
+
85
+ on("--[no-]switch","-s","Some switch")
86
+ on("-f FILE","--flag","Some flag")
87
+ on("-x FOO") do |foo|
88
+ # something more complex; this is exactly OptionParser opts.on
89
+ end
90
+
91
+ arg :name
92
+ arg :password, :optional
93
+
94
+ go!
95
+
96
+ +go!+ runs the block you gave to +main+, passing it the unparsed +ARGV+ as parameters to the block. It will
97
+ also parse the command-line via +OptionParser+ and do a check on the remaining arguments to see
98
+ if there's enough to satisfy the <tt>:required</tt> args you've specified.
99
+
100
+ Finally, the banner/help string will be full constructed based on the interface you've declared. If you
101
+ don't accept options, <tt>[options]</tt> won't appear in the help. The names of your arguments
102
+ will appear in proper order and <tt>:optional</tt> ones will be in square brackets. You don't have to
103
+ touch a thing.
104
+
105
+ Why not just <tt>def main</tt> and call that method? +go!+ also handles exceptions:
106
+
107
+ * Any uncaught exception that isn't a subclass of <tt>Methadone::Error</tt> will cause the app to exit with a 70 (standard Linux "internal error" exit status) and print the exception's messages to standard error, no backtrace
108
+ * An instance of <tt>Methadone::Error</tt>, if caught, will have similar behavior, but the +exit_code+ method will be called on the exception to determine the exit status. You can trigger this exception via <tt>exit_now! exit_stats,message</tt>
109
+
51
110
  == Utility Classes
52
111
 
53
112
  Currently, there are classes the assist in directing output logger-style to the right place; basically ensuring that errors go to +STDERR+ and everything else goes to +STDOUT+. All of this is, of course, configurable
data/bin/methadone CHANGED
@@ -11,13 +11,13 @@ include FileUtils
11
11
  include Methadone::Main
12
12
  include Methadone::CLI
13
13
 
14
- main do |basedir,force|
15
- check_and_prepare_basedir!(basedir,force)
14
+ main do |app_name|
15
+ check_and_prepare_basedir!(app_name,options[:force])
16
16
 
17
- gemname = File.basename(basedir)
17
+ gemname = File.basename(app_name)
18
18
  debug "Creating project for gem #{gemname}"
19
19
 
20
- chdir File.dirname(basedir)
20
+ chdir File.dirname(app_name)
21
21
 
22
22
  %x[bundle gem #{gemname}]
23
23
 
@@ -41,26 +41,11 @@ main do |basedir,force|
41
41
  ], :before => /^end\s*$/
42
42
  end
43
43
 
44
- options = {}
45
- option_parser = OptionParser.new do |opts|
46
- executable = File.basename(__FILE__)
47
- opts.banner = "Usage: #{executable} [options] app_name\n\n" +
48
- "Kick the bash habit by bootstrapping your Ruby command-line apps\n\nOptions:"
44
+ description "Kick the bash habit by bootstrapping your Ruby command-line apps"
49
45
 
50
- opts.on("--force","Overwrite files if they exist") do
51
- options[:force] = true
52
- end
53
- end
54
-
55
- option_parser.parse!
46
+ on("--force","Overwrite files if they exist")
56
47
 
57
- EXE = File.expand_path(__FILE__)
58
-
59
- if ARGV.empty?
60
- STDERR.puts("error: app_dir required")
61
- exit 2
62
- end
48
+ arg :app_name, :required
63
49
 
64
- ARGV << options[:force]
65
50
  go!
66
51
 
@@ -79,10 +79,7 @@ Feature: Bootstrap a new command-line app
79
79
  Scenario: We must supply a dirname
80
80
  When I run `methadone`
81
81
  Then the exit status should not be 0
82
- And the stderr should contain:
83
- """
84
- error: app_dir required
85
- """
82
+ And the stderr should match /'app_name' is required/
86
83
 
87
84
  @debug
88
85
  Scenario: Help is properly documented
@@ -9,3 +9,7 @@ end
9
9
  Given /^my app's name is "([^"]*)"$/ do |app_name|
10
10
  @app_name = app_name
11
11
  end
12
+
13
+ Then /^the stderr should match \/([^\/]*)\/$/ do |expected|
14
+ assert_matching_output(expected, all_stderr)
15
+ end
data/lib/methadone.rb CHANGED
@@ -3,3 +3,4 @@ require 'methadone/cli_logger'
3
3
  require 'methadone/cli_logging'
4
4
  require 'methadone/main'
5
5
  require 'methadone/error'
6
+ # Note: DO NOT require cli.rb OR cucumber.rb here
data/lib/methadone/cli.rb CHANGED
@@ -83,7 +83,7 @@ module Methadone
83
83
 
84
84
  # Get the location of the templates for profile "from"
85
85
  def template_dir(from)
86
- File.join(File.dirname(EXE),'..','templates',from.to_s)
86
+ File.join(File.dirname(__FILE__),'..','..','templates',from.to_s)
87
87
  end
88
88
 
89
89
  def template_dirs_in(profile)
@@ -1,8 +1,44 @@
1
+ require 'optparse'
2
+
1
3
  module Methadone
2
4
  # Include this module to gain access to the "canonical command-line app structure"
3
5
  # DSL. This is a *very* lightweight layer on top of what you might
4
6
  # normally write that gives you just a bit of help to keep your code structured
5
- # in a sensible way.
7
+ # in a sensible way. You can use as much or as little as you want, though
8
+ # you must at least use #main to get any benefits.
9
+ #
10
+ # You also get a more expedient interface to OptionParser as well
11
+ # as checking for required arguments to your app. For example, if
12
+ # we want our app to accept a negatable switch named "switch", a flag
13
+ # named "flag", and two arguments "needed" (which is required)
14
+ # and "maybe" which optional, we can do the following:
15
+ #
16
+ # #!/usr/bin/env ruby -w
17
+ #
18
+ # require 'methadone'
19
+ #
20
+ # include Methadone::Main
21
+ #
22
+ # main do |needed, maybe|
23
+ # options[:switch] => true or false, based on command line
24
+ # options[:flag] => value of flag passed on command line
25
+ # end
26
+ #
27
+ # # Proxy to an OptionParser instance's on method
28
+ # on("--[no]-switch")
29
+ # on("--flag VALUE")
30
+ #
31
+ # arg :needed
32
+ # arg :maybe, :optional
33
+ #
34
+ # go!
35
+ #
36
+ # Our app then acts as follows:
37
+ #
38
+ # $ our_app
39
+ # # => parse error: 'needed' is required
40
+ # $ our_app foo
41
+ # # => succeeds; "maybe" in main is nil
6
42
  #
7
43
  # This also includes Methadone::CLILogging to give you access to simple logging
8
44
  module Main
@@ -40,21 +76,36 @@ module Methadone
40
76
  # To run this method, call #go!
41
77
  def main(&block)
42
78
  @main_block = block
79
+ @options = {}
80
+ @option_parser = OptionParserProxy.new(OptionParser.new,@options)
43
81
  end
44
82
 
83
+
45
84
  # Start your command-line app, exiting appropriately when
46
- # complete
85
+ # complete.
47
86
  #
48
87
  # This *will* exit your program when it completes. If your
49
88
  # #main block evaluates to an integer, that value will be sent
50
89
  # to Kernel#exit, otherwise, this will exit with 0
90
+ #
91
+ # If the command-line options couldn't be parsed, this
92
+ # will exit with 64 and whatever message OptionParser provided.
93
+ #
94
+ # If a required argument (see #arg) is not found, this exits with
95
+ # 64 and a message about that missing argument.
96
+ #
51
97
  def go!
98
+ opts.parse!
99
+ opts.check_args!
52
100
  result = call_main
53
101
  if result.kind_of? Fixnum
54
102
  exit result
55
103
  else
56
104
  exit 0
57
105
  end
106
+ rescue OptionParser::ParseError => ex
107
+ error ex.message
108
+ exit 64 # Linux standard for bad command line
58
109
  end
59
110
 
60
111
  # Call this to exit the program immediately
@@ -66,6 +117,65 @@ module Methadone
66
117
  raise Methadone::Error.new(exit_code,message)
67
118
  end
68
119
 
120
+ # Returns an OptionParser that you can use
121
+ # to declare your command-line interface. The object returned as
122
+ # an additional feature that implements typical use of OptionParser.
123
+ #
124
+ # opts.on("--flag VALUE")
125
+ #
126
+ # Does this under the covers:
127
+ #
128
+ # opts.on("--flag VALUE") do |value|
129
+ # options[:flag] = value
130
+ # end
131
+ #
132
+ # Since, most of the time, this is all you want to do,
133
+ # this makes it more expedient to do so. The key that is
134
+ # is set in #options will be a symbol of the option name, without
135
+ # the dashes. Note that if you use multiple option names, a key
136
+ # will be generated for each. Further, if you use the negatable form,
137
+ # only the positive key will be set, e.g. for <tt>--[no-]verbose</tt>,
138
+ # only <tt>:verbose</tt> will be set (to true or false).
139
+ def opts
140
+ @option_parser
141
+ end
142
+
143
+ # Calls <tt>opts.on</tt> with the given arguments
144
+ def on(*args,&block)
145
+ opts.on(*args,&block)
146
+ end
147
+
148
+ # Sets the name of an arguments your app accepts. Note
149
+ # that no sanity checking is done on the configuration
150
+ # of your arguments you create via multiple calls to this method.
151
+ # Namely, the last argument should be the only one that is
152
+ # a :many or a :any, but the system here won't sanity check that.
153
+ #
154
+ # +arg_name+:: name of the argument to appear in documentation
155
+ # This will be converted into a String and used to create
156
+ # the banner (unless you have overridden the banner)
157
+ # +options+:: list (not Hash) of options:
158
+ # <tt>:required</tt> - this arg is required (this is the default)
159
+ # <tt>:optional</tt> - this arg is optional
160
+ # <tt>:one</tt> - only one of this arg should be supplied (default)
161
+ # <tt>:many</tt> - many of this arg may be supplied, but at least one is required
162
+ # <tt>:any</tt> - any number, include zero, may be supplied
163
+ def arg(arg_name,*options)
164
+ opts.arg(arg_name,*options)
165
+ end
166
+
167
+ # Set the description of your app for inclusion in the help output.
168
+ # +desc+:: a short, one-line description of your app
169
+ def description(desc)
170
+ opts.description(desc)
171
+ end
172
+
173
+ # Returns a Hash that you can use to store or retrieve options
174
+ # parsed from the command line
175
+ def options
176
+ @options
177
+ end
178
+
69
179
  private
70
180
 
71
181
  # Handle calling main and trapping any exceptions thrown
@@ -83,4 +193,124 @@ module Methadone
83
193
  exception.message.nil? || exception.message.strip.empty?
84
194
  end
85
195
  end
196
+
197
+ # A proxy to OptionParser that intercepts #on
198
+ # so that we can allow a simpler interface
199
+ class OptionParserProxy < BasicObject
200
+ # Create the proxy
201
+ #
202
+ # +option_parser+:: An OptionParser instance
203
+ # +options+:: a hash that will store the options
204
+ # set via automatic setting. The caller should
205
+ # retain a reference to this
206
+ def initialize(option_parser,options)
207
+ @option_parser = option_parser
208
+ @options = options
209
+ @user_specified_banner = false
210
+ @accept_options = false
211
+ @args = []
212
+ @arg_options = {}
213
+ @description = nil
214
+ set_banner
215
+ end
216
+
217
+ def check_args!
218
+ ::Hash[@args.zip(::ARGV)].each do |arg_name,arg_value|
219
+ if @arg_options[arg_name].include? :required
220
+ if arg_value.nil?
221
+ message = "'#{arg_name.to_s}' is required"
222
+ message = "at least one " + message if @arg_options[arg_name].include? :many
223
+ raise ::OptionParser::ParseError,message
224
+ end
225
+ end
226
+ end
227
+ end
228
+
229
+ # If invoked as with OptionParser, behaves the exact same way.
230
+ # If invoked without a block, however, the options hash given
231
+ # to the constructor will be used to store
232
+ # the parsed command-line value. See #opts in the Main module
233
+ # for how that works.
234
+ def on(*args,&block)
235
+ @accept_options = true
236
+ if block
237
+ @option_parser.on(*args,&block)
238
+ else
239
+ opt_names = option_names(*args)
240
+ @option_parser.on(*args) do |value|
241
+ opt_names.each { |name| @options[name] = value }
242
+ end
243
+ end
244
+ set_banner
245
+ end
246
+
247
+ # Proxies to underlying OptionParser
248
+ def banner=(new_banner)
249
+ @option_parser.banner=new_banner
250
+ @user_specified_banner = true
251
+ end
252
+
253
+ # Sets the banner to include these arg names
254
+ def arg(arg_name,*options)
255
+ options << :optional if options.include?(:any) && !options.include?(:optional)
256
+ options << :required unless options.include? :optional
257
+ options << :one unless options.include?(:any) || options.include?(:many)
258
+ @args << arg_name
259
+ @arg_options[arg_name] = options
260
+ set_banner
261
+ end
262
+
263
+ def description(desc)
264
+ @description = desc
265
+ set_banner
266
+ end
267
+
268
+ # Defers all calls save #on to
269
+ # the underlying OptionParser instance
270
+ def method_missing(sym,*args,&block)
271
+ @option_parser.send(sym,*args,&block)
272
+ end
273
+
274
+ private
275
+
276
+ def set_banner
277
+ unless @user_specified_banner
278
+ new_banner="Usage: #{::File.basename($0)}"
279
+ new_banner += " [options]" if @accept_options
280
+ unless @args.empty?
281
+ new_banner += " "
282
+ new_banner += @args.map { |arg|
283
+ if @arg_options[arg].include? :any
284
+ "[#{arg.to_s}...]"
285
+ elsif @arg_options[arg].include? :optional
286
+ "[#{arg.to_s}]"
287
+ elsif @arg_options[arg].include? :many
288
+ "#{arg.to_s}..."
289
+ else
290
+ arg.to_s
291
+ end
292
+ }.join(' ')
293
+ end
294
+ new_banner += "\n\n#{@description}" if @description
295
+ new_banner += "\n\nOptions:" if @accept_options
296
+
297
+ @option_parser.banner=new_banner
298
+ end
299
+ end
300
+
301
+ def option_names(*opts_on_args,&block)
302
+ opts_on_args.map { |arg|
303
+ if arg =~ /^--\[no-\]([^-\s]*)/
304
+ $1.to_sym
305
+ elsif arg =~ /^--([^-\s]*)/
306
+ $1.to_sym
307
+ elsif arg =~ /^-([^-\s]*)/
308
+ $1.to_sym
309
+ else
310
+ nil
311
+ end
312
+ }.reject(&:nil?)
313
+ end
314
+
315
+ end
86
316
  end
@@ -1,3 +1,3 @@
1
1
  module Methadone
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -5,31 +5,31 @@ require 'methadone'
5
5
 
6
6
  include Methadone::Main
7
7
 
8
- main do |options|
8
+ main do # Add args you want: |like,so|
9
9
  # your program code here
10
+ # You can access CLI options via
11
+ # the options Hash
10
12
  end
11
13
 
12
14
  # supplemental methods here
13
15
 
14
- options = {}
15
- option_parser = OptionParser.new do |opts|
16
- executable_name = File.basename(__FILE__)
17
- opts.banner = "Usage: #{executable_name}"
18
- # When/if you add options:
19
- # opts.banner = "Usage: #{executable_name} [options]\n\n" +
20
- # "One-line description of your app\n\n" +
21
- # "Options:"
16
+ # Declare command-line interface here
17
+
18
+ # description "one line description of your app"
19
+ #
20
+ # Accept flags via:
21
+ # on("--flag VAL","Some flag")
22
+ # options[flag] will contain VAL
23
+ #
24
+ # Specify switches via:
25
+ # on("--[no-]switch","Some switch")
26
+ #
27
+ # Or, just call OptionParser methods on opts
28
+ #
29
+ # Require an argument
30
+ # arg :some_arg
31
+ #
32
+ # # Make an argument optional
33
+ # arg :optional_arg, :optional
22
34
 
23
- # opts.on("--flag VAL","Some flag") do |val|
24
- # options[:flag] = val
25
- # end
26
- #
27
- # opts.on("--[no-]switch","Some switch") do |switch|
28
- # options[:switch] = switch
29
- # end
30
- end
31
-
32
- option_parser.parse!
33
-
34
- ARGV << options
35
35
  go!
data/test/test_main.rb CHANGED
@@ -110,6 +110,153 @@ class TestMain < BaseTest
110
110
  assert_logged_at_error "oh noes"
111
111
  end
112
112
 
113
+ test "opts allows us to more expediently set up OptionParser" do
114
+ switch = nil
115
+ flag = nil
116
+ main do
117
+ switch = options[:switch]
118
+ flag = options[:flag]
119
+ end
120
+
121
+ opts.on("--switch") { options[:switch] = true }
122
+ opts.on("--flag FLAG") { |value| options[:flag] = value }
123
+
124
+ set_argv %w(--switch --flag value)
125
+
126
+ safe_go!
127
+
128
+ assert switch
129
+ assert_equal 'value',flag
130
+ end
131
+
132
+ test "when the command line is invalid, we exit with 64" do
133
+ main do
134
+ end
135
+
136
+ opts.on("--switch") { options[:switch] = true }
137
+ opts.on("--flag FLAG") { |value| options[:flag] = value }
138
+
139
+ set_argv %w(--invalid --flag value)
140
+
141
+ assert_exits(64) { go! }
142
+ end
143
+
144
+ test "omitting the block to opts simply sets the value in the options hash and returns itself" do
145
+ switch = nil
146
+ negatable = nil
147
+ flag = nil
148
+ f = nil
149
+ other = nil
150
+ some_other = nil
151
+ main do
152
+ switch = options[:switch]
153
+ flag = options[:flag]
154
+ f = options[:f]
155
+ negatable = options[:negatable]
156
+ other = options[:other]
157
+ some_other = options[:some_other]
158
+ end
159
+
160
+ on("--switch")
161
+ on("--[no-]negatable")
162
+ on("--flag FLAG","-f","Some documentation string")
163
+ on("--other") do
164
+ options[:some_other] = true
165
+ end
166
+
167
+ set_argv %w(--switch --flag value --negatable --other)
168
+
169
+ safe_go!
170
+
171
+ assert switch
172
+ assert some_other
173
+ refute other
174
+ assert_equal 'value',flag
175
+ assert_equal 'value',f,opts.to_s
176
+ assert_match /Some documentation string/,opts.to_s
177
+ end
178
+
179
+ test "without specifying options, [options] doesn't show up in our banner" do
180
+ main {}
181
+
182
+ refute_match /\[options\]/,opts.banner
183
+ end
184
+
185
+ test "when specifying an option, [options] shows up in the banner" do
186
+ main {}
187
+ on("-s")
188
+
189
+ assert_match /\[options\]/,opts.banner
190
+ end
191
+
192
+ test "I can specify which arguments my app takes and if they are required" do
193
+ main {}
194
+
195
+ arg :db_name
196
+ arg :user, :required
197
+ arg :password, :optional
198
+
199
+ assert_match /db_name user \[password\]$/,opts.banner
200
+ end
201
+
202
+ test "I can specify which arguments my app takes and if they are singular or plural" do
203
+ main {}
204
+
205
+ arg :db_name
206
+ arg :user, :required, :one
207
+ arg :tables, :many
208
+
209
+ assert_match /db_name user tables...$/,opts.banner
210
+ end
211
+
212
+ test "I can specify which arguments my app takes and if they are singular or optional plural" do
213
+ main {}
214
+
215
+ arg :db_name
216
+ arg :user, :required, :one
217
+ arg :tables, :any
218
+
219
+ assert_match /db_name user \[tables...\]$/,opts.banner
220
+ end
221
+
222
+ test "I can set a description for my app" do
223
+ main {}
224
+ description "An app of total awesome"
225
+
226
+ assert_match /^An app of total awesome$/,opts.banner
227
+ end
228
+
229
+ test "when I override the banner, we don't automatically do anything" do
230
+ main {}
231
+ opts.banner = "FOOBAR"
232
+
233
+ on("-s")
234
+
235
+ assert_equal "FOOBAR",opts.banner
236
+ end
237
+
238
+ test "when I say an argument is required and its omitted, I get an error" do
239
+ main {}
240
+ arg :foo
241
+ arg :bar
242
+
243
+ set_argv %w(blah)
244
+
245
+ assert_exits(64) { go! }
246
+ assert_logged_at_error("parse error: 'bar' is required")
247
+ end
248
+
249
+ test "when I say an argument is many and its omitted, I get an error" do
250
+ main {}
251
+ arg :foo
252
+ arg :bar, :many
253
+
254
+ set_argv %w(blah)
255
+
256
+ assert_exits(64) { go! }
257
+ assert_logged_at_error("parse error: at least one 'bar' is required")
258
+ end
259
+
113
260
  private
114
261
 
115
262
  # Calls go!, but traps the exit
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: methadone
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-01 00:00:00.000000000Z
12
+ date: 2011-10-02 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec-expectations
16
- requirement: &70156777550060 !ruby/object:Gem::Requirement
16
+ requirement: &70140138849040 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '2.6'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70156777550060
24
+ version_requirements: *70140138849040
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70156777549580 !ruby/object:Gem::Requirement
27
+ requirement: &70140138848500 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70156777549580
35
+ version_requirements: *70140138848500
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rdoc
38
- requirement: &70156777548800 !ruby/object:Gem::Requirement
38
+ requirement: &70140138847780 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '3.9'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70156777548800
46
+ version_requirements: *70140138847780
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: aruba
49
- requirement: &70156777548320 !ruby/object:Gem::Requirement
49
+ requirement: &70140138847360 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70156777548320
57
+ version_requirements: *70140138847360
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: simplecov
60
- requirement: &70156777547600 !ruby/object:Gem::Requirement
60
+ requirement: &70140138846780 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '0.5'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70156777547600
68
+ version_requirements: *70140138846780
69
69
  description: Methadone provides a lot of small but useful features for developing
70
70
  a command-line app, including an opinionated bootstrapping process, some helpful
71
71
  cucumber steps, and some classes to bridge logging and output into a simple, unified,