diff -Nru ruby-thor-0.20.3/CHANGELOG.md ruby-thor-1.0.1/CHANGELOG.md --- ruby-thor-0.20.3/CHANGELOG.md 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/CHANGELOG.md 2019-12-17 15:12:43.000000000 +0000 @@ -1,3 +1,19 @@ +# 1.0.1 +* Fix thor when `thor/base` and `thor/group` are required without `thor.rb`. +* Handle relative source path in `create_link`. + +# 1.0.0 +* Drop support to Ruby 1.8 and 1.9. +* Deprecate relying on default `exit_on_failure?`. + In preparation to make Thor commands exit when there is a failure we are deprecating + defining a command without defining what behavior is expected when there is a failure. + + To fix the deprecation you need to define a class method called `exit_on_failure?` returning + + `false` if you want the current behavior or `true` if you want the new behavior. +* Deprecate defining an option with the default value using a different type as defined in the option. +* Allow options to be repeatable. See #674. + # 0.20.3 * Support old versions of `did_you_mean`. @@ -5,7 +21,7 @@ * Fix `did_you_mean` support. # 0.20.1 -* Support new versions fo ERB. +* Support new versions of ERB. * 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!` * Add `did_you_mean` support. diff -Nru ruby-thor-0.20.3/debian/changelog ruby-thor-1.0.1/debian/changelog --- ruby-thor-0.20.3/debian/changelog 2020-04-13 14:56:57.000000000 +0000 +++ ruby-thor-1.0.1/debian/changelog 2021-01-19 21:22:47.000000000 +0000 @@ -1,3 +1,24 @@ +ruby-thor (1.0.1-1) unstable; urgency=medium + + [ Cédric Boutillier ] + * [ci skip] Update team name + * [ci skip] Add .gitattributes to keep unwanted files out of the source + package + + [ Pirate Praveen ] + * Use backports friendly version in Breaks + + [ Lucas Nussbaum ] + * New upstream version 1.0.1 + * Refresh patches. isolate-test-from-charset.patch is no longer needed + * Refresh packaging + * Refresh patch exclude-git-test.patch + * Add patch: 0002-Disable-tests-that-depend-on-readline-as-ruby-rb-rea.patch + * Add patch: 0003-Disable-broken-test.patch + * Bump Standards-Version to 4.5.1. No changes needed. + + -- Lucas Nussbaum Tue, 19 Jan 2021 22:22:47 +0100 + ruby-thor (0.20.3-2) unstable; urgency=medium * Team upload diff -Nru ruby-thor-0.20.3/debian/control ruby-thor-1.0.1/debian/control --- ruby-thor-0.20.3/debian/control 2020-04-13 14:56:57.000000000 +0000 +++ ruby-thor-1.0.1/debian/control 2021-01-19 21:22:47.000000000 +0000 @@ -1,11 +1,11 @@ Source: ruby-thor Section: ruby Priority: optional -Maintainer: Debian Ruby Extras Maintainers +Maintainer: Debian Ruby Team Uploaders: Lucas Nussbaum Build-Depends: ruby-bundler, - debhelper-compat (= 12), - gem2deb, + debhelper-compat (= 13), + gem2deb (>= 1), rake, ruby-coveralls, ruby-diff-lcs, @@ -13,12 +13,13 @@ ruby-rspec (>= 2.13.0~), ruby-simplecov, ruby-webmock -Standards-Version: 4.5.0 +Standards-Version: 4.5.1 Vcs-Git: https://salsa.debian.org/ruby-team/ruby-thor.git Vcs-Browser: https://salsa.debian.org/ruby-team/ruby-thor Homepage: http://whatisthor.com/ Testsuite: autopkgtest-pkg-ruby XS-Ruby-Versions: all +Rules-Requires-Root: no Package: ruby-thor Architecture: all @@ -26,7 +27,7 @@ Depends: ruby | ruby-interpreter, ${misc:Depends}, ${shlibs:Depends} -Breaks: ruby-coveralls (<< 0.8.23-1) +Breaks: ruby-coveralls (<< 0.8.23-1~) Description: Ruby scripting framework Thor is a simple and efficient scripting framework for building self-documenting command line utilities. diff -Nru ruby-thor-0.20.3/debian/patches/0002-Disable-tests-that-depend-on-readline-as-ruby-rb-rea.patch ruby-thor-1.0.1/debian/patches/0002-Disable-tests-that-depend-on-readline-as-ruby-rb-rea.patch --- ruby-thor-0.20.3/debian/patches/0002-Disable-tests-that-depend-on-readline-as-ruby-rb-rea.patch 1970-01-01 00:00:00.000000000 +0000 +++ ruby-thor-1.0.1/debian/patches/0002-Disable-tests-that-depend-on-readline-as-ruby-rb-rea.patch 2021-01-19 21:22:47.000000000 +0000 @@ -0,0 +1,85 @@ +From: Lucas Nussbaum +Date: Sat, 16 Jan 2021 09:40:25 +0100 +Subject: Disable tests that depend on readline as ruby-rb-readline is not + available in Debian currently + +--- + spec/line_editor/readline_spec.rb | 69 --------------------------------------- + 1 file changed, 69 deletions(-) + delete mode 100644 spec/line_editor/readline_spec.rb + +diff --git a/spec/line_editor/readline_spec.rb b/spec/line_editor/readline_spec.rb +deleted file mode 100644 +index 86cf746..0000000 +--- a/spec/line_editor/readline_spec.rb ++++ /dev/null +@@ -1,69 +0,0 @@ +-require "helper" +- +-describe Thor::LineEditor::Readline do +- before do +- unless defined? ::Readline +- ::Readline = double("Readline") +- allow(::Readline).to receive(:completion_append_character=).with(nil) +- end +- end +- +- describe ".available?" do +- it "returns true when ::Readline exists" do +- allow(Object).to receive(:const_defined?).with(:Readline).and_return(true) +- expect(described_class).to be_available +- end +- +- it "returns false when ::Readline does not exist" do +- allow(Object).to receive(:const_defined?).with(:Readline).and_return(false) +- expect(described_class).not_to be_available +- end +- end +- +- describe "#readline" do +- it "invokes the readline library" do +- expect(::Readline).to receive(:readline).with("> ", true).and_return("foo") +- expect(::Readline).to_not receive(:completion_proc=) +- editor = Thor::LineEditor::Readline.new("> ", {}) +- expect(editor.readline).to eq("foo") +- end +- +- it "supports the add_to_history option" do +- expect(::Readline).to receive(:readline).with("> ", false).and_return("foo") +- expect(::Readline).to_not receive(:completion_proc=) +- editor = Thor::LineEditor::Readline.new("> ", :add_to_history => false) +- expect(editor.readline).to eq("foo") +- end +- +- it "provides tab completion when given a limited_to option" do +- expect(::Readline).to receive(:readline) +- expect(::Readline).to receive(:completion_proc=) do |proc| +- expect(proc.call("")).to eq %w(Apples Chicken Chocolate) +- expect(proc.call("Ch")).to eq %w(Chicken Chocolate) +- expect(proc.call("Chi")).to eq ["Chicken"] +- end +- +- editor = Thor::LineEditor::Readline.new("Best food: ", :limited_to => %w(Apples Chicken Chocolate)) +- editor.readline +- end +- +- it "provides path tab completion when given the path option" do +- expect(::Readline).to receive(:readline) +- expect(::Readline).to receive(:completion_proc=) do |proc| +- expect(proc.call("../line_ed").sort).to eq ["../line_editor/", "../line_editor_spec.rb"].sort +- end +- +- editor = Thor::LineEditor::Readline.new("Path to file: ", :path => true) +- Dir.chdir(File.dirname(__FILE__)) { editor.readline } +- end +- +- it "uses STDIN when asked not to echo input" do +- expect($stdout).to receive(:print).with("Password: ") +- noecho_stdin = double("noecho_stdin") +- expect(noecho_stdin).to receive(:gets).and_return("secret") +- expect($stdin).to receive(:noecho).and_yield(noecho_stdin) +- editor = Thor::LineEditor::Readline.new("Password: ", :echo => false) +- expect(editor.readline).to eq("secret") +- end +- end +-end diff -Nru ruby-thor-0.20.3/debian/patches/0003-Disable-broken-test.patch ruby-thor-1.0.1/debian/patches/0003-Disable-broken-test.patch --- ruby-thor-0.20.3/debian/patches/0003-Disable-broken-test.patch 1970-01-01 00:00:00.000000000 +0000 +++ ruby-thor-1.0.1/debian/patches/0003-Disable-broken-test.patch 2021-01-19 21:22:47.000000000 +0000 @@ -0,0 +1,38 @@ +From: Lucas Nussbaum +Date: Sat, 16 Jan 2021 21:47:28 +0100 +Subject: Disable broken test + +--- + spec/line_editor_spec.rb | 20 -------------------- + 1 file changed, 20 deletions(-) + +diff --git a/spec/line_editor_spec.rb b/spec/line_editor_spec.rb +index 575fd33..a92d3c4 100644 +--- a/spec/line_editor_spec.rb ++++ b/spec/line_editor_spec.rb +@@ -1,25 +1,5 @@ + require "helper" + +-describe Thor::LineEditor, "on a system with Readline support" do +- before do +- @original_readline = ::Readline if defined? ::Readline +- silence_warnings { ::Readline = double("Readline") } +- end +- +- after do +- silence_warnings { ::Readline = @original_readline } +- end +- +- describe ".readline" do +- it "uses the Readline line editor" do +- editor = double("Readline") +- expect(Thor::LineEditor::Readline).to receive(:new).with("Enter your name ", :default => "Brian").and_return(editor) +- expect(editor).to receive(:readline).and_return("George") +- expect(Thor::LineEditor.readline("Enter your name ", :default => "Brian")).to eq("George") +- end +- end +-end +- + describe Thor::LineEditor, "on a system without Readline support" do + before do + if defined? ::Readline diff -Nru ruby-thor-0.20.3/debian/patches/exclude-git-test.patch ruby-thor-1.0.1/debian/patches/exclude-git-test.patch --- ruby-thor-0.20.3/debian/patches/exclude-git-test.patch 2020-04-13 14:56:57.000000000 +0000 +++ ruby-thor-1.0.1/debian/patches/exclude-git-test.patch 2021-01-19 21:22:47.000000000 +0000 @@ -1,9 +1,17 @@ -Description: Exclude test which uses git -Author: Jongmin Kim +From: Jongmin Kim +Date: Sat, 16 Jan 2021 09:37:49 +0100 +Subject: Exclude test which uses git + Last-Update: 2019-06-01 +--- + spec/quality_spec.rb | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/spec/quality_spec.rb b/spec/quality_spec.rb +index 99efb62..3942a0c 100644 --- a/spec/quality_spec.rb +++ b/spec/quality_spec.rb -@@ -36,6 +36,7 @@ +@@ -36,6 +36,7 @@ describe "The library itself" do match(&:empty?) end @@ -11,7 +19,7 @@ it "has no malformed whitespace" do exempt = /\.gitmodules|\.marshal|fixtures|vendor|spec|ssl_certs|LICENSE/ error_messages = [] -@@ -60,4 +61,5 @@ +@@ -60,4 +61,5 @@ describe "The library itself" do end expect(error_messages.compact).to be_well_formed end diff -Nru ruby-thor-0.20.3/debian/patches/isolate-test-from-charset.patch ruby-thor-1.0.1/debian/patches/isolate-test-from-charset.patch --- ruby-thor-0.20.3/debian/patches/isolate-test-from-charset.patch 2020-04-13 14:56:57.000000000 +0000 +++ ruby-thor-1.0.1/debian/patches/isolate-test-from-charset.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -Description: Isolate the test from system charset - When the testing system uses non-chinese charset (eg. US-ASCII), the - test comes fail. - . - This patch will isolate the test from local system environment. -Author: Jongmin Kim -Forwarded: https://github.com/erikhuda/thor/pull/662 -Last-Update: 2019-06-15 ---- a/spec/actions/inject_into_file_spec.rb -+++ b/spec/actions/inject_into_file_spec.rb -@@ -105,8 +105,15 @@ - end - - it "can insert chinese" do -- invoke! "doc/README.zh", "\n中文", :after => "__start__" -- expect(File.read(File.join(destination_root, "doc/README.zh"))).to eq("__start__\n中文\n说明\n__end__\n") -+ original_encoding = Encoding.default_external -+ -+ begin -+ Encoding.default_external = Encoding.find("UTF-8") -+ invoke! "doc/README.zh", "\n中文", :after => "__start__" -+ expect(File.read(File.join(destination_root, "doc/README.zh"))).to eq("__start__\n中文\n说明\n__end__\n") -+ ensure -+ Encoding.default_external = original_encoding -+ end - end - end - diff -Nru ruby-thor-0.20.3/debian/patches/series ruby-thor-1.0.1/debian/patches/series --- ruby-thor-0.20.3/debian/patches/series 2020-04-13 14:56:57.000000000 +0000 +++ ruby-thor-1.0.1/debian/patches/series 2021-01-19 21:22:47.000000000 +0000 @@ -1,2 +1,3 @@ exclude-git-test.patch -isolate-test-from-charset.patch +0002-Disable-tests-that-depend-on-readline-as-ruby-rb-rea.patch +0003-Disable-broken-test.patch diff -Nru ruby-thor-0.20.3/debian/rules ruby-thor-1.0.1/debian/rules --- ruby-thor-0.20.3/debian/rules 2020-04-13 14:56:57.000000000 +0000 +++ ruby-thor-1.0.1/debian/rules 2021-01-19 21:22:47.000000000 +0000 @@ -1,6 +1,7 @@ #!/usr/bin/make -f export GEM2DEB_TEST_RUNNER = --check-dependencies +export DH_RUBY = --gem-install %: dh $@ --buildsystem=ruby --with ruby diff -Nru ruby-thor-0.20.3/debian/salsa-ci.yml ruby-thor-1.0.1/debian/salsa-ci.yml --- ruby-thor-0.20.3/debian/salsa-ci.yml 2020-04-13 14:56:57.000000000 +0000 +++ ruby-thor-1.0.1/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ ---- -include: - - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml - - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru ruby-thor-0.20.3/debian/upstream/metadata ruby-thor-1.0.1/debian/upstream/metadata --- ruby-thor-0.20.3/debian/upstream/metadata 2020-04-13 14:56:57.000000000 +0000 +++ ruby-thor-1.0.1/debian/upstream/metadata 2021-01-19 21:22:47.000000000 +0000 @@ -1,4 +1,6 @@ -Bug-Database: https://github.com/wycats/thor/issues -Bug-Submit: https://github.com/wycats/thor/issues/new -Repository: https://github.com/wycats/thor.git -Repository-Browse: https://github.com/wycats/thor +--- +Archive: GitHub +Bug-Database: https://github.com/erikhuda/thor/issues +Bug-Submit: https://github.com/erikhuda/thor/issues/new +Repository: https://github.com/erikhuda/thor.git +Repository-Browse: https://github.com/erikhuda/thor diff -Nru ruby-thor-0.20.3/Gemfile ruby-thor-1.0.1/Gemfile --- ruby-thor-0.20.3/Gemfile 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/Gemfile 2019-12-17 15:12:43.000000000 +0000 @@ -1,42 +1,22 @@ source "https://rubygems.org" -gem "rake", "< 11" -gem "rdoc", "~> 4.2.2" # This is to support Ruby 1.8 and 1.9 +gem "rake" group :development do gem "pry" - platforms :ruby_21 do - gem "pry-byebug" - end - platforms :ruby_19, :ruby_20 do - gem "pry-debugger" - gem "pry-stack_explorer" - end + gem "pry-byebug" end group :test do - gem "addressable", "~> 2.3.6", :platforms => [:ruby_18] gem "childlabor" gem "coveralls", ">= 0.8.19" - gem "json", "< 2" # This is to support Ruby 1.8 and 1.9 - gem "mime-types", "~> 1.25", :platforms => [:jruby, :ruby_18] - gem "rest-client", "~> 1.6.0", :platforms => [:jruby, :ruby_18] gem "rspec", ">= 3" gem "rspec-mocks", ">= 3" - gem "rubocop", ">= 0.19", :platforms => [:ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24] + gem "rubocop", ">= 0.19" gem "simplecov", ">= 0.13" - gem "term-ansicolor", "~> 1.3.2" # This is to support Ruby 1.8 and 1.9 - gem "tins", "< 1.7" # This is to support Ruby 1.8 and 1.9 - if RUBY_VERSION < "1.9.3" - gem "webmock", ">= 1.20", "< 2" # This is to support Ruby 1.8 and 1.9.2 - gem "hashdiff", "< 0.3.6" # Hashdiff 0.3.6 no longer supports Ruby 1.8 - else - gem "webmock" - end - if RUBY_VERSION >= '1.9' - # `did_you_mean` can't build with Ruby 1.8. - gem 'did_you_mean' - end + gem "webmock" end +gem 'did_you_mean' + gemspec diff -Nru ruby-thor-0.20.3/lib/thor/actions/create_file.rb ruby-thor-1.0.1/lib/thor/actions/create_file.rb --- ruby-thor-0.20.3/lib/thor/actions/create_file.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/actions/create_file.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,4 +1,4 @@ -require "thor/actions/empty_directory" +require_relative "empty_directory" class Thor module Actions diff -Nru ruby-thor-0.20.3/lib/thor/actions/create_link.rb ruby-thor-1.0.1/lib/thor/actions/create_link.rb --- ruby-thor-0.20.3/lib/thor/actions/create_link.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/actions/create_link.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,4 +1,4 @@ -require "thor/actions/create_file" +require_relative "create_file" class Thor module Actions @@ -33,7 +33,8 @@ # Boolean:: true if it is identical, false otherwise. # def identical? - exists? && File.identical?(render, destination) + source = File.expand_path(render, File.dirname(destination)) + exists? && File.identical?(source, destination) end def invoke! diff -Nru ruby-thor-0.20.3/lib/thor/actions/directory.rb ruby-thor-1.0.1/lib/thor/actions/directory.rb --- ruby-thor-0.20.3/lib/thor/actions/directory.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/actions/directory.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,4 +1,4 @@ -require "thor/actions/empty_directory" +require_relative "empty_directory" class Thor module Actions @@ -56,7 +56,7 @@ attr_reader :source def initialize(base, source, destination = nil, config = {}, &block) - @source = File.expand_path(base.find_in_source_paths(source.to_s)) + @source = File.expand_path(Dir[Util.escape_globs(base.find_in_source_paths(source.to_s))].first) @block = block super(base, destination, {:recursive => true}.merge(config)) end @@ -96,22 +96,12 @@ end end - if RUBY_VERSION < "2.0" - def file_level_lookup(previous_lookup) - File.join(previous_lookup, "{*,.[a-z]*}") - end - - def files(lookup) - Dir[lookup] - end - else - def file_level_lookup(previous_lookup) - File.join(previous_lookup, "*") - end + def file_level_lookup(previous_lookup) + File.join(previous_lookup, "*") + end - def files(lookup) - Dir.glob(lookup, File::FNM_DOTMATCH) - end + def files(lookup) + Dir.glob(lookup, File::FNM_DOTMATCH) end end end diff -Nru ruby-thor-0.20.3/lib/thor/actions/file_manipulation.rb ruby-thor-1.0.1/lib/thor/actions/file_manipulation.rb --- ruby-thor-0.20.3/lib/thor/actions/file_manipulation.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/actions/file_manipulation.rb 2019-12-17 15:12:43.000000000 +0000 @@ -23,14 +23,14 @@ destination = args.first || source source = File.expand_path(find_in_source_paths(source.to_s)) - create_file destination, nil, config do + resulting_destination = create_file destination, nil, config do content = File.binread(source) content = yield(content) if block content end if config[:mode] == :preserve mode = File.stat(source).mode - chmod(destination, mode, config) + chmod(resulting_destination, mode, config) end end @@ -80,14 +80,14 @@ config = args.last.is_a?(Hash) ? args.pop : {} destination = args.first - if source =~ %r{^https?\://} + render = if source =~ %r{^https?\://} require "open-uri" + URI.send(:open, source) { |input| input.binmode.read } else source = File.expand_path(find_in_source_paths(source.to_s)) + open(source) { |input| input.binmode.read } end - render = open(source) { |input| input.binmode.read } - destination ||= if block_given? block.arity == 1 ? yield(render) : yield else diff -Nru ruby-thor-0.20.3/lib/thor/actions/inject_into_file.rb ruby-thor-1.0.1/lib/thor/actions/inject_into_file.rb --- ruby-thor-0.20.3/lib/thor/actions/inject_into_file.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/actions/inject_into_file.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,4 +1,4 @@ -require "thor/actions/empty_directory" +require_relative "empty_directory" class Thor module Actions @@ -21,9 +21,14 @@ # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") # end # + WARNINGS = { unchanged_no_flag: 'File unchanged! The supplied flag value not found!' } + def insert_into_file(destination, *args, &block) data = block_given? ? block : args.shift - config = args.shift + + config = args.shift || {} + config[:after] = /\z/ unless config.key?(:before) || config.key?(:after) + action InjectIntoFile.new(self, destination, data, config) end alias_method :inject_into_file, :insert_into_file @@ -45,8 +50,6 @@ end def invoke! - say_status :invoke - content = if @behavior == :after '\0' + replacement else @@ -54,7 +57,11 @@ end if exists? - replace!(/#{flag}/, content, config[:force]) + if replace!(/#{flag}/, content, config[:force]) + say_status(:invoke) + else + say_status(:unchanged, warning: WARNINGS[:unchanged_no_flag], color: :red) + end else unless pretend? raise Thor::Error, "The file #{ destination } does not appear to exist" @@ -78,7 +85,7 @@ protected - def say_status(behavior) + def say_status(behavior, warning: nil, color: nil) status = if behavior == :invoke if flag == /\A/ :prepend @@ -87,11 +94,13 @@ else :insert end + elsif warning + warning else :subtract end - super(status, config[:verbose]) + super(status, (color || config[:verbose])) end # Adds the content to the file. @@ -100,8 +109,10 @@ return if pretend? content = File.read(destination) if force || !content.include?(replacement) - content.gsub!(regexp, string) + success = content.gsub!(regexp, string) + File.open(destination, "wb") { |file| file.write(content) } + success end end end diff -Nru ruby-thor-0.20.3/lib/thor/actions.rb ruby-thor-1.0.1/lib/thor/actions.rb --- ruby-thor-0.20.3/lib/thor/actions.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/actions.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,17 +1,16 @@ -require "uri" -require "thor/core_ext/io_binary_read" -require "thor/actions/create_file" -require "thor/actions/create_link" -require "thor/actions/directory" -require "thor/actions/empty_directory" -require "thor/actions/file_manipulation" -require "thor/actions/inject_into_file" +require_relative "actions/create_file" +require_relative "actions/create_link" +require_relative "actions/directory" +require_relative "actions/empty_directory" +require_relative "actions/file_manipulation" +require_relative "actions/inject_into_file" class Thor module Actions attr_accessor :behavior def self.included(base) #:nodoc: + super(base) base.extend ClassMethods end @@ -257,13 +256,19 @@ return if options[:pretend] - result = config[:capture] ? `#{command}` : system(command.to_s) + env_splat = [config[:env]] if config[:env] - if config[:abort_on_failure] - success = config[:capture] ? $?.success? : result - abort unless success + if config[:capture] + require "open3" + result, status = Open3.capture2e(*env_splat, command.to_s) + success = status.success? + else + result = system(*env_splat, command.to_s) + success = result end + abort if !success && config.fetch(:abort_on_failure, self.class.exit_on_failure?) + result end diff -Nru ruby-thor-0.20.3/lib/thor/base.rb ruby-thor-1.0.1/lib/thor/base.rb --- ruby-thor-0.20.3/lib/thor/base.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/base.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,17 +1,17 @@ -require "thor/command" -require "thor/core_ext/hash_with_indifferent_access" -require "thor/core_ext/ordered_hash" -require "thor/error" -require "thor/invocation" -require "thor/parser" -require "thor/shell" -require "thor/line_editor" -require "thor/util" +require_relative "command" +require_relative "core_ext/hash_with_indifferent_access" +require_relative "error" +require_relative "invocation" +require_relative "nested_context" +require_relative "parser" +require_relative "shell" +require_relative "line_editor" +require_relative "util" class Thor - autoload :Actions, "thor/actions" - autoload :RakeCompat, "thor/rake_compat" - autoload :Group, "thor/group" + autoload :Actions, File.expand_path("actions", __dir__) + autoload :RakeCompat, File.expand_path("rake_compat", __dir__) + autoload :Group, File.expand_path("group", __dir__) # Shortcuts for help. HELP_MAPPINGS = %w(-h -? --help -D) @@ -22,6 +22,15 @@ TEMPLATE_EXTNAME = ".tt" + class << self + def deprecation_warning(message) #:nodoc: + unless ENV['THOR_SILENCE_DEPRECATION'] + warn "Deprecation warning: #{message}\n" + + 'You can silence deprecations warning by setting the environment variable THOR_SILENCE_DEPRECATION.' + end + end + end + module Base attr_accessor :options, :parent_options, :args @@ -89,6 +98,7 @@ class << self def included(base) #:nodoc: + super(base) base.extend ClassMethods base.send :include, Invocation base.send :include, Shell @@ -153,17 +163,20 @@ # If you want to raise an error when the default value of an option does not match # the type call check_default_type! - # This is disabled by default for compatibility. + # This will be the default; for compatibility a deprecation warning is issued if necessary. def check_default_type! @check_default_type = true end - def check_default_type #:nodoc: - @check_default_type ||= from_superclass(:check_default_type, false) + # If you want to use defaults that don't match the type of an option, + # either specify `check_default_type: false` or call `allow_incompatible_default_type!` + def allow_incompatible_default_type! + @check_default_type = false end - def check_default_type? #:nodoc: - !!check_default_type + def check_default_type #:nodoc: + @check_default_type = from_superclass(:check_default_type, nil) unless defined?(@check_default_type) + @check_default_type end # If true, option parsing is suspended as soon as an unknown option or a @@ -353,22 +366,22 @@ # Returns the commands for this Thor class. # # ==== Returns - # OrderedHash:: An ordered hash with commands names as keys and Thor::Command - # objects as values. + # Hash:: An ordered hash with commands names as keys and Thor::Command + # objects as values. # def commands - @commands ||= Thor::CoreExt::OrderedHash.new + @commands ||= Hash.new end alias_method :tasks, :commands # Returns the commands for this Thor class and all subclasses. # # ==== Returns - # OrderedHash:: An ordered hash with commands names as keys and Thor::Command - # objects as values. + # Hash:: An ordered hash with commands names as keys and Thor::Command + # objects as values. # def all_commands - @all_commands ||= from_superclass(:all_commands, Thor::CoreExt::OrderedHash.new) + @all_commands ||= from_superclass(:all_commands, Hash.new) @all_commands.merge!(commands) end alias_method :all_tasks, :all_commands @@ -415,14 +428,20 @@ # remove_command :this_is_not_a_command # end # - def no_commands - @no_commands = true - yield - ensure - @no_commands = false + def no_commands(&block) + no_commands_context.enter(&block) end + alias_method :no_tasks, :no_commands + def no_commands_context + @no_commands_context ||= NestedContext.new + end + + def no_commands? + no_commands_context.entered? + end + # Sets the namespace for the Thor or Thor::Group class. By default the # namespace is retrieved from the class name. If your Thor class is named # Scripts::MyScript, the help method, for example, will be called as: @@ -502,10 +521,16 @@ msg = "ERROR: \"#{basename} #{name}\" was called with ".dup msg << "no arguments" if args.empty? msg << "arguments " << args.inspect unless args.empty? - msg << "\nUsage: #{banner(command).inspect}" + msg << "\nUsage: \"#{banner(command).split("\n").join("\"\n \"")}\"" raise InvocationError, msg end + # A flag that makes the process exit with status 1 if any error happens. + def exit_on_failure? + Thor.deprecation_warning "Thor exit with status 0 on errors. To keep this behavior, you must define `exit_on_failure?` in `#{self.name}`" + false + end + protected # Prints the class options per group. If an option does not belong to @@ -563,7 +588,7 @@ # options:: Described in both class_option and method_option. # scope:: Options hash that is being built up def build_option(name, options, scope) #:nodoc: - scope[name] = Thor::Option.new(name, options.merge(:check_default_type => check_default_type?)) + scope[name] = Thor::Option.new(name, {:check_default_type => check_default_type}.merge!(options)) end # Receives a hash of options, parse them and add to the scope. This is a @@ -596,13 +621,15 @@ # Everytime someone inherits from a Thor class, register the klass # and file into baseclass. def inherited(klass) + super(klass) Thor::Base.register_klass_file(klass) - klass.instance_variable_set(:@no_commands, false) + klass.instance_variable_set(:@no_commands, 0) end # Fire this callback whenever a method is added. Added methods are # tracked as commands by invoking the create_command method. def method_added(meth) + super(meth) meth = meth.to_s if meth == "initialize" @@ -613,8 +640,7 @@ # Return if it's not a public instance method return unless public_method_defined?(meth.to_sym) - @no_commands ||= false - return if @no_commands || !create_command(meth) + return if no_commands? || !create_command(meth) is_thor_reserved_word?(meth, :command) Thor::Base.register_klass_file(self) @@ -641,11 +667,6 @@ end end - # A flag that makes the process exit with status 1 if any error happens. - def exit_on_failure? - false - end - # # The basename of the program invoking the thor class. # diff -Nru ruby-thor-0.20.3/lib/thor/command.rb ruby-thor-1.0.1/lib/thor/command.rb --- ruby-thor-0.20.3/lib/thor/command.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/command.rb 2019-12-17 15:12:43.000000000 +0000 @@ -49,24 +49,32 @@ formatted ||= "".dup - # Add usage with required arguments - formatted << if klass && !klass.arguments.empty? - usage.to_s.gsub(/^#{name}/) do |match| - match << " " << klass.arguments.map(&:usage).compact.join(" ") - end - else - usage.to_s - end + Array(usage).map do |specific_usage| + formatted_specific_usage = formatted - # Add required options - formatted << " #{required_options}" + formatted_specific_usage += required_arguments_for(klass, specific_usage) - # Strip and go! - formatted.strip + # Add required options + formatted_specific_usage += " #{required_options}" + + # Strip and go! + formatted_specific_usage.strip + end.join("\n") end protected + # Add usage with required arguments + def required_arguments_for(klass, usage) + if klass && !klass.arguments.empty? + usage.to_s.gsub(/^#{name}/) do |match| + match << " " << klass.arguments.map(&:usage).compact.join(" ") + end + else + usage.to_s + end + end + def not_debugging?(instance) !(instance.class.respond_to?(:debugging) && instance.class.debugging) end @@ -97,8 +105,7 @@ def handle_argument_error?(instance, error, caller) not_debugging?(instance) && (error.message =~ /wrong number of arguments/ || error.message =~ /given \d*, expected \d*/) && begin saned = sans_backtrace(error.backtrace, caller) - # Ruby 1.9 always include the called method in the backtrace - saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9") + saned.empty? || saned.size == 1 end end diff -Nru ruby-thor-0.20.3/lib/thor/core_ext/io_binary_read.rb ruby-thor-1.0.1/lib/thor/core_ext/io_binary_read.rb --- ruby-thor-0.20.3/lib/thor/core_ext/io_binary_read.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/core_ext/io_binary_read.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -class IO #:nodoc: - class << self - unless method_defined? :binread - def binread(file, *args) - raise ArgumentError, "wrong number of arguments (#{1 + args.size} for 1..3)" unless args.size < 3 - File.open(file, "rb") do |f| - f.read(*args) - end - end - end - end -end diff -Nru ruby-thor-0.20.3/lib/thor/core_ext/ordered_hash.rb ruby-thor-1.0.1/lib/thor/core_ext/ordered_hash.rb --- ruby-thor-0.20.3/lib/thor/core_ext/ordered_hash.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/core_ext/ordered_hash.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,129 +0,0 @@ -class Thor - module CoreExt - class OrderedHash < ::Hash - if RUBY_VERSION < "1.9" - def initialize(*args, &block) - super - @keys = [] - end - - def initialize_copy(other) - super - # make a deep copy of keys - @keys = other.keys - end - - def []=(key, value) - @keys << key unless key?(key) - super - end - - def delete(key) - if key? key - index = @keys.index(key) - @keys.delete_at index - end - super - end - - def delete_if - super - sync_keys! - self - end - - alias_method :reject!, :delete_if - - def reject(&block) - dup.reject!(&block) - end - - def keys - @keys.dup - end - - def values - @keys.map { |key| self[key] } - end - - def to_hash - self - end - - def to_a - @keys.map { |key| [key, self[key]] } - end - - def each_key - return to_enum(:each_key) unless block_given? - @keys.each { |key| yield(key) } - self - end - - def each_value - return to_enum(:each_value) unless block_given? - @keys.each { |key| yield(self[key]) } - self - end - - def each - return to_enum(:each) unless block_given? - @keys.each { |key| yield([key, self[key]]) } - self - end - - def each_pair - return to_enum(:each_pair) unless block_given? - @keys.each { |key| yield(key, self[key]) } - self - end - - alias_method :select, :find_all - - def clear - super - @keys.clear - self - end - - def shift - k = @keys.first - v = delete(k) - [k, v] - end - - def merge!(other_hash) - if block_given? - other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v } - else - other_hash.each { |k, v| self[k] = v } - end - self - end - - alias_method :update, :merge! - - def merge(other_hash, &block) - dup.merge!(other_hash, &block) - end - - # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not. - def replace(other) - super - @keys = other.keys - self - end - - def inspect - "#<#{self.class} #{super}>" - end - - private - - def sync_keys! - @keys.delete_if { |k| !key?(k) } - end - end - end - end -end diff -Nru ruby-thor-0.20.3/lib/thor/error.rb ruby-thor-1.0.1/lib/thor/error.rb --- ruby-thor-0.20.3/lib/thor/error.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/error.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,22 +1,18 @@ class Thor - Correctable = - begin - require 'did_you_mean' + Correctable = if defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable) + # In order to support versions of Ruby that don't have keyword + # arguments, we need our own spell checker class that doesn't take key + # words. Even though this code wouldn't be hit because of the check + # above, it's still necessary because the interpreter would otherwise be + # unable to parse the file. + class NoKwargSpellChecker < DidYouMean::SpellChecker # :nodoc: + def initialize(dictionary) + @dictionary = dictionary + end + end - # In order to support versions of Ruby that don't have keyword - # arguments, we need our own spell checker class that doesn't take key - # words. Even though this code wouldn't be hit because of the check - # above, it's still necessary because the interpreter would otherwise be - # unable to parse the file. - class NoKwargSpellChecker < DidYouMean::SpellChecker # :nodoc: - def initialize(dictionary) - @dictionary = dictionary - end - end - - DidYouMean::Correctable - rescue LoadError, NameError - end + DidYouMean::Correctable + end # Thor::Error is raised when it's caused by wrong usage of thor classes. Those # errors have their backtrace suppressed and are nicely shown to the user. diff -Nru ruby-thor-0.20.3/lib/thor/group.rb ruby-thor-1.0.1/lib/thor/group.rb --- ruby-thor-0.20.3/lib/thor/group.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/group.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,4 +1,4 @@ -require "thor/base" +require_relative "base" # Thor has a special class called Thor::Group. The main difference to Thor class # is that it invokes all commands at once. It also include some methods that allows diff -Nru ruby-thor-0.20.3/lib/thor/invocation.rb ruby-thor-1.0.1/lib/thor/invocation.rb --- ruby-thor-0.20.3/lib/thor/invocation.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/invocation.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,6 +1,7 @@ class Thor module Invocation def self.included(base) #:nodoc: + super(base) base.extend ClassMethods end diff -Nru ruby-thor-0.20.3/lib/thor/line_editor/basic.rb ruby-thor-1.0.1/lib/thor/line_editor/basic.rb --- ruby-thor-0.20.3/lib/thor/line_editor/basic.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/line_editor/basic.rb 2019-12-17 15:12:43.000000000 +0000 @@ -24,7 +24,7 @@ $stdin.gets else # Lazy-load io/console since it is gem-ified as of 2.3 - require "io/console" if RUBY_VERSION > "1.9.2" + require "io/console" $stdin.noecho(&:gets) end end diff -Nru ruby-thor-0.20.3/lib/thor/line_editor/readline.rb ruby-thor-1.0.1/lib/thor/line_editor/readline.rb --- ruby-thor-0.20.3/lib/thor/line_editor/readline.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/line_editor/readline.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,19 +1,19 @@ -begin - require "readline" -rescue LoadError -end - class Thor module LineEditor class Readline < Basic def self.available? + begin + require "readline" + rescue LoadError + end + Object.const_defined?(:Readline) end def readline if echo? ::Readline.completion_append_character = nil - # Ruby 1.8.7 does not allow Readline.completion_proc= to receive nil. + # rb-readline does not allow Readline.completion_proc= to receive nil. if complete = completion_proc ::Readline.completion_proc = complete end diff -Nru ruby-thor-0.20.3/lib/thor/line_editor.rb ruby-thor-1.0.1/lib/thor/line_editor.rb --- ruby-thor-0.20.3/lib/thor/line_editor.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/line_editor.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,5 +1,5 @@ -require "thor/line_editor/basic" -require "thor/line_editor/readline" +require_relative "line_editor/basic" +require_relative "line_editor/readline" class Thor module LineEditor diff -Nru ruby-thor-0.20.3/lib/thor/nested_context.rb ruby-thor-1.0.1/lib/thor/nested_context.rb --- ruby-thor-0.20.3/lib/thor/nested_context.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/nested_context.rb 2019-12-17 15:12:43.000000000 +0000 @@ -0,0 +1,29 @@ +class Thor + class NestedContext + def initialize + @depth = 0 + end + + def enter + push + + yield + ensure + pop + end + + def entered? + @depth > 0 + end + + private + + def push + @depth += 1 + end + + def pop + @depth -= 1 + end + end +end diff -Nru ruby-thor-0.20.3/lib/thor/parser/arguments.rb ruby-thor-1.0.1/lib/thor/parser/arguments.rb --- ruby-thor-0.20.3/lib/thor/parser/arguments.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/parser/arguments.rb 2019-12-17 15:12:43.000000000 +0000 @@ -9,7 +9,7 @@ arguments = [] args.each do |item| - break if item =~ /^-/ + break if item.is_a?(String) && item =~ /^-/ arguments << item end @@ -82,7 +82,7 @@ end def current_is_value? - peek && peek.to_s !~ /^-/ + peek && peek.to_s !~ /^-{1,2}\S+/ end # Runs through the argument array getting strings that contains ":" and diff -Nru ruby-thor-0.20.3/lib/thor/parser/option.rb ruby-thor-1.0.1/lib/thor/parser/option.rb --- ruby-thor-0.20.3/lib/thor/parser/option.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/parser/option.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,17 +1,18 @@ class Thor class Option < Argument #:nodoc: - attr_reader :aliases, :group, :lazy_default, :hide + attr_reader :aliases, :group, :lazy_default, :hide, :repeatable VALID_TYPES = [:boolean, :numeric, :hash, :array, :string] def initialize(name, options = {}) @check_default_type = options[:check_default_type] options[:required] = false unless options.key?(:required) + @repeatable = options.fetch(:repeatable, false) super - @lazy_default = options[:lazy_default] - @group = options[:group].to_s.capitalize if options[:group] - @aliases = Array(options[:aliases]) - @hide = options[:hide] + @lazy_default = options[:lazy_default] + @group = options[:group].to_s.capitalize if options[:group] + @aliases = Array(options[:aliases]) + @hide = options[:hide] end # This parse quick options given as method_options. It makes several @@ -111,7 +112,7 @@ def validate! raise ArgumentError, "An option cannot be boolean and required." if boolean? && required? - validate_default_type! if @check_default_type + validate_default_type! end def validate_default_type! @@ -128,7 +129,19 @@ @default.class.name.downcase.to_sym end - raise ArgumentError, "Expected #{@type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})" unless default_type == @type + expected_type = (@repeatable && @type != :hash) ? :array : @type + + if default_type != expected_type + err = "Expected #{expected_type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})" + + if @check_default_type + raise ArgumentError, err + elsif @check_default_type == nil + Thor.deprecation_warning "#{err}.\n" + + 'This will be rejected in the future unless you explicitly pass the options `check_default_type: false`' + + ' or call `allow_incompatible_default_type!` in your code' + end + end end def dasherized? diff -Nru ruby-thor-0.20.3/lib/thor/parser/options.rb ruby-thor-1.0.1/lib/thor/parser/options.rb --- ruby-thor-0.20.3/lib/thor/parser/options.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/parser/options.rb 2019-12-17 15:12:43.000000000 +0000 @@ -97,7 +97,8 @@ switch = normalize_switch(switch) option = switch_option(switch) - @assigns[option.human_name] = parse_peek(switch, option) + result = parse_peek(switch, option) + assign_result!(option, result) elsif @stop_on_unknown @parsing_options = false @extra << shifted @@ -132,6 +133,15 @@ protected + def assign_result!(option, result) + if option.repeatable && option.type == :hash + (@assigns[option.human_name] ||= {}).merge!(result) + elsif option.repeatable + (@assigns[option.human_name] ||= []) << result + else + @assigns[option.human_name] = result + end + end # Check if the current value in peek is a registered switch. # # Two booleans are returned. The first is true if the current value @@ -161,7 +171,7 @@ end def switch?(arg) - switch_option(normalize_switch(arg)) + !switch_option(normalize_switch(arg)).nil? end def switch_option(arg) @@ -194,7 +204,7 @@ shift false else - !no_or_skip?(switch) + @switches.key?(switch) || !no_or_skip?(switch) end else @switches.key?(switch) || !no_or_skip?(switch) diff -Nru ruby-thor-0.20.3/lib/thor/parser.rb ruby-thor-1.0.1/lib/thor/parser.rb --- ruby-thor-0.20.3/lib/thor/parser.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/parser.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,4 +1,4 @@ -require "thor/parser/argument" -require "thor/parser/arguments" -require "thor/parser/option" -require "thor/parser/options" +require_relative "parser/argument" +require_relative "parser/arguments" +require_relative "parser/option" +require_relative "parser/options" diff -Nru ruby-thor-0.20.3/lib/thor/rake_compat.rb ruby-thor-1.0.1/lib/thor/rake_compat.rb --- ruby-thor-0.20.3/lib/thor/rake_compat.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/rake_compat.rb 2019-12-17 15:12:43.000000000 +0000 @@ -25,6 +25,7 @@ end def self.included(base) + super(base) # Hack. Make rakefile point to invoker, so rdoc task is generated properly. rakefile = File.basename(caller[0].match(/(.*):\d+/)[1]) Rake.application.instance_variable_set(:@rakefile, rakefile) diff -Nru ruby-thor-0.20.3/lib/thor/runner.rb ruby-thor-1.0.1/lib/thor/runner.rb --- ruby-thor-0.20.3/lib/thor/runner.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/runner.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,12 +1,13 @@ -require "thor" -require "thor/group" -require "thor/core_ext/io_binary_read" +require_relative "../thor" +require_relative "group" require "yaml" require "digest/md5" require "pathname" class Thor::Runner < Thor #:nodoc: # rubocop:disable ClassLength + autoload :OpenURI, "open-uri" + map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version def self.banner(command, all = false, subcommand = false) @@ -111,7 +112,7 @@ desc "version", "Show Thor version" def version - require "thor/version" + require_relative "version" say "Thor #{Thor::VERSION}" end diff -Nru ruby-thor-0.20.3/lib/thor/shell/basic.rb ruby-thor-1.0.1/lib/thor/shell/basic.rb --- ruby-thor-0.20.3/lib/thor/shell/basic.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/shell/basic.rb 2019-12-17 15:12:43.000000000 +0000 @@ -451,16 +451,25 @@ def ask_filtered(statement, color, options) answer_set = options[:limited_to] + case_insensitive = options.fetch(:case_insensitive, false) correct_answer = nil until correct_answer answers = answer_set.join(", ") answer = ask_simply("#{statement} [#{answers}]", color, options) - correct_answer = answer_set.include?(answer) ? answer : nil + correct_answer = answer_match(answer_set, answer, case_insensitive) say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer end correct_answer end + def answer_match(possibilities, answer, case_insensitive) + if case_insensitive + possibilities.detect{ |possibility| possibility.downcase == answer.downcase } + else + possibilities.detect{ |possibility| possibility == answer } + end + end + def merge(destination, content) #:nodoc: require "tempfile" Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp| diff -Nru ruby-thor-0.20.3/lib/thor/shell/color.rb ruby-thor-1.0.1/lib/thor/shell/color.rb --- ruby-thor-0.20.3/lib/thor/shell/color.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/shell/color.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,4 +1,4 @@ -require "thor/shell/basic" +require_relative "basic" class Thor module Shell @@ -97,7 +97,11 @@ protected def can_display_colors? - stdout.tty? + stdout.tty? && !are_colors_disabled? + end + + def are_colors_disabled? + !ENV['NO_COLOR'].nil? end # Overwrite show_diff to show diff with colors if Diff::LCS is diff -Nru ruby-thor-0.20.3/lib/thor/shell/html.rb ruby-thor-1.0.1/lib/thor/shell/html.rb --- ruby-thor-0.20.3/lib/thor/shell/html.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/shell/html.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,4 +1,4 @@ -require "thor/shell/basic" +require_relative "basic" class Thor module Shell @@ -51,13 +51,13 @@ def set_color(string, *colors) if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } html_colors = colors.map { |color| lookup_color(color) } - "#{string}" + "#{Thor::Util.escape_html(string)}" else color, bold = colors html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol) styles = [html_color] styles << BOLD if bold - "#{string}" + "#{Thor::Util.escape_html(string)}" end end diff -Nru ruby-thor-0.20.3/lib/thor/shell.rb ruby-thor-1.0.1/lib/thor/shell.rb --- ruby-thor-0.20.3/lib/thor/shell.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/shell.rb 2019-12-17 15:12:43.000000000 +0000 @@ -24,9 +24,9 @@ SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width] attr_writer :shell - autoload :Basic, "thor/shell/basic" - autoload :Color, "thor/shell/color" - autoload :HTML, "thor/shell/html" + autoload :Basic, File.expand_path("shell/basic", __dir__) + autoload :Color, File.expand_path("shell/color", __dir__) + autoload :HTML, File.expand_path("shell/html", __dir__) # Add shell to initialize config values. # diff -Nru ruby-thor-0.20.3/lib/thor/util.rb ruby-thor-1.0.1/lib/thor/util.rb --- ruby-thor-0.20.3/lib/thor/util.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/util.rb 2019-12-17 15:12:43.000000000 +0000 @@ -263,6 +263,22 @@ def escape_globs(path) path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&') end + + # Returns a string that has had any HTML characters escaped. + # + # ==== Examples + # + # Thor::Util.escape_html('
') # => "<div>" + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def escape_html(string) + CGI.escapeHTML(string) + end end end end diff -Nru ruby-thor-0.20.3/lib/thor/version.rb ruby-thor-1.0.1/lib/thor/version.rb --- ruby-thor-0.20.3/lib/thor/version.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor/version.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,3 +1,3 @@ class Thor - VERSION = "0.20.3" + VERSION = "1.0.1" end diff -Nru ruby-thor-0.20.3/lib/thor.rb ruby-thor-1.0.1/lib/thor.rb --- ruby-thor-0.20.3/lib/thor.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/lib/thor.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,5 +1,5 @@ require "set" -require "thor/base" +require_relative "thor/base" class Thor class << self @@ -90,9 +90,14 @@ # ==== Parameters # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command. # - def map(mappings = nil) + def map(mappings = nil, **kw) @map ||= from_superclass(:map, {}) + if mappings && !kw.empty? + mappings = kw.merge!(mappings) + else + mappings ||= kw + end if mappings mappings.each do |key, value| if key.respond_to?(:each) @@ -170,7 +175,7 @@ handle_no_command_error(meth) unless command shell.say "Usage:" - shell.say " #{banner(command)}" + shell.say " #{banner(command).split("\n").join("\n ")}" shell.say class_options_help(shell, nil => command.options.values) if command.long_description @@ -393,7 +398,10 @@ # the namespace should be displayed as arguments. # def banner(command, namespace = nil, subcommand = false) - "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}" + $thor_runner ||= false + command.formatted_usage(self, $thor_runner, subcommand).split("\n").map do |formatted_usage| + "#{basename} #{formatted_usage}" + end.join("\n") end def baseclass #:nodoc: diff -Nru ruby-thor-0.20.3/spec/actions/create_link_spec.rb ruby-thor-1.0.1/spec/actions/create_link_spec.rb --- ruby-thor-0.20.3/spec/actions/create_link_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/actions/create_link_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -2,95 +2,118 @@ require "thor/actions" require "tempfile" -describe Thor::Actions::CreateLink do +describe Thor::Actions::CreateLink, :unless => windows? do before do - @silence = false @hardlink_to = File.join(Dir.tmpdir, "linkdest.rb") ::FileUtils.rm_rf(destination_root) ::FileUtils.rm_rf(@hardlink_to) end - def create_link(destination = nil, config = {}, options = {}) - @base = MyCounter.new([1, 2], options, :destination_root => destination_root) - allow(@base).to receive(:file_name).and_return("rdoc") + let(:config) { {} } + let(:options) { {} } - @tempfile = Tempfile.new("config.rb") + let(:base) do + base = MyCounter.new([1, 2], options, :destination_root => destination_root) + allow(base).to receive(:file_name).and_return("rdoc") + base + end + + let(:tempfile) { Tempfile.new("config.rb") } + + let(:source) { tempfile.path } + + let(:destination) { "doc/config.rb" } - @action = Thor::Actions::CreateLink.new(@base, destination, @tempfile.path, - {:verbose => !@silence}.merge(config)) + let(:action) do + Thor::Actions::CreateLink.new(base, destination, source, config) end def invoke! - capture(:stdout) { @action.invoke! } + capture(:stdout) { action.invoke! } end def revoke! - capture(:stdout) { @action.revoke! } - end - - def silence! - @silence = true + capture(:stdout) { action.revoke! } end describe "#invoke!" do - it "creates a symbolic link for :symbolic => true" do - create_link("doc/config.rb", :symbolic => true) - invoke! - destination_path = File.join(destination_root, "doc/config.rb") - expect(File.exist?(destination_path)).to be true - expect(File.symlink?(destination_path)).to be true - end + context "specifying :symbolic => true" do + let(:config) { {:symbolic => true} } - it "creates a hard link for :symbolic => false" do - create_link(@hardlink_to, :symbolic => false) - invoke! - destination_path = @hardlink_to - expect(File.exist?(destination_path)).to be true - expect(File.symlink?(destination_path)).to be false + it "creates a symbolic link" do + invoke! + destination_path = File.join(destination_root, "doc/config.rb") + expect(File.exist?(destination_path)).to be true + expect(File.symlink?(destination_path)).to be true + end + end + + context "specifying :symbolic => false" do + let(:config) { {:symbolic => false} } + let(:destination) { @hardlink_to } + + it "creates a hard link" do + invoke! + destination_path = @hardlink_to + expect(File.exist?(destination_path)).to be true + expect(File.symlink?(destination_path)).to be false + end end it "creates a symbolic link by default" do - create_link("doc/config.rb") invoke! destination_path = File.join(destination_root, "doc/config.rb") expect(File.exist?(destination_path)).to be true expect(File.symlink?(destination_path)).to be true end - it "does not create a link if pretending" do - create_link("doc/config.rb", {}, :pretend => true) - invoke! - expect(File.exist?(File.join(destination_root, "doc/config.rb"))).to be false + context "specifying :pretend => true" do + let(:options) { {:pretend => true} } + it "does not create a link" do + invoke! + expect(File.exist?(File.join(destination_root, "doc/config.rb"))).to be false + end end it "shows created status to the user" do - create_link("doc/config.rb") expect(invoke!).to eq(" create doc/config.rb\n") end - it "does not show any information if log status is false" do - silence! - create_link("doc/config.rb") - expect(invoke!).to be_empty + context "specifying :verbose => false" do + let(:config) { {:verbose => false} } + it "does not show any information" do + expect(invoke!).to be_empty + end end end describe "#identical?" do it "returns true if the destination link exists and is identical" do - create_link("doc/config.rb") - expect(@action.identical?).to be false + expect(action.identical?).to be false invoke! - expect(@action.identical?).to be true + expect(action.identical?).to be true + end + + context "with source path relative to destination" do + let(:source) do + destination_path = File.dirname(File.join(destination_root, destination)) + Pathname.new(super()).relative_path_from(Pathname.new(destination_path)).to_s + end + + it "returns true if the destination link exists and is identical" do + expect(action.identical?).to be false + invoke! + expect(action.identical?).to be true + end end end describe "#revoke!" do it "removes the symbolic link of non-existent destination" do - create_link("doc/config.rb") invoke! - File.delete(@tempfile.path) + File.delete(tempfile.path) revoke! - expect(File.symlink?(@action.destination)).to be false + expect(File.symlink?(action.destination)).to be false end end end diff -Nru ruby-thor-0.20.3/spec/actions/directory_spec.rb ruby-thor-1.0.1/spec/actions/directory_spec.rb --- ruby-thor-0.20.3/spec/actions/directory_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/actions/directory_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,3 +1,4 @@ +require "tmpdir" require "helper" require "thor/actions" @@ -25,7 +26,7 @@ def exists_and_identical?(source_path, destination_path) %w(config.rb README).each do |file| - source = File.join(source_root, source_path, file) + source = File.join(source_root, source_path, file) destination = File.join(destination_root, destination_path, file) expect(File.exist?(destination)).to be true @@ -146,6 +147,24 @@ content = invoke!("app{1}") expect(content).to match(%r{create app\{1\}/README}) end + + context "windows temp directories", :if => windows? do + let(:spec_dir) { File.join(@temp_dir, "spec") } + + before(:each) do + @temp_dir = Dir.mktmpdir("thor") + Dir.mkdir(spec_dir) + File.new(File.join(spec_dir, "spec_helper.rb"), "w").close + end + + after(:each) { FileUtils.rm_rf(@temp_dir) } + it "works with windows temp dir" do + invoke! spec_dir, "specs" + file = File.join(destination_root, "specs") + expect(File.exist?(file)).to be true + expect(File.directory?(file)).to be true + end + end end describe "#revoke!" do diff -Nru ruby-thor-0.20.3/spec/actions/file_manipulation_spec.rb ruby-thor-1.0.1/spec/actions/file_manipulation_spec.rb --- ruby-thor-0.20.3/spec/actions/file_manipulation_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/actions/file_manipulation_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -76,6 +76,14 @@ expect(File.stat(original).mode).to eq(File.stat(copy).mode) end + it "copies file from source to default destination and preserves file mode for templated filenames" do + expect(runner).to receive(:filename).and_return("app") + action :copy_file, "preserve/%filename%.sh", :mode => :preserve + original = File.join(source_root, "preserve/%filename%.sh") + copy = File.join(destination_root, "preserve/app.sh") + expect(File.stat(original).mode).to eq(File.stat(copy).mode) + end + it "logs status" do expect(action(:copy_file, "command.thor")).to eq(" create command.thor\n") end @@ -88,7 +96,7 @@ end end - describe "#link_file" do + describe "#link_file", :unless => windows? do it "links file from source to default destination" do action :link_file, "command.thor" exists_and_identical?("command.thor", "command.thor") diff -Nru ruby-thor-0.20.3/spec/actions/inject_into_file_spec.rb ruby-thor-1.0.1/spec/actions/inject_into_file_spec.rb --- ruby-thor-0.20.3/spec/actions/inject_into_file_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/actions/inject_into_file_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -39,6 +39,17 @@ expect(File.read(file)).to eq("__start__\nREADME\nmore content\n__end__\n") end + it "appends content to the file if before and after arguments not provided" do + invoke!("doc/README", "more content\n") + expect(File.read(file)).to eq("__start__\nREADME\n__end__\nmore content\n") + end + + it "does not change the file and logs the warning if flag not found in the file" do + expect(invoke!("doc/README", "more content\n", after: "whatever")).to( + eq("#{Thor::Actions::WARNINGS[:unchanged_no_flag]} doc/README\n") + ) + end + it "accepts data as a block" do invoke! "doc/README", :before => "__end__" do "more content\n" @@ -105,8 +116,15 @@ end it "can insert chinese" do - invoke! "doc/README.zh", "\n中文", :after => "__start__" - expect(File.read(File.join(destination_root, "doc/README.zh"))).to eq("__start__\n中文\n说明\n__end__\n") + encoding_original = Encoding.default_external + + begin + Encoding.default_external = Encoding.find("UTF-8") + invoke! "doc/README.zh", "\n中文", :after => "__start__" + expect(File.read(File.join(destination_root, "doc/README.zh"))).to eq("__start__\n中文\n说明\n__end__\n") + ensure + Encoding.default_external = encoding_original + end end end diff -Nru ruby-thor-0.20.3/spec/actions_spec.rb ruby-thor-1.0.1/spec/actions_spec.rb --- ruby-thor-0.20.3/spec/actions_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/actions_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -291,29 +291,55 @@ end end - describe "aborting on failure" do + describe "when pretending" do + it "doesn't execute the command" do + runner = MyCounter.new([1], %w(--pretend)) + expect(runner).not_to receive(:system) + runner.run("ls", :verbose => false) + end + end + + describe "when not capturing" do it "aborts when abort_on_failure is given and command fails" do expect { action :run, "false", :abort_on_failure => true }.to raise_error(SystemExit) end - it "suceeds when abort_on_failure is given and command succeeds" do + it "succeeds when abort_on_failure is given and command succeeds" do expect { action :run, "true", :abort_on_failure => true }.not_to raise_error end + it "supports env option" do + expect { action :run, "echo $BAR", :env => { "BAR" => "foo" } }.to output("foo\n").to_stdout_from_any_process + end + end + + describe "when capturing" do it "aborts when abort_on_failure is given, capture is given and command fails" do expect { action :run, "false", :abort_on_failure => true, :capture => true }.to raise_error(SystemExit) end - it "suceeds when abort_on_failure is given and command succeeds" do + it "succeeds when abort_on_failure is given and command succeeds" do expect { action :run, "true", :abort_on_failure => true, :capture => true }.not_to raise_error end + + it "supports env option" do + silence(:stdout) do + expect(runner.run "echo $BAR", :env => { "BAR" => "foo" }, :capture => true).to eq("foo\n") + end + end end - describe "when pretending" do - it "doesn't execute the command" do - runner = MyCounter.new([1], %w(--pretend)) - expect(runner).not_to receive(:system) - runner.run("ls", :verbose => false) + context "exit_on_failure? is true" do + before do + allow(MyCounter).to receive(:exit_on_failure?).and_return(true) + end + + it "aborts when command fails even if abort_on_failure is not given" do + expect { action :run, "false" }.to raise_error(SystemExit) + end + + it "does not abort when abort_on_failure is false even if the command fails" do + expect { action :run, "false", :abort_on_failure => false }.not_to raise_error end end end @@ -367,8 +393,8 @@ end it "captures the output when :capture is given" do - expect(runner).to receive(:`).with("thor foo bar") - action(:thor, "foo", "bar", :capture => true) + expect(runner).to receive(:run).with("list", hash_including(:capture => true)) + action :thor, :list, :capture => true end end end diff -Nru ruby-thor-0.20.3/spec/base_spec.rb ruby-thor-1.0.1/spec/base_spec.rb --- ruby-thor-0.20.3/spec/base_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/base_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -49,6 +49,7 @@ it "avoids methods being added as commands" do expect(MyScript.commands.keys).to include("animal") expect(MyScript.commands.keys).not_to include("this_is_not_a_command") + expect(MyScript.commands.keys).not_to include("neither_is_this") end end diff -Nru ruby-thor-0.20.3/spec/command_spec.rb ruby-thor-1.0.1/spec/command_spec.rb --- ruby-thor-0.20.3/spec/command_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/command_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,12 +1,12 @@ require "helper" describe Thor::Command do - def command(options = {}) + def command(options = {}, usage = "can_has") options.each do |key, value| options[key] = Thor::Option.parse(key, value) end - @command ||= Thor::Command.new(:can_has, "I can has cheezburger", "I can has cheezburger\nLots and lots of it", "can_has", options) + @command ||= Thor::Command.new(:can_has, "I can has cheezburger", "I can has cheezburger\nLots and lots of it", usage, options) end describe "#formatted_usage" do @@ -30,6 +30,11 @@ object = Struct.new(:namespace, :arguments).new("foo", [Thor::Argument.new(:bar, options)]) expect(command(:foo => :required).formatted_usage(object)).to eq("foo:can_has BAR --foo=FOO") end + + it "allows multiple usages" do + object = Struct.new(:namespace, :arguments).new("foo", []) + expect(command({ :bar => :required }, ["can_has FOO", "can_has BAR"]).formatted_usage(object, false)).to eq("can_has FOO --bar=BAR\ncan_has BAR --bar=BAR") + end end describe "#dynamic" do diff -Nru ruby-thor-0.20.3/spec/core_ext/ordered_hash_spec.rb ruby-thor-1.0.1/spec/core_ext/ordered_hash_spec.rb --- ruby-thor-0.20.3/spec/core_ext/ordered_hash_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/core_ext/ordered_hash_spec.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,198 +0,0 @@ -require "helper" -require "thor/core_ext/ordered_hash" - -describe Thor::CoreExt::OrderedHash do - subject { Thor::CoreExt::OrderedHash.new } - - def populate_subject - subject[:foo] = "Foo!" - subject[:bar] = "Bar!" - subject[:baz] = "Baz!" - subject[:bop] = "Bop!" - subject[:bat] = "Bat!" - end - - describe "#initialize" do - it "is empty" do - expect(subject).to be_empty - end - end - - describe "#replace" do - before { populate_subject } - it "replaces the keys" do - other_hash = Thor::CoreExt::OrderedHash.new - other_hash[1] = "one" - other_hash[2] = "two" - other_hash[3] = "three" - - subject.replace(other_hash) - expect(subject.keys).to eq [1,2,3] - end - end - - describe "#[]" do - it "returns nil for an undefined key" do - expect(subject[:boom]).to be nil - end - - before { populate_subject } - it "returns the value for each key" do - expect(subject[:foo]).to eq "Foo!" - end - end - - describe "#[]=" do - it "does not duplicate keys" do - subject[:key] = 1 - subject[:key] = 2 - - expect(subject.keys.size).to eq 1 - expect(subject[:key]).to eq 2 - end - - it "does not move an overwritten node to the end of the ordering" do - populate_subject - - subject[:baz] = "Bip!" - expect(subject.values).to eq(["Foo!", "Bar!", "Bip!", "Bop!", "Bat!"]) - - subject[:foo] = "Bip!" - expect(subject.values).to eq(["Bip!", "Bar!", "Bip!", "Bop!", "Bat!"]) - - subject[:bat] = "Bip!" - expect(subject.values).to eq(["Bip!", "Bar!", "Bip!", "Bop!", "Bip!"]) - end - end - - describe "#clear" do - before { populate_subject } - it "clears the keys" do - subject.clear - expect(subject.keys).to be_empty - end - end - - describe "#shift" do - before { populate_subject } - it "pops the first key/value" do - arr = subject.shift - expect(arr).to eq [:foo, "Foo!"] - end - - it "removes the key" do - subject.shift - expect(subject.keys).to_not include(:foo) - end - end - - describe "#each" do - before { populate_subject } - it "iterates through the keys and values in order of assignment" do - arr = [] - subject.each do |key, value| - arr << [key, value] - end - - expect(arr).to eq([[:foo, "Foo!"], [:bar, "Bar!"], [:baz, "Baz!"], - [:bop, "Bop!"], [:bat, "Bat!"]]) - end - end - - describe "#merge!" do - it "modifies the existing object" do - populate_subject - - other_hash = Thor::CoreExt::OrderedHash.new - other_hash[1] = "one" - other_hash[2] = "two" - other_hash[3] = "three" - - subject.merge!(other_hash) - - expect(subject.values).to eq(["Foo!", "Bar!", "Baz!", "Bop!", "Bat!", "one", "two", "three"]) - end - end - - describe "#merge" do - it "appends another ordered hash while preserving ordering" do - populate_subject - - other_hash = Thor::CoreExt::OrderedHash.new - other_hash[1] = "one" - other_hash[2] = "two" - other_hash[3] = "three" - - - merged_list = subject.merge(other_hash) - expect(merged_list.values).to eq(["Foo!", "Bar!", "Baz!", "Bop!", "Bat!", "one", "two", "three"]) - end - - it "overwrites hash keys with matching appended keys" do - populate_subject - - other_hash = Thor::CoreExt::OrderedHash.new - other_hash[:bar] = "bar" - - expect(subject.merge(other_hash)[:bar]).to eq("bar") - expect(subject[:bar]).to eq("Bar!") - end - end - - describe "#to_a" do - before { populate_subject } - it "converts to an array" do - expect(subject.to_a).to eq([[:foo, "Foo!"], [:bar, "Bar!"], [:baz, "Baz!"], [:bop, "Bop!"], [:bat, "Bat!"]]) - end - end - - describe "#keys" do - context "when list is unpopulated" do - it "has an empty keys list" do - expect(subject.keys).to be_empty - end - end - - it "returns the keys in order of insertion" do - populate_subject - expect(subject.keys).to eq([:foo, :bar, :baz, :bop, :bat]) - end - end - - describe "#values" do - it "returns the values in order of insertion" do - populate_subject - expect(subject.values).to eq(["Foo!", "Bar!", "Baz!", "Bop!", "Bat!"]) - end - - context "when list is unpopulated" do - it "has an empty list" do - list = described_class.new - expect(list.values).to be_empty - end - end - end - - describe "#delete" do - before { populate_subject } - it "deletes the value given the key" do - expect(subject.delete(:baz)).to eq("Baz!") - expect(subject.values).to eq(["Foo!", "Bar!", "Bop!", "Bat!"]) - - expect(subject.delete(:foo)).to eq("Foo!") - expect(subject.values).to eq(["Bar!", "Bop!", "Bat!"]) - - expect(subject.delete(:bat)).to eq("Bat!") - expect(subject.values).to eq(["Bar!", "Bop!"]) - end - - it "returns nil if the value to be deleted can't be found" do - expect(subject.delete(:nothing)).to be nil - end - - it "deletes the given key" do - subject.delete(:baz) - expect(subject.keys).to_not include(:baz) - end - end -end diff -Nru ruby-thor-0.20.3/spec/fixtures/command.thor ruby-thor-1.0.1/spec/fixtures/command.thor --- ruby-thor-0.20.3/spec/fixtures/command.thor 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/fixtures/command.thor 2019-12-17 15:12:43.000000000 +0000 @@ -1,6 +1,10 @@ # module: random class Amazing < Thor + def self.exit_on_failure? + false + end + desc "describe NAME", "say that someone is amazing" method_options :forcefully => :boolean def describe(name, opts) diff -Nru ruby-thor-0.20.3/spec/fixtures/group.thor ruby-thor-1.0.1/spec/fixtures/group.thor --- ruby-thor-0.20.3/spec/fixtures/group.thor 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/fixtures/group.thor 2019-12-17 15:12:43.000000000 +0000 @@ -2,6 +2,10 @@ include Thor::Actions add_runtime_options! + def self.exit_on_failure? + false + end + def self.get_from_super from_superclass(:get_from_super, 13) end diff -Nru ruby-thor-0.20.3/spec/fixtures/preserve/%filename%.sh ruby-thor-1.0.1/spec/fixtures/preserve/%filename%.sh --- ruby-thor-0.20.3/spec/fixtures/preserve/%filename%.sh 1970-01-01 00:00:00.000000000 +0000 +++ ruby-thor-1.0.1/spec/fixtures/preserve/%filename%.sh 2019-12-17 15:12:43.000000000 +0000 @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff -Nru ruby-thor-0.20.3/spec/fixtures/script.thor ruby-thor-1.0.1/spec/fixtures/script.thor --- ruby-thor-0.20.3/spec/fixtures/script.thor 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/fixtures/script.thor 2019-12-17 15:12:43.000000000 +0000 @@ -1,6 +1,10 @@ class MyScript < Thor check_unknown_options! :except => :with_optional + def self.exit_on_failure? + false + end + attr_accessor :some_attribute attr_writer :another_attribute attr_reader :another_attribute @@ -24,7 +28,12 @@ desc "animal TYPE", "horse around" no_commands do - def this_is_not_a_command + no_commands do + def this_is_not_a_command + end + end + + def neither_is_this end end @@ -51,6 +60,12 @@ [bar, options] end + method_option :all, :desc => "Do bazing for all the things" + desc ["baz THING", "baz --all"], "super cool" + def baz(thing = nil) + raise if thing.nil? && !options.include?(:all) + end + desc "example_default_command", "example!" method_options :with => :string def example_default_command @@ -146,6 +161,10 @@ end class Barn < Thor + def self.exit_on_failure? + false + end + desc "open [ITEM]", "open the barn door" def open(item = nil) if item == "shotgun" @@ -180,6 +199,10 @@ class MyDefaults < Thor check_unknown_options! + def self.exit_on_failure? + false + end + namespace :default desc "cow", "prints 'moo'" def cow @@ -200,6 +223,10 @@ end class Arities < Thor + def self.exit_on_failure? + false + end + desc "zero_args", "takes zero args" def zero_args end @@ -215,6 +242,10 @@ desc "optional_arg [ARG]", "takes an optional arg" def optional_arg(arg='default') end + + desc ["multiple_usages ARG --foo", "multiple_usages ARG --bar"], "takes mutually exclusive combinations of args and flags" + def multiple_usages(arg) + end end end diff -Nru ruby-thor-0.20.3/spec/fixtures/verbose.thor ruby-thor-1.0.1/spec/fixtures/verbose.thor --- ruby-thor-0.20.3/spec/fixtures/verbose.thor 1970-01-01 00:00:00.000000000 +0000 +++ ruby-thor-1.0.1/spec/fixtures/verbose.thor 2019-12-17 15:12:43.000000000 +0000 @@ -0,0 +1,10 @@ +#!/usr/bin/ruby + +$VERBOSE = true + +require 'thor' + +class Test < Thor +end + +Test.start(ARGV) diff -Nru ruby-thor-0.20.3/spec/helper.rb ruby-thor-1.0.1/spec/helper.rb --- ruby-thor-0.20.3/spec/helper.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/helper.rb 2019-12-17 15:12:43.000000000 +0000 @@ -1,15 +1,13 @@ $TESTING = true -if RUBY_VERSION >= "1.9" - require "simplecov" - require "coveralls" - - SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] - - SimpleCov.start do - add_filter "/spec" - minimum_coverage(90) - end +require "simplecov" +require "coveralls" + +SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] + +SimpleCov.start do + add_filter "/spec" + minimum_coverage(90) end $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) @@ -25,10 +23,10 @@ WebMock.disable_net_connect!(:allow => "coveralls.io") # Set shell to basic +ENV["THOR_COLUMNS"] = "10000" $0 = "thor" $thor_runner = true ARGV.clear -Encoding.default_external = Encoding::UTF_8 if RUBY_VERSION > '1.8.7' && RUBY_VERSION < '2.0.0' Thor::Base.shell = Thor::Shell::Basic # Load fixtures @@ -79,5 +77,12 @@ $VERBOSE = old_verbose end + # true if running on windows, used for conditional spec skips + # + # @return [TrueClass/FalseClass] + def windows? + Gem.win_platform? + end + alias silence capture end diff -Nru ruby-thor-0.20.3/spec/invocation_spec.rb ruby-thor-1.0.1/spec/invocation_spec.rb --- ruby-thor-0.20.3/spec/invocation_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/invocation_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -61,13 +61,7 @@ it "returns the command chain" do expect(I.new.invoke("two")).to eq([:two]) - if RUBY_VERSION < "1.9.3" - result = J.start(%w(one two)) - expect(result).to include(:one) - expect(result).to include(:two) - else - expect(J.start(%w(one two))).to eq([:one, :two]) - end + expect(J.start(%w(one two))).to eq([:one, :two]) end it "dump configuration values to be used in the invoked class" do diff -Nru ruby-thor-0.20.3/spec/line_editor/readline_spec.rb ruby-thor-1.0.1/spec/line_editor/readline_spec.rb --- ruby-thor-0.20.3/spec/line_editor/readline_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/line_editor/readline_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -23,14 +23,14 @@ describe "#readline" do it "invokes the readline library" do expect(::Readline).to receive(:readline).with("> ", true).and_return("foo") - expect(::Readline).not_to receive(:completion_proc=) + expect(::Readline).to_not receive(:completion_proc=) editor = Thor::LineEditor::Readline.new("> ", {}) expect(editor.readline).to eq("foo") end it "supports the add_to_history option" do expect(::Readline).to receive(:readline).with("> ", false).and_return("foo") - expect(::Readline).not_to receive(:completion_proc=) + expect(::Readline).to_not receive(:completion_proc=) editor = Thor::LineEditor::Readline.new("> ", :add_to_history => false) expect(editor.readline).to eq("foo") end diff -Nru ruby-thor-0.20.3/spec/nested_context_spec.rb ruby-thor-1.0.1/spec/nested_context_spec.rb --- ruby-thor-0.20.3/spec/nested_context_spec.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-thor-1.0.1/spec/nested_context_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -0,0 +1,20 @@ +require "helper" + +describe Thor::NestedContext do + subject(:context) { described_class.new } + + describe "#enter" do + it "is never empty within the entered block" do + context.enter do + context.enter {} + + expect(context).to be_entered + end + end + + it "is empty when outside of all blocks" do + context.enter { context.enter {} } + expect(context).not_to be_entered + end + end +end \ No newline at end of file diff -Nru ruby-thor-0.20.3/spec/no_warnings_spec.rb ruby-thor-1.0.1/spec/no_warnings_spec.rb --- ruby-thor-0.20.3/spec/no_warnings_spec.rb 1970-01-01 00:00:00.000000000 +0000 +++ ruby-thor-1.0.1/spec/no_warnings_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -0,0 +1,10 @@ +require "open3" + +context "when $VERBOSE is enabled" do + it "prints no warnings" do + root = File.expand_path("..", __dir__) + _, err, _ = Open3.capture3("ruby -I #{root}/lib #{root}/spec/fixtures/verbose.thor") + + expect(err).to be_empty + end +end diff -Nru ruby-thor-0.20.3/spec/parser/arguments_spec.rb ruby-thor-1.0.1/spec/parser/arguments_spec.rb --- ruby-thor-0.20.3/spec/parser/arguments_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/parser/arguments_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -40,6 +40,12 @@ expect(parse("product", "title", "age")["array"]).to eq(%w(title age)) end + it "accepts - as an array argument" do + create :array => nil + expect(parse("-")["array"]).to eq(%w(-)) + expect(parse("-", "title", "-")["array"]).to eq(%w(- title -)) + end + describe "with no inputs" do it "and no arguments returns an empty hash" do create diff -Nru ruby-thor-0.20.3/spec/parser/option_spec.rb ruby-thor-1.0.1/spec/parser/option_spec.rb --- ruby-thor-0.20.3/spec/parser/option_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/parser/option_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -140,6 +140,30 @@ end.to raise_error(ArgumentError, 'Expected numeric default value for \'--foo-bar\'; got "baz" (string)') end + it "raises an error if repeatable and default is inconsistent with type and check_default_type is true" do + expect do + option("foo_bar", :type => :numeric, :repeatable => true, :default => "baz", :check_default_type => true) + end.to raise_error(ArgumentError, 'Expected array default value for \'--foo-bar\'; got "baz" (string)') + end + + it "raises an error type hash is repeatable and default is inconsistent with type and check_default_type is true" do + expect do + option("foo_bar", :type => :hash, :repeatable => true, :default => "baz", :check_default_type => true) + end.to raise_error(ArgumentError, 'Expected hash default value for \'--foo-bar\'; got "baz" (string)') + end + + it "does not raises an error if type hash is repeatable and default is consistent with type and check_default_type is true" do + expect do + option("foo_bar", :type => :hash, :repeatable => true, :default => {}, :check_default_type => true) + end.not_to raise_error + end + + it "does not raises an error if repeatable and default is consistent with type and check_default_type is true" do + expect do + option("foo_bar", :type => :numeric, :repeatable => true, :default => [1], :check_default_type => true) + end.not_to raise_error + end + it "does not raises an error if default is an symbol and type string and check_default_type is true" do expect do option("foo", :type => :string, :default => :bar, :check_default_type => true) diff -Nru ruby-thor-0.20.3/spec/parser/options_spec.rb ruby-thor-1.0.1/spec/parser/options_spec.rb --- ruby-thor-0.20.3/spec/parser/options_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/parser/options_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -6,7 +6,6 @@ opts.each do |key, value| opts[key] = Thor::Option.parse(key, value) unless value.is_a?(Thor::Option) end - @opt = Thor::Options.new(opts, defaults, stop_on_unknown) end @@ -302,6 +301,13 @@ expect { parse("--fruit", "orange") }.to raise_error(Thor::MalformattedArgumentError, "Expected '--fruit' to be one of #{enum.join(', ')}; got orange") end + + it "allows multiple values if repeatable is specified" do + create :foo => Thor::Option.new("foo", :type => :string, :repeatable => true) + + expect(parse("--foo=bar", "--foo", "12")["foo"]).to eq(["bar", "12"]) + expect(parse("--foo", "13", "--foo", "14")["foo"]).to eq(["bar", "12", "13", "14"]) + end end describe "with :boolean type" do @@ -331,6 +337,10 @@ expect(parse("--skip-foo")["foo"]).to eq(false) end + it "accepts --[skip-]opt variant, setting false for value, even if there's a trailing non-switch" do + expect(parse("--skip-foo", "asdf")["foo"]).to eq(false) + end + it "will prefer 'no-opt' variant over inverting 'opt' if explicitly set" do create "--no-foo" => true expect(parse("--no-foo")["no-foo"]).to eq(true) @@ -341,6 +351,19 @@ expect(parse("--skip-foo")["skip-foo"]).to eq(true) end + it "will prefer 'skip-opt' variant over inverting 'opt' if explicitly set, even if there's a trailing non-switch" do + create "--skip-foo" => true + expect(parse("--skip-foo", "asdf")["skip-foo"]).to eq(true) + end + + it "will prefer 'skip-opt' variant over inverting 'opt' if explicitly set, and given a value" do + create "--skip-foo" => true + expect(parse("--skip-foo=f")["skip-foo"]).to eq(false) + expect(parse("--skip-foo=false")["skip-foo"]).to eq(false) + expect(parse("--skip-foo=t")["skip-foo"]).to eq(true) + expect(parse("--skip-foo=true")["skip-foo"]).to eq(true) + end + it "accepts inputs in the human name format" do create :foo_bar => :boolean expect(parse("--foo-bar")["foo_bar"]).to eq(true) @@ -362,6 +385,11 @@ expect(parse("--skip-foo", "bar")).to eq("foo" => false) expect(@opt.remaining).to eq(%w(bar)) end + + it "allows multiple values if repeatable is specified" do + create :verbose => Thor::Option.new("verbose", :type => :boolean, :aliases => '-v', :repeatable => true) + expect(parse("-v", "-v", "-v")["verbose"].count).to eq(3) + end end describe "with :hash type" do @@ -384,6 +412,11 @@ it "must not allow the same hash key to be specified multiple times" do expect { parse("--attributes", "name:string", "name:integer") }.to raise_error(Thor::MalformattedArgumentError, "You can't specify 'name' more than once in option '--attributes'; got name:string and name:integer") end + + it "allows multiple values if repeatable is specified" do + create :attributes => Thor::Option.new("attributes", :type => :hash, :repeatable => true) + expect(parse("--attributes", "name:one", "foo:1", "--attributes", "name:two", "bar:2")["attributes"]).to eq({"name"=>"two", "foo"=>"1", "bar" => "2"}) + end end describe "with :array type" do @@ -402,6 +435,11 @@ it "must not mix values with other switches" do expect(parse("--attributes", "a", "b", "c", "--baz", "cool")["attributes"]).to eq(%w(a b c)) end + + it "allows multiple values if repeatable is specified" do + create :attributes => Thor::Option.new("attributes", :type => :array, :repeatable => true) + expect(parse("--attributes", "1", "2", "--attributes", "3", "4")["attributes"]).to eq([["1", "2"], ["3", "4"]]) + end end describe "with :numeric type" do @@ -428,6 +466,11 @@ expect { parse("--limit", "3") }.to raise_error(Thor::MalformattedArgumentError, "Expected '--limit' to be one of #{enum.join(', ')}; got 3") end + + it "allows multiple values if repeatable is specified" do + create :run => Thor::Option.new("run", :type => :numeric, :repeatable => true) + expect(parse("--run", "1", "--run", "2")["run"]).to eq([1, 2]) + end end end end diff -Nru ruby-thor-0.20.3/spec/register_spec.rb ruby-thor-1.0.1/spec/register_spec.rb --- ruby-thor-0.20.3/spec/register_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/register_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -47,7 +47,7 @@ :default => "zebra" end -class CompatibleWith19Plugin < ClassOptionGroupPlugin +class PluginInheritingFromClassOptionsGroup < ClassOptionGroupPlugin desc "animal" def animal p options[:who] @@ -119,7 +119,7 @@ ) BoringVendorProvidedCLI.register( - CompatibleWith19Plugin, + PluginInheritingFromClassOptionsGroup, "zoo", "zoo [-w animal]", "Shows a provided animal or just zebra" @@ -221,13 +221,13 @@ end end -describe "1.8 and 1.9 syntax compatibility" do - it "is compatible with both 1.8 and 1.9 syntax w/o command options" do +describe ".register-ing a Thor::Group subclass with class options" do + it "works w/o command options" do group_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w(zoo)) } expect(group_output).to match(/zebra/) end - it "is compatible with both 1.8 and 1.9 syntax w/command options" do + it "works w/command options" do group_output = capture(:stdout) { BoringVendorProvidedCLI.start(%w(zoo -w lion)) } expect(group_output).to match(/lion/) end diff -Nru ruby-thor-0.20.3/spec/script_exit_status_spec.rb ruby-thor-1.0.1/spec/script_exit_status_spec.rb --- ruby-thor-0.20.3/spec/script_exit_status_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/script_exit_status_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -26,4 +26,4 @@ it "a command that does not raise a Thor::Error exits with a status of 0" do expect(thor_command("ok")).to eq(0) end -end if RUBY_VERSION > "1.8.7" +end diff -Nru ruby-thor-0.20.3/spec/shell/basic_spec.rb ruby-thor-1.0.1/spec/shell/basic_spec.rb --- ruby-thor-0.20.3/spec/shell/basic_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/shell/basic_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -68,19 +68,39 @@ expect(shell.ask("What's your password?", :echo => false)).to eq("mysecretpass") end - it "prints a message to the user with the available options and determines the correctness of the answer" do + it "prints a message to the user with the available options, expects case-sensitive matching, and determines the correctness of the answer" do flavors = %w(strawberry chocolate vanilla) expect(Thor::LineEditor).to receive(:readline).with('What\'s your favorite Neopolitan flavor? [strawberry, chocolate, vanilla] ', :limited_to => flavors).and_return("chocolate") expect(shell.ask('What\'s your favorite Neopolitan flavor?', :limited_to => flavors)).to eq("chocolate") end - it "prints a message to the user with the available options and reasks the question after an incorrect response" do + it "prints a message to the user with the available options, expects case-sensitive matching, and reasks the question after an incorrect response" do flavors = %w(strawberry chocolate vanilla) expect($stdout).to receive(:print).with("Your response must be one of: [strawberry, chocolate, vanilla]. Please try again.\n") expect(Thor::LineEditor).to receive(:readline).with('What\'s your favorite Neopolitan flavor? [strawberry, chocolate, vanilla] ', :limited_to => flavors).and_return("moose tracks", "chocolate") expect(shell.ask('What\'s your favorite Neopolitan flavor?', :limited_to => flavors)).to eq("chocolate") end + it "prints a message to the user with the available options, expects case-sensitive matching, and reasks the question after a case-insensitive match" do + flavors = %w(strawberry chocolate vanilla) + expect($stdout).to receive(:print).with("Your response must be one of: [strawberry, chocolate, vanilla]. Please try again.\n") + expect(Thor::LineEditor).to receive(:readline).with('What\'s your favorite Neopolitan flavor? [strawberry, chocolate, vanilla] ', :limited_to => flavors).and_return("cHoCoLaTe", "chocolate") + expect(shell.ask('What\'s your favorite Neopolitan flavor?', :limited_to => flavors)).to eq("chocolate") + end + + it "prints a message to the user with the available options, expects case-insensitive matching, and determines the correctness of the answer" do + flavors = %w(strawberry chocolate vanilla) + expect(Thor::LineEditor).to receive(:readline).with('What\'s your favorite Neopolitan flavor? [strawberry, chocolate, vanilla] ', :limited_to => flavors, :case_insensitive => true).and_return("CHOCOLATE") + expect(shell.ask('What\'s your favorite Neopolitan flavor?', :limited_to => flavors, :case_insensitive => true)).to eq("chocolate") + end + + it "prints a message to the user with the available options, expects case-insensitive matching, and reasks the question after an incorrect response" do + flavors = %w(strawberry chocolate vanilla) + expect($stdout).to receive(:print).with("Your response must be one of: [strawberry, chocolate, vanilla]. Please try again.\n") + expect(Thor::LineEditor).to receive(:readline).with('What\'s your favorite Neopolitan flavor? [strawberry, chocolate, vanilla] ', :limited_to => flavors, :case_insensitive => true).and_return("moose tracks", "chocolate") + expect(shell.ask('What\'s your favorite Neopolitan flavor?', :limited_to => flavors, :case_insensitive => true)).to eq("chocolate") + end + it "prints a message to the user containing a default and sets the default if only enter is pressed" do expect(Thor::LineEditor).to receive(:readline).with('What\'s your favorite Neopolitan flavor? (vanilla) ', :default => "vanilla").and_return("") expect(shell.ask('What\'s your favorite Neopolitan flavor?', :default => "vanilla")).to eq("vanilla") diff -Nru ruby-thor-0.20.3/spec/shell/color_spec.rb ruby-thor-1.0.1/spec/shell/color_spec.rb --- ruby-thor-0.20.3/spec/shell/color_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/shell/color_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -19,6 +19,15 @@ shell.ask "Is this green?", :green, :limited_to => %w(Yes No Maybe) end + it "does not set the color if specified and NO_COLOR is set" do + allow(ENV).to receive(:[]).with("NO_COLOR").and_return("") + expect(Thor::LineEditor).to receive(:readline).with("Is this green? ", anything).and_return("yes") + shell.ask "Is this green?", :green + + expect(Thor::LineEditor).to receive(:readline).with("Is this green? [Yes, No, Maybe] ", anything).and_return("Yes") + shell.ask "Is this green?", :green, :limited_to => %w(Yes No Maybe) + end + it "handles an Array of colors" do expect(Thor::LineEditor).to receive(:readline).with("\e[32m\e[47m\e[1mIs this green on white? \e[0m", anything).and_return("yes") shell.ask "Is this green on white?", [:green, :on_white, :bold] @@ -48,6 +57,15 @@ expect(out.chomp).to eq("Wow! Now we have colors!") end + it "does not set the color if NO_COLOR is set" do + allow(ENV).to receive(:[]).with("NO_COLOR").and_return("") + out = capture(:stdout) do + shell.say "Wow! Now we have colors!", :green + end + + expect(out.chomp).to eq("Wow! Now we have colors!") + end + it "does not use a new line even with colors" do out = capture(:stdout) do shell.say "Wow! Now we have colors! ", :green @@ -118,6 +136,13 @@ colorless = shell.set_color "hi!", :white expect(colorless).to eq("hi!") end + + it "does nothing when the terminal has the NO_COLOR environment variable set" do + allow(ENV).to receive(:[]).with("NO_COLOR").and_return("") + allow($stdout).to receive(:tty?).and_return(true) + colorless = shell.set_color "hi!", :white + expect(colorless).to eq("hi!") + end end describe "#file_collision" do diff -Nru ruby-thor-0.20.3/spec/shell/html_spec.rb ruby-thor-1.0.1/spec/shell/html_spec.rb --- ruby-thor-0.20.3/spec/shell/html_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/shell/html_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -28,4 +28,14 @@ shell.say_status :conflict, "README", :red end end + + describe "#set_color" do + it "escapes HTML content when unsing the default colors" do + expect(shell.set_color("", :blue)).to eq "<htmlcontent>" + end + + it "escapes HTML content when not using the default colors" do + expect(shell.set_color("", [:nocolor])).to eq "<htmlcontent>" + end + end end diff -Nru ruby-thor-0.20.3/spec/subcommand_spec.rb ruby-thor-1.0.1/spec/subcommand_spec.rb --- ruby-thor-0.20.3/spec/subcommand_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/subcommand_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -55,6 +55,10 @@ class Parent < Thor desc "child1", "child1 description" subcommand "child1", Child1 + + def self.exit_on_failure? + false + end end end diff -Nru ruby-thor-0.20.3/spec/thor_spec.rb ruby-thor-1.0.1/spec/thor_spec.rb --- ruby-thor-0.20.3/spec/thor_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/thor_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -173,6 +173,10 @@ def exec(*args) [options, args] end + + def self.exit_on_failure? + false + end end it "passes remaining args to command when it encounters a non-option" do @@ -223,6 +227,10 @@ def checked(*args) [options, args] end + + def self.exit_on_failure? + false + end end it "still accept options and arguments" do @@ -285,6 +293,10 @@ def boring(*args) [options, args] end + + def self.exit_on_failure? + false + end end it "does not check the required option in the given command" do @@ -440,6 +452,9 @@ Usage: "thor scripts:arities:two_args ARG1 ARG2"' arity_asserter.call %w(optional_arg one two), 'ERROR: "thor optional_arg" was called with arguments ["one", "two"] Usage: "thor scripts:arities:optional_arg [ARG]"' + arity_asserter.call %w(multiple_usages), 'ERROR: "thor multiple_usages" was called with no arguments +Usage: "thor scripts:arities:multiple_usages ARG --foo" + "thor scripts:arities:multiple_usages ARG --bar"' end it "raises an error if the invoked command does not exist" do @@ -552,6 +567,19 @@ END end + it "provides full help info when talking about a specific command with multiple usages" do + expect(capture(:stdout) { MyScript.command_help(shell, "baz") }).to eq(<<-END) +Usage: + thor my_script:baz THING + thor my_script:baz --all + +Options: + [--all=ALL] # Do bazing for all the things + +super cool +END + end + it "raises an error if the command can't be found" do expect do MyScript.command_help(shell, "unknown") @@ -694,12 +722,30 @@ expect(klass.start(%w(unknown foo --bar baz))).to eq(%w(foo)) end - it "does not check the default type when check_default_type! is not called" do + it "issues a deprecation warning on incompatible types by default" do + expect do + Class.new(Thor) do + option "bar", :type => :numeric, :default => "foo" + end + end.to output(/^Deprecation warning/).to_stderr + end + + it "allows incompatible types if allow_incompatible_default_type! is called" do expect do Class.new(Thor) do + allow_incompatible_default_type! + option "bar", :type => :numeric, :default => "foo" end - end.not_to raise_error + end.not_to output.to_stderr + end + + it "allows incompatible types if `check_default_type: false` is given" do + expect do + Class.new(Thor) do + option "bar", :type => :numeric, :default => "foo", :check_default_type => false + end + end.not_to output.to_stderr end it "checks the default type when check_default_type! is called" do @@ -716,4 +762,19 @@ expect(MyScript.start(%w(send))).to eq(true) end end + + context "without an exit_on_failure? method" do + my_script = Class.new(Thor) do + desc "no arg", "do nothing" + def no_arg + end + end + + it "outputs a deprecation warning on error" do + expect do + my_script.start(%w[no_arg one]) + end.to output(/^Deprecation.*exit_on_failure/).to_stderr + end + end + end diff -Nru ruby-thor-0.20.3/spec/util_spec.rb ruby-thor-1.0.1/spec/util_spec.rb --- ruby-thor-0.20.3/spec/util_spec.rb 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/spec/util_spec.rb 2019-12-17 15:12:43.000000000 +0000 @@ -17,7 +17,7 @@ end it "returns nil if the namespace can't be found" do - expect(Thor::Util.find_by_namespace("thor:core_ext:ordered_hash")).to be nil + expect(Thor::Util.find_by_namespace("thor:core_ext:hash_with_indifferent_access")).to be nil end it "returns a class if it matches the namespace" do @@ -39,7 +39,7 @@ end it "accepts class and module objects" do - expect(Thor::Util.namespace_from_thor_class(Thor::CoreExt::OrderedHash)).to eq("thor:core_ext:ordered_hash") + expect(Thor::Util.namespace_from_thor_class(Thor::CoreExt::HashWithIndifferentAccess)).to eq("thor:core_ext:hash_with_indifferent_access") expect(Thor::Util.namespace_from_thor_class(Thor::Util)).to eq("thor:util") end diff -Nru ruby-thor-0.20.3/thor.gemspec ruby-thor-1.0.1/thor.gemspec --- ruby-thor-0.20.3/thor.gemspec 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/thor.gemspec 2019-12-17 15:12:43.000000000 +0000 @@ -4,7 +4,7 @@ require "thor/version" Gem::Specification.new do |spec| - spec.add_development_dependency "bundler", "~> 1.0" + spec.add_development_dependency "bundler", ">= 1.0", "< 3" spec.authors = ["Yehuda Katz", "José Valim"] spec.description = "Thor is a toolkit for building powerful command-line interfaces." spec.email = "ruby-thor@googlegroups.com" @@ -13,8 +13,15 @@ spec.homepage = "http://whatisthor.com/" spec.licenses = %w(MIT) spec.name = "thor" + spec.metadata = { + "bug_tracker_uri" => "https://github.com/erikhuda/thor/issues", + "changelog_uri" => "https://github.com/erikhuda/thor/blob/master/CHANGELOG.md", + "documentation_uri" => "http://whatisthor.com/", + "source_code_uri" => "https://github.com/erikhuda/thor/tree/v#{Thor::VERSION}", + "wiki_uri" => "https://github.com/erikhuda/thor/wiki" + } spec.require_paths = %w(lib) - spec.required_ruby_version = ">= 1.8.7" + spec.required_ruby_version = ">= 2.0.0" spec.required_rubygems_version = ">= 1.3.5" spec.summary = spec.description spec.version = Thor::VERSION diff -Nru ruby-thor-0.20.3/.travis.yml ruby-thor-1.0.1/.travis.yml --- ruby-thor-0.20.3/.travis.yml 2018-11-10 02:56:58.000000000 +0000 +++ ruby-thor-1.0.1/.travis.yml 2019-12-17 15:12:43.000000000 +0000 @@ -1,23 +1,21 @@ before_install: - - gem update --system - - gem install bundler + - gem update --system 2.7.9 + - gem install bundler:1.17.3 - rvm @global do gem uninstall did_you_mean bundler_args: --without development language: ruby rvm: - - 1.8.7 - - 1.9.3 - 2.0.0 - 2.1.10 - 2.2.10 - - 2.3.7 - - 2.4.4 - - 2.5.1 + - 2.3.8 + - 2.4.7 + - 2.5.6 + - 2.6.4 - ruby-head matrix: allow_failures: - rvm: ruby-head fast_finish: true script: bundle exec thor spec -sudo: false cache: bundler