thor 0.20.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dbcaa89ae8040cc061b9243fce97dbe6345e775ec5916d45d37ed9989bd14d79
4
- data.tar.gz: b78c5c55482372cb805ac15c7326fe598c0483640b80e089e23ad5a58c574422
3
+ metadata.gz: 49e18474eddb5dbf02c13be5b42104385928e460511272e7293cc66110d1c3d7
4
+ data.tar.gz: ffea33d8f08051882551af3a444fe68a77ffe0ecfe16404f2ead662364d36b5e
5
5
  SHA512:
6
- metadata.gz: dfe4f6047443f79cdd7974ad556f39897e97c83dcbd4803a95bc26770e9f0b9f8225e310391863600779ae11c570543d8d97411ede4c77465435c9a24b82d32d
7
- data.tar.gz: 40ba578626f442737b917a6ffd97fbea548bb512d105f6c35f6e99b91be368e862c1fea1803117659ac6f426b22cf5a4ec26614cdc4c619202144cb03eb58105
6
+ metadata.gz: bc1a58088c4c7b48166152c1f7cfc0c4dc91e9c3199e583e6085b74a2982f65c0eaa8d8b7595f8f5781c08b0f60818e4a014bb23adbdfb07fd2ded558ceef67e
7
+ data.tar.gz: f9a3c7f81a55d75298e3655569a86f04aa2055088058eb0a30ce5b80e05c6be7ed9c0f1840031dd266c453a4ac1454d8749849b47659d2f53f8b1221b9db2b7b
@@ -1,6 +1,37 @@
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
+
25
+ # 0.20.3
26
+ * Support old versions of `did_you_mean`.
27
+
28
+ # 0.20.2
29
+ * Fix `did_you_mean` support.
30
+
1
31
  # 0.20.1
2
- * Support new versions fo ERB.
32
+ * Support new versions of ERB.
3
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!`
34
+ * Add `did_you_mean` support.
4
35
 
5
36
  ## 0.20.0
6
37
  * Add `check_default_type!` to check if the default value of an option matches the defined type.
@@ -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:
@@ -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
@@ -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
  #