Skip to content

Commit

Permalink
Change to validate arity when parsing keywords
Browse files Browse the repository at this point in the history
  • Loading branch information
piotrmurach committed Apr 10, 2020
1 parent 36005d2 commit bb31266
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
35 changes: 35 additions & 0 deletions lib/tty/option/parser/keywords.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ def initialize(keywords, **config)
@remaining = []
@names = {}
@required = []
@arities = Hash.new(0)
@multiplies = {}

@keywords.each do |kwarg|
@names[kwarg.name.to_s] = kwarg
@multiplies[kwarg.name] = kwarg if kwarg.multiple?

if kwarg.default?
case kwarg.default
Expand All @@ -49,13 +52,15 @@ def parse(argv)
kwarg, value = next_keyword
break if kwarg.nil?
@required.delete(kwarg)
@arities[kwarg.name] += 1

if block_given?
yield(kwarg, value)
end
assign_keyword(kwarg, value)
end

check_arity
check_required

[@parsed, @remaining, @errors]
Expand Down Expand Up @@ -133,6 +138,36 @@ def record_error(type, message, arg = nil)
end
end

# Check if parameter matches arity
#
# @raise [InvalidArity]
#
# @api private
def check_arity
@multiplies.each do |name, kwarg|
arity = @arities[name]

if 0 < kwarg.arity.abs && arity < kwarg.arity.abs
prefix = kwarg.arity < 0 ? "at least " : ""
expected_arity = kwarg.arity < 0 ? kwarg.arity.abs - 1 : kwarg.arity

record_error(InvalidArity, format(
"expected keyword %s to appear %s but appeared %s",
name.inspect,
prefix + pluralize("time", expected_arity),
pluralize("time", arity)
))
end
end
end

# Pluralize a noun
#
# @api private
def pluralize(noun, count = 1)
"#{count} #{noun}#{'s' unless count == 1}"
end

# Check if required parameters are provided
#
# @raise [MissingParameter]
Expand Down
22 changes: 22 additions & 0 deletions spec/unit/parser/keywords_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,28 @@ def parse(argv, kwargs, **config)
expect(rest).to eq(["foo=3"])
end

it "doesn't find enough keywords to match specific arity" do
expect {
parse(%w[foo=1], keyword(:foo, arity: 2))
}.to raise_error(TTY::Option::InvalidArity,
"expected keyword :foo to appear 2 times but " \
"appeared 1 time")
end

it "parses minimum number of keywords to satisfy at least arity" do
params, = parse(%w[foo=1 foo=2 foo=3], keyword(:foo, arity: -3))

expect(params[:foo]).to eq(%w[1 2 3])
end

it "doesn't find enough keywords to match at least arity" do
expect {
parse(%w[foo=1], keyword(:foo, arity: -3))
}.to raise_error(TTY::Option::InvalidArity,
"expected keyword :foo to appear at least 2 times but " \
"appeared 1 time")
end

it "parses multiple keywords" do
params, rest = parse(%w[foo=1 foo=2 foo=3], keyword(:foo, arity: :any))

Expand Down

0 comments on commit bb31266

Please sign in to comment.