Skip to content

Commit

Permalink
Add Comparable to Spree::Money
Browse files Browse the repository at this point in the history
This commit includes Comparable and implements `<=>`. Including
Comparable also gives us some basic comparison methods like `<`, `>`,
`.between?`, and `sort`.

We need to add an additional currency comparison because `::Money` will
automatically try to convert the second currency in order to be able to
compare it with the first. We don't want this to happen so we're going
to explicitly raise an error.
  • Loading branch information
graygilmore committed Jan 16, 2017
1 parent 798a3ed commit 943b2c5
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 3 deletions.
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions core/lib/spree/money.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<self
Expand Down Expand Up @@ -92,6 +94,22 @@ def as_json(*)
to_s
end

def <=>(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
Expand Down
32 changes: 32 additions & 0 deletions core/spec/lib/spree/money_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 943b2c5

Please sign in to comment.