diff --git a/lib/prius/errors.rb b/lib/prius/errors.rb index fd64364..6041ef1 100644 --- a/lib/prius/errors.rb +++ b/lib/prius/errors.rb @@ -4,4 +4,5 @@ module Prius class MissingValueError < StandardError; end class TypeMismatchError < StandardError; end class UndeclaredNameError < StandardError; end + class InvalidLoadError < StandardError; end end diff --git a/lib/prius/registry.rb b/lib/prius/registry.rb index abe78bf..50a51c3 100644 --- a/lib/prius/registry.rb +++ b/lib/prius/registry.rb @@ -15,13 +15,19 @@ def initialize(env) end # See Prius.load for documentation. - def load(name, env_var: nil, type: :string, required: true) + def load(name, env_var: nil, type: :string, required: true, default: nil) env_var = name.to_s.upcase if env_var.nil? + + if required && !default.nil? + raise InvalidLoadError, "required config value #{env_var} cannot have default value" + end + + value = load_value(env_var, required, default) @registry[name] = case type - when :string then load_string(env_var, required) - when :int then load_int(env_var, required) - when :bool then load_bool(env_var, required) - when :date then load_date(env_var, required) + when :string then value + when :int then parse_int(env_var, value) + when :bool then parse_bool(env_var, value) + when :date then parse_date(env_var, value) else raise ArgumentError, "Invalid type #{type}" end end @@ -35,18 +41,15 @@ def get(name) private - def load_string(name, required) + def load_value(name, required, default) @env.fetch(name) rescue KeyError - return nil unless required + return default unless required raise MissingValueError, "config value '#{name}' not present" end - def load_int(name, required) - value = load_string(name, required) - return value if value.nil? - + def parse_int(name, value) unless /\A[0-9]+\z/.match?(value) raise TypeMismatchError, "'#{name}' value '#{value}' is not an integer" end @@ -54,10 +57,7 @@ def load_int(name, required) value.to_i end - def load_bool(name, required) - value = load_string(name, required) - return nil if value.nil? - + def parse_bool(name, value) value = value.downcase return true if %w[yes y true t 1].include?(value) return false if %w[no n false f 0].include?(value) @@ -65,10 +65,7 @@ def load_bool(name, required) raise TypeMismatchError, "'#{name}' value '#{value}' is not a boolean" end - def load_date(name, required) - value = load_string(name, required) - return nil if value.nil? - + def parse_date(name, value) Date.parse(value) rescue ArgumentError raise TypeMismatchError, "'#{name}' value '#{value}' is not a date" diff --git a/spec/prius/registry_spec.rb b/spec/prius/registry_spec.rb index 8685e60..e6ffab0 100644 --- a/spec/prius/registry_spec.rb +++ b/spec/prius/registry_spec.rb @@ -26,6 +26,13 @@ it "doesn't blow up" do expect { registry.load(:name) }.to_not raise_error end + + context "but also a default value" do + it "raises an error" do + expect { registry.load(:name, default: "foo") }. + to raise_error(Prius::InvalidLoadError) + end + end end context "given a name that's not present in the environment" do @@ -38,6 +45,14 @@ it "doesn't blow up" do expect { registry.load(:slogan, required: false) }.to_not raise_error end + + context "when a default value is provided" do + it "doesn't blow up" do + expect do + registry.load(:slogan, required: false, default: "GO GO GO") + end.to_not raise_error + end + end end end @@ -48,6 +63,14 @@ end end + # TODO: Decide if default values should be strings or of the parsed type + context "when specifying a default for a non-string type" do + it "doesn't blow up" do + expect { registry.load(:blah, type: :int, required: false, default: "56") }. + to_not raise_error + end + end + context "when specifying :int as the type" do context "given a integer value" do it "doesn't blow up" do @@ -74,7 +97,7 @@ expect { registry.load(:alive, type: :bool) }.to_not raise_error end - it "stores an boolean" do + it "stores a boolean" do registry.load(:alive, type: :bool) expect(registry.get(:alive)).to be_a(TrueClass) end @@ -133,12 +156,20 @@ end end - context "given a nillable name that has been loaded" do + context "given a nilable name that has been loaded" do before { registry.load(:lightsabre, required: false) } it "returns nil" do expect(registry.get(:lightsabre)).to be_nil end + + context "with a default" do + before { registry.load(:lightsabre, required: false, default: "blue") } + + it "returns the default" do + expect(registry.get(:lightsabre)).to eq("blue") + end + end end end end