experimental-money 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []