Skip to content

Commit

Permalink
Added support for validating a range of acceptable Num values (closes
Browse files Browse the repository at this point in the history
#8).
  • Loading branch information
postmodern committed Apr 17, 2022
1 parent 082dd50 commit 52913f0
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 6 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ Defines an option that accepts a numeric value:
option "--count", value: {type: Num.new}
```

Define an option that only accepts a range of acceptable values:

```ruby
option "--count", value: {type: Num.new(range: 1..100)}
```

Defines an option that accepts a comma-separated list:

```ruby
Expand Down
12 changes: 10 additions & 2 deletions lib/command_mapper/types/hex.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ class Hex < Num
# Specifies whether the hex value will start with `0x` or not.
#
# @param [Hash{Symbol => Object}] kwargs
# Additional keyword arguments for {Type#initialize}.
# Additional keyword arguments for {Num#initialize}.
#
def initialize(leading_zero: false)
def initialize(leading_zero: false, **kwargs)
super(**kwargs)

@leading_zero = leading_zero
end

Expand Down Expand Up @@ -46,6 +48,12 @@ def validate(value)
return [false, "not in hex format (#{value.inspect})"]
end

if @range
unless @range.include?(value.to_i(16))
return [false, "unacceptable value (#{value.inspect})"]
end
end

return true
else
super(value)
Expand Down
23 changes: 22 additions & 1 deletion lib/command_mapper/types/num.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ module Types
#
class Num < Type

# The optional range of acceptable numbers.
#
# @return [Range, nil]
attr_reader :range

#
# Initializes the numeric value.
#
# @param [Range] range
# Specifies the range of acceptable numbers.
#
def initialize(range: nil)
@range = range
end

#
# Validates a value.
#
Expand All @@ -20,7 +35,7 @@ class Num < Type
def validate(value)
case value
when Integer
return true
# no-op
when String
unless value =~ /\A\d+\z/
return [false, "contains non-numeric characters (#{value.inspect})"]
Expand All @@ -31,6 +46,12 @@ def validate(value)
end
end

if @range
unless @range.include?(value.to_i)
return [false, "unacceptable value (#{value.inspect})"]
end
end

return true
end

Expand Down
58 changes: 58 additions & 0 deletions spec/types/hex_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@
expect(subject.leading_zero?).to be(true)
end
end

context "when initialized with range: ..." do
let(:range) { 1..10 }

subject { described_class.new(range: range) }

it "must set #range" do
expect(subject.range).to eq(range)
end
end
end

describe "#leading_zero?" do
Expand Down Expand Up @@ -69,12 +79,60 @@
expect(subject.validate(value)).to be(true)
end

context "when initialized with range: ..." do
let(:range) { 2..16 }

subject { described_class.new(range: range) }

context "and the value is within the range of values" do
let(:value) { 'a' }

it "must return true" do
expect(subject.validate(value)).to be(true)
end
end

context "but the value is not within the range of values" do
let(:value) { '0' }

it "must return [false, \"unacceptable value (...)\"]" do
expect(subject.validate(value)).to eq(
[false, "unacceptable value (#{value.inspect})"]
)
end
end
end

context "and the String begins with a '0x'" do
let(:value) { "0xabcdef" }

it "must return true" do
expect(subject.validate(value)).to be(true)
end

context "when initialized with range: ..." do
let(:range) { 2..16 }

subject { described_class.new(range: range) }

context "and the value is within the range of values" do
let(:value) { '0x4' }

it "must return true" do
expect(subject.validate(value)).to be(true)
end
end

context "but the value is not within the range of values" do
let(:value) { '0x0' }

it "must return [false, \"unacceptable value (...)\"]" do
expect(subject.validate(value)).to eq(
[false, "unacceptable value (#{value.inspect})"]
)
end
end
end
end

context "and the String contains a newline" do
Expand Down
96 changes: 93 additions & 3 deletions spec/types/num_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,55 @@
require 'command_mapper/types/num'

describe CommandMapper::Types::Num do
describe "#initialize" do
context "when initialized with no keyword arguments" do
it "must set #range to nil" do
expect(subject.range).to be(nil)
end
end

context "when initialized with range: ..." do
let(:range) { 1..10 }

subject { described_class.new(range: range) }

it "must set #range" do
expect(subject.range).to eq(range)
end
end
end

describe "#validate" do
context "when given an Integer" do
let(:value) { 1 }

it "must return true" do
expect(subject.validate(value)).to be(true)
end

context "when initialized with range: ..." do
let(:range) { 2..10 }

subject { described_class.new(range: range) }

context "and the value is within the range of values" do
let(:value) { 4 }

it "must return true" do
expect(subject.validate(value)).to be(true)
end
end

context "but the value is not within the range of values" do
let(:value) { 0 }

it "must return [false, \"unacceptable value (...)\"]" do
expect(subject.validate(value)).to eq(
[false, "unacceptable value (#{value.inspect})"]
)
end
end
end
end

context "when given a String" do
Expand All @@ -19,7 +61,31 @@
expect(subject.validate(value)).to be(true)
end

context "and the String contains a newline" do
context "when initialized with range: ..." do
let(:range) { 2..10 }

subject { described_class.new(range: range) }

context "and the value is within the range of values" do
let(:value) { '4' }

it "must return true" do
expect(subject.validate(value)).to be(true)
end
end

context "but the value is not within the range of values" do
let(:value) { '0' }

it "must return [false, \"unacceptable value (...)\"]" do
expect(subject.validate(value)).to eq(
[false, "unacceptable value (#{value.inspect})"]
)
end
end
end

context "but the String contains a newline" do
let(:value) { "01234\n56789" }

it "must return [false, \"contains non-numeric characters (...)\"]" do
Expand All @@ -30,7 +96,7 @@
end
end

context "and it contains non-digits" do
context "but it contains non-digits" do
let(:value) { "12abc34" }

it "must return [false, \"contains non-numeric characters (...)\"]" do
Expand All @@ -42,14 +108,38 @@
end

context "when given another type of Object" do
context "but it defines a #to_i method" do
context "and it defines a #to_i method" do
let(:value) { 1.0 }

it "must return true" do
expect(subject.validate(value)).to be(true)
end
end

context "when initialized with range: ..." do
let(:range) { 2..10 }

subject { described_class.new(range: range) }

context "and the value is within the range of values" do
let(:value) { 4.0 }

it "must return true" do
expect(subject.validate(value)).to be(true)
end
end

context "but the value is not within the range of values" do
let(:value) { 0.0 }

it "must return [false, \"unacceptable value (...)\"]" do
expect(subject.validate(value)).to eq(
[false, "unacceptable value (#{value.inspect})"]
)
end
end
end

context "but it does not define a #to_i method" do
let(:value) { Object.new }

Expand Down

0 comments on commit 52913f0

Please sign in to comment.