money-joshm1 5.1.2

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