thor 0.20.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b35ad01aa15321a80d1e8c579b3c979c3ff6985a98cca96ffc3e21beba4dd7b5
4
- data.tar.gz: f6aa82269a71a418fa99f82ed56672a32da5ce7ebaa4f6f9e2bdb8a16459ec95
3
+ metadata.gz: 49e18474eddb5dbf02c13be5b42104385928e460511272e7293cc66110d1c3d7
4
+ data.tar.gz: ffea33d8f08051882551af3a444fe68a77ffe0ecfe16404f2ead662364d36b5e
5
5
  SHA512:
6
- metadata.gz: 3a405c6ac1be920be0d5f1dfe2f4d01ddae7fd68f229d6968c619e243abedfe6adb6b5b58d935b5a8528b4561c6f16fbd9c9ca58befd993ea0bb43950333983e
7
- data.tar.gz: 659d3e822725f0bc9161feb69fc2449dfd4b355279a0c59406abf909f23435f157e97b353deec047cd3dbce38a5214d3cd6010844ffd86f50e797a0ab21eac55
6
+ metadata.gz: bc1a58088c4c7b48166152c1f7cfc0c4dc91e9c3199e583e6085b74a2982f65c0eaa8d8b7595f8f5781c08b0f60818e4a014bb23adbdfb07fd2ded558ceef67e
7
+ data.tar.gz: f9a3c7f81a55d75298e3655569a86f04aa2055088058eb0a30ce5b80e05c6be7ed9c0f1840031dd266c453a4ac1454d8749849b47659d2f53f8b1221b9db2b7b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ # 1.1.0
2
+ * Don't use ANSI colors when terminal is dumb.
3
+ * Ensure default option/argument is not erroneously aliased.
4
+ * Fixes a bug in the calculation of the print_wrapped method.
5
+ * Obey `:mute` and `options[:quiet]` in `Shell#say`.
6
+ * Support Ruby 3.0.
7
+ * Add force option to the `gsub_file` action.
8
+
9
+ # 1.0.1
10
+ * Fix thor when `thor/base` and `thor/group` are required without `thor.rb`.
11
+ * Handle relative source path in `create_link`.
12
+
13
+ # 1.0.0
14
+ * Drop support to Ruby 1.8 and 1.9.
15
+ * Deprecate relying on default `exit_on_failure?`.
16
+ In preparation to make Thor commands exit when there is a failure we are deprecating
17
+ defining a command without defining what behavior is expected when there is a failure.
18
+
19
+ To fix the deprecation you need to define a class method called `exit_on_failure?` returning
20
+
21
+ `false` if you want the current behavior or `true` if you want the new behavior.
22
+ * Deprecate defining an option with the default value using a different type as defined in the option.
23
+ * Allow options to be repeatable. See #674.
24
+
1
25
  # 0.20.3
2
26
  * Support old versions of `did_you_mean`.
3
27
 
@@ -5,7 +29,7 @@
5
29
  * Fix `did_you_mean` support.
6
30
 
7
31
  # 0.20.1
8
- * Support new versions fo ERB.
32
+ * Support new versions of ERB.
9
33
  * Fix `check_unknown_options!` to not check the content that was not parsed, i.e. after a `--` or after the first unknown with `stop_on_unknown_option!`
10
34
  * Add `did_you_mean` support.
11
35
 
data/lib/thor.rb CHANGED
@@ -1,7 +1,7 @@
1
- require "set"
2
- require "thor/base"
1
+ require_relative "thor/base"
3
2
 
4
3
  class Thor
4
+ $thor_runner ||= false
5
5
  class << self
6
6
  # Allows for custom "Command" package naming.
7
7
  #
@@ -90,9 +90,14 @@ class Thor
90
90
  # ==== Parameters
91
91
  # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command.
92
92
  #
93
- def map(mappings = nil)
93
+ def map(mappings = nil, **kw)
94
94
  @map ||= from_superclass(:map, {})
95
95
 
96
+ if mappings && !kw.empty?
97
+ mappings = kw.merge!(mappings)
98
+ else
99
+ mappings ||= kw
100
+ end
96
101
  if mappings
97
102
  mappings.each do |key, value|
98
103
  if key.respond_to?(:each)
@@ -170,7 +175,7 @@ class Thor
170
175
  handle_no_command_error(meth) unless command
171
176
 
172
177
  shell.say "Usage:"
173
- shell.say " #{banner(command)}"
178
+ shell.say " #{banner(command).split("\n").join("\n ")}"
174
179
  shell.say
175
180
  class_options_help(shell, nil => command.options.values)
176
181
  if command.long_description
@@ -318,7 +323,7 @@ class Thor
318
323
  # ==== Parameters
319
324
  # Symbol ...:: A list of commands that should be affected.
320
325
  def stop_on_unknown_option!(*command_names)
321
- stop_on_unknown_option.merge(command_names)
326
+ @stop_on_unknown_option = stop_on_unknown_option | command_names
322
327
  end
323
328
 
324
329
  def stop_on_unknown_option?(command) #:nodoc:
@@ -332,7 +337,7 @@ class Thor
332
337
  # ==== Parameters
333
338
  # Symbol ...:: A list of commands that should be affected.
334
339
  def disable_required_check!(*command_names)
335
- disable_required_check.merge(command_names)
340
+ @disable_required_check = disable_required_check | command_names
336
341
  end
337
342
 
338
343
  def disable_required_check?(command) #:nodoc:
@@ -342,12 +347,12 @@ class Thor
342
347
  protected
343
348
 
344
349
  def stop_on_unknown_option #:nodoc:
345
- @stop_on_unknown_option ||= Set.new
350
+ @stop_on_unknown_option ||= []
346
351
  end
347
352
 
348
353
  # help command has the required check disabled by default.
349
354
  def disable_required_check #:nodoc:
350
- @disable_required_check ||= Set.new([:help])
355
+ @disable_required_check ||= [:help]
351
356
  end
352
357
 
353
358
  # The method responsible for dispatching given the args.
@@ -393,7 +398,9 @@ class Thor
393
398
  # the namespace should be displayed as arguments.
394
399
  #
395
400
  def banner(command, namespace = nil, subcommand = false)
396
- "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}"
401
+ command.formatted_usage(self, $thor_runner, subcommand).split("\n").map do |formatted_usage|
402
+ "#{basename} #{formatted_usage}"
403
+ end.join("\n")
397
404
  end
398
405
 
399
406
  def baseclass #:nodoc:
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
 
@@ -220,7 +219,7 @@ class Thor
220
219
 
221
220
  contents = if is_uri
222
221
  require "open-uri"
223
- open(path, "Accept" => "application/x-thor-template", &:read)
222
+ URI.open(path, "Accept" => "application/x-thor-template", &:read)
224
223
  else
225
224
  open(path, &:read)
226
225
  end
@@ -257,13 +256,19 @@ class Thor
257
256
 
258
257
  return if options[:pretend]
259
258
 
260
- result = config[:capture] ? `#{command}` : system(command.to_s)
259
+ env_splat = [config[:env]] if config[:env]
261
260
 
262
- if config[:abort_on_failure]
263
- success = config[:capture] ? $?.success? : result
264
- abort unless success
261
+ if config[:capture]
262
+ require "open3"
263
+ result, status = Open3.capture2e(*env_splat, command.to_s)
264
+ success = status.success?
265
+ else
266
+ result = system(*env_splat, command.to_s)
267
+ success = result
265
268
  end
266
269
 
270
+ abort if !success && config.fetch(:abort_on_failure, self.class.exit_on_failure?)
271
+
267
272
  result
268
273
  end
269
274
 
@@ -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
@@ -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
 
@@ -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,11 +94,13 @@ 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.
@@ -100,8 +109,10 @@ class Thor
100
109
  return if pretend?
101
110
  content = File.read(destination)
102
111
  if force || !content.include?(replacement)
103
- content.gsub!(regexp, string)
112
+ success = content.gsub!(regexp, string)
113
+
104
114
  File.open(destination, "wb") { |file| file.write(content) }
115
+ success
105
116
  end
106
117
  end
107
118
  end
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
  #