diff --git a/lib/open_feature/sdk/hooks/hints.rb b/lib/open_feature/sdk/hooks/hints.rb new file mode 100644 index 0000000..1fbec14 --- /dev/null +++ b/lib/open_feature/sdk/hooks/hints.rb @@ -0,0 +1,40 @@ +require "delegate" + +module OpenFeature + module SDK + module Hooks + class Hints < DelegateClass(Hash) + ALLOWED_TYPES = [String, Symbol, Numeric, TrueClass, FalseClass, Time, Hash, Array].freeze + + def initialize(hash) + @hash = hash.dup + @hash.each_key { |key| raise ArgumentError, "Only String or Symbol are allowed as keys" unless key.is_a?(String) || key.is_a?(Symbol) } + @hash.each_value do |v| + raise ArgumentError, "Only #{ALLOWED_TYPES.join(", ")} are allowed as values." unless ALLOWED_TYPES.any? { |t| v.is_a?(t) } + end + super + freeze + end + + def freeze + each do |k, v| + v.freeze + if v.is_a?(Hash) + end + end + super + end + + private + + def deep_freeze + each_value do |k, v| + k.freeze + v.each_value { |v| v.freeze } if v.is_a?(Hash) || v.is_a?(Array) + v.freeze + end + end + end + end + end +end diff --git a/spec/open_feature/sdk/hooks/hints_spec.rb b/spec/open_feature/sdk/hooks/hints_spec.rb new file mode 100644 index 0000000..3cc1848 --- /dev/null +++ b/spec/open_feature/sdk/hooks/hints_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require "spec_helper" + +require "open_feature/sdk/hooks/hints" + +RSpec.describe OpenFeature::SDK::Hooks::Hints do + let(:hint_hash) { {key: []} } + subject(:hints) { described_class.new(hint_hash) } + context "Immutability" do + it "is frozen" do + expect(hints).to be_frozen + end + it "does not allow addition of new keys" do + expect { hints[:new_key] = "new_value" }.to raise_error(FrozenError) + end + it "does not allow modification of existing keys" do + expect { hints[:key] << "abc" }.to raise_error(FrozenError) + end + it "does not allow deletion of keys" do + expect { hints.delete(:key) }.to raise_error(FrozenError) + end + + it "allows reading of keys" do + expect(hints[:key]).to eq([]) + end + + it "only allows string keys" do + expect { described_class.new(1 => []) }.to raise_error(ArgumentError) do |e| + expect(e.message).to include("Only String or Symbol are allowed as keys") + end + end + + it "only allows values of certain types" do + expect { described_class.new(key: Object.new) }.to raise_error(ArgumentError) do |e| + expect(e.message).to include("Only String, Symbol, Numeric, TrueClass, FalseClass, Time, Hash, Array are allowed as values") + end + end + end +end