thor 0.20.3 → 1.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b35ad01aa15321a80d1e8c579b3c979c3ff6985a98cca96ffc3e21beba4dd7b5
4
- data.tar.gz: f6aa82269a71a418fa99f82ed56672a32da5ce7ebaa4f6f9e2bdb8a16459ec95
3
+ metadata.gz: 30e79d2b0a96e87c8e6348467db577c6ad1e9acbbbac5d375417bc3e5a2b7698
4
+ data.tar.gz: 701f1ab842da90e599b96bd00d90481d716eb29e39e0283b5ff527c2033fc742
5
5
  SHA512:
6
- metadata.gz: 3a405c6ac1be920be0d5f1dfe2f4d01ddae7fd68f229d6968c619e243abedfe6adb6b5b58d935b5a8528b4561c6f16fbd9c9ca58befd993ea0bb43950333983e
7
- data.tar.gz: 659d3e822725f0bc9161feb69fc2449dfd4b355279a0c59406abf909f23435f157e97b353deec047cd3dbce38a5214d3cd6010844ffd86f50e797a0ab21eac55
6
+ metadata.gz: 73b1ac80575d4422204cd8072950b5594739db3b6f3fde0f2f04359d51b1d4428524d25b9a3003ae9ec3f6be615cf635f3057bbc65558e6a17ba490ff045988b
7
+ data.tar.gz: eb7761a5e6f3674cb3231398145978b0eb53a6fa2c10e4cb9e99d8d523988efcefc8cae5dd163b8a3d6ead7424422d58b92311c200790ad6e2416e3f9757a90e
data/README.md CHANGED
@@ -2,14 +2,8 @@ Thor
2
2
  ====
3
3
 
4
4
  [![Gem Version](http://img.shields.io/gem/v/thor.svg)][gem]
5
- [![Build Status](http://img.shields.io/travis/erikhuda/thor.svg)][travis]
6
- [![Code Climate](http://img.shields.io/codeclimate/github/erikhuda/thor.svg)][codeclimate]
7
- [![Coverage Status](http://img.shields.io/coveralls/erikhuda/thor.svg)][coveralls]
8
5
 
9
6
  [gem]: https://rubygems.org/gems/thor
10
- [travis]: http://travis-ci.org/erikhuda/thor
11
- [codeclimate]: https://codeclimate.com/github/erikhuda/thor
12
- [coveralls]: https://coveralls.io/r/erikhuda/thor
13
7
 
14
8
  Description
15
9
  -----------
@@ -35,7 +29,7 @@ Usage and documentation
35
29
  -----------------------
36
30
  Please see the [wiki][] for basic usage and other documentation on using Thor. You can also checkout the [official homepage][homepage].
37
31
 
38
- [wiki]: https://github.com/erikhuda/thor/wiki
32
+ [wiki]: https://github.com/rails/thor/wiki
39
33
  [homepage]: http://whatisthor.com/
40
34
 
41
35
  Contributing
@@ -1,4 +1,4 @@
1
- require "thor/actions/empty_directory"
1
+ require_relative "empty_directory"
2
2
 
3
3
  class Thor
4
4
  module Actions
@@ -1,4 +1,4 @@
1
- require "thor/actions/create_file"
1
+ require_relative "create_file"
2
2
 
3
3
  class Thor
4
4
  module Actions
@@ -33,7 +33,8 @@ class Thor
33
33
  # Boolean:: true if it is identical, false otherwise.
34
34
  #
35
35
  def identical?
36
- exists? && File.identical?(render, destination)
36
+ source = File.expand_path(render, File.dirname(destination))
37
+ exists? && File.identical?(source, destination)
37
38
  end
38
39
 
39
40
  def invoke!
@@ -1,4 +1,4 @@
1
- require "thor/actions/empty_directory"
1
+ require_relative "empty_directory"
2
2
 
3
3
  class Thor
4
4
  module Actions
@@ -56,7 +56,7 @@ class Thor
56
56
  attr_reader :source
57
57
 
58
58
  def initialize(base, source, destination = nil, config = {}, &block)
59
- @source = File.expand_path(base.find_in_source_paths(source.to_s))
59
+ @source = File.expand_path(Dir[Util.escape_globs(base.find_in_source_paths(source.to_s))].first)
60
60
  @block = block
61
61
  super(base, destination, {:recursive => true}.merge(config))
62
62
  end
@@ -96,22 +96,12 @@ class Thor
96
96
  end
97
97
  end
98
98
 
99
- if RUBY_VERSION < "2.0"
100
- def file_level_lookup(previous_lookup)
101
- File.join(previous_lookup, "{*,.[a-z]*}")
102
- end
103
-
104
- def files(lookup)
105
- Dir[lookup]
106
- end
107
- else
108
- def file_level_lookup(previous_lookup)
109
- File.join(previous_lookup, "*")
110
- end
99
+ def file_level_lookup(previous_lookup)
100
+ File.join(previous_lookup, "*")
101
+ end
111
102
 
112
- def files(lookup)
113
- Dir.glob(lookup, File::FNM_DOTMATCH)
114
- end
103
+ def files(lookup)
104
+ Dir.glob(lookup, File::FNM_DOTMATCH)
115
105
  end
116
106
  end
117
107
  end
@@ -23,14 +23,14 @@ class Thor
23
23
  destination = args.first || source
24
24
  source = File.expand_path(find_in_source_paths(source.to_s))
25
25
 
26
- create_file destination, nil, config do
26
+ resulting_destination = create_file destination, nil, config do
27
27
  content = File.binread(source)
28
28
  content = yield(content) if block
29
29
  content
30
30
  end
31
31
  if config[:mode] == :preserve
32
32
  mode = File.stat(source).mode
33
- chmod(destination, mode, config)
33
+ chmod(resulting_destination, mode, config)
34
34
  end
35
35
  end
36
36
 
@@ -80,14 +80,14 @@ class Thor
80
80
  config = args.last.is_a?(Hash) ? args.pop : {}
81
81
  destination = args.first
82
82
 
83
- if source =~ %r{^https?\://}
83
+ render = if source =~ %r{^https?\://}
84
84
  require "open-uri"
85
+ URI.send(:open, source) { |input| input.binmode.read }
85
86
  else
86
87
  source = File.expand_path(find_in_source_paths(source.to_s))
88
+ open(source) { |input| input.binmode.read }
87
89
  end
88
90
 
89
- render = open(source) { |input| input.binmode.read }
90
-
91
91
  destination ||= if block_given?
92
92
  block.arity == 1 ? yield(render) : yield
93
93
  else
@@ -210,9 +210,9 @@ class Thor
210
210
  #
211
211
  # ==== Examples
212
212
  #
213
- # inject_into_class "app/controllers/application_controller.rb", ApplicationController, " filter_parameter :password\n"
213
+ # inject_into_class "app/controllers/application_controller.rb", "ApplicationController", " filter_parameter :password\n"
214
214
  #
215
- # inject_into_class "app/controllers/application_controller.rb", ApplicationController do
215
+ # inject_into_class "app/controllers/application_controller.rb", "ApplicationController" do
216
216
  # " filter_parameter :password\n"
217
217
  # end
218
218
  #
@@ -233,9 +233,9 @@ class Thor
233
233
  #
234
234
  # ==== Examples
235
235
  #
236
- # inject_into_module "app/helpers/application_helper.rb", ApplicationHelper, " def help; 'help'; end\n"
236
+ # inject_into_module "app/helpers/application_helper.rb", "ApplicationHelper", " def help; 'help'; end\n"
237
237
  #
238
- # inject_into_module "app/helpers/application_helper.rb", ApplicationHelper do
238
+ # inject_into_module "app/helpers/application_helper.rb", "ApplicationHelper" do
239
239
  # " def help; 'help'; end\n"
240
240
  # end
241
241
  #
@@ -251,7 +251,8 @@ class Thor
251
251
  # path<String>:: path of the file to be changed
252
252
  # flag<Regexp|String>:: the regexp or string to be replaced
253
253
  # replacement<String>:: the replacement, can be also given as a block
254
- # config<Hash>:: give :verbose => false to not log the status.
254
+ # config<Hash>:: give :verbose => false to not log the status, and
255
+ # :force => true, to force the replacement regardles of runner behavior.
255
256
  #
256
257
  # ==== Example
257
258
  #
@@ -262,9 +263,10 @@ class Thor
262
263
  # end
263
264
  #
264
265
  def gsub_file(path, flag, *args, &block)
265
- return unless behavior == :invoke
266
266
  config = args.last.is_a?(Hash) ? args.pop : {}
267
267
 
268
+ return unless behavior == :invoke || config.fetch(:force, false)
269
+
268
270
  path = File.expand_path(path, destination_root)
269
271
  say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true)
270
272
 
@@ -329,7 +331,7 @@ class Thor
329
331
  path = File.expand_path(path, destination_root)
330
332
 
331
333
  say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true)
332
- if !options[:pretend] && File.exist?(path)
334
+ if !options[:pretend] && (File.exist?(path) || File.symlink?(path))
333
335
  require "fileutils"
334
336
  ::FileUtils.rm_rf(path)
335
337
  end
@@ -1,4 +1,4 @@
1
- require "thor/actions/empty_directory"
1
+ require_relative "empty_directory"
2
2
 
3
3
  class Thor
4
4
  module Actions
@@ -21,9 +21,14 @@ class Thor
21
21
  # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
22
22
  # end
23
23
  #
24
+ WARNINGS = { unchanged_no_flag: 'File unchanged! The supplied flag value not found!' }
25
+
24
26
  def insert_into_file(destination, *args, &block)
25
27
  data = block_given? ? block : args.shift
26
- config = args.shift
28
+
29
+ config = args.shift || {}
30
+ config[:after] = /\z/ unless config.key?(:before) || config.key?(:after)
31
+
27
32
  action InjectIntoFile.new(self, destination, data, config)
28
33
  end
29
34
  alias_method :inject_into_file, :insert_into_file
@@ -45,8 +50,6 @@ class Thor
45
50
  end
46
51
 
47
52
  def invoke!
48
- say_status :invoke
49
-
50
53
  content = if @behavior == :after
51
54
  '\0' + replacement
52
55
  else
@@ -54,7 +57,11 @@ class Thor
54
57
  end
55
58
 
56
59
  if exists?
57
- replace!(/#{flag}/, content, config[:force])
60
+ if replace!(/#{flag}/, content, config[:force])
61
+ say_status(:invoke)
62
+ else
63
+ say_status(:unchanged, warning: WARNINGS[:unchanged_no_flag], color: :red)
64
+ end
58
65
  else
59
66
  unless pretend?
60
67
  raise Thor::Error, "The file #{ destination } does not appear to exist"
@@ -78,7 +85,7 @@ class Thor
78
85
 
79
86
  protected
80
87
 
81
- def say_status(behavior)
88
+ def say_status(behavior, warning: nil, color: nil)
82
89
  status = if behavior == :invoke
83
90
  if flag == /\A/
84
91
  :prepend
@@ -87,21 +94,24 @@ class Thor
87
94
  else
88
95
  :insert
89
96
  end
97
+ elsif warning
98
+ warning
90
99
  else
91
100
  :subtract
92
101
  end
93
102
 
94
- super(status, config[:verbose])
103
+ super(status, (color || config[:verbose]))
95
104
  end
96
105
 
97
106
  # Adds the content to the file.
98
107
  #
99
108
  def replace!(regexp, string, force)
100
- return if pretend?
101
109
  content = File.read(destination)
102
110
  if force || !content.include?(replacement)
103
- content.gsub!(regexp, string)
104
- File.open(destination, "wb") { |file| file.write(content) }
111
+ success = content.gsub!(regexp, string)
112
+
113
+ File.open(destination, "wb") { |file| file.write(content) } unless pretend?
114
+ success
105
115
  end
106
116
  end
107
117
  end
data/lib/thor/actions.rb CHANGED
@@ -1,17 +1,16 @@
1
- require "uri"
2
- require "thor/core_ext/io_binary_read"
3
- require "thor/actions/create_file"
4
- require "thor/actions/create_link"
5
- require "thor/actions/directory"
6
- require "thor/actions/empty_directory"
7
- require "thor/actions/file_manipulation"
8
- require "thor/actions/inject_into_file"
1
+ require_relative "actions/create_file"
2
+ require_relative "actions/create_link"
3
+ require_relative "actions/directory"
4
+ require_relative "actions/empty_directory"
5
+ require_relative "actions/file_manipulation"
6
+ require_relative "actions/inject_into_file"
9
7
 
10
8
  class Thor
11
9
  module Actions
12
10
  attr_accessor :behavior
13
11
 
14
12
  def self.included(base) #:nodoc:
13
+ super(base)
15
14
  base.extend ClassMethods
16
15
  end
17
16
 
@@ -162,6 +161,8 @@ class Thor
162
161
  # to the block you provide. The path is set back to the previous path when
163
162
  # the method exits.
164
163
  #
164
+ # Returns the value yielded by the block.
165
+ #
165
166
  # ==== Parameters
166
167
  # dir<String>:: the directory to move to.
167
168
  # config<Hash>:: give :verbose => true to log and use padding.
@@ -180,16 +181,18 @@ class Thor
180
181
  FileUtils.mkdir_p(destination_root)
181
182
  end
182
183
 
184
+ result = nil
183
185
  if pretend
184
186
  # In pretend mode, just yield down to the block
185
- block.arity == 1 ? yield(destination_root) : yield
187
+ result = block.arity == 1 ? yield(destination_root) : yield
186
188
  else
187
189
  require "fileutils"
188
- FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield }
190
+ FileUtils.cd(destination_root) { result = block.arity == 1 ? yield(destination_root) : yield }
189
191
  end
190
192
 
191
193
  @destination_stack.pop
192
194
  shell.padding -= 1 if verbose
195
+ result
193
196
  end
194
197
 
195
198
  # Goes to the root and execute the given block.
@@ -220,7 +223,7 @@ class Thor
220
223
 
221
224
  contents = if is_uri
222
225
  require "open-uri"
223
- open(path, "Accept" => "application/x-thor-template", &:read)
226
+ URI.open(path, "Accept" => "application/x-thor-template", &:read)
224
227
  else
225
228
  open(path, &:read)
226
229
  end
@@ -257,13 +260,19 @@ class Thor
257
260
 
258
261
  return if options[:pretend]
259
262
 
260
- result = config[:capture] ? `#{command}` : system(command.to_s)
263
+ env_splat = [config[:env]] if config[:env]
261
264
 
262
- if config[:abort_on_failure]
263
- success = config[:capture] ? $?.success? : result
264
- abort unless success
265
+ if config[:capture]
266
+ require "open3"
267
+ result, status = Open3.capture2e(*env_splat, command.to_s)
268
+ success = status.success?
269
+ else
270
+ result = system(*env_splat, command.to_s)
271
+ success = result
265
272
  end
266
273
 
274
+ abort if !success && config.fetch(:abort_on_failure, self.class.exit_on_failure?)
275
+
267
276
  result
268
277
  end
269
278
 
data/lib/thor/base.rb CHANGED
@@ -1,17 +1,17 @@
1
- require "thor/command"
2
- require "thor/core_ext/hash_with_indifferent_access"
3
- require "thor/core_ext/ordered_hash"
4
- require "thor/error"
5
- require "thor/invocation"
6
- require "thor/parser"
7
- require "thor/shell"
8
- require "thor/line_editor"
9
- require "thor/util"
1
+ require_relative "command"
2
+ require_relative "core_ext/hash_with_indifferent_access"
3
+ require_relative "error"
4
+ require_relative "invocation"
5
+ require_relative "nested_context"
6
+ require_relative "parser"
7
+ require_relative "shell"
8
+ require_relative "line_editor"
9
+ require_relative "util"
10
10
 
11
11
  class Thor
12
- autoload :Actions, "thor/actions"
13
- autoload :RakeCompat, "thor/rake_compat"
14
- autoload :Group, "thor/group"
12
+ autoload :Actions, File.expand_path("actions", __dir__)
13
+ autoload :RakeCompat, File.expand_path("rake_compat", __dir__)
14
+ autoload :Group, File.expand_path("group", __dir__)
15
15
 
16
16
  # Shortcuts for help.
17
17
  HELP_MAPPINGS = %w(-h -? --help -D)
@@ -22,6 +22,15 @@ class Thor
22
22
 
23
23
  TEMPLATE_EXTNAME = ".tt"
24
24
 
25
+ class << self
26
+ def deprecation_warning(message) #:nodoc:
27
+ unless ENV['THOR_SILENCE_DEPRECATION']
28
+ warn "Deprecation warning: #{message}\n" +
29
+ 'You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION.'
30
+ end
31
+ end
32
+ end
33
+
25
34
  module Base
26
35
  attr_accessor :options, :parent_options, :args
27
36
 
@@ -89,6 +98,7 @@ class Thor
89
98
 
90
99
  class << self
91
100
  def included(base) #:nodoc:
101
+ super(base)
92
102
  base.extend ClassMethods
93
103
  base.send :include, Invocation
94
104
  base.send :include, Shell
@@ -153,17 +163,20 @@ class Thor
153
163
 
154
164
  # If you want to raise an error when the default value of an option does not match
155
165
  # the type call check_default_type!
156
- # This is disabled by default for compatibility.
166
+ # This will be the default; for compatibility a deprecation warning is issued if necessary.
157
167
  def check_default_type!
158
168
  @check_default_type = true
159
169
  end
160
170
 
161
- def check_default_type #:nodoc:
162
- @check_default_type ||= from_superclass(:check_default_type, false)
171
+ # If you want to use defaults that don't match the type of an option,
172
+ # either specify `check_default_type: false` or call `allow_incompatible_default_type!`
173
+ def allow_incompatible_default_type!
174
+ @check_default_type = false
163
175
  end
164
176
 
165
- def check_default_type? #:nodoc:
166
- !!check_default_type
177
+ def check_default_type #:nodoc:
178
+ @check_default_type = from_superclass(:check_default_type, nil) unless defined?(@check_default_type)
179
+ @check_default_type
167
180
  end
168
181
 
169
182
  # If true, option parsing is suspended as soon as an unknown option or a
@@ -353,22 +366,22 @@ class Thor
353
366
  # Returns the commands for this Thor class.
354
367
  #
355
368
  # ==== Returns
356
- # OrderedHash:: An ordered hash with commands names as keys and Thor::Command
357
- # objects as values.
369
+ # Hash:: An ordered hash with commands names as keys and Thor::Command
370
+ # objects as values.
358
371
  #
359
372
  def commands
360
- @commands ||= Thor::CoreExt::OrderedHash.new
373
+ @commands ||= Hash.new
361
374
  end
362
375
  alias_method :tasks, :commands
363
376
 
364
377
  # Returns the commands for this Thor class and all subclasses.
365
378
  #
366
379
  # ==== Returns
367
- # OrderedHash:: An ordered hash with commands names as keys and Thor::Command
368
- # objects as values.
380
+ # Hash:: An ordered hash with commands names as keys and Thor::Command
381
+ # objects as values.
369
382
  #
370
383
  def all_commands
371
- @all_commands ||= from_superclass(:all_commands, Thor::CoreExt::OrderedHash.new)
384
+ @all_commands ||= from_superclass(:all_commands, Hash.new)
372
385
  @all_commands.merge!(commands)
373
386
  end
374
387
  alias_method :all_tasks, :all_commands
@@ -415,14 +428,20 @@ class Thor
415
428
  # remove_command :this_is_not_a_command
416
429
  # end
417
430
  #
418
- def no_commands
419
- @no_commands = true
420
- yield
421
- ensure
422
- @no_commands = false
431
+ def no_commands(&block)
432
+ no_commands_context.enter(&block)
423
433
  end
434
+
424
435
  alias_method :no_tasks, :no_commands
425
436
 
437
+ def no_commands_context
438
+ @no_commands_context ||= NestedContext.new
439
+ end
440
+
441
+ def no_commands?
442
+ no_commands_context.entered?
443
+ end
444
+
426
445
  # Sets the namespace for the Thor or Thor::Group class. By default the
427
446
  # namespace is retrieved from the class name. If your Thor class is named
428
447
  # Scripts::MyScript, the help method, for example, will be called as:
@@ -502,10 +521,16 @@ class Thor
502
521
  msg = "ERROR: \"#{basename} #{name}\" was called with ".dup
503
522
  msg << "no arguments" if args.empty?
504
523
  msg << "arguments " << args.inspect unless args.empty?
505
- msg << "\nUsage: #{banner(command).inspect}"
524
+ msg << "\nUsage: \"#{banner(command).split("\n").join("\"\n \"")}\""
506
525
  raise InvocationError, msg
507
526
  end
508
527
 
528
+ # A flag that makes the process exit with status 1 if any error happens.
529
+ def exit_on_failure?
530
+ Thor.deprecation_warning "Thor exit with status 0 on errors. To keep this behavior, you must define `exit_on_failure?` in `#{self.name}`"
531
+ false
532
+ end
533
+
509
534
  protected
510
535
 
511
536
  # Prints the class options per group. If an option does not belong to
@@ -563,7 +588,7 @@ class Thor
563
588
  # options<Hash>:: Described in both class_option and method_option.
564
589
  # scope<Hash>:: Options hash that is being built up
565
590
  def build_option(name, options, scope) #:nodoc:
566
- scope[name] = Thor::Option.new(name, options.merge(:check_default_type => check_default_type?))
591
+ scope[name] = Thor::Option.new(name, {:check_default_type => check_default_type}.merge!(options))
567
592
  end
568
593
 
569
594
  # Receives a hash of options, parse them and add to the scope. This is a
@@ -596,13 +621,15 @@ class Thor
596
621
  # Everytime someone inherits from a Thor class, register the klass
597
622
  # and file into baseclass.
598
623
  def inherited(klass)
624
+ super(klass)
599
625
  Thor::Base.register_klass_file(klass)
600
- klass.instance_variable_set(:@no_commands, false)
626
+ klass.instance_variable_set(:@no_commands, 0)
601
627
  end
602
628
 
603
629
  # Fire this callback whenever a method is added. Added methods are
604
630
  # tracked as commands by invoking the create_command method.
605
631
  def method_added(meth)
632
+ super(meth)
606
633
  meth = meth.to_s
607
634
 
608
635
  if meth == "initialize"
@@ -613,8 +640,7 @@ class Thor
613
640
  # Return if it's not a public instance method
614
641
  return unless public_method_defined?(meth.to_sym)
615
642
 
616
- @no_commands ||= false
617
- return if @no_commands || !create_command(meth)
643
+ return if no_commands? || !create_command(meth)
618
644
 
619
645
  is_thor_reserved_word?(meth, :command)
620
646
  Thor::Base.register_klass_file(self)
@@ -641,11 +667,6 @@ class Thor
641
667
  end
642
668
  end
643
669
 
644
- # A flag that makes the process exit with status 1 if any error happens.
645
- def exit_on_failure?
646
- false
647
- end
648
-
649
670
  #
650
671
  # The basename of the program invoking the thor class.
651
672
  #
data/lib/thor/command.rb CHANGED
@@ -49,24 +49,32 @@ class Thor
49
49
 
50
50
  formatted ||= "".dup
51
51
 
52
- # Add usage with required arguments
53
- formatted << if klass && !klass.arguments.empty?
54
- usage.to_s.gsub(/^#{name}/) do |match|
55
- match << " " << klass.arguments.map(&:usage).compact.join(" ")
56
- end
57
- else
58
- usage.to_s
59
- end
52
+ Array(usage).map do |specific_usage|
53
+ formatted_specific_usage = formatted
60
54
 
61
- # Add required options
62
- formatted << " #{required_options}"
55
+ formatted_specific_usage += required_arguments_for(klass, specific_usage)
63
56
 
64
- # Strip and go!
65
- formatted.strip
57
+ # Add required options
58
+ formatted_specific_usage += " #{required_options}"
59
+
60
+ # Strip and go!
61
+ formatted_specific_usage.strip
62
+ end.join("\n")
66
63
  end
67
64
 
68
65
  protected
69
66
 
67
+ # Add usage with required arguments
68
+ def required_arguments_for(klass, usage)
69
+ if klass && !klass.arguments.empty?
70
+ usage.to_s.gsub(/^#{name}/) do |match|
71
+ match << " " << klass.arguments.map(&:usage).compact.join(" ")
72
+ end
73
+ else
74
+ usage.to_s
75
+ end
76
+ end
77
+
70
78
  def not_debugging?(instance)
71
79
  !(instance.class.respond_to?(:debugging) && instance.class.debugging)
72
80
  end
@@ -97,8 +105,7 @@ class Thor
97
105
  def handle_argument_error?(instance, error, caller)
98
106
  not_debugging?(instance) && (error.message =~ /wrong number of arguments/ || error.message =~ /given \d*, expected \d*/) && begin
99
107
  saned = sans_backtrace(error.backtrace, caller)
100
- # Ruby 1.9 always include the called method in the backtrace
101
- saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9")
108
+ saned.empty? || saned.size == 1
102
109
  end
103
110
  end
104
111
 
@@ -28,6 +28,12 @@ class Thor
28
28
  super(convert_key(key))
29
29
  end
30
30
 
31
+ def except(*keys)
32
+ dup.tap do |hash|
33
+ keys.each { |key| hash.delete(convert_key(key)) }
34
+ end
35
+ end
36
+
31
37
  def fetch(key, *args)
32
38
  super(convert_key(key), *args)
33
39
  end