money-joshm1 5.1.2

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,30 @@
1
+ {
2
+ "btc": {
3
+ "priority": 100,
4
+ "iso_code": "BTC",
5
+ "name": "Bitcoin",
6
+ "symbol": "B⃦",
7
+ "alternate_symbols": [],
8
+ "subunit": "Satoshi",
9
+ "subunit_to_unit": 10000000,
10
+ "symbol_first": true,
11
+ "html_entity": "",
12
+ "decimal_mark": ".",
13
+ "thousands_separator": ",",
14
+ "iso_numeric": ""
15
+ },
16
+ "jep": {
17
+ "priority": 100,
18
+ "iso_code": "JEP",
19
+ "name": "Jersey Pound",
20
+ "symbol": "£",
21
+ "alternate_symbols": [],
22
+ "subunit": "Penny",
23
+ "subunit_to_unit": 100,
24
+ "symbol_first": true,
25
+ "html_entity": "£",
26
+ "decimal_mark": ".",
27
+ "thousands_separator": ",",
28
+ "iso_numeric": ""
29
+ }
30
+ }
data/lib/money.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "bigdecimal"
2
+ require "bigdecimal/util"
3
+ require "i18n" rescue LoadError
4
+ require "money/currency"
5
+ require "money/money"
6
+ require "money/core_extensions"
@@ -0,0 +1,130 @@
1
+ require 'thread'
2
+
3
+ class Money
4
+ # Provides classes that aid in the ability of exchange one currency with
5
+ # another.
6
+ module Bank
7
+
8
+ # The lowest Money::Bank error class.
9
+ # All Money::Bank errors should inherit from it.
10
+ class Error < StandardError
11
+ end
12
+
13
+ # Raised when the bank doesn't know about the conversion rate
14
+ # for specified currencies.
15
+ class UnknownRate < Error
16
+ end
17
+
18
+
19
+ # Money::Bank::Base is the basic interface for creating a money exchange
20
+ # object, also called Bank.
21
+ #
22
+ # A Bank is responsible for storing exchange rates, take a Money object as
23
+ # input and returns the corresponding Money object converted into an other
24
+ # currency.
25
+ #
26
+ # This class exists for aiding in the creating of other classes to exchange
27
+ # money between different currencies. When creating a subclass you will
28
+ # need to implement the following methods to exchange money between
29
+ # currencies:
30
+ #
31
+ # - #exchange_with(Money) #=> Money
32
+ #
33
+ # See Money::Bank::VariableExchange for a real example.
34
+ #
35
+ # Also, you can extend +Money::Bank::VariableExchange+ instead of
36
+ # +Money::Bank::Base+ if your bank implementation needs to store rates
37
+ # internally.
38
+ #
39
+ # @abstract Subclass and override +#exchange_with+ to implement a custom
40
+ # +Money::Bank+ class. You can also override +#setup+ instead of
41
+ # +#initialize+ to setup initial variables, etc.
42
+ class Base
43
+
44
+ # Returns the singleton instance of the Base bank.
45
+ #
46
+ # @return [Money::Bank::Base]
47
+ def self.instance
48
+ @singleton ||= self.new
49
+ end
50
+
51
+ # The rounding method to use when exchanging rates.
52
+ #
53
+ # @return [Proc]
54
+ attr_reader :rounding_method
55
+
56
+ # Initializes a new +Money::Bank::Base+ object. An optional block can be
57
+ # passed to dictate the rounding method that +#exchange_with+ can use.
58
+ #
59
+ # @yield [n] Optional block to use when rounding after exchanging one
60
+ # currency for another.
61
+ # @yieldparam [Float] n The resulting float after exchanging one currency
62
+ # for another.
63
+ # @yieldreturn [Integer]
64
+ #
65
+ # @return [Money::Bank::Base]
66
+ #
67
+ # @example
68
+ # Money::Bank::Base.new #=> #<Money::Bank::Base @rounding_method=nil>
69
+ # Money::Bank::Base.new {|n|
70
+ # n.floor
71
+ # } #=> #<Money::Bank::Base @round_method=#<Proc>>
72
+ def initialize(&block)
73
+ @rounding_method = block
74
+ setup
75
+ end
76
+
77
+ # Called after initialize. Subclasses can use this method to setup
78
+ # variables, etc that they normally would in +#initialize+.
79
+ #
80
+ # @abstract Subclass and override +#setup+ to implement a custom
81
+ # +Money::Bank+ class.
82
+ #
83
+ # @return [self]
84
+ def setup
85
+ end
86
+
87
+ # Exchanges the given +Money+ object to a new +Money+ object in
88
+ # +to_currency+.
89
+ #
90
+ # @abstract Subclass and override +#exchange_with+ to implement a custom
91
+ # +Money::Bank+ class.
92
+ #
93
+ # @raise NotImplementedError
94
+ #
95
+ # @param [Money] from The +Money+ object to exchange from.
96
+ # @param [Money::Currency, String, Symbol] to_currency The currency
97
+ # string or object to exchange to.
98
+ # @yield [n] Optional block to use to round the result after making
99
+ # the exchange.
100
+ # @yieldparam [Float] n The result after exchanging from one currency to
101
+ # the other.
102
+ # @yieldreturn [Integer]
103
+ #
104
+ # @return [Money]
105
+ def exchange_with(from, to_currency, &block)
106
+ raise NotImplementedError, "#exchange_with must be implemented"
107
+ end
108
+
109
+ # Given two currency strings or object, checks whether they're both the
110
+ # same currency. Return +true+ if the currencies are the same, +false+
111
+ # otherwise.
112
+ #
113
+ # @param [Money::Currency, String, Symbol] currency1 The first currency
114
+ # to compare.
115
+ # @param [Money::Currency, String, Symbol] currency2 The second currency
116
+ # to compare.
117
+ #
118
+ # @return [Boolean]
119
+ #
120
+ # @example
121
+ # same_currency?("usd", "USD") #=> true
122
+ # same_currency?("usd", "EUR") #=> false
123
+ # same_currency?("usd", Currency.new("USD") #=> true
124
+ # same_currency?("usd", "USD") #=> true
125
+ def same_currency?(currency1, currency2)
126
+ Currency.wrap(currency1) == Currency.wrap(currency2)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,252 @@
1
+ require 'money/bank/base'
2
+ require 'json'
3
+ require 'yaml'
4
+
5
+ class Money
6
+ module Bank
7
+ # Thrown when an unknown rate format is requested.
8
+ class UnknownRateFormat < StandardError; end
9
+
10
+ # Class for aiding in exchanging money between different currencies. By
11
+ # default, the +Money+ class uses an object of this class (accessible
12
+ # through +Money#bank+) for performing currency exchanges.
13
+ #
14
+ # By default, +Money::Bank::VariableExchange+ has no knowledge about
15
+ # conversion rates. One must manually specify them with +add_rate+, after
16
+ # which one can perform exchanges with +#exchange_with+.
17
+ #
18
+ # @example
19
+ # bank = Money::Bank::VariableExchange.new
20
+ # bank.add_rate("USD", "CAD", 1.24515)
21
+ # bank.add_rate("CAD", "USD", 0.803115)
22
+ #
23
+ # c1 = 100_00.to_money("USD")
24
+ # c2 = 100_00.to_money("CAD")
25
+ #
26
+ # # Exchange 100 USD to CAD:
27
+ # bank.exchange_with(c1, "CAD") #=> #<Money @fractional=1245150>
28
+ #
29
+ # # Exchange 100 CAD to USD:
30
+ # bank.exchange_with(c2, "USD") #=> #<Money @fractional=803115>
31
+ class VariableExchange < Base
32
+
33
+ attr_reader :rates
34
+
35
+ # Available formats for importing/exporting rates.
36
+ RATE_FORMATS = [:json, :ruby, :yaml]
37
+
38
+ # Setup rates hash and mutex for rates locking
39
+ #
40
+ # @return [self]
41
+ def setup
42
+ @rates = {}
43
+ @mutex = Mutex.new
44
+ self
45
+ end
46
+
47
+ def marshal_dump
48
+ [@rates, @rounding_method]
49
+ end
50
+
51
+ def marshal_load(arr)
52
+ @rates, @rounding_method = arr
53
+ @mutex = Mutex.new
54
+ end
55
+
56
+ # Exchanges the given +Money+ object to a new +Money+ object in
57
+ # +to_currency+.
58
+ #
59
+ # @param [Money] from
60
+ # The +Money+ object to exchange.
61
+ # @param [Currency, String, Symbol] to_currency
62
+ # The currency to exchange to.
63
+ #
64
+ # @yield [n] Optional block to use when rounding after exchanging one
65
+ # currency for another.
66
+ # @yieldparam [Float] n The resulting float after exchanging one currency
67
+ # for another.
68
+ # @yieldreturn [Integer]
69
+ #
70
+ # @return [Money]
71
+ #
72
+ # @raise +Money::Bank::UnknownRate+ if the conversion rate is unknown.
73
+ #
74
+ # @example
75
+ # bank = Money::Bank::VariableExchange.new
76
+ # bank.add_rate("USD", "CAD", 1.24515)
77
+ # bank.add_rate("CAD", "USD", 0.803115)
78
+ #
79
+ # c1 = 100_00.to_money("USD")
80
+ # c2 = 100_00.to_money("CAD")
81
+ #
82
+ # # Exchange 100 USD to CAD:
83
+ # bank.exchange_with(c1, "CAD") #=> #<Money @fractional=1245150>
84
+ #
85
+ # # Exchange 100 CAD to USD:
86
+ # bank.exchange_with(c2, "USD") #=> #<Money @fractional=803115>
87
+ def exchange_with(from, to_currency)
88
+ return from if same_currency?(from.currency, to_currency)
89
+
90
+ rate = get_rate(from.currency, to_currency)
91
+ unless rate
92
+ raise UnknownRate, "No conversion rate known for '#{from.currency.iso_code}' -> '#{to_currency}'"
93
+ end
94
+ _to_currency_ = Currency.wrap(to_currency)
95
+
96
+ fractional = BigDecimal.new(from.fractional.to_s) / (BigDecimal.new(from.currency.subunit_to_unit.to_s) / BigDecimal.new(_to_currency_.subunit_to_unit.to_s))
97
+
98
+ ex = fractional * BigDecimal.new(rate.to_s)
99
+ ex = ex.to_f
100
+ ex = if block_given?
101
+ yield ex
102
+ elsif @rounding_method
103
+ @rounding_method.call(ex)
104
+ else
105
+ ex.to_s.to_i
106
+ end
107
+ Money.new(ex, _to_currency_)
108
+ end
109
+
110
+ # Registers a conversion rate and returns it (uses +#set_rate+).
111
+ #
112
+ # @param [Currency, String, Symbol] from Currency to exchange from.
113
+ # @param [Currency, String, Symbol] to Currency to exchange to.
114
+ # @param [Numeric] rate Rate to use when exchanging currencies.
115
+ #
116
+ # @return [Numeric]
117
+ #
118
+ # @example
119
+ # bank = Money::Bank::VariableExchange.new
120
+ # bank.add_rate("USD", "CAD", 1.24515)
121
+ # bank.add_rate("CAD", "USD", 0.803115)
122
+ def add_rate(from, to, rate)
123
+ set_rate(from, to, rate)
124
+ end
125
+
126
+ # Set the rate for the given currencies. Uses +Mutex+ to synchronize data
127
+ # access.
128
+ #
129
+ # @param [Currency, String, Symbol] from Currency to exchange from.
130
+ # @param [Currency, String, Symbol] to Currency to exchange to.
131
+ # @param [Numeric] rate Rate to use when exchanging currencies.
132
+ #
133
+ # @return [Numeric]
134
+ #
135
+ # @example
136
+ # bank = Money::Bank::VariableExchange.new
137
+ # bank.set_rate("USD", "CAD", 1.24515)
138
+ # bank.set_rate("CAD", "USD", 0.803115)
139
+ def set_rate(from, to, rate)
140
+ @mutex.synchronize { @rates[rate_key_for(from, to)] = rate }
141
+ end
142
+
143
+ # Retrieve the rate for the given currencies. Uses +Mutex+ to synchronize
144
+ # data access.
145
+ #
146
+ # @param [Currency, String, Symbol] from Currency to exchange from.
147
+ # @param [Currency, String, Symbol] to Currency to exchange to.
148
+ #
149
+ # @return [Numeric]
150
+ #
151
+ # @example
152
+ # bank = Money::Bank::VariableExchange.new
153
+ # bank.set_rate("USD", "CAD", 1.24515)
154
+ # bank.set_rate("CAD", "USD", 0.803115)
155
+ #
156
+ # bank.get_rate("USD", "CAD") #=> 1.24515
157
+ # bank.get_rate("CAD", "USD") #=> 0.803115
158
+ def get_rate(from, to)
159
+ @mutex.synchronize { @rates[rate_key_for(from, to)] }
160
+ end
161
+
162
+ # Return the known rates as a string in the format specified. If +file+
163
+ # is given will also write the string out to the file specified.
164
+ # Available formats are +:json+, +:ruby+ and +:yaml+.
165
+ #
166
+ # @param [Symbol] format Request format for the resulting string.
167
+ # @param [String] file Optional file location to write the rates to.
168
+ #
169
+ # @return [String]
170
+ #
171
+ # @raise +Money::Bank::UnknownRateFormat+ if format is unknown.
172
+ #
173
+ # @example
174
+ # bank = Money::Bank::VariableExchange.new
175
+ # bank.set_rate("USD", "CAD", 1.24515)
176
+ # bank.set_rate("CAD", "USD", 0.803115)
177
+ #
178
+ # s = bank.export_rates(:json)
179
+ # s #=> "{\"USD_TO_CAD\":1.24515,\"CAD_TO_USD\":0.803115}"
180
+ def export_rates(format, file=nil)
181
+ raise Money::Bank::UnknownRateFormat unless
182
+ RATE_FORMATS.include? format
183
+
184
+ s = ""
185
+ @mutex.synchronize {
186
+ s = case format
187
+ when :json
188
+ JSON.dump(@rates)
189
+ when :ruby
190
+ Marshal.dump(@rates)
191
+ when :yaml
192
+ YAML.dump(@rates)
193
+ end
194
+
195
+ unless file.nil?
196
+ File.open(file, "w") {|f| f.write(s) }
197
+ end
198
+ }
199
+ s
200
+ end
201
+
202
+ # Loads rates provided in +s+ given the specified format. Available
203
+ # formats are +:json+, +:ruby+ and +:yaml+.
204
+ #
205
+ # @param [Symbol] format The format of +s+.
206
+ # @param [String] s The rates string.
207
+ #
208
+ # @return [self]
209
+ #
210
+ # @raise +Money::Bank::UnknownRateFormat+ if format is unknown.
211
+ #
212
+ # @example
213
+ # s = "{\"USD_TO_CAD\":1.24515,\"CAD_TO_USD\":0.803115}"
214
+ # bank = Money::Bank::VariableExchange.new
215
+ # bank.import_rates(:json, s)
216
+ #
217
+ # bank.get_rate("USD", "CAD") #=> 1.24515
218
+ # bank.get_rate("CAD", "USD") #=> 0.803115
219
+ def import_rates(format, s)
220
+ raise Money::Bank::UnknownRateFormat unless
221
+ RATE_FORMATS.include? format
222
+
223
+ @mutex.synchronize {
224
+ @rates = case format
225
+ when :json
226
+ JSON.load(s)
227
+ when :ruby
228
+ Marshal.load(s)
229
+ when :yaml
230
+ YAML.load(s)
231
+ end
232
+ }
233
+ self
234
+ end
235
+
236
+ private
237
+
238
+ # Return the rate hashkey for the given currencies.
239
+ #
240
+ # @param [Currency, String, Symbol] from The currency to exchange from.
241
+ # @param [Currency, String, Symbol] to The currency to exchange to.
242
+ #
243
+ # @return [String]
244
+ #
245
+ # @example
246
+ # rate_key_for("USD", "CAD") #=> "USD_TO_CAD"
247
+ def rate_key_for(from, to)
248
+ "#{Currency.wrap(from).iso_code}_TO_#{Currency.wrap(to).iso_code}".upcase
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,82 @@
1
+ # Open +Numeric+ to add new method.
2
+ class Numeric
3
+
4
+ # Converts this numeric into a +Money+ object in the given +currency+.
5
+ #
6
+ # @param [Currency, String, Symbol] currency
7
+ # The currency to set the resulting +Money+ object to.
8
+ #
9
+ # @return [Money]
10
+ #
11
+ # @example
12
+ # 100.to_money #=> #<Money @fractional=10000>
13
+ # 100.37.to_money #=> #<Money @fractional=10037>
14
+ # BigDecimal.new('100').to_money #=> #<Money @fractional=10000>
15
+ #
16
+ # @see Money.from_numeric
17
+ #
18
+ def to_money(currency = nil)
19
+ Money.from_numeric(self, currency || Money.default_currency)
20
+ end
21
+
22
+ end
23
+
24
+ # Open +String+ to add new methods.
25
+ class String
26
+
27
+ # Parses the current string and converts it to a +Money+ object.
28
+ # Excess characters will be discarded.
29
+ #
30
+ # @param [Currency, String, Symbol] currency
31
+ # The currency to set the resulting +Money+ object to.
32
+ #
33
+ # @return [Money]
34
+ #
35
+ # @example
36
+ # '100'.to_money #=> #<Money @fractional=10000>
37
+ # '100.37'.to_money #=> #<Money @fractional=10037>
38
+ # '100 USD'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
39
+ # 'USD 100'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
40
+ # '$100 USD'.to_money #=> #<Money @fractional=10000, @currency=#<Money::Currency id: usd>>
41
+ # 'hello 2000 world'.to_money #=> #<Money @fractional=200000 @currency=#<Money::Currency id: usd>>
42
+ #
43
+ # @see Money.from_string
44
+ #
45
+ def to_money(currency = nil)
46
+ Money.parse(self, currency)
47
+ end
48
+
49
+ # Converts the current string into a +Currency+ object.
50
+ #
51
+ # @return [Money::Currency]
52
+ #
53
+ # @raise [Money::Currency::UnknownCurrency]
54
+ # If this String reference an unknown currency.
55
+ #
56
+ # @example
57
+ # "USD".to_currency #=> #<Money::Currency id: usd>
58
+ #
59
+ def to_currency
60
+ Money::Currency.new(self)
61
+ end
62
+
63
+ end
64
+
65
+ # Open +Symbol+ to add new methods.
66
+ class Symbol
67
+
68
+ # Converts the current symbol into a +Currency+ object.
69
+ #
70
+ # @return [Money::Currency]
71
+ #
72
+ # @raise [Money::Currency::UnknownCurrency]
73
+ # If this String reference an unknown currency.
74
+ #
75
+ # @example
76
+ # :ars.to_currency #=> #<Money::Currency id: ars>
77
+ #
78
+ def to_currency
79
+ Money::Currency.new(self)
80
+ end
81
+
82
+ end