From e3d6657b8c2306a64ef7600d10667fb91c22d332 Mon Sep 17 00:00:00 2001 From: safakferhatkaya Date: Thu, 24 Aug 2023 00:57:33 +0300 Subject: [PATCH] Power arithmetic added --- lib/money/money/arithmetic.rb | 30 ++++++++++++++++++++++++++++++ spec/money/arithmetic_spec.rb | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/lib/money/money/arithmetic.rb b/lib/money/money/arithmetic.rb index b3ba0e711b..c5c342d332 100644 --- a/lib/money/money/arithmetic.rb +++ b/lib/money/money/arithmetic.rb @@ -173,6 +173,36 @@ def *(value) end end + + # Raises the monetary value to the power of the given number and returns a new + # +Money+ object with the modified monetary value and the same currency. + # + # Note that you can exponentiate a Money object by another +Money+ object only + # if they share the same currency. + # + # @param [Numeric, Money] value The exponent to raise the monetary value to, + # or a +Money+ object of the same currency to exponentiate by. + # + # @return [Money] The resulting money after exponentiation. + # + # @raise [TypeError] If the input +value+ is not a number, or if it's a +Money+ object with a different currency. + # + # @example Exponentiating by a number: + # Money.new(2, :USD) ** 3 #=> # + # + # @example Exponentiating by a Money object (same currency): + # Money.new(2, :USD) ** Money.new(3, :USD) #=> # + # + def **(value) + if value.is_a?(Numeric) + dup_with(fractional: fractional ** value) + elsif value.is_a?(self.class) && value.currency == currency + dup_with(fractional: fractional ** value.fractional) + else + raise TypeError, "Can't exponentiate a #{self.class.name} by a #{value.class.name}" + end + end + # Divides the monetary value with the given number and returns a new +Money+ # object with this monetary value and the same currency. # Can also divide by another +Money+ object to get a ratio. diff --git a/spec/money/arithmetic_spec.rb b/spec/money/arithmetic_spec.rb index 336212d9bd..fa83b45356 100644 --- a/spec/money/arithmetic_spec.rb +++ b/spec/money/arithmetic_spec.rb @@ -306,6 +306,41 @@ it_behaves_like 'instance with custom bank', :*, 1 end + describe "#**" do + it "Exponentiates Money by Integer and returns Money" do + ts = [ + {a: Money.new( 2, :USD), b: 4, c: Money.new(16, :USD)}, + {a: Money.new( 2, :USD), b: -4, c: Money.new((1/16), :USD)}, + {a: Money.new(-2, :USD), b: 4, c: Money.new(16, :USD)}, + {a: Money.new(-2, :USD), b: -4, c: Money.new(-(1/16), :USD)}, + {a: Money.new(2, :USD), b: 0, c: Money.new(1, :USD)}, + {a: Money.new(-2, :USD), b: 0, c: Money.new(1, :USD)}, + ] + ts.each do |t| + expect(t[:a] ** t[:b]).to eq t[:c] + end + end + + it "does exponantiate Money by Money (same currency)" do + expect(Money.new(10, :USD) ** Money.new(0, :USD)).to eq Money.new(1, :USD) + end + + it "does not exponentiate Money by Money (different currency)" do + expect { Money.new(10, :USD) ** Money.new(4, :EUR) }.to raise_error(TypeError) + end + + it "does not exponentiate Money by an object which is not a number" do + expect { Money.new(10, :USD) ** 'abc' }.to raise_error(TypeError) + end + + it "preserves the class in the result when using a subclass of Money" do + special_money_class = Class.new(Money) + expect(special_money_class.new(10_00, "USD") ** 2).to be_a special_money_class + end + + it_behaves_like 'instance with custom bank', :**, 1 + end + describe "#/" do it "divides Money by Integer and returns Money" do ts = [