From 7c01836135f0b39baae06eb1d9d0aa713c9352dc Mon Sep 17 00:00:00 2001 From: Brandur Date: Thu, 13 Sep 2018 17:21:30 -0600 Subject: [PATCH] Override `StripeObject#eql?` and `#hash` to produce more optimistic equivalency Overrides `#eql?` (hash equality) and `#hash` so that Stripe objects can be used more easily as Hash keys and that certain other frameworks that rely on these methods will have an easier time (e.g. RSpec's `change`, see #687). I think this might be a little controversial if we weren't already overriding the `#==` implementation, but because we are, I think it makes sense to extent it to these two methods as well. --- lib/stripe/stripe_object.rb | 11 +++++++++ test/stripe/stripe_object_test.rb | 40 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/lib/stripe/stripe_object.rb b/lib/stripe/stripe_object.rb index c63ac67c5..0fd969da6 100644 --- a/lib/stripe/stripe_object.rb +++ b/lib/stripe/stripe_object.rb @@ -96,6 +96,17 @@ def ==(other) other.is_a?(StripeObject) && @values == other.instance_variable_get(:@values) end + # Hash equality. As with ==, we consider two equivalent Stripe objects equal. + def eql?(other) + self == other + end + + # As for equality, we hash to Stripe objects to the same value if they're + # equivalent objects. + def hash + @values.hash + end + # Indicates whether or not the resource has been deleted on the server. # Note that some, but not all, resources can indicate whether they have # been deleted. diff --git a/test/stripe/stripe_object_test.rb b/test/stripe/stripe_object_test.rb index cfcf07a28..d6c62edfa 100644 --- a/test/stripe/stripe_object_test.rb +++ b/test/stripe/stripe_object_test.rb @@ -117,6 +117,46 @@ class TestObject < Stripe::StripeObject; end end end + context "#eql?" do + should "produce the true for two equivalent Stripe objects" do + obj1 = Stripe::StripeObject.construct_from(id: 1, name: "Stripe") + obj2 = Stripe::StripeObject.construct_from(id: 1, name: "Stripe") + assert obj1.eql?(obj2) + end + + should "produce false for non-equivalent Stripe objects" do + obj1 = Stripe::StripeObject.construct_from(id: 1, name: "Stripe") + obj2 = Stripe::StripeObject.construct_from(id: 2, name: "Stripe") + refute obj1.eql?(obj2) + end + + should "produce false for different types" do + obj1 = Stripe::StripeObject.construct_from(id: 1, name: "Stripe") + obj2 = 7 + refute obj1.eql?(obj2) + end + end + + context "#hash" do + should "produce the same hash for two equivalent Stripe objects" do + obj1 = Stripe::StripeObject.construct_from(id: 1, name: "Stripe") + obj2 = Stripe::StripeObject.construct_from(id: 1, name: "Stripe") + assert_equal obj1.hash, obj2.hash + end + + should "produce different hashes for non-equivalent Stripe objects" do + obj1 = Stripe::StripeObject.construct_from(id: 1, name: "Stripe") + obj2 = Stripe::StripeObject.construct_from(id: 2, name: "Stripe") + refute_equal obj1.hash, obj2.hash + end + + should "produce different hashes for different types" do + obj1 = Stripe::StripeObject.construct_from(id: 1, name: "Stripe") + obj2 = 7 + refute_equal obj1.hash, obj2.hash + end + end + context "#to_hash" do should "skip calling to_hash on nil" do module NilWithToHash