From dab1cd6ddb48d06b9e6995038769f497b9d99fa8 Mon Sep 17 00:00:00 2001 From: schneems Date: Wed, 3 Nov 2021 20:01:06 -0500 Subject: [PATCH] Fix CLI edge case around position of arguments The DeadEnd vscode extension relies on calling `dead_end --terminal ` which was previously possible because calling `OptionParser#parse` mutates ARGV. The filename should be the first argument, but only after parsing, not before. --- CHANGELOG.md | 2 ++ lib/dead_end/cli.rb | 21 ++++++++++++----- spec/unit/cli_spec.rb | 52 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cf836d..3d6e75c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## HEAD (unreleased) +- Fix CLI parsing when flags come before filename (https://github.com/zombocom/dead_end/pull/102) + ## 3.0.0 - [Breaking] Remove previously deprecated `require "dead_end/fyi"` interface (https://github.com/zombocom/dead_end/pull/94) diff --git a/lib/dead_end/cli.rb b/lib/dead_end/cli.rb index a81d9a3..6ea3aba 100644 --- a/lib/dead_end/cli.rb +++ b/lib/dead_end/cli.rb @@ -12,7 +12,7 @@ module DeadEnd # Cli.new(argv: [".rb", "--terminal"]).call # class Cli - attr_accessor :options, :file_name + attr_accessor :options # ARGV is Everything passed to the executable, does not include executable name # @@ -26,22 +26,33 @@ def initialize(argv:, exit_obj: Kernel, io: $stdout, env: ENV) @io = io @argv = argv - @file_name = argv[0] @exit_obj = exit_obj end def call - if file_name.nil? || file_name.empty? + if @argv.empty? # Display help if raw command parser.parse! %w[--help] + return else + # Mutates @argv parse + return if options[:exit] end - # Needed for testing since we fake exit - return if options[:exit] + file_name = @argv.first + if file_name.nil? + @io.puts "No file given" + @exit_obj.exit(1) + return + end file = Pathname(file_name) + if !file.exist? + @io.puts "file not found: #{file.expand_path} " + @exit_obj.exit(1) + return + end @io.puts "Record dir: #{options[:record_dir]}" if options[:record_dir] diff --git a/spec/unit/cli_spec.rb b/spec/unit/cli_spec.rb index 79a3340..8925eb4 100644 --- a/spec/unit/cli_spec.rb +++ b/spec/unit/cli_spec.rb @@ -61,6 +61,58 @@ def called? expect(out.strip).to include("❯ 36 def filename") end + it "parses valid code with flags" do + Dir.mktmpdir do |dir| + dir = Pathname(dir) + file = dir.join("script.rb") + file.write("puts 'lol'") + + io = StringIO.new + exit_obj = FakeExit.new + cli = Cli.new( + io: io, + argv: ["--terminal", file.to_s], + exit_obj: exit_obj + ) + cli.call + + expect(exit_obj.called?).to be_truthy + expect(exit_obj.value).to eq(0) + expect(cli.options[:terminal]).to be_truthy + expect(io.string.strip).to eq("Syntax OK") + end + end + + it "errors when no file given" do + io = StringIO.new + exit_obj = FakeExit.new + cli = Cli.new( + io: io, + argv: ["--terminal"], + exit_obj: exit_obj + ) + cli.call + + expect(exit_obj.called?).to be_truthy + expect(exit_obj.value).to eq(1) + expect(io.string.strip).to eq("No file given") + end + + it "errors when file does not exist" do + io = StringIO.new + exit_obj = FakeExit.new + cli = Cli.new( + io: io, + argv: ["lol-i-d-o-not-ex-ist-yololo.txtblerglol"], + exit_obj: exit_obj + ) + cli.call + + expect(exit_obj.called?).to be_truthy + expect(exit_obj.value).to eq(1) + expect(io.string.strip).to include("file not found:") + end + # We cannot execute the parser here # because it calls `exit` and it will exit # our tests, however we can assert that the