Skip to content

Commit

Permalink
Merge pull request #19 from nevinera/nev-6--support-date-time
Browse files Browse the repository at this point in the history
Support `ENV.date_time`
  • Loading branch information
nevinera authored May 16, 2023
2 parents 725295d + eab20d8 commit 0a75d09
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 1 deletion.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ ENV.integer_range("ID_RANGE", default: (500..6000))
ENV.integer("MAX_THREAD_COUNT", default: 5)
ENV.file_path("FILE_PATH", default: "/some/path", required: true)
ENV.date("SCHEDULED_DATE", required: true, format: "%Y-%m-%d")
ENV.date_time("RUN_AT", required: true, default: DateTime.now)
```

Each of the supplied methods takes a positional parameter for the name of the environment variable,
Expand All @@ -64,3 +65,8 @@ The available methods added to `ENV`:
which would parse a date like `2023-12-25`. It will handle invalid values (or format strings) like
the variable not being present, though if it's specified as `required`, you will see a different
exception in each case.
* `date_time` - produces a `DateTime` object, using either `DateTime.strptime` or `DateTime.iso8601`.
The default format is `:iso8601`, and `:unix` is also an allowed 'format'. But if it is supplied
as a _string_, it will be handled as a strptime format string (the `:unix` format is equivalent to
the format string `"%s"`). It handles invalid or unparseable values like `ENV.date` does, in that
they are treated as if not supplied.
2 changes: 2 additions & 0 deletions lib/environment_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ module EnvironmentHelpers
Error = Class.new(::StandardError)
MissingVariableError = Class.new(Error)
BadDefault = Class.new(Error)
BadFormat = Class.new(Error)

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

include AccessHelpers
include StringHelpers
Expand Down
42 changes: 42 additions & 0 deletions lib/environment_helpers/datetime_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ def date(name, format: "%Y-%m-%d", default: nil, required: false)
fail(InvalidDateText, "Required date environment variable #{name} had inappropriate content '#{text}'")
end

def date_time(name, format: :iso8601, default: nil, required: false)
check_default_type(:date_time, default, DateTime)
text = fetch_value(name, required: required)
dt = parse_date_time_from(text, format: format)

return dt if dt
return default unless required
fail(InvalidDateTimeText, "Require date_time environment variable #{name} had inappropriate content '#{text}'")
end

private

def parse_date_from(text, format:)
Expand All @@ -20,5 +30,37 @@ def parse_date_from(text, format:)
rescue ArgumentError
nil
end

def parse_date_time_from(text, format:)
if text.nil?
nil
elsif format == :iso8601
iso8601_date_time(text)
elsif format == :unix
unix_date_time(text)
elsif format.is_a?(String)
strptime_date_time(text, format: format)
else
fail(BadFormat, "ENV.date_time requires either a strptime format string, :unix, or :iso8601")
end
end

def iso8601_date_time(text)
DateTime.iso8601(text)
rescue
nil
end

def unix_date_time(text)
DateTime.strptime(text, "%s")
rescue
nil
end

def strptime_date_time(text, format:)
DateTime.strptime(text, format)
rescue
nil
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.2.1"
VERSION = "1.3.0"
end
111 changes: 111 additions & 0 deletions spec/environment_helpers/datetime_helpers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,115 @@ def self.it_parses_date_as(text:, format:, result:)
it_parses_date_as text: "2023-4-12", format: "hello", result: nil
end
end

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

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

context "when the environment value is not set" do
before { expect(ENV["FOO"]).to be_nil }

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

context "when the environment value is set" do
with_env("FOO" => "2023-04-25T13:44:59.75+07:30")
it { is_expected.to eq(DateTime.new(2023, 4, 25, 13, 44, 59.75, "+7:30")) }

context "to an invalid value" do
with_env("FOO" => "hello")

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

context "with default set" do
let(:options) { {default: DateTime.new(2000, 1, 1, 4, 5, 6, "+7")} }

context "to a value of the wrong type" do
let(:options) { {default: Date.new(2023, 1, 2)} }

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

context "when the environment value is not set" do
before { expect(ENV["FOO"]).to be_nil }
it { is_expected.to eq(options[:default]) }
end

context "when the environment value is set" do
with_env("FOO" => "2023-04-25T14:22:33+02")
it { is_expected.to eq(DateTime.new(2023, 4, 25, 14, 22, 33, "+2")) }
end
end

context "with default not set" do
before { expect(options).not_to include(:default) }

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

context "when the environment value is set" do
with_env("FOO" => "2023-04-25T03:13:56Z")
it { is_expected.to eq(DateTime.new(2023, 4, 25, 3, 13, 56, "UTC")) }
end
end

context "with other formats supplied" do
def self.it_parses_datetime_as(text:, format:, result:)
context "for supplied text '#{text}' and format '#{format}'" do
let(:options) { {format: format} }
with_env("FOO" => text)
it { is_expected.to eq(result) }
end
end

context "for :unix format" do
it_parses_datetime_as text: "1684200709", format: :unix, result: DateTime.new(2023, 5, 16, 1, 31, 49, "UTC")
it_parses_datetime_as text: "hello", format: :unix, result: nil
end

context "for :iso8601 format" do
it_parses_datetime_as text: "2023-05-15T23:25:24-04:00", format: :iso8601, result: DateTime.new(2023, 5, 15, 23, 25, 24, "-4")
it_parses_datetime_as text: "2023-05-15T23:25:24.75-04:00", format: :iso8601, result: DateTime.new(2023, 5, 15, 23, 25, 24.75, "-4")
it_parses_datetime_as text: "hello", format: :iso8601, result: nil
end

context "for string formats" do
it_parses_datetime_as text: "2023.05.15.23.25.25", format: "%Y.%m.%d.%H.%M.%S", result: DateTime.new(2023, 5, 15, 23, 25, 25, "UTC")
it_parses_datetime_as text: "hello", format: "%Y.%m.%d.%H.%M.%S", result: nil
end

context "with a bad format" do
let(:options) { {format: :notreal} }
with_env("FOO" => "2023-05-15T23:25:24-04:00")

it "raises a BadFormat exception" do
expect { date_time }.to raise_error(EnvironmentHelpers::BadFormat, /date_time requires either/)
end
end
end
end
end

0 comments on commit 0a75d09

Please sign in to comment.