latinum 0.2.0 → 0.2.3

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.
data/README.md CHANGED
@@ -7,6 +7,83 @@ Latinum
7
7
 
8
8
  Latinum is a framework for resource and currency calculations. *It is currently a work in progress and not designed to be taken seriously at this time*.
9
9
 
10
+ Basic Usage
11
+ -----------
12
+
13
+ Latinum has several core concepts:
14
+
15
+ - A `Resource` represents an immutable value with a specific face name (e.g. `'USD'`).
16
+ - A `Resource` can only be combined with resources with the same face name.
17
+ - A `Bank` is responsible for managing currencies and formatting options.
18
+ - A `Bank` can exchange currencies explicitly with a given set of exchange rates.
19
+ - A `Collection` is responsible for adding currencies together and is completely deterministic.
20
+
21
+ ### Resources and Collections ###
22
+
23
+ To create a new resource, use a string for accuracy:
24
+
25
+ > ten = Latinum::Resource.new("10.00", "NZD")
26
+ => 10.0 NZD
27
+ > ten.amount == "10.00".to_d
28
+ => true
29
+
30
+ You can add resources of different values but with the same name:
31
+
32
+ > ten + ten
33
+ => 20.0 NZD
34
+
35
+ But, you can't add resources of different names together:
36
+
37
+ > twenty = Latinum::Resource.new("20.00", "AUD")
38
+ => 20.0 AUD
39
+ > ten + twenty
40
+ ArgumentError: Cannot operate on different currencies!
41
+
42
+ To add multiple currencies together, use a collection:
43
+
44
+ > currencies = Set.new
45
+ > collection = Latinum::Collection.new(currencies)
46
+ > collection << ten
47
+ > collection << twenty
48
+ > currencies.collect {|currency| collection[currency]}
49
+ => [10.0 NZD, 20.0 AUD]
50
+
51
+ ### Banks and Exchange Rates ###
52
+
53
+ The bank is responsible for formatting and exchange rates:
54
+
55
+ require 'latinum/bank'
56
+ require 'latinum/currencies/global'
57
+
58
+ > bank = Latinum::Bank.new(Latinum::Currencies::Global)
59
+ > bank << Latinum::ExchangeRate.new("NZD", "AUD", "0.5")
60
+
61
+ > nzd = Latinum::Resource.new("10", "NZD")
62
+ => 10.0 NZD
63
+ > aud = bank.exchange nzd, "AUD"
64
+ => 5.0 AUD
65
+
66
+ Formatting an amount is typically required for presentation to the end user:
67
+
68
+ > bank.format(nzd)
69
+ => "$10.00 NZD"
70
+
71
+ > bank.format(aud, :format => :compact)
72
+ => "$5.00"
73
+
74
+ The bank can also be used to parse currency, which will depend on the priority of currencies if a symbol that matches multiple currencies is supplied:
75
+
76
+ > bank.parse("$5")
77
+ => 5.0 USD
78
+
79
+ > bank.parse("€5")
80
+ => 5.0 EUR
81
+
82
+ Currency codes take priority over symbols if specified:
83
+
84
+ > bank.parse("€5 NZD")
85
+ => 5.0 NZD
86
+
10
87
  License
11
88
  -------
12
89
 
data/lib/latinum.rb CHANGED
@@ -18,5 +18,13 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require 'latinum/bank'
21
22
  require 'latinum/resource'
22
- require 'latinum/collection'
23
+ require 'latinum/collection'
24
+
25
+ require 'bigdecimal'
26
+ require 'bigdecimal/util'
27
+
28
+ if RUBY_VERSION < "1.9"
29
+ require 'latinum/extensions/bigdecimal-1.8'
30
+ end
data/lib/latinum/bank.rb CHANGED
@@ -25,7 +25,7 @@ module Latinum
25
25
  def initialize(input, output, factor)
26
26
  @input = input
27
27
  @output = output
28
- @factor = BigDecimal(factor)
28
+ @factor = factor.to_d
29
29
  end
30
30
 
31
31
  attr :input
@@ -38,6 +38,8 @@ module Latinum
38
38
  @rates = []
39
39
  @exchange = {}
40
40
 
41
+ # This implementation may change:
42
+ @currencies = {}
41
43
  @formatters = {}
42
44
 
43
45
  # Symbols and their associated priorities
@@ -52,8 +54,10 @@ module Latinum
52
54
  resources.each do |name, config|
53
55
  name = (config[:name] || name).to_s
54
56
 
57
+ @currencies[name] = config
58
+
55
59
  # Create a formatter:
56
- self[name] = config[:formatter].new(config)
60
+ @formatters[name] = config[:formatter].new(config)
57
61
 
58
62
  if config[:symbol]
59
63
  symbols = (@symbols[config[:symbol]] ||= [])
@@ -63,30 +67,32 @@ module Latinum
63
67
  end
64
68
  end
65
69
 
70
+ def [] name
71
+ @currencies[name]
72
+ end
73
+
66
74
  attr :rates
67
75
  attr :symbols
68
- attr :formatters
76
+ attr :currencies
69
77
 
70
- def << object
78
+ def << rate
71
79
  @rates << rate
72
80
 
73
81
  @exchange[rate.input] ||= {}
74
82
  @exchange[rate.input][rate.output] = rate
75
83
  end
76
84
 
77
- def []= name, formatter
78
- @formatters[name] = formatter
79
- end
80
-
81
85
  def exchange(resource, for_name)
82
86
  rate = @exchange[resource.name][for_name]
83
87
  raise ArgumentError.new("Invalid rate specified #{rate}") if rate == nil
84
88
 
85
- Resource.new(resource.amount * rate.factor, for_name)
89
+ config = self[for_name]
90
+
91
+ resource.exchange(rate.factor, for_name, config[:precision])
86
92
  end
87
93
 
88
94
  def parse(string)
89
- parts = string.strip.split(/\s+, 2/)
95
+ parts = string.strip.split(/\s+/, 2)
90
96
 
91
97
  if parts.size == 2
92
98
  Resource.new(parts[0].gsub(/[^\.0-9]/, ''), parts[1])
@@ -25,7 +25,7 @@ module Latinum
25
25
  class Collection
26
26
  def initialize(names = Set.new)
27
27
  @names = names
28
- @resources = Hash.new {|hash, key| @names << key; BigDecimal.new(0)}
28
+ @resources = Hash.new {|hash, key| @names << key; BigDecimal.new("0")}
29
29
  end
30
30
 
31
31
  attr :names
@@ -33,6 +33,8 @@ module Latinum
33
33
 
34
34
  def << resource
35
35
  @resources[resource.name] += resource.amount
36
+
37
+ return self
36
38
  end
37
39
 
38
40
  def [] key
@@ -27,6 +27,7 @@ module Latinum
27
27
  Global = {}
28
28
 
29
29
  Global[:NZD] = {
30
+ :precision => 2,
30
31
  :symbol => '$',
31
32
  :name => 'NZD',
32
33
  :description => 'New Zealand Dollar',
@@ -34,6 +35,7 @@ module Latinum
34
35
  }
35
36
 
36
37
  Global[:GBP] = {
38
+ :precision => 2,
37
39
  :symbol => '£',
38
40
  :name => 'GBP',
39
41
  :description => 'Pound Sterling',
@@ -41,6 +43,7 @@ module Latinum
41
43
  }
42
44
 
43
45
  Global[:AUD] = {
46
+ :precision => 2,
44
47
  :symbol => '$',
45
48
  :name => 'AUD',
46
49
  :description => 'Australian Dollar',
@@ -48,6 +51,7 @@ module Latinum
48
51
  }
49
52
 
50
53
  Global[:USD] = {
54
+ :precision => 2,
51
55
  :symbol => '$',
52
56
  :name => 'USD',
53
57
  :description => 'United States Dollar',
@@ -55,6 +59,7 @@ module Latinum
55
59
  }
56
60
 
57
61
  Global[:EUR] = {
62
+ :precision => 2,
58
63
  :symbol => '€',
59
64
  :name => 'EUR',
60
65
  :description => 'Euro',
@@ -62,5 +67,13 @@ module Latinum
62
67
  #:delimeter => '.',
63
68
  #:separator => ','
64
69
  }
70
+
71
+ Global[:JPY] = {
72
+ :precision => 0,
73
+ :symbol => '¥',
74
+ :name => 'JPY',
75
+ :description => 'Japanese Yen',
76
+ :formatter => Formatters::DecimalCurrencyFormatter
77
+ }
65
78
  end
66
79
  end
@@ -0,0 +1,6 @@
1
+
2
+ class BigDecimal
3
+ def to_d
4
+ self
5
+ end
6
+ end
@@ -39,14 +39,14 @@ module Latinum
39
39
  @symbol = options[:symbol] || '$'
40
40
  @separator = options[:separator] || '.'
41
41
  @delimeter = options[:delimter] || ','
42
- @places = options[:places] || 2
42
+ @places = options[:precision] || 2
43
43
  @zero = options[:zero] || '0'
44
44
 
45
45
  @name = options[:name]
46
46
  end
47
47
 
48
48
  def format(amount, options = DEFAULT_OPTIONS)
49
- fix, frac = amount.to_s('F').split(/\./, 2)
49
+ fix, frac = amount.abs.to_s('F').split(/\./, 2)
50
50
 
51
51
  # The sign of the number
52
52
  sign = amount.sign < 0 ? '-' : ''
@@ -29,19 +29,19 @@ module Latinum
29
29
  attr :name
30
30
 
31
31
  def initialize(amount, name)
32
- @amount = BigDecimal(amount)
32
+ @amount = amount.to_d
33
33
  @name = name
34
34
  end
35
35
 
36
36
  # By default, we can only add and subtract if the name is the same
37
37
  def + other
38
- throw ArgumentError.new("Cannot operate on different currencies!") if @name != other.name
38
+ raise ArgumentError.new("Cannot operate on different currencies!") if @name != other.name
39
39
 
40
40
  self.class.new(@amount + other.amount, @name)
41
41
  end
42
42
 
43
43
  def - other
44
- throw ArgumentError.new("Cannot operate on different currencies!") if @name != other.name
44
+ raise ArgumentError.new("Cannot operate on different currencies!") if @name != other.name
45
45
 
46
46
  self.class.new(@amount - other.amount, @name)
47
47
  end
@@ -54,6 +54,14 @@ module Latinum
54
54
  self.class.new(@amount * factor, @name)
55
55
  end
56
56
 
57
+ def exchange(rate, name, precision = nil)
58
+ exchanged_amount = @amount * rate
59
+
60
+ exchanged_amount = exchanged_amount.round(precision) if precision
61
+
62
+ self.class.new(exchanged_amount, name)
63
+ end
64
+
57
65
  def to_s(options = {})
58
66
  @amount.to_s('F') + ' ' + @name.to_s
59
67
  end
@@ -22,7 +22,7 @@ module Latinum
22
22
  module VERSION
23
23
  MAJOR = 0
24
24
  MINOR = 2
25
- TINY = 0
25
+ TINY = 3
26
26
 
27
27
  STRING = [MAJOR, MINOR, TINY].join('.')
28
28
  end
data/test/test_bank.rb CHANGED
@@ -1,13 +1,15 @@
1
1
 
2
2
  require 'helper'
3
3
 
4
- require 'latinum/bank'
4
+ require 'latinum'
5
5
  require 'latinum/currencies/global'
6
6
 
7
7
  class BankTest < Test::Unit::TestCase
8
8
  def setup
9
9
  @bank = Latinum::Bank.new
10
10
  @bank.import(Latinum::Currencies::Global)
11
+
12
+ @bank << Latinum::ExchangeRate.new("NZD", "AUD", "0.5")
11
13
  end
12
14
 
13
15
  def test_formatting
@@ -17,5 +19,23 @@ class BankTest < Test::Unit::TestCase
17
19
 
18
20
  resource = Latinum::Resource.new("391", "AUD")
19
21
  assert_equal "$391.00 AUD", @bank.format(resource)
22
+
23
+ resource = Latinum::Resource.new("-100", "NZD")
24
+ assert_equal "-$100.00 NZD", @bank.format(resource)
25
+ end
26
+
27
+ def test_exchange
28
+ nzd = Latinum::Resource.new("10", "NZD")
29
+
30
+ aud = @bank.exchange nzd, "AUD"
31
+ assert_equal Latinum::Resource.new("5", "AUD"), aud
32
+ end
33
+
34
+ def test_parsing
35
+ assert_equal Latinum::Resource.new("5", "USD"), @bank.parse("$5")
36
+ assert_equal Latinum::Resource.new("5", "NZD"), @bank.parse("$5 NZD")
37
+ assert_equal Latinum::Resource.new("5", "EUR"), @bank.parse("€5")
38
+
39
+ assert_equal Latinum::Resource.new("5", "NZD"), @bank.parse("5 NZD")
20
40
  end
21
41
  end
@@ -0,0 +1,27 @@
1
+
2
+ require 'helper'
3
+
4
+ require 'latinum'
5
+ require 'latinum/currencies/global'
6
+
7
+ require 'set'
8
+
9
+ class CollectionTest < Test::Unit::TestCase
10
+ def setup
11
+ @bank = Latinum::Bank.new
12
+ @bank.import(Latinum::Currencies::Global)
13
+ end
14
+
15
+ def test_collections
16
+ resource = Latinum::Resource.new("10", "NZD")
17
+
18
+ currencies = Set.new
19
+ collection = Latinum::Collection.new(currencies)
20
+
21
+ collection << resource
22
+ assert_equal resource, collection["NZD"]
23
+
24
+ collection << resource
25
+ assert_equal resource * 2, collection["NZD"]
26
+ end
27
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: latinum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-23 00:00:00.000000000 Z
12
+ date: 2012-07-27 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description:
15
15
  email: samuel.williams@oriontransfer.co.nz
@@ -20,12 +20,14 @@ files:
20
20
  - lib/latinum/bank.rb
21
21
  - lib/latinum/collection.rb
22
22
  - lib/latinum/currencies/global.rb
23
+ - lib/latinum/extensions/bigdecimal-1.8.rb
23
24
  - lib/latinum/formatters.rb
24
25
  - lib/latinum/resource.rb
25
26
  - lib/latinum/version.rb
26
27
  - lib/latinum.rb
27
28
  - test/helper.rb
28
29
  - test/test_bank.rb
30
+ - test/test_collection.rb
29
31
  - README.md
30
32
  homepage: http://www.oriontransfer.co.nz/gems/latinum
31
33
  licenses: []