experimental-money 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 635267ff25b3ccfccd66e5f8394f63d398b9fe5d
4
+ data.tar.gz: 433e055f791c12cf60eb71d9be04cf51d75ced6c
5
+ SHA512:
6
+ metadata.gz: 4ea02386d29e47978d7ccda94291dd5ba71a11916bf8ab161ef99bfbe212e950bce85dea295a3e53ef59341ff6d9c11a11f94a590c15c6d8e877d40072ca33cd
7
+ data.tar.gz: 326812d7421639b0eeb7f1f3e753fa73f99a2cd9f6679de31761366fb0387176b625812e8529ac8e6796d4e9efc191bf540a1af4dc54d822abb26ef91bbef0ff
data/.gems ADDED
@@ -0,0 +1 @@
1
+ cutest -v 1.2.3
@@ -0,0 +1,71 @@
1
+ # Money test
2
+
3
+ Crear una gema que pueda manejar las diferentes conversiones de monedas, hacer operaciones aritméticas y demas.
4
+ Las caractersticas y el uso serán descritos a continuacion
5
+
6
+ Configurar la moneda default y sus respectivas conversiones (here, EUR)
7
+ ```
8
+ Money.configure do |config|
9
+ config.default_currency = "EUR"
10
+ config.conversions = {
11
+ 'USD' => 1.11,
12
+ 'Bitcoin' => 0.0047
13
+ }
14
+ end
15
+ ```
16
+
17
+ #### Instanciar objeto
18
+ ```
19
+ fifty_eur = Money.new(50, 'EUR')
20
+ ```
21
+
22
+ #### Get monto y moneda
23
+ ```
24
+ fifty_eur.amount # => 50
25
+ fifty_eur.currency # => "EUR"
26
+ fifty_eur.inspect # => "50.00 EUR"
27
+ ```
28
+
29
+ #### Convertir a otra moneda (Debe devolver una instancia, no un string)
30
+ ```
31
+ fifty_eur.convert_to('USD') # => 55.50 USD
32
+ ```
33
+
34
+ #### Realizar operaciones aritmeticas
35
+ ```
36
+ twenty_dollars = Money.new(20, 'USD')
37
+ ```
38
+
39
+ ### Aritmeticas:
40
+ ```
41
+ fifty_eur + twenty_dollars # => 68.02 EUR
42
+ fifty_eur - twenty_dollars # => 31.98 EUR
43
+ fifty_eur / 2 # => 25 EUR
44
+ twenty_dollars * 3 # => 60 USD
45
+ ```
46
+
47
+ #### Comparaciones (también en diferentes monedas):
48
+ ```
49
+ twenty_dollars == Money.new(20, 'USD') # => true
50
+ twenty_dollars == Money.new(30, 'USD') # => false
51
+
52
+ fifty_eur_in_usd = fifty_eur.convert_to('USD')
53
+ fifty_eur_in_usd == fifty_eur # => true
54
+
55
+ twenty_dollars > Money.new(5, 'USD') # => true
56
+ twenty_dollars < fifty_eur # => true
57
+ ```
58
+
59
+ La idea del test es que cuente con sus respectivos tests. Además se analizará calidad del codigo.
60
+ Objeto Money no debe conetener más caracteristicas que las mencionadas, please keep it simple.
61
+ Tambien la gema debe estar subida a RubyGems y debe ser instalable desde
62
+ ```
63
+ gem install your_gem
64
+ ```
65
+
66
+ y debe ser capaz de ser cargada desde irb
67
+ ```
68
+ irb
69
+ require 'your_gem'
70
+ Money.new
71
+ ```
@@ -0,0 +1,100 @@
1
+ Experimental Money
2
+ ====
3
+
4
+
5
+ Installation
6
+ ------------
7
+
8
+ ``` console
9
+ $ gem install experimental-money
10
+ ```
11
+
12
+ Usage
13
+ -----
14
+
15
+ Here's a simple example:
16
+
17
+ ``` ruby
18
+ Money.configure do |config|
19
+ config.default_currency = "EUR"
20
+ config.conversions = {
21
+ 'USD' => 1.11,
22
+ 'Bitcoin' => 0.0047
23
+ }
24
+ end
25
+
26
+ fifty_eur = Money.new(50, 'EUR')
27
+ fifty_eur.amount # => 50
28
+ fifty_eur.currency # => "EUR"
29
+ fifty_eur.inspect # => "50.00 EUR"
30
+
31
+ # Currency exchange
32
+ fifty_eur.convert_to('USD') # => 55.50 USD
33
+
34
+ # Arithmetic
35
+
36
+ twenty_dollars = Money.new(20, 'USD')
37
+ fifty_eur + twenty_dollars # => 68.02 EUR
38
+ fifty_eur - twenty_dollars # => 31.98 EUR
39
+ fifty_eur / 2 # => 25 EUR
40
+ twenty_dollars * 3 # => 60 USD
41
+
42
+ # Comparations
43
+
44
+ twenty_dollars == Money.new(20, 'USD') # => true
45
+ twenty_dollars == Money.new(30, 'USD') # => false
46
+
47
+ fifty_eur_in_usd = fifty_eur.convert_to('USD')
48
+ fifty_eur_in_usd == fifty_eur # => true
49
+
50
+ twenty_dollars > Money.new(5, 'USD') # => true
51
+ twenty_dollars < fifty_eur # => true
52
+
53
+ ```
54
+
55
+ Caveats
56
+ ------------
57
+
58
+ - We are using BigDecimals. Another option could be using Integers
59
+ instead. As our desired API expects to initialize Money as `Money.new(float_amount, currency)` we already needed to convert the amount to BigDecimal in order to avoid `Float` precision errors. Integers should work way faster, though.
60
+ - Precision and rounding: When talking about money we need to know its
61
+ currency precision. The normal case is having two decimal positions
62
+ in order to represent `cents`. Other currencies use up to 8 decimal
63
+ places (to represent `Satoshis`).
64
+
65
+ I arbitrarily set up Bitcoin precision (8) and set a default of (2)
66
+ for the remaining currencies. A pull request would be needed to add
67
+ weird currencies with different precision.
68
+
69
+ Rounding:
70
+ When dividing money (or converting it to another currency -which
71
+ involves a division) we end up often with a number that has to be
72
+ rounded. There are many ways to round 0.006 to a two decimal places
73
+ number representing USD. I hardcoded the Bankers' rounding way, but
74
+ for some problems it may be suitable another method.
75
+
76
+ - Last caveat: Arithmetic doesn't always represent Money operations as
77
+ we expect. If we need to divide 100USD among three people and we try
78
+ to do `Money.new(100, "USD")/3` to know how much each of them is
79
+ receiving we'll find a surprising result: It's not possible to
80
+ equally divide 100USD. With the current implementation this operation
81
+ would return `Money.new(33.33, "USD")`
82
+
83
+
84
+ Contributing
85
+ ------------
86
+
87
+ If you want to test this gem, you may want to use a gemset to isolate
88
+ the requirements. We recommend the use of tools like [dep][dep] and
89
+ [gs][gs], but you can use similar tools like [gst][gst] or [bs][bs].
90
+
91
+ The required gems for testing and development are listed in the
92
+ `.gems` file. If you are using [dep][dep], you can create a gemset
93
+ and run `dep install`.
94
+
95
+ After `cutest` is installed you can run the tests by doing `make`
96
+
97
+ [dep]: http://cyx.github.io/dep/
98
+ [gs]: http://soveran.github.io/gs/
99
+ [gst]: https://github.com/tonchis/gst
100
+ [bs]: https://github.com/educabilia/bs
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "experimental-money"
3
+ s.version = "0.0.1"
4
+ s.summary = "Money arithmetic with BigDecimal"
5
+ s.description = "Money arithmetic with BigDecimal"
6
+ s.authors = ["CarlosIPe"]
7
+ s.email = ["carlos2@compendium.com.ar"]
8
+ s.homepage = "https://github.com/carlosipe/experimental-money"
9
+ s.license = "MIT"
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+
13
+ s.add_development_dependency "cutest", '~> 1'
14
+ end
@@ -0,0 +1,88 @@
1
+ require 'bigdecimal'
2
+
3
+ class Money
4
+ class << self
5
+ attr_writer :configuration
6
+
7
+ def configuration
8
+ @configuration ||= Configuration.new
9
+ end
10
+
11
+ def configure
12
+ yield(configuration)
13
+ end
14
+ end
15
+
16
+ class Configuration
17
+ attr_writer :default_currency
18
+ def conversions=(hash)
19
+ @conversions = hash.map { |k, v| [k, BigDecimal(v.to_s)] }.to_h
20
+ end
21
+
22
+ def conversions
23
+ raise('No conversions configured') unless @conversions
24
+ @conversions.merge(default_currency => 1)
25
+ end
26
+
27
+ def default_currency
28
+ raise('Default currency is not configured') unless @default_currency
29
+ @default_currency
30
+ end
31
+ end
32
+
33
+ include Comparable
34
+
35
+ attr_reader :currency, :amount
36
+ def initialize(amount, currency)
37
+ raise 'Invalid currency' unless conversions.member?(currency)
38
+ @currency = currency
39
+ @amount = BigDecimal(amount.to_s).round(currency_precision)
40
+ end
41
+
42
+ def currency_precision
43
+ {
44
+ 'Bitcoin' => 8,
45
+ }.fetch(currency, 2)
46
+ end
47
+
48
+ def convert_to(new_currency)
49
+ new_amount = amount * conversions.fetch(new_currency) / conversions.fetch(currency)
50
+ self.class.new(new_amount, new_currency)
51
+ end
52
+
53
+ def <=>(other)
54
+ amount <=> other.convert_to(currency).amount
55
+ end
56
+
57
+ def inspect
58
+ "#{format('%.2f', amount)} #{currency}"
59
+ end
60
+
61
+ def conversions
62
+ self.class.configuration.conversions
63
+ end
64
+
65
+ def +(other)
66
+ return Money.new(amount + other.amount, currency) if currency == other.currency
67
+ total = convert_to(default_currency).amount + other.convert_to(default_currency).amount
68
+ self.class.new(total, default_currency)
69
+ end
70
+
71
+ def -(other)
72
+ return Money.new(amount - other.amount, currency) if currency == other.currency
73
+ total = convert_to(default_currency).amount - other.convert_to(default_currency).amount
74
+ self.class.new(total, default_currency)
75
+ end
76
+
77
+ def /(num)
78
+ Money.new(amount / num, currency)
79
+ end
80
+
81
+ def *(num)
82
+ Money.new(amount * num, currency)
83
+ end
84
+
85
+ def default_currency
86
+ self.class.configuration.default_currency
87
+ end
88
+ end
@@ -0,0 +1,4 @@
1
+ .PHONY: test
2
+
3
+ test:
4
+ cutest ./test/*.rb
@@ -0,0 +1,67 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
2
+ require 'money'
3
+
4
+ Money.configure do |config|
5
+ config.default_currency = 'EUR'
6
+ config.conversions = {
7
+ 'USD' => 1.11,
8
+ 'Bitcoin' => 0.0047
9
+ }
10
+ end
11
+
12
+ def fifty_eur
13
+ Money.new(50, 'EUR')
14
+ end
15
+
16
+ test '.amount' do
17
+ assert_equal(fifty_eur.amount, 50)
18
+ end
19
+
20
+ test '.currency' do
21
+ assert_equal(fifty_eur.currency, 'EUR')
22
+ end
23
+
24
+ test '.inspect' do
25
+ assert_equal(fifty_eur.inspect, '50.00 EUR')
26
+ end
27
+
28
+ test '.convert_to' do
29
+ assert_equal(fifty_eur.convert_to('USD'), Money.new(55.50, 'USD'))
30
+ end
31
+
32
+ test 'sum different currencies' do
33
+ assert_equal(Money.new(50, 'EUR') + Money.new(20, 'USD'), Money.new(68.02, 'EUR'))
34
+ end
35
+
36
+ test 'subtract different currencies' do
37
+ assert_equal(Money.new(50, 'EUR') - Money.new(20, 'USD'), Money.new(31.98, 'EUR'))
38
+ end
39
+
40
+ test 'dividing money by scalar' do
41
+ assert_equal(Money.new(50, 'EUR') / 2, Money.new(25, 'EUR'))
42
+ end
43
+
44
+ test 'multiplying money by scalar' do
45
+ assert_equal(Money.new(20, 'USD') * 3, Money.new(60, 'USD'))
46
+ end
47
+
48
+ test 'Equal compare on same amount and currency' do
49
+ assert_equal(Money.new(20, 'USD'), Money.new(20, 'USD'))
50
+ end
51
+
52
+ test 'not equal on same currency and different amount' do
53
+ assert Money.new(20, 'USD') != Money.new(30, 'USD')
54
+ end
55
+
56
+ test 'equal on different currencies but equivalent amount' do
57
+ fifty_eur_in_usd = fifty_eur.convert_to('USD')
58
+ assert_equal(fifty_eur, fifty_eur_in_usd)
59
+ end
60
+
61
+ test 'greater than' do
62
+ assert Money.new(20, 'USD') > Money.new(5, 'USD')
63
+ end
64
+
65
+ test 'less than' do
66
+ assert Money.new(20, 'USD') < Money.new(50, 'EUR')
67
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: experimental-money
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - CarlosIPe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cutest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ description: Money arithmetic with BigDecimal
28
+ email:
29
+ - carlos2@compendium.com.ar
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gems"
35
+ - Challenge.md
36
+ - README.md
37
+ - experimental-money.gemspec
38
+ - lib/experimental-money.rb
39
+ - makefile
40
+ - test/money.rb
41
+ homepage: https://github.com/carlosipe/experimental-money
42
+ licenses:
43
+ - MIT
44
+ metadata: {}
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 2.5.1
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: Money arithmetic with BigDecimal
65
+ test_files: []