From 14855ed1432a16cbba23d3423f2a34f02406a83a Mon Sep 17 00:00:00 2001 From: Matthew Keeler Date: Wed, 27 Dec 2023 10:09:26 -0500 Subject: [PATCH] feat: Add hash access and equality support to LDContext --- lib/ldclient-rb/context.rb | 26 ++++++++++++++ spec/context_spec.rb | 70 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/lib/ldclient-rb/context.rb b/lib/ldclient-rb/context.rb index a5217be9..2cfcc603 100644 --- a/lib/ldclient-rb/context.rb +++ b/lib/ldclient-rb/context.rb @@ -281,7 +281,18 @@ def individual_context(kind) nil end + # + # An LDContext can be compared to other LDContexts or to a hash object. If + # a hash is provided, it is first converted to an LDContext using the + # `LDContext.create` method. + # + # @param other [LDContext, Hash] + # @return [Boolean] + # def ==(other) + other = LDContext.create(other) if other.is_a? Hash + return false unless other.is_a? LDContext + return false unless self.kind == other.kind return false unless self.valid? == other.valid? return false unless self.error == other.error @@ -308,6 +319,21 @@ def ==(other) end alias eql? == + # + # For a single-kind context, the provided key will return the attribute value specified. This is the same as calling + # `LDCotnext.get_value`. + # + # For multi-kind contexts, the key will be interpreted as a context kind. If the multi-kind context has an + # individual context of that kind, it will be returned. Otherwise, this method will return nil. This behaves the + # same as calling `LDContext.individual_context`. + # + # @param key [Symbol, String] + # + def [](key) + return nil unless key.is_a? Symbol or key.is_a? String + multi_kind? ? individual_context(key.to_s) : get_value(key) + end + # # Retrieve the value of any top level, addressable attribute. # diff --git a/spec/context_spec.rb b/spec/context_spec.rb index 260f0863..be261515 100644 --- a/spec/context_spec.rb +++ b/spec/context_spec.rb @@ -211,6 +211,44 @@ module LaunchDarkly end end + describe "hash-like behavior" do + it "multi-kind contexts return nested contexts" do + user_context = subject.create({ key: "user-key", kind: "user" }) + org_context = subject.create({ key: "org-key", kind: "org" }) + multi_context = subject.create_multi([user_context, org_context]) + + expect(multi_context.valid?).to be true + expect(multi_context["user"]).to eq(user_context) + expect(multi_context["org"]).to eq(org_context) + expect(multi_context["no-such-type"]).to be_nil + end + + describe "single-kind contexts" do + it "can retrieve the correct simple attribute value" do + context = subject.create({ key: "my-key", kind: "org", name: "x", :"my-attr" => "y", :"/starts-with-slash" => "z" }) + + expect(context["kind"]).to eq("org") + expect(context["key"]).to eq("my-key") + expect(context["name"]).to eq("x") + expect(context["my-attr"]).to eq("y") + expect(context["/starts-with-slash"]).to eq("z") + expect(context["a-value-that-is-not-set"]).to be_nil + end + + it "cannot query subpath/elements" do + object_value = { a: 1 } + array_value = [1] + + context = subject.create({ key: "my-key", kind: "org", :"obj-attr" => object_value, :"array-attr" => array_value }) + expect(context["obj-attr"]).to eq(object_value) + expect(context[:"array-attr"]).to eq(array_value) + + expect(context[:"/obj-attr/a"]).to be_nil + expect(context[:"/array-attr/0"]).to be_nil + end + end + end + describe "value retrieval" do describe "supports simple attribute retrieval" do it "can retrieve the correct simple attribute value" do @@ -298,6 +336,38 @@ module LaunchDarkly end describe "equality comparisons" do + it "wrong types are not equal" do + context = subject.create({ key: 'context-key', kind: 'user' }) + expect(context).to_not eq(true) + expect(context).to_not eq(3) + end + + it "single-kind context can compare with hash" do + hash = { key: 'context-key', kind: 'user', name: 'Example name', groups: ['test', 'it', 'here'], address: {street: '123 Easy St', city: 'Every Town'}, + _meta: { privateAttributes: ['name', 'out of order attribute'] } + } + context = subject.create(hash) + expect(context).to eq(hash) + end + + it "multi-kind context can compare with hash" do + hash = { + kind: "multi", + org: { key: 'org-key', kind: 'org' }, + user: { key: 'user-key', kind: 'user' }, + device: { key: 'device-key', kind: 'device' }, + } + org_context = subject.create(hash[:org]) + user_context = subject.create(hash[:user]) + device_context = subject.create(hash[:device]) + context = subject.create(hash) + + expect(context).to eq(hash) + expect(context["org"]).to eq(hash[:org]) + expect(context["user"]).to eq(hash[:user]) + expect(context["device"]).to eq(hash[:device]) + end + it "single kind contexts are equal" do original_context = subject.create( { key: 'context-key', kind: 'user', name: 'Example name', groups: ['test', 'it', 'here'], address: {street: '123 Easy St', city: 'Every Town'},