Skip to content
Permalink
Browse files

Clean up the option parser

This removes the regular expression logic and splitting out of the 
parser_args vs exec_args in favor of using OptionParser.order! which 
will not complain about unknown flags because order will stop at the 
first unknown argument.

This is a dramatic improvement to the CLI code clarity.

Co-authored-by: Ben Sherman <bensherman@github.com>
  • Loading branch information...
cadwallion and bensherman committed Mar 22, 2019
1 parent b6559f6 commit 84731616786eecb57a4d5119e2f4a196750e6057
Showing with 22 additions and 35 deletions.
  1. +12 −33 lib/dotenv/cli.rb
  2. +10 −2 spec/dotenv/cli_spec.rb
@@ -6,82 +6,61 @@ module Dotenv
# The CLI is a class responsible of handling all the command line interface
# logic.
class CLI
attr_reader :argv, :exec_args, :parser_args, :filenames
attr_reader :argv, :filenames

def initialize(argv = [])
@argv = argv.dup
@filenames = []
@flag_matchers = []
end

def run
parse_argv!(@argv)
parse_argv! @argv

begin
Dotenv.load!(*@filenames)
rescue Errno::ENOENT => e
abort e.message
else
exec(*@exec_args) unless @exec_args.empty?
exec(*@argv) unless @argv.empty?
end
end

private

def parse_argv!(argv)
parser = create_option_parser
add_options(parser, @flag_matchers)
@parser_args, @exec_args = split_argv(argv.join(" "), @flag_matchers)
parser.parse! @parser_args
add_options(parser)
parser.order! argv

@filenames
end

def add_options(parser, flag_matchers)
add_files_option(parser, flag_matchers)
add_help_option(parser, flag_matchers)
add_version_option(parser, flag_matchers)
def add_options(parser)
add_files_option(parser)
add_help_option(parser)
add_version_option(parser)
end

def add_files_option(parser, flag_matchers)
flag_matchers.push("-f \\S+")
def add_files_option(parser)
parser.on("-f FILES", Array, "List of env files to parse") do |list|
@filenames = list
end
end

def add_help_option(parser, flag_matchers)
flag_matchers.push("-h", "--help")
def add_help_option(parser)
parser.on("-h", "--help", "Display help") do
puts parser
exit
end
end

def add_version_option(parser, flag_matchers)
flag_matchers.push("-v", "--version")
def add_version_option(parser)
parser.on("-v", "--version", "Show version") do
puts "dotenv #{Dotenv::VERSION}"
exit
end
end

# Detect dotenv flags vs executable args so we can parse properly and still
# take advantage of OptionParser for dotenv flags
def split_argv(arg_string, matchers)
matcher = /^((?:#{matchers.join("|")})\s?)?(.+)?$/
data = matcher.match(arg_string)
dotenv_args = []
exec_args = []

unless data.nil?
dotenv_args = (!data[1].nil? ? data[1].split(" ") : [])
exec_args = (!data[2].nil? ? data[2].split(" ") : [])
end

[dotenv_args, exec_args]
end

def create_option_parser
OptionParser.new do |parser|
parser.banner = "Usage: dotenv [options]"
@@ -43,15 +43,23 @@ def run(*args)
cli.send(:parse_argv!, cli.argv)

expect(cli.filenames).to eql(["plain.env"])
expect(cli.exec_args).to eql(["foo", "--switch"])
expect(cli.argv).to eql(["foo", "--switch"])
end

it "does not consume dotenv flags from subcommand" do
cli = Dotenv::CLI.new(["foo", "-f", "something"])
cli.send(:parse_argv!, cli.argv)

expect(cli.filenames).to eql([])
expect(cli.exec_args).to eql(["foo", "-f", "something"])
expect(cli.argv).to eql(["foo", "-f", "something"])
end

it "does not mess with quoted args" do
cli = Dotenv::CLI.new(["foo something"])
cli.send(:parse_argv!, cli.argv)

expect(cli.filenames).to eql([])
expect(cli.argv).to eql(["foo something"])
end

# Capture output to $stdout and $stderr

0 comments on commit 8473161

Please sign in to comment.
You can’t perform that action at this time.