Skip to content

Commit

Permalink
Merge pull request #15 from nevinera/nev-5--implement-integer-ranges
Browse files Browse the repository at this point in the history
Implement `ENV.integer_range`
  • Loading branch information
nevinera authored Apr 25, 2023
2 parents 7c4fe9b + a574156 commit 62511b6
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 1 deletion.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ methods onto `ENV` for your use.
ENV.string("APP_NAME", default: "local")
ENV.symbol("BUSINESS_DOMAIN", default: :engineering, required: true)
ENV.boolean("ENABLE_FEATURE_FOO", default: false)
ENV.integer_range("ID_RANGE", default: (500..6000))
ENV.integer("MAX_THREAD_COUNT", default: 5)
ENV.date("SCHEDULED_DATE", required: true, format: "%Y-%m-%d")
```
Expand All @@ -51,6 +52,8 @@ The available methods added to `ENV`:
a fair variety of strings to map onto those boolean value, though you should probably just use
"true" and "false" really. If you specify `required: true` and get a value like "maybe?", it'll
raise an `EnvironmentHelpers::InvalidBooleanText` exception.
* `integer_range` - produces an integer Range object. It accepts `N-N`, `N..N`, or `N...N`, (the
latter means 'excluding the upper bound, as in ruby).
* `integer` - produces an integer from the environment variable, by calling `to_i` on it (if it's
present). Note that this means that providing a value like "hello" means you'll get `0`, since
that's what ruby does when you call `"hello".to_i`.
Expand Down
3 changes: 3 additions & 0 deletions lib/environment_helpers.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require_relative "./environment_helpers/access_helpers"
require_relative "./environment_helpers/string_helpers"
require_relative "./environment_helpers/boolean_helpers"
require_relative "./environment_helpers/range_helpers"
require_relative "./environment_helpers/numeric_helpers"
require_relative "./environment_helpers/datetime_helpers"

Expand All @@ -11,12 +12,14 @@ module EnvironmentHelpers

InvalidValue = Class.new(Error)
InvalidBooleanText = Class.new(InvalidValue)
InvalidRangeText = Class.new(InvalidValue)
InvalidIntegerText = Class.new(InvalidValue)
InvalidDateText = Class.new(InvalidValue)

include AccessHelpers
include StringHelpers
include BooleanHelpers
include RangeHelpers
include NumericHelpers
include DatetimeHelpers
end
Expand Down
42 changes: 42 additions & 0 deletions lib/environment_helpers/range_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module EnvironmentHelpers
module RangeHelpers
def integer_range(name, default: nil, required: false)
check_default_type(:integer_range, default, Range)
check_range_endpoint(:integer_range, default.begin) if default
check_range_endpoint(:integer_range, default.end) if default

text = fetch_value(name, required: required)
range = parse_range_from(text)
return range if range
return default unless required
fail(InvalidRangeText, "Required Integer Range environment variable #{name} had inappropriate content '#{text}'")
end

private

def check_range_endpoint(context, value)
return if value.is_a?(Integer)
fail(BadDefault, "Invalid endpoint for default range of #{context} - must be Integer")
end

def parse_range_bound_from(text)
return nil if text.nil?
return nil if text.empty?
text.to_i
end

def parse_range_from(text)
text =~ /\A(\d*)(-|\.\.|\.\.\.)(\d*)\z/
lower_bound = parse_range_bound_from($1)
separator = $2
upper_bound = parse_range_bound_from($3)

return nil if lower_bound.nil? || upper_bound.nil?
if separator == "..."
(lower_bound...upper_bound)
else
(lower_bound..upper_bound)
end
end
end
end
2 changes: 1 addition & 1 deletion lib/environment_helpers/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module EnvironmentHelpers
VERSION = "1.0.1"
VERSION = "1.1.0"
end
118 changes: 118 additions & 0 deletions spec/environment_helpers/range_helpers_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
RSpec.describe EnvironmentHelpers::RangeHelpers do
subject(:env) { ENV }

describe "#integer_range" do
let(:name) { "FOO" }
let(:options) { {} }
subject(:integer_range) { ENV.integer_range(name, **options) }

context "when required: true" do
let(:options) { {required: true} }

context "and the key is supplied" do
with_env("FOO" => "52-63")
it { is_expected.to eq((52..63)) }

context "but has invalid content" do
with_env("FOO" => "hello")

it "raises a MissingVariableError" do
expect { integer_range }.to raise_error(
EnvironmentHelpers::InvalidRangeText,
/inappropriate content/
)
end
end
end

context "and the environment variable is not supplied" do
before { expect(ENV["FOO"]).to be_nil }

it "raises a MissingVariableError" do
expect { integer_range }.to raise_error(
EnvironmentHelpers::MissingVariableError,
/not supplied/
)
end
end
end

context "without a default specified" do
let(:options) { {} }

context "when the environment variable is present" do
with_env("FOO" => "58..61")
it { is_expected.to eq((58..61)) }
end

context "when the environment variable is not present" do
before { expect(ENV["FOO"]).to be_nil }
it { is_expected.to be_nil }
end
end

context "with a default specified" do
let(:options) { {default: (91..93)} }

context "but of the wrong type" do
let(:options) { {default: "91"} }

it "raises a BadDefault error" do
expect { integer_range }.to raise_error(
EnvironmentHelpers::BadDefault,
/inappropriate default/i
)
end
end

context "with the wrong type of endpoint" do
let(:options) { {default: ("a".."c")} }

it "raises a BadDefault error" do
expect { integer_range }.to raise_error(
EnvironmentHelpers::BadDefault,
/invalid endpoint for default range/i
)
end
end

context "when the environment variable is present" do
with_env("FOO" => "58..62")
it { is_expected.to eq((58..62)) }

context "but not actually an integer" do
with_env("FOO" => "hello")
it { is_expected.to eq((91..93)) }
end
end

context "when the environment variable is not present" do
before { expect(ENV["FOO"]).to be_nil }
it { is_expected.to eq((91..93)) }
end
end

context "for various formats" do
context "with a dash" do
with_env("FOO" => "3-5")
it { is_expected.to eq((3..5)) }
end

context "with two dots" do
with_env("FOO" => "3..5")
it { is_expected.to eq((3..5)) }
end

context "with three dots" do
with_env("FOO" => "3...5")
it { is_expected.to eq((3...5)) }
it { is_expected.not_to be_cover(5) }
end

context "with missing bound" do
with_env("FOO" => "3..")
it { is_expected.to be_nil }
end
end
end
end

0 comments on commit 62511b6

Please sign in to comment.