From 439a5930804a05b1079ea33b7e862dbe2dff291b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Fri, 3 Jun 2022 13:41:09 +0200 Subject: [PATCH] Support `thor install ` to install remote thor files The previous code suggested that this was supported, but it was not really working as expected. --- lib/thor/runner.rb | 45 ++++++++++++++++++------------- spec/runner_spec.rb | 65 ++++++++++++++++++++++++++++++--------------- 2 files changed, 70 insertions(+), 40 deletions(-) diff --git a/lib/thor/runner.rb b/lib/thor/runner.rb index 971e59cc..441ab7df 100644 --- a/lib/thor/runner.rb +++ b/lib/thor/runner.rb @@ -6,8 +6,6 @@ require "pathname" class Thor::Runner < Thor #:nodoc: - autoload :OpenURI, "open-uri" - map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version def self.banner(command, all = false, subcommand = false) @@ -48,22 +46,33 @@ def method_missing(meth, *args) def install(name) # rubocop:disable Metrics/MethodLength initialize_thorfiles - # If a directory name is provided as the argument, look for a 'main.thor' - # command in said directory. - begin - if File.directory?(File.expand_path(name)) - base = File.join(name, "main.thor") - package = :directory - contents = open(base, &:read) - else - base = name - package = :file - contents = open(name, &:read) + is_uri = name =~ %r{^https?\://} + + if is_uri + base = name + package = :file + require "open-uri" + begin + contents = URI.send(:open, name, &:read) # Using `send` for Ruby 2.4- support + rescue OpenURI::HTTPError + raise Error, "Error opening URI '#{name}'" + end + else + # If a directory name is provided as the argument, look for a 'main.thor' + # command in said directory. + begin + if File.directory?(File.expand_path(name)) + base = File.join(name, "main.thor") + package = :directory + contents = open(base, &:read) + else + base = name + package = :file + contents = open(name, &:read) + end + rescue Errno::ENOENT + raise Error, "Error opening file '#{name}'" end - rescue OpenURI::HTTPError - raise Error, "Error opening URI '#{name}'" - rescue Errno::ENOENT - raise Error, "Error opening file '#{name}'" end say "Your Thorfile contains:" @@ -84,7 +93,7 @@ def install(name) # rubocop:disable Metrics/MethodLength as = basename if as.empty? end - location = if options[:relative] || name =~ %r{^https?://} + location = if options[:relative] || is_uri name else File.expand_path(name) diff --git a/spec/runner_spec.rb b/spec/runner_spec.rb index 52748fcc..0edbe2f1 100644 --- a/spec/runner_spec.rb +++ b/spec/runner_spec.rb @@ -118,11 +118,11 @@ def when_no_thorfiles_exist end describe "commands" do + let(:location) { "#{File.dirname(__FILE__)}/fixtures/command.thor" } before do - @location = "#{File.dirname(__FILE__)}/fixtures/command.thor" @original_yaml = { "random" => { - :location => @location, + :location => location, :filename => "4a33b894ffce85d7b412fc1b36f88fe0", :namespaces => %w(amazing) } @@ -214,31 +214,52 @@ def when_no_thorfiles_exist end describe "install/update" do - before do - allow(FileUtils).to receive(:mkdir_p) - allow(FileUtils).to receive(:touch) - allow(Thor::LineEditor).to receive(:readline).and_return("Y") - - path = File.join(Thor::Util.thor_root, Digest::SHA256.hexdigest(@location + "random")) - expect(File).to receive(:open).with(path, "w") - end + context "with local thor files" do + before do + allow(FileUtils).to receive(:mkdir_p) + allow(FileUtils).to receive(:touch) + allow(Thor::LineEditor).to receive(:readline).and_return("Y") + + path = File.join(Thor::Util.thor_root, Digest::SHA256.hexdigest(location + "random")) + expect(File).to receive(:open).with(path, "w") + end - it "updates existing thor files" do - path = File.join(Thor::Util.thor_root, @original_yaml["random"][:filename]) - if File.directory? path - expect(FileUtils).to receive(:rm_rf).with(path) - else - expect(File).to receive(:delete).with(path) + it "updates existing thor files" do + path = File.join(Thor::Util.thor_root, @original_yaml["random"][:filename]) + if File.directory? path + expect(FileUtils).to receive(:rm_rf).with(path) + else + expect(File).to receive(:delete).with(path) + end + silence_warnings do + silence(:stdout) { Thor::Runner.start(%w(update random)) } + end end - silence_warnings do - silence(:stdout) { Thor::Runner.start(%w(update random)) } + + it "installs thor files" do + ARGV.replace %W(install #{location}) + silence_warnings do + silence(:stdout) { Thor::Runner.start } + end end end - it "installs thor files" do - ARGV.replace %W(install #{@location}) - silence_warnings do - silence(:stdout) { Thor::Runner.start } + context "with remote thor files" do + let(:location) { "https://example.com/Thorfile" } + + it "installs thor files" do + allow(Thor::LineEditor).to receive(:readline).and_return("Y", "random") + stub_request(:get, location).to_return(:body => "class Foo < Thor; end") + path = File.join(Thor::Util.thor_root, Digest::SHA256.hexdigest(location + "random")) + expect(File).to receive(:open).with(path, "w") + expect { silence(:stdout) { Thor::Runner.start(%W(install #{location})) } }.not_to raise_error + end + + it "shows proper errors" do + expect(Thor::Runner).to receive :exit + expect(URI).to receive(:open).with(location).and_raise(OpenURI::HTTPError.new("foo", StringIO.new)) + content = capture(:stderr) { Thor::Runner.start(%W(install #{location})) } + expect(content).to include("Error opening URI '#{location}'") end end end