methadone 0.2.0 → 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.
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,