diff --git a/CHANGELOG.md b/CHANGELOG.md index 8484ec5721a..87d3f9da252 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,21 @@ ## Solidus 2.2.0 (master, unreleased) * Promotion and Shipping calculators can be created or have their type - changed without saving and reloading the page. [#1618](https://github.com/solidusio/solidus/pull/1618) + changed without saving and reloading the page. + [#1618](https://github.com/solidusio/solidus/pull/1618) -* Product slugs no longer have a minimum length validation to match the Product name validation. +* Product slugs no longer have a minimum length validation to match the + Product name validation. [#1616](https://github.com/solidusio/solidus/pull/1616) * Changed the promotion field in the admin order search to only find orders which used the specified promotion code, instead of any code on an applied promotion. [#1662](https://github.com/solidusio/solidus/pull/1662) +* `Spree::Money` now includes `Comparable` and the `<=>` operator for + comparisons. A comparison will fail if the two objects do not use the same + currency. [#1682](https://github.com/solidusio/solidus/pull/1682) + ## Solidus 2.1.0 (unreleased) * The OrderUpdater (as used by `order.update!`) now fully updates taxes. @@ -83,7 +89,7 @@ For now `PromotionAction` provides a default remove_from method, with a deprecation warning that subclasses should define their own remove_from method. - + [#1451](https://github.com/solidusio/solidus/pull/1451) * Remove `is_default` boolean from `Spree::Price` model diff --git a/core/lib/spree/money.rb b/core/lib/spree/money.rb index cdb224605ae..2ecbeaec29b 100644 --- a/core/lib/spree/money.rb +++ b/core/lib/spree/money.rb @@ -6,6 +6,8 @@ module Spree # Spree::Money is a relatively thin wrapper around Monetize which handles # formatting via Spree::Config. class Money + include Comparable + DifferentCurrencyError = Class.new(StandardError) RUBY_NUMERIC_STRING = /\A-?\d+(\.\d+)?\z/ class <(other) + if !other.respond_to?(:money) + raise TypeError, "Can't compare #{other.class} to Spree::Money" + end + if self.currency != other.currency + # By default, ::Money will try to run a conversion on `other.money` and + # try a comparison on that. We do not want any currency conversion to + # take place so we'll catch this here and raise an error. + raise( + DifferentCurrencyError, + "Can't compare #{self.currency} with #{other.currency}" + ) + end + @money <=> other.money + end + # Delegates comparison to the internal ruby money instance. # # @see http://www.rubydoc.info/gems/money/Money/Arithmetic#%3D%3D-instance_method diff --git a/core/spec/lib/spree/money_spec.rb b/core/spec/lib/spree/money_spec.rb index cc82022a4bf..e9d8720503c 100644 --- a/core/spec/lib/spree/money_spec.rb +++ b/core/spec/lib/spree/money_spec.rb @@ -268,4 +268,36 @@ end end end + + it "includes Comparable" do + expect(described_class).to include(Comparable) + end + + describe "<=>" do + let(:usd_10) { Spree::Money.new(10, currency: "USD") } + let(:usd_20) { Spree::Money.new(20, currency: "USD") } + let(:usd_30) { Spree::Money.new(30, currency: "USD") } + + it "compares the two amounts" do + expect(usd_20 <=> usd_20).to eq 0 + expect(usd_20 <=> usd_10).to be > 0 + expect(usd_20 <=> usd_30).to be < 0 + end + + context "with a non Spree::Money object" do + it "raises an error" do + expect { usd_10 <=> 20 }.to raise_error(TypeError) + end + end + + context "with differing currencies" do + let(:cad) { Spree::Money.new(10, currency: "CAD") } + + it "raises an error" do + expect { usd_10 <=> cad }.to raise_error( + Spree::Money::DifferentCurrencyError + ) + end + end + end end