cashify 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0d31e15d5b41014afa8408f2eb88f2951ceb129c8b1f189c0fb4783d506a9e99
4
+ data.tar.gz: 3d88a0eab4b01d2dbfb4ea67cc32c26e255d677f6d71ea34b056fcaca426a3ef
5
+ SHA512:
6
+ metadata.gz: 4fc2adb94912de958291b7a5aa878a89cde682aa1dad935136576e4df4a9705570b7bfc85fbeadb027e9d0478de0b269e4482fbefbbe664f0914c2d8b83247ac
7
+ data.tar.gz: 8dac497d6a56923a1d7dadfe96ef64dba8e5fb7aba33b1278e392b8759b83c4ee0f6515a8c7e55c8a0ac7d948b4298cbc2e210449415bc93b19fecafc7377b14
data/.rubocop.yml ADDED
@@ -0,0 +1,26 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0.0
3
+ NewCops: enable
4
+
5
+ Layout/LineLength:
6
+ Max: 120
7
+
8
+ Metrics/CyclomaticComplexity:
9
+ Max: 8
10
+
11
+ Metrics/MethodLength:
12
+ Max: 16
13
+
14
+ Style/Documentation:
15
+ Enabled: false
16
+
17
+ Style/FrozenStringLiteralComment:
18
+ EnforcedStyle: never
19
+
20
+ Style/StringLiterals:
21
+ Enabled: true
22
+ EnforcedStyle: double_quotes
23
+
24
+ Style/StringLiteralsInInterpolation:
25
+ Enabled: true
26
+ EnforcedStyle: double_quotes
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-3.0.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## [0.9.1] - 2021-10-08
2
+
3
+ - Add `cashify` method for Active Record users (Johan Halse)
4
+
5
+ ## [0.9.0] - 2021-09-24
6
+
7
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in cashify.gemspec
4
+ gemspec
5
+
6
+ gem "minitest", "~> 5.0"
7
+ gem "pry"
8
+ gem "rake", "~> 13.0"
9
+ gem "rubocop"
10
+ gem "rubocop-minitest"
11
+ gem "rubocop-rake"
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cashify (0.9.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ coderay (1.1.3)
11
+ method_source (1.0.0)
12
+ minitest (5.14.4)
13
+ parallel (1.21.0)
14
+ parser (3.0.2.0)
15
+ ast (~> 2.4.1)
16
+ pry (0.14.1)
17
+ coderay (~> 1.1)
18
+ method_source (~> 1.0)
19
+ rainbow (3.0.0)
20
+ rake (13.0.6)
21
+ regexp_parser (2.1.1)
22
+ rexml (3.2.5)
23
+ rubocop (1.22.1)
24
+ parallel (~> 1.10)
25
+ parser (>= 3.0.0.0)
26
+ rainbow (>= 2.2.2, < 4.0)
27
+ regexp_parser (>= 1.8, < 3.0)
28
+ rexml
29
+ rubocop-ast (>= 1.12.0, < 2.0)
30
+ ruby-progressbar (~> 1.7)
31
+ unicode-display_width (>= 1.4.0, < 3.0)
32
+ rubocop-ast (1.12.0)
33
+ parser (>= 3.0.1.1)
34
+ rubocop-minitest (0.15.1)
35
+ rubocop (>= 0.90, < 2.0)
36
+ rubocop-rake (0.6.0)
37
+ rubocop (~> 1.0)
38
+ ruby-progressbar (1.11.0)
39
+ unicode-display_width (2.1.0)
40
+
41
+ PLATFORMS
42
+ x86_64-darwin-19
43
+ x86_64-linux
44
+
45
+ DEPENDENCIES
46
+ cashify!
47
+ minitest (~> 5.0)
48
+ pry
49
+ rake (~> 13.0)
50
+ rubocop
51
+ rubocop-minitest
52
+ rubocop-rake
53
+
54
+ BUNDLED WITH
55
+ 2.2.26
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Johan Halse
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,227 @@
1
+ [![Main](https://github.com/johanhalse/cashify/actions/workflows/main.yml/badge.svg)](https://github.com/johanhalse/cashify/actions/workflows/main.yml)
2
+
3
+ # Cashify
4
+
5
+ Ever had to work with money in more than one currency? Ever wish you could do stuff like add them to one another without getting murdered by exceptions? Here's the gem for you.
6
+
7
+ ![Cashify](https://media4.giphy.com/media/EnoO73pTnn99JrnRR3/giphy.gif)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem "cashify"
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle install
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install cashify
24
+
25
+ ## Usage
26
+
27
+ Cashify is a money handling gem that's inherently nice to currencies. It lets you treat your money as mostly numbers, freeing you from a lot of headache around manipulating and handling it. Instantiate a new `Cash` object like so:
28
+
29
+ ```ruby
30
+ puts Cash.new(SEK: 100_00)
31
+ # 100 SEK
32
+ ```
33
+
34
+ Add another currency? Sure.
35
+
36
+ ```ruby
37
+ puts Cash.new(EUR: 100_00, SEK: 100_00)
38
+ # 100 EUR, 100 SEK
39
+ ```
40
+
41
+ And then maybe do some addition or subtraction?
42
+
43
+ ```ruby
44
+ puts Cash.new(SEK: 100_00) + Cash.new(USD: 100_00) + Cash.new(USD: 50_00)
45
+ # 100 SEK, 150 USD
46
+ ```
47
+
48
+ Perhaps add a little discount here or there?
49
+
50
+ ```ruby
51
+ puts (Cash.new(SEK: 100_00) + Cash.new(USD: 100_00)) * 0.9
52
+ # 90 SEK, 90 USD
53
+ ```
54
+
55
+ Increase the values a little? Why not!
56
+
57
+ ```ruby
58
+ puts (Cash.new(SEK: 100_00, USD: 100_00) + 20
59
+ # 120 SEK, 120 USD
60
+ ```
61
+
62
+ ### What. You can't just add integers to currencies
63
+
64
+ I can, in fact, totally add integers to currencies. And now you can, too! Also subtract, divide, multiply... look: this probably sounds bad to you. Foreign, somehow. Your parents raised you well, and you've already used the ubiquitous [Money](https://github.com/RubyMoney/money) gem and tried to add a shipping cost or something. The Money gem then told you to stop immediately and stomped off into Exception Land, leaving you empty-handed and full of regret.
65
+
66
+ Or maybe you wanted to work with zero? You'll get answers on the Internet saying things like "aha but what would zero even mean when using a currency" and my answer to that would be "well at least 0 USD is the same as 0 SEK, right?" and so you can casually do `Cash.new(SEK: 0) + Cash.new(USD: 100)` and get `100 USD` back and then imagine these pedants popping a vein and dying. A zero is a zero. Don't try to tell me otherwise.
67
+
68
+ ### Can you _really_ compare currencies that way though?
69
+
70
+ Well, I guess we can! And we're not even done. Check this out:
71
+
72
+ ```ruby
73
+ Cash.new(SEK: 100_00) > Cash.new(USD: 50_00)
74
+ # true
75
+ ```
76
+
77
+ Is this right or wrong? Who even knows at this point. But it's _useful_, and that's all we care about! I'll agree that the comparison is super naive, and you can Freedom Patch that if you like, or go the Money Gem route and pipe those objects through your own functions. But you see where we're going, right? Wouldn't it be nice to just grab various sums of currencies from your database, cast them to Cash objects, and have them just sort of _work_ without putting up guardrails everywhere? Yes it would. The default assumption of "when you try to add one money to another money that probably means you want a conversion somewhere" is wrong in most cases. Also, initialization matters! Cashify is very database friendly. Imagine a Rails app with `Purchase` objects that all have a cost. Then look at this beauty:
78
+
79
+ ```ruby
80
+ Cash.new Purchase.all.group(:cost_currency).sum(:cost_cents)
81
+ ```
82
+
83
+ That piece of code pulls _however many objects in however many currencies_ and tallies them up in SQL, returning a useful Cash object containing something along the lines of `1100 EUR, 1400 SEK, 2155 USD`. If you want, you can smack a multiplier on those and add a discount. Or add a processing fee of `100 SEK`. You be the boss.
84
+
85
+ ### This sounds dangerous and illegal, shouldn't these operations throw exceptions?
86
+
87
+ No, they probably shouldn't. The Money Gem school of thought says that every time you do something potentially hazardous to your money — like try to add an integer, not caring which currency you're dealing with — there's an exception, and then you'll have to handle that exception in a way that makes sense for your use case. I do see the logic, but in practice it gets old REAL fast. You'll be stuck in a never-ending shouting match with your money objects, having to drag them kicking and screaming through a bunch of guard statements in order to do simple things like addition. If you're only dealing with a single currency it's not too bad, but as soon as you add another one to the mix the noise level is deafening. And honestly: if you're only dealing with a single currency, why are you even using a money gem? You could use fixed point integers instead, pushing any currency-specific stuff to the boundaries of your code.
88
+
89
+ This in mind, Cashify posits that if you want to get these things right, you should be treating your money as simple numbers as often as possible. Currencies are a necessary evil but they're not your overarching concern, and this gem will allow them to lurk in the background where they belong. Throw those Cash objects around, stuff them into arrays, collide them in interesting ways. Swim around in your money like you're Scrooge McDuck and let your database do most of the work for you! It'll feel great, I promise.
90
+
91
+ ### Do you handle displaying currencies nicely, too?
92
+
93
+ Oof, sounds hard. Out of scope. Write a view helper or something.
94
+
95
+ ### And when you actually do want to convert currencies?
96
+
97
+ There are plenty of gems for that and it's honestly pretty easy. But you'll have to write your own code for it.
98
+
99
+ ## API
100
+
101
+ #### `.positive?` and `.negative?`
102
+
103
+ Returns true if all currencies are positive/negative.
104
+
105
+ ```ruby
106
+ Cash.new(SEK: 100_00, USD: 100_00).positive?
107
+ # true
108
+
109
+ Cash.new(SEK: 100_00, USD: -1_00).positive?
110
+ # false
111
+
112
+ Cash.new(SEK: -100_00, USD: -100_00).negative?
113
+ # true
114
+ ```
115
+
116
+ #### `.zero?`
117
+
118
+ Returns true if all currencies are zero.
119
+
120
+ ```ruby
121
+ Cash.new(SEK: 0).zero?
122
+ # true
123
+
124
+ Cash.new(SEK: 0, USD: 1_00).zero?
125
+ # false
126
+ ```
127
+
128
+ #### `.abs`
129
+
130
+ Makes all values positive.
131
+
132
+ ```ruby
133
+ Cash.new(SEK: -100_00, USD: 1_00).abs
134
+ # 100 SEK, 1 USD
135
+ ```
136
+
137
+ #### `.zero`
138
+
139
+ Shorthand for a zero currency. Use it instead of `Cash.new(DKK: 0)` because having to specify currency for zero feels wordy and awkward.
140
+
141
+ ```ruby
142
+ Cash.zero
143
+ # 0 USD
144
+ ```
145
+
146
+ #### `.to_a`
147
+
148
+ Splits all the currencies out into an array of `Cash` objects.
149
+
150
+ ```ruby
151
+ Cash.new(EUR: 100_00, SEK: 100_00, USD: 100_00).to_a
152
+ # [Cash(EUR: 100_00), Cash(SEK: 100_00), Cash(USD: 100_00)]
153
+ ```
154
+
155
+ #### `.to_s`
156
+
157
+ Get a simple string back.
158
+
159
+ ```ruby
160
+ Cash.new(EUR: 100_00, SEK: 100_00, USD: 100_00).to_s
161
+ # "100 EUR, 100 SEK, 100 USD"
162
+ ```
163
+
164
+ #### `.sum`
165
+
166
+ You'll be working with arrays of Cash objects a lot. You should absolutely be using Ruby's excellent array methods on them, but unfortunately `cash_array.sum` won't work. Ruby's `sum` method injects a `0` as the first value, and even though `Cash.new(USD: 100) + 0` is super valid and nice, `0 + Cash.new(USD: 100)` will try to coerce your Cash object into an integer which just won't work. So if you want to sum an array of Cash, you can instead use:
167
+
168
+ ```ruby
169
+ Cash.sum [Cash.new(EUR: 100_00), Cash.new(SEK: 100_00), Cash.new(EUR: 100_00)]
170
+ # 200 EUR, 100 SEK
171
+ ```
172
+
173
+ #### `.empty?`
174
+
175
+ Returns true if there are no currencies in the object.
176
+
177
+ ```ruby
178
+ Cash.new.empty?
179
+ # true
180
+
181
+ Cash.new(NOK: 100_00).empty?
182
+ # false
183
+ ```
184
+
185
+ #### `.round`
186
+
187
+ Rounds currencies to a power interval.
188
+
189
+ ```ruby
190
+ Cash.new(EUR: 100_50, SEK: 122_25).round(100)
191
+ # 101 EUR, 122 SEK
192
+
193
+ Cash.new(EUR: 100_50, SEK: 122_25).round(1000)
194
+ # 100 EUR, 120 SEK
195
+ ```
196
+
197
+ #### `.currency`
198
+
199
+ Returns the first currency in the Cash object as a symbol
200
+
201
+ ```ruby
202
+ Cash.new(NOK: 100).currency
203
+ # :NOK
204
+ ```
205
+
206
+ #### `.value`
207
+
208
+ Returns the first value in the Cash object.
209
+
210
+ ```ruby
211
+ Cash.new(NOK: 100).value
212
+ # 100
213
+ ```
214
+
215
+ ## Development
216
+
217
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
218
+
219
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
220
+
221
+ ## Contributing
222
+
223
+ Bug reports and pull requests are welcome on GitHub at https://github.com/johanhalse/cashify.
224
+
225
+ ## License
226
+
227
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ require "rubocop/rake_task"
11
+
12
+ RuboCop::RakeTask.new
13
+
14
+ task default: %i[test rubocop]
data/bin/console ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require "bundler/setup"
3
+ require "cashify"
4
+
5
+ # You can add fixtures and/or initialization code here to make experimenting
6
+ # with your gem easier. You can also use a different console, if you like.
7
+
8
+ # (If you use this, don't forget to add pry to your Gemfile!)
9
+ # require "pry"
10
+ # Pry.start
11
+
12
+ require "irb"
13
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,83 @@
1
+ class Cash
2
+ module Arithmetic
3
+ def +(other)
4
+ raise Cash::Errors::AdditionError unless other.respond_to?(:zero?)
5
+ return self unless present?(other)
6
+ return self if other.zero?
7
+ return other if zero?
8
+
9
+ return Cash.new(**add_currencies(other)) if other.is_a?(Cash)
10
+
11
+ Cash.new(**add_number(other)) if other.is_a?(Integer) || other.is_a?(Float)
12
+ end
13
+
14
+ def -(other)
15
+ raise Cash::Errors::SubtractionError unless other.respond_to?(:zero?)
16
+ return self unless present?(other)
17
+ return Cash.new(**subtract_currencies(other)) if other.is_a?(Cash) || other.zero?
18
+
19
+ Cash.new(**subtract_number(other)) if other.is_a?(Integer) || other.is_a?(Float)
20
+ end
21
+
22
+ def *(other)
23
+ return Cash.new(**multiply_number(other)) if other.is_a?(Integer) || other.is_a?(Float)
24
+ return Cash.new(**multiply_currencies(other)) if other.is_a?(Cash)
25
+
26
+ raise Cash::Errors::MultiplicationError
27
+ end
28
+
29
+ def /(other)
30
+ return Cash.new(**divide_number(other)) if other.is_a?(Integer) || other.is_a?(Float)
31
+ return Cash.new(**divide_currencies(other)) if other.is_a?(Cash)
32
+
33
+ raise Cash::Errors::DivisionError
34
+ end
35
+
36
+ private
37
+
38
+ def present?(other)
39
+ !(other.nil? || other == "" || other == false)
40
+ end
41
+
42
+ def multiply_number(other)
43
+ currencies.transform_values { |v| v * other }
44
+ end
45
+
46
+ def multiply_currencies(other)
47
+ currencies.merge(other.currencies) { |_k, a, b| a * b }
48
+ end
49
+
50
+ def divide_number(other)
51
+ currencies.transform_values { |v| v / other }
52
+ end
53
+
54
+ def divide_currencies(other)
55
+ currencies.merge(other.currencies) { |_k, a, b| a / b }
56
+ end
57
+
58
+ def add_currencies(other)
59
+ currencies.merge(other.currencies) { |_k, a, b| a + b }
60
+ end
61
+
62
+ def add_number(other)
63
+ currencies.transform_values { |v| v + other }
64
+ end
65
+
66
+ def subtract_number(other)
67
+ currencies.transform_values { |v| v - other }
68
+ end
69
+
70
+ def subtract_currencies(other) # rubocop:disable Metrics/AbcSize
71
+ return currencies unless other.respond_to?(:zero?)
72
+ return currencies if other.zero?
73
+ return other.currencies.transform_values { |v| 0 - v } if zero?
74
+
75
+ (@currencies.keys + other.currencies.keys).uniq.map do |k|
76
+ minuend = @currencies[k] || 0
77
+ subtrahend = other.currencies[k] || 0
78
+
79
+ [k, minuend - subtrahend]
80
+ end.to_h
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,25 @@
1
+ class Cash
2
+ module Cashify
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def self.cashify(*fields)
7
+ fields.each do |field_name|
8
+ cents_field = "#{field_name}_cents"
9
+ currency_field = "#{field_name}_currency"
10
+
11
+ define_method(field_name) do
12
+ return nil if read_attribute(currency_field).nil? || read_attribute(cents_field).nil?
13
+
14
+ Cash.new(read_attribute(currency_field) => read_attribute(cents_field))
15
+ end
16
+
17
+ define_method("#{field_name}=") do |cash|
18
+ write_attribute(cents_field, cash.value)
19
+ write_attribute(currency_field, cash.currency)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ class Cash
2
+ module Errors
3
+ class AdditionError < StandardError; end
4
+
5
+ class SubtractionError < StandardError; end
6
+
7
+ class MultiplicationError < StandardError; end
8
+
9
+ class DivisionError < StandardError; end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Cashify
2
+ VERSION = "0.9.1".freeze
3
+ end
data/lib/cashify.rb ADDED
@@ -0,0 +1,91 @@
1
+ require_relative "cashify/version"
2
+ require_relative "cashify/arithmetic"
3
+ require_relative "cashify/errors"
4
+
5
+ if defined?(::Rails::Railtie)
6
+ require_relative "cashify/cashify"
7
+
8
+ class CashifyRailtie < Rails::Railtie
9
+ initializer "cashify.configure_rails_initialization" do
10
+ ActiveSupport.on_load(:active_record) do
11
+ ::ActiveRecord::Base.include(Cash::Cashify)
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ class Cash
18
+ include Cash::Arithmetic
19
+ include Cash::Errors
20
+ include Comparable
21
+
22
+ attr_accessor :currencies
23
+
24
+ def self.zero
25
+ new(USD: 0)
26
+ end
27
+
28
+ def self.sum(cashes)
29
+ cashes.inject(Cash.zero, :+)
30
+ end
31
+
32
+ def initialize(**currencies)
33
+ @currencies = currencies
34
+ .transform_keys(&:to_sym)
35
+ .transform_values(&:to_i)
36
+ .sort.to_h
37
+ end
38
+
39
+ def ==(other)
40
+ return currencies == other.currencies || (zero? && other.zero?) if other.is_a?(Cash)
41
+
42
+ zero? && (other.respond_to?(:zero?) && other.zero?)
43
+ end
44
+
45
+ def zero?
46
+ currencies.values.all?(&:zero?)
47
+ end
48
+
49
+ def positive?
50
+ currencies.values.all?(&:positive?)
51
+ end
52
+
53
+ def negative?
54
+ currencies.values.all?(&:negative?)
55
+ end
56
+
57
+ def <=>(other)
58
+ other_sum = other if other.is_a?(Integer) || other.is_a?(Float)
59
+ other_sum = other.currencies.values.sum if other.is_a?(Cash)
60
+
61
+ currencies.values.sum <=> other_sum
62
+ end
63
+
64
+ def to_a
65
+ currencies.map { |k, v| Cash.new(k => v) }
66
+ end
67
+
68
+ def to_s
69
+ currencies.map { |k, v| "#{(v * 0.01).round} #{k}" }.join(", ")
70
+ end
71
+
72
+ def currency
73
+ currencies.keys.first
74
+ end
75
+
76
+ def value
77
+ currencies.values.first
78
+ end
79
+
80
+ def empty?
81
+ currencies.empty?
82
+ end
83
+
84
+ def abs
85
+ Cash.new(**currencies.transform_values(&:abs))
86
+ end
87
+
88
+ def round(interval = 100)
89
+ Cash.new(**currencies.transform_values { |v| (v / interval.to_f).round * interval })
90
+ end
91
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cashify
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.1
5
+ platform: ruby
6
+ authors:
7
+ - Johan Halse
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-10-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Money trouble no more! Add, subtract, and have fun with money in different
14
+ currencies.
15
+ email:
16
+ - johan@hal.se
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".rubocop.yml"
22
+ - ".ruby-version"
23
+ - CHANGELOG.md
24
+ - Gemfile
25
+ - Gemfile.lock
26
+ - LICENSE.txt
27
+ - README.md
28
+ - Rakefile
29
+ - bin/console
30
+ - bin/setup
31
+ - lib/cashify.rb
32
+ - lib/cashify/arithmetic.rb
33
+ - lib/cashify/cashify.rb
34
+ - lib/cashify/errors.rb
35
+ - lib/cashify/version.rb
36
+ homepage: https://github.com/johanhalse/cashify
37
+ licenses:
38
+ - MIT
39
+ metadata:
40
+ allowed_push_host: https://rubygems.org
41
+ homepage_uri: https://github.com/johanhalse/cashify
42
+ source_code_uri: https://github.com/johanhalse/cashify
43
+ changelog_uri: https://github.com/johanhalse/cashify/CHANGELOG.md
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.0.0
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubygems_version: 3.2.22
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: A sensible way to handle money
63
+ test_files: []