money 6.0.1.beta2 → 6.0.1.beta3

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.
@@ -1,149 +1,153 @@
1
1
  # encoding: utf-8
2
2
 
3
- module Money::Currency::Heuristics
4
-
5
- # An robust and efficient algorithm for finding currencies in
6
- # text. Using several algorithms it can find symbols, iso codes and
7
- # even names of currencies.
8
- # Although not recommendable, it can also attempt to find the given
9
- # currency in an entire sentence
10
- #
11
- # Returns: Array (matched results)
12
- def analyze(str)
13
- return Analyzer.new(str, search_tree).process
14
- end
3
+ class Money
4
+ class Currency
5
+ module Heuristics
6
+
7
+ # An robust and efficient algorithm for finding currencies in
8
+ # text. Using several algorithms it can find symbols, iso codes and
9
+ # even names of currencies.
10
+ # Although not recommendable, it can also attempt to find the given
11
+ # currency in an entire sentence
12
+ #
13
+ # Returns: Array (matched results)
14
+ def analyze(str)
15
+ return Analyzer.new(str, search_tree).process
16
+ end
15
17
 
16
- private
18
+ private
17
19
 
18
- # Build a search tree from the currency database
19
- def search_tree
20
- @_search_tree ||= {
21
- :by_symbol => currencies_by_symbol,
22
- :by_iso_code => currencies_by_iso_code,
23
- :by_name => currencies_by_name
24
- }
25
- end
20
+ # Build a search tree from the currency database
21
+ def search_tree
22
+ @_search_tree ||= {
23
+ :by_symbol => currencies_by_symbol,
24
+ :by_iso_code => currencies_by_iso_code,
25
+ :by_name => currencies_by_name
26
+ }
27
+ end
26
28
 
27
- def currencies_by_symbol
28
- {}.tap do |r|
29
- table.each do |dummy, c|
30
- symbol = (c[:symbol]||"").downcase
31
- symbol.chomp!('.')
32
- (r[symbol] ||= []) << c
33
-
34
- (c[:alternate_symbols]||[]).each do |ac|
35
- ac = ac.downcase
36
- ac.chomp!('.')
37
- (r[ac] ||= []) << c
29
+ def currencies_by_symbol
30
+ {}.tap do |r|
31
+ table.each do |dummy, c|
32
+ symbol = (c[:symbol]||"").downcase
33
+ symbol.chomp!('.')
34
+ (r[symbol] ||= []) << c
35
+
36
+ (c[:alternate_symbols]||[]).each do |ac|
37
+ ac = ac.downcase
38
+ ac.chomp!('.')
39
+ (r[ac] ||= []) << c
40
+ end
41
+ end
38
42
  end
39
43
  end
40
- end
41
- end
42
44
 
43
- def currencies_by_iso_code
44
- {}.tap do |r|
45
- table.each do |dummy,c|
46
- (r[c[:iso_code].downcase] ||= []) << c
45
+ def currencies_by_iso_code
46
+ {}.tap do |r|
47
+ table.each do |dummy,c|
48
+ (r[c[:iso_code].downcase] ||= []) << c
49
+ end
50
+ end
47
51
  end
48
- end
49
- end
50
52
 
51
- def currencies_by_name
52
- {}.tap do |r|
53
- table.each do |dummy,c|
54
- name_parts = c[:name].downcase.split
55
- name_parts.each {|part| part.chomp!('.')}
53
+ def currencies_by_name
54
+ {}.tap do |r|
55
+ table.each do |dummy,c|
56
+ name_parts = c[:name].downcase.split
57
+ name_parts.each {|part| part.chomp!('.')}
56
58
 
57
- # construct one branch per word
58
- root = r
59
- while name_part = name_parts.shift
60
- root = (root[name_part] ||= {})
61
- end
59
+ # construct one branch per word
60
+ root = r
61
+ while name_part = name_parts.shift
62
+ root = (root[name_part] ||= {})
63
+ end
62
64
 
63
- # the leaf is a currency
64
- (root[:value] ||= []) << c
65
+ # the leaf is a currency
66
+ (root[:value] ||= []) << c
67
+ end
68
+ end
65
69
  end
66
- end
67
- end
68
-
69
- class Analyzer
70
- attr_reader :search_tree, :words
71
- attr_accessor :str, :currencies
72
-
73
- def initialize str, search_tree
74
- @str = (str||'').dup
75
- @search_tree = search_tree
76
- @currencies = []
77
- end
78
70
 
79
- def process
80
- format
81
- return [] if str.empty?
71
+ class Analyzer
72
+ attr_reader :search_tree, :words
73
+ attr_accessor :str, :currencies
82
74
 
83
- search_by_symbol
84
- search_by_iso_code
85
- search_by_name
75
+ def initialize str, search_tree
76
+ @str = (str||'').dup
77
+ @search_tree = search_tree
78
+ @currencies = []
79
+ end
86
80
 
87
- prepare_reply
88
- end
81
+ def process
82
+ format
83
+ return [] if str.empty?
89
84
 
90
- def format
91
- str.gsub!(/[\r\n\t]/,'')
92
- str.gsub!(/[0-9][\.,:0-9]*[0-9]/,'')
93
- str.gsub!(/[0-9]/, '')
94
- str.downcase!
95
- @words = str.split
96
- @words.each {|word| word.chomp!('.'); word.chomp!(',') }
97
- end
85
+ search_by_symbol
86
+ search_by_iso_code
87
+ search_by_name
98
88
 
99
- def search_by_symbol
100
- words.each do |word|
101
- if found = search_tree[:by_symbol][word]
102
- currencies.concat(found)
89
+ prepare_reply
103
90
  end
104
- end
105
- end
106
91
 
107
- def search_by_iso_code
108
- words.each do |word|
109
- if found = search_tree[:by_iso_code][word]
110
- currencies.concat(found)
92
+ def format
93
+ str.gsub!(/[\r\n\t]/,'')
94
+ str.gsub!(/[0-9][\.,:0-9]*[0-9]/,'')
95
+ str.gsub!(/[0-9]/, '')
96
+ str.downcase!
97
+ @words = str.split
98
+ @words.each {|word| word.chomp!('.'); word.chomp!(',') }
111
99
  end
112
- end
113
- end
114
-
115
- def search_by_name
116
- # remember, the search tree by name is a construct of branches and leaf!
117
- # We need to try every combination of words within the sentence, so we
118
- # end up with a x^2 equation, which should be fine as most names are either
119
- # one or two words, and this is multiplied with the words of given sentence
120
100
 
121
- search_words = words.dup
122
-
123
- while search_words.length > 0
124
- root = search_tree[:by_name]
101
+ def search_by_symbol
102
+ words.each do |word|
103
+ if found = search_tree[:by_symbol][word]
104
+ currencies.concat(found)
105
+ end
106
+ end
107
+ end
125
108
 
126
- search_words.each do |word|
127
- if root = root[word]
128
- if root[:value]
129
- currencies.concat(root[:value])
109
+ def search_by_iso_code
110
+ words.each do |word|
111
+ if found = search_tree[:by_iso_code][word]
112
+ currencies.concat(found)
130
113
  end
131
- else
132
- break
133
114
  end
134
115
  end
135
116
 
136
- search_words.delete_at(0)
137
- end
138
- end
117
+ def search_by_name
118
+ # remember, the search tree by name is a construct of branches and leaf!
119
+ # We need to try every combination of words within the sentence, so we
120
+ # end up with a x^2 equation, which should be fine as most names are either
121
+ # one or two words, and this is multiplied with the words of given sentence
122
+
123
+ search_words = words.dup
124
+
125
+ while search_words.length > 0
126
+ root = search_tree[:by_name]
127
+
128
+ search_words.each do |word|
129
+ if root = root[word]
130
+ if root[:value]
131
+ currencies.concat(root[:value])
132
+ end
133
+ else
134
+ break
135
+ end
136
+ end
139
137
 
140
- def prepare_reply
141
- codes = currencies.map do |currency|
142
- currency[:iso_code]
138
+ search_words.delete_at(0)
139
+ end
140
+ end
141
+
142
+ def prepare_reply
143
+ codes = currencies.map do |currency|
144
+ currency[:iso_code]
145
+ end
146
+ codes.uniq!
147
+ codes.sort!
148
+ codes
149
+ end
143
150
  end
144
- codes.uniq!
145
- codes.sort!
146
- codes
147
151
  end
148
152
  end
149
153
  end
@@ -1,22 +1,24 @@
1
- module Money::Currency::Loader
2
- extend self
1
+ class Money
2
+ class Currency
3
+ module Loader
4
+ DATA_PATH = File.expand_path("../../../../config", __FILE__)
3
5
 
4
- DATA_PATH = File.expand_path("../../../../config", __FILE__)
6
+ # Loads and returns the currencies stored in JSON files in the config directory.
7
+ #
8
+ # @return [Hash]
9
+ def load_currencies
10
+ currencies = parse_currency_file("currency_iso.json")
11
+ currencies.merge! parse_currency_file("currency_non_iso.json")
12
+ currencies.merge! parse_currency_file("currency_backwards_compatible.json")
13
+ end
5
14
 
6
- # Loads and returns the currencies stored in JSON files in the config directory.
7
- #
8
- # @return [Hash]
9
- def load_currencies
10
- currencies = parse_currency_file("currency_iso.json")
11
- currencies.merge! parse_currency_file("currency_non_iso.json")
12
- currencies.merge! parse_currency_file("currency_backwards_compatible.json")
13
- end
14
-
15
- private
15
+ private
16
16
 
17
- def parse_currency_file(filename)
18
- json = File.read("#{DATA_PATH}/#{filename}")
19
- json.force_encoding(::Encoding::UTF_8) if defined?(::Encoding)
20
- JSON.parse(json, :symbolize_names => true)
17
+ def parse_currency_file(filename)
18
+ json = File.read("#{DATA_PATH}/#{filename}")
19
+ json.force_encoding(::Encoding::UTF_8) if defined?(::Encoding)
20
+ JSON.parse(json, :symbolize_names => true)
21
+ end
22
+ end
21
23
  end
22
24
  end
@@ -78,56 +78,36 @@ class Money
78
78
  end
79
79
  private :as_d
80
80
 
81
- # The currency the money is in.
82
- #
83
- # @return [Currency]
84
- attr_reader :currency
85
-
86
- # The +Money::Bank+ based object used to perform currency exchanges with.
87
- #
88
- # @return [Money::Bank::*]
89
- attr_reader :bank
81
+ # @attr_reader [Currency] currency The currency the money is in.
82
+ # @attr_reader [Money::Bank::*] bank The +Money::Bank+ based object used to
83
+ # perform currency exchanges with.
84
+ attr_reader :currency, :bank
90
85
 
91
86
  # Class Methods
92
87
  class << self
93
- # Each Money object is associated to a bank object, which is responsible
94
- # for currency exchange. This property allows you to specify the default
95
- # bank object. The default value for this property is an instance of
96
- # +Bank::VariableExchange.+ It allows one to specify custom exchange rates.
97
- #
98
- # @return [Money::Bank::*]
99
- attr_accessor :default_bank
100
-
101
- # The default currency, which is used when +Money.new+ is called without an
102
- # explicit currency argument. The default value is Currency.new("USD"). The
103
- # value must be a valid +Money::Currency+ instance.
104
- #
105
- # @return [Money::Currency]
106
- attr_accessor :default_currency
107
-
108
- # Use this to disable i18n even if it's used by other objects in your app.
109
- #
110
- # @return [true,false]
111
- attr_accessor :use_i18n
112
-
113
- # Use this to enable the ability to assume the currency from a passed symbol
114
- #
115
- # @return [true,false]
116
- attr_accessor :assume_from_symbol
117
-
118
- # Use this to enable infinite precision cents
119
- #
120
- # @return [true,false]
121
- attr_accessor :infinite_precision
122
-
123
- # Use this to specify the rounding mode
88
+ # @attr_accessor [Money::Bank::*] default_bank Each Money object is
89
+ # associated to a bank object, which is responsible for currency exchange.
90
+ # This property allows you to specify the default bank object. The default
91
+ # value for this property is an instance of +Bank::VariableExchange.+ It
92
+ # allows one to specify custom exchange rates.
93
+ # @attr_accessor [Money::Currency] default_currency The default currency,
94
+ # which is used when +Money.new+ is called without an explicit currency
95
+ # argument. The default value is Currency.new("USD"). The value must be a
96
+ # valid +Money::Currency+ instance.
97
+ # @attr_accessor [true, false] use_i18n Use this to disable i18n even if
98
+ # it's used by other objects in your app.
99
+ # @attr_accessor [true, false] assume_from_symbol Use this to enable the
100
+ # ability to assume the currency from a passed symbol
101
+ # @attr_accessor [true, false] infinite_precision Use this to enable
102
+ # infinite precision cents
103
+ # @attr_accessor [Integer] conversion_precision Use this to specify
104
+ # precision for converting Rational to BigDecimal
105
+ attr_accessor :default_bank, :default_currency, :use_i18n,
106
+ :assume_from_symbol, :infinite_precision, :conversion_precision
107
+
108
+ # @attr_writer rounding_mode Use this to specify the rounding mode
124
109
  attr_writer :rounding_mode
125
110
 
126
- # Use this to specify precision for converting Rational to BigDecimal
127
- #
128
- # @return [Integer]
129
- attr_accessor :conversion_precision
130
-
131
111
  # Create a new money object with value 0.
132
112
  #
133
113
  # @param [Currency, String, Symbol] currency The currency to use.
@@ -185,10 +165,12 @@ class Money
185
165
  # Money.new(1200) * BigDecimal.new('0.029')
186
166
  # end
187
167
  def self.rounding_mode(mode=nil)
188
- return Thread.current[:money_rounding_mode] || @rounding_mode if mode.nil?
189
-
190
- Thread.current[:money_rounding_mode] = mode
191
- yield
168
+ if mode.nil?
169
+ Thread.current[:money_rounding_mode] || @rounding_mode
170
+ else
171
+ Thread.current[:money_rounding_mode] = mode
172
+ yield
173
+ end
192
174
  ensure
193
175
  Thread.current[:money_rounding_mode] = nil
194
176
  end
@@ -258,10 +240,9 @@ class Money
258
240
  # @see Money.new
259
241
  #
260
242
  def self.new_with_amount(amount, currency = Money.default_currency, bank = Money.default_bank)
261
- money = from_numeric(amount, currency)
262
- # Hack! You can't change a bank
263
- money.instance_variable_set("@bank", bank)
264
- money
243
+ from_numeric(amount, currency).tap do |money|
244
+ money.instance_variable_set("@bank", bank) # Hack! You can't change a bank
245
+ end
265
246
  end
266
247
 
267
248
  # Synonym of #new_with_amount
@@ -310,17 +291,14 @@ class Money
310
291
  #
311
292
  # @see Money.new_with_dollars
312
293
  #
313
- def initialize(fractional, currency = Money.default_currency, bank = Money.default_bank)
314
- if (fractional.is_a? Money)
315
- money = fractional
316
- @fractional = money.fractional
317
- @currency = money.currency
318
- @bank = money.bank
319
- else
320
- @fractional = as_d(fractional)
321
- @currency = Currency.wrap(currency)
322
- @bank = bank
323
- end
294
+ def initialize(obj, currency = Money.default_currency, bank = Money.default_bank)
295
+ @fractional = obj.fractional
296
+ @currency = obj.currency
297
+ @bank = obj.bank
298
+ rescue NoMethodError
299
+ @fractional = as_d(obj)
300
+ @currency = Currency.wrap(currency)
301
+ @bank = bank
324
302
  end
325
303
 
326
304
  # Assuming using a currency using dollars:
@@ -479,7 +457,7 @@ class Money
479
457
  #
480
458
  # @return [self]
481
459
  def to_money(given_currency = nil)
482
- given_currency = Currency.wrap(given_currency) if given_currency
460
+ given_currency = Currency.wrap(given_currency)
483
461
  if given_currency.nil? || self.currency == given_currency
484
462
  self
485
463
  else
@@ -551,30 +529,42 @@ class Money
551
529
  # Money.new(100, "USD").allocate([0.33, 0.33, 0.33]) #=> [Money.new(34), Money.new(33), Money.new(33)]
552
530
  #
553
531
  def allocate(splits)
554
- allocations = splits.inject(0) { |sum, n| sum + as_d(n) }
532
+ allocations = allocations_from_splits(splits)
555
533
 
556
534
  if (allocations - BigDecimal("1")) > Float::EPSILON
557
535
  raise ArgumentError, "splits add to more then 100%"
558
536
  end
559
537
 
538
+ amounts, left_over = amounts_from_splits(allocations, splits)
539
+
540
+ unless self.class.infinite_precision
541
+ left_over.to_i.times { |i| amounts[i % amounts.length] += 1 }
542
+ end
543
+
544
+ amounts.collect { |fractional| Money.new(fractional, currency) }
545
+ end
546
+
547
+ def allocations_from_splits(splits)
548
+ splits.inject(0) { |sum, n| sum + as_d(n) }
549
+ end
550
+ private :allocations_from_splits
551
+
552
+ def amounts_from_splits(allocations, splits)
560
553
  left_over = fractional
561
554
 
562
555
  amounts = splits.map do |ratio|
563
556
  if self.class.infinite_precision
564
- fraction = fractional * ratio
557
+ fractional * ratio
565
558
  else
566
- fraction = (fractional * ratio / allocations).floor
567
- left_over -= fraction
568
- fraction
559
+ (fractional * ratio / allocations).floor.tap do |frac|
560
+ left_over -= frac
561
+ end
569
562
  end
570
563
  end
571
564
 
572
- unless self.class.infinite_precision
573
- left_over.to_i.times { |i| amounts[i % amounts.length] += 1 }
574
- end
575
-
576
- amounts.collect { |fractional| Money.new(fractional, currency) }
565
+ [amounts, left_over]
577
566
  end
567
+ private :amounts_from_splits
578
568
 
579
569
  # Split money amongst parties evenly without loosing pennies.
580
570
  #
@@ -588,22 +578,29 @@ class Money
588
578
  raise ArgumentError, "need at least one party" if num < 1
589
579
 
590
580
  if self.class.infinite_precision
591
- amt = div(as_d(num))
592
- return 1.upto(num).map{amt}
581
+ split_infinite(num)
582
+ else
583
+ split_flat(num)
593
584
  end
585
+ end
594
586
 
587
+ def split_infinite(num)
588
+ amt = div(as_d(num))
589
+ 1.upto(num).map{amt}
590
+ end
591
+ private :split_infinite
592
+
593
+ def split_flat(num)
595
594
  low = Money.new(fractional / num, currency)
596
595
  high = Money.new(low.fractional + 1, currency)
597
596
 
598
597
  remainder = fractional % num
599
- result = []
600
598
 
601
- num.times do |index|
602
- result[index] = index < remainder ? high : low
599
+ Array.new(num).each_with_index.map do |_, index|
600
+ index < remainder ? high : low
603
601
  end
604
-
605
- result
606
602
  end
603
+ private :split_flat
607
604
 
608
605
  # Round the monetary amount to smallest unit of coinage.
609
606
  #
@@ -622,10 +619,9 @@ class Money
622
619
  #
623
620
  def round(rounding_mode = self.class.rounding_mode)
624
621
  if self.class.infinite_precision
625
- return Money.new(fractional.round(0, rounding_mode), self.currency)
622
+ Money.new(fractional.round(0, rounding_mode), self.currency)
626
623
  else
627
- return self
624
+ self
628
625
  end
629
626
  end
630
-
631
627
  end