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,288 @@
1
+ class Money
2
+ module Arithmetic
3
+
4
+ # Returns a money object with changed polarity.
5
+ #
6
+ # @return [Money]
7
+ #
8
+ # @example
9
+ # - Money.new(100) #=> #<Money @fractional=-100>
10
+ def -@
11
+ Money.new(-fractional, currency)
12
+ end
13
+
14
+
15
+ # Checks whether two money objects have the same currency and the same
16
+ # amount. Checks against money objects with a different currency and checks
17
+ # against objects that do not respond to #to_money will always return false.
18
+ #
19
+ # @param [Money] other_money Value to compare with.
20
+ #
21
+ # @return [Boolean]
22
+ #
23
+ # @example
24
+ # Money.new(100) == Money.new(101) #=> false
25
+ # Money.new(100) == Money.new(100) #=> true
26
+ def ==(other_money)
27
+ if other_money.respond_to?(:to_money)
28
+ other_money = other_money.to_money
29
+ fractional == other_money.fractional && self.currency == other_money.currency
30
+ else
31
+ false
32
+ end
33
+ end
34
+
35
+ # Synonymous with +#==+.
36
+ #
37
+ # @param [Money] other_money Value to compare with.
38
+ #
39
+ # @return [Money]
40
+ #
41
+ # @see #==
42
+ def eql?(other_money)
43
+ self == other_money
44
+ end
45
+
46
+ def <=>(other_money)
47
+ if other_money.respond_to?(:to_money)
48
+ other_money = other_money.to_money
49
+ if fractional == 0 || other_money.fractional == 0 || currency == other_money.currency
50
+ fractional <=> other_money.fractional
51
+ else
52
+ fractional <=> other_money.exchange_to(currency).fractional
53
+ end
54
+ else
55
+ raise ArgumentError, "Comparison of #{self.class} with #{other_money.inspect} failed"
56
+ end
57
+ end
58
+
59
+ # Test if the amount is positive. Returns +true+ if the money amount is
60
+ # greater than 0, +false+ otherwise.
61
+ #
62
+ # @return [Boolean]
63
+ #
64
+ # @example
65
+ # Money.new(1).positive? #=> true
66
+ # Money.new(0).positive? #=> false
67
+ # Money.new(-1).positive? #=> false
68
+ def positive?
69
+ fractional > 0
70
+ end
71
+
72
+ # Test if the amount is negative. Returns +true+ if the money amount is
73
+ # less than 0, +false+ otherwise.
74
+ #
75
+ # @return [Boolean]
76
+ #
77
+ # @example
78
+ # Money.new(-1).negative? #=> true
79
+ # Money.new(0).negative? #=> false
80
+ # Money.new(1).negative? #=> false
81
+ def negative?
82
+ fractional < 0
83
+ end
84
+
85
+ # Returns a new Money object containing the sum of the two operands' monetary
86
+ # values. If +other_money+ has a different currency then its monetary value
87
+ # is automatically exchanged to this object's currency using +exchange_to+.
88
+ #
89
+ # @param [Money] other_money Other +Money+ object to add.
90
+ #
91
+ # @return [Money]
92
+ #
93
+ # @example
94
+ # Money.new(100) + Money.new(100) #=> #<Money @fractional=200>
95
+ def +(other_money)
96
+ if currency == other_money.currency
97
+ Money.new(fractional + other_money.fractional, other_money.currency)
98
+ else
99
+ Money.new(fractional + other_money.exchange_to(currency).fractional, currency)
100
+ end
101
+ end
102
+
103
+ # Returns a new Money object containing the difference between the two
104
+ # operands' monetary values. If +other_money+ has a different currency then
105
+ # its monetary value is automatically exchanged to this object's currency
106
+ # using +exchange_to+.
107
+ #
108
+ # @param [Money] other_money Other +Money+ object to subtract.
109
+ #
110
+ # @return [Money]
111
+ #
112
+ # @example
113
+ # Money.new(100) - Money.new(99) #=> #<Money @fractional=1>
114
+ def -(other_money)
115
+ if currency == other_money.currency
116
+ Money.new(fractional - other_money.fractional, other_money.currency)
117
+ else
118
+ Money.new(fractional - other_money.exchange_to(currency).fractional, currency)
119
+ end
120
+ end
121
+
122
+ # Multiplies the monetary value with the given number and returns a new
123
+ # +Money+ object with this monetary value and the same currency.
124
+ #
125
+ # Note that you can't multiply a Money object by an other +Money+ object.
126
+ #
127
+ # @param [Numeric] value Number to multiply by.
128
+ #
129
+ # @return [Money] The resulting money.
130
+ #
131
+ # @raise [ArgumentError] If +value+ is a Money instance.
132
+ #
133
+ # @example
134
+ # Money.new(100) * 2 #=> #<Money @fractional=200>
135
+ #
136
+ def *(value)
137
+ if value.is_a?(Money)
138
+ raise ArgumentError, "Can't multiply a Money by a Money"
139
+ else
140
+ Money.new(fractional * value, currency)
141
+ end
142
+ end
143
+
144
+ # Divides the monetary value with the given number and returns a new +Money+
145
+ # object with this monetary value and the same currency.
146
+ # Can also divide by another +Money+ object to get a ratio.
147
+ #
148
+ # +Money/Numeric+ returns +Money+. +Money/Money+ returns +Float+.
149
+ #
150
+ # @param [Money, Numeric] value Number to divide by.
151
+ #
152
+ # @return [Money] The resulting money if you divide Money by a number.
153
+ # @return [Float] The resulting number if you divide Money by a Money.
154
+ #
155
+ # @example
156
+ # Money.new(100) / 10 #=> #<Money @fractional=10>
157
+ # Money.new(100) / Money.new(10) #=> 10.0
158
+ #
159
+ def /(value)
160
+ if value.is_a?(Money)
161
+ if currency == value.currency
162
+ (fractional / BigDecimal.new(value.fractional.to_s)).to_f
163
+ else
164
+ (fractional / BigDecimal(value.exchange_to(currency).fractional.to_s)).to_f
165
+ end
166
+ else
167
+ Money.new(fractional / value, currency)
168
+ end
169
+ end
170
+
171
+ # Synonym for +#/+.
172
+ #
173
+ # @param [Money, Numeric] value Number to divide by.
174
+ #
175
+ # @return [Money] The resulting money if you divide Money by a number.
176
+ # @return [Float] The resulting number if you divide Money by a Money.
177
+ #
178
+ # @see #/
179
+ #
180
+ def div(value)
181
+ self / value
182
+ end
183
+
184
+ # Divide money by money or fixnum and return array containing quotient and
185
+ # modulus.
186
+ #
187
+ # @param [Money, Fixnum] val Number to divmod by.
188
+ #
189
+ # @return [Array<Money,Money>,Array<Fixnum,Money>]
190
+ #
191
+ # @example
192
+ # Money.new(100).divmod(9) #=> [#<Money @fractional=11>, #<Money @fractional=1>]
193
+ # Money.new(100).divmod(Money.new(9)) #=> [11, #<Money @fractional=1>]
194
+ def divmod(val)
195
+ if val.is_a?(Money)
196
+ a = self.fractional
197
+ b = self.currency == val.currency ? val.fractional : val.exchange_to(self.currency).cents
198
+ q, m = a.divmod(b)
199
+ return [q, Money.new(m, self.currency)]
200
+ else
201
+ if self.class.infinite_precision
202
+ q, m = self.fractional.divmod(BigDecimal(val.to_s))
203
+ return [Money.new(q, self.currency), Money.new(m, self.currency)]
204
+ else
205
+ return [self.div(val), Money.new(self.fractional.modulo(val), self.currency)]
206
+ end
207
+ end
208
+ end
209
+
210
+ # Equivalent to +self.divmod(val)[1]+
211
+ #
212
+ # @param [Money, Fixnum] val Number take modulo with.
213
+ #
214
+ # @return [Money]
215
+ #
216
+ # @example
217
+ # Money.new(100).modulo(9) #=> #<Money @fractional=1>
218
+ # Money.new(100).modulo(Money.new(9)) #=> #<Money @fractional=1>
219
+ def modulo(val)
220
+ self.divmod(val)[1]
221
+ end
222
+
223
+ # Synonym for +#modulo+.
224
+ #
225
+ # @param [Money, Fixnum] val Number take modulo with.
226
+ #
227
+ # @return [Money]
228
+ #
229
+ # @see #modulo
230
+ def %(val)
231
+ self.modulo(val)
232
+ end
233
+
234
+ # If different signs +self.modulo(val) - val+ otherwise +self.modulo(val)+
235
+ #
236
+ # @param [Money, Fixnum] val Number to rake remainder with.
237
+ #
238
+ # @return [Money]
239
+ #
240
+ # @example
241
+ # Money.new(100).remainder(9) #=> #<Money @fractional=1>
242
+ def remainder(val)
243
+ a, b = self, val
244
+ b = b.exchange_to(a.currency) if b.is_a?(Money) and a.currency != b.currency
245
+
246
+ a_sign, b_sign = :pos, :pos
247
+ a_sign = :neg if a.fractional < 0
248
+ b_sign = :neg if (b.is_a?(Money) and b.fractional < 0) or (b < 0)
249
+
250
+ return a.modulo(b) if a_sign == b_sign
251
+ a.modulo(b) - (b.is_a?(Money) ? b : Money.new(b, a.currency))
252
+ end
253
+
254
+ # Return absolute value of self as a new Money object.
255
+ #
256
+ # @return [Money]
257
+ #
258
+ # @example
259
+ # Money.new(-100).abs #=> #<Money @fractional=100>
260
+ def abs
261
+ Money.new(self.fractional.abs, self.currency)
262
+ end
263
+
264
+ # Test if the money amount is zero.
265
+ #
266
+ # @return [Boolean]
267
+ #
268
+ # @example
269
+ # Money.new(100).zero? #=> false
270
+ # Money.new(0).zero? #=> true
271
+ def zero?
272
+ fractional == 0
273
+ end
274
+
275
+ # Test if the money amount is non-zero. Returns this money object if it is
276
+ # non-zero, or nil otherwise, like +Numeric#nonzero?+.
277
+ #
278
+ # @return [Money, nil]
279
+ #
280
+ # @example
281
+ # Money.new(100).nonzero? #=> #<Money @fractional=100>
282
+ # Money.new(0).nonzero? #=> nil
283
+ def nonzero?
284
+ fractional != 0 ? self : nil
285
+ end
286
+
287
+ end
288
+ end
@@ -0,0 +1,315 @@
1
+ # encoding: UTF-8
2
+ class Money
3
+ module Formatting
4
+
5
+ if Object.const_defined?("I18n")
6
+ def thousands_separator
7
+ if self.class.use_i18n
8
+ I18n.t(
9
+ :"number.currency.format.delimiter",
10
+ :default => I18n.t(
11
+ :"number.format.delimiter",
12
+ :default => (currency.thousands_separator || ",")
13
+ )
14
+ )
15
+ else
16
+ currency.thousands_separator || ","
17
+ end
18
+ end
19
+ else
20
+ def thousands_separator
21
+ currency.thousands_separator || ","
22
+ end
23
+ end
24
+ alias :delimiter :thousands_separator
25
+
26
+
27
+ if Object.const_defined?("I18n")
28
+ def decimal_mark
29
+ if self.class.use_i18n
30
+ I18n.t(
31
+ :"number.currency.format.separator",
32
+ :default => I18n.t(
33
+ :"number.format.separator",
34
+ :default => (currency.decimal_mark || ".")
35
+ )
36
+ )
37
+ else
38
+ currency.decimal_mark || "."
39
+ end
40
+ end
41
+ else
42
+ def decimal_mark
43
+ currency.decimal_mark || "."
44
+ end
45
+ end
46
+ alias :separator :decimal_mark
47
+
48
+ # Creates a formatted price string according to several rules.
49
+ #
50
+ # @param [Hash] rules The options used to format the string.
51
+ #
52
+ # @return [String]
53
+ #
54
+ # @option *rules [Boolean, String] :display_free (false) Whether a zero
55
+ # amount of money should be formatted of "free" or as the supplied string.
56
+ #
57
+ # @example
58
+ # Money.us_dollar(0).format(:display_free => true) #=> "free"
59
+ # Money.us_dollar(0).format(:display_free => "gratis") #=> "gratis"
60
+ # Money.us_dollar(0).format #=> "$0.00"
61
+ #
62
+ # @option *rules [Boolean] :with_currency (false) Whether the currency name
63
+ # should be appended to the result string.
64
+ #
65
+ # @example
66
+ # Money.ca_dollar(100).format => "$1.00"
67
+ # Money.ca_dollar(100).format(:with_currency => true) #=> "$1.00 CAD"
68
+ # Money.us_dollar(85).format(:with_currency => true) #=> "$0.85 USD"
69
+ #
70
+ # @option *rules [Boolean] :no_cents (false) Whether cents should be omitted.
71
+ #
72
+ # @example
73
+ # Money.ca_dollar(100).format(:no_cents => true) #=> "$1"
74
+ # Money.ca_dollar(599).format(:no_cents => true) #=> "$5"
75
+ #
76
+ # @option *rules [Boolean] :no_cents_if_whole (false) Whether cents should be
77
+ # omitted if the cent value is zero
78
+ #
79
+ # @example
80
+ # Money.ca_dollar(10000).format(:no_cents_if_whole => true) #=> "$100"
81
+ # Money.ca_dollar(10034).format(:no_cents_if_whole => true) #=> "$100.34"
82
+ #
83
+ # @option *rules [Boolean, String, nil] :symbol (true) Whether a money symbol
84
+ # should be prepended to the result string. The default is true. This method
85
+ # attempts to pick a symbol that's suitable for the given currency.
86
+ #
87
+ # @example
88
+ # Money.new(100, "USD") #=> "$1.00"
89
+ # Money.new(100, "GBP") #=> "£1.00"
90
+ # Money.new(100, "EUR") #=> "€1.00"
91
+ #
92
+ # # Same thing.
93
+ # Money.new(100, "USD").format(:symbol => true) #=> "$1.00"
94
+ # Money.new(100, "GBP").format(:symbol => true) #=> "£1.00"
95
+ # Money.new(100, "EUR").format(:symbol => true) #=> "€1.00"
96
+ #
97
+ # # You can specify a false expression or an empty string to disable
98
+ # # prepending a money symbol.§
99
+ # Money.new(100, "USD").format(:symbol => false) #=> "1.00"
100
+ # Money.new(100, "GBP").format(:symbol => nil) #=> "1.00"
101
+ # Money.new(100, "EUR").format(:symbol => "") #=> "1.00"
102
+ #
103
+ # # If the symbol for the given currency isn't known, then it will default
104
+ # # to "¤" as symbol.
105
+ # Money.new(100, "AWG").format(:symbol => true) #=> "¤1.00"
106
+ #
107
+ # # You can specify a string as value to enforce using a particular symbol.
108
+ # Money.new(100, "AWG").format(:symbol => "ƒ") #=> "ƒ1.00"
109
+ #
110
+ # # You can specify a indian currency format
111
+ # Money.new(10000000, "INR").format(:south_asian_number_formatting => true) #=> "1,00,000.00"
112
+ # Money.new(10000000).format(:south_asian_number_formatting => true) #=> "$1,00,000.00"
113
+ #
114
+ # @option *rules [Boolean, nil] :symbol_before_without_space (true) Whether
115
+ # a space between the money symbol and the amount should be inserted when
116
+ # +:symbol_position+ is +:before+. The default is true (meaning no space). Ignored
117
+ # if +:symbol+ is false or +:symbol_position+ is not +:before+.
118
+ #
119
+ # @example
120
+ # # Default is to not insert a space.
121
+ # Money.new(100, "USD").format #=> "$1.00"
122
+ #
123
+ # # Same thing.
124
+ # Money.new(100, "USD").format(:symbol_before_without_space => true) #=> "$1.00"
125
+ #
126
+ # # If set to false, will insert a space.
127
+ # Money.new(100, "USD").format(:symbol_before_without_space => false) #=> "$ 1.00"
128
+ #
129
+ # @option *rules [Boolean, nil] :symbol_after_without_space (false) Whether
130
+ # a space between the the amount and the money symbol should be inserted when
131
+ # +:symbol_position+ is +:after+. The default is false (meaning space). Ignored
132
+ # if +:symbol+ is false or +:symbol_position+ is not +:after+.
133
+ #
134
+ # @example
135
+ # # Default is to insert a space.
136
+ # Money.new(100, "USD").format(:symbol_position => :after) #=> "1.00 $"
137
+ #
138
+ # # If set to true, will not insert a space.
139
+ # Money.new(100, "USD").format(:symbol_position => :after, :symbol_after_without_space => true) #=> "1.00$"
140
+ #
141
+ # @option *rules [Boolean, String, nil] :decimal_mark (true) Whether the
142
+ # currency should be separated by the specified character or '.'
143
+ #
144
+ # @example
145
+ # # If a string is specified, it's value is used.
146
+ # Money.new(100, "USD").format(:decimal_mark => ",") #=> "$1,00"
147
+ #
148
+ # # If the decimal_mark for a given currency isn't known, then it will default
149
+ # # to "." as decimal_mark.
150
+ # Money.new(100, "FOO").format #=> "$1.00"
151
+ #
152
+ # @option *rules [Boolean, String, nil] :thousands_separator (true) Whether
153
+ # the currency should be delimited by the specified character or ','
154
+ #
155
+ # @example
156
+ # # If false is specified, no thousands_separator is used.
157
+ # Money.new(100000, "USD").format(:thousands_separator => false) #=> "1000.00"
158
+ # Money.new(100000, "USD").format(:thousands_separator => nil) #=> "1000.00"
159
+ # Money.new(100000, "USD").format(:thousands_separator => "") #=> "1000.00"
160
+ #
161
+ # # If a string is specified, it's value is used.
162
+ # Money.new(100000, "USD").format(:thousands_separator => ".") #=> "$1.000.00"
163
+ #
164
+ # # If the thousands_separator for a given currency isn't known, then it will
165
+ # # default to "," as thousands_separator.
166
+ # Money.new(100000, "FOO").format #=> "$1,000.00"
167
+ #
168
+ # @option *rules [Boolean] :html (false) Whether the currency should be
169
+ # HTML-formatted. Only useful in combination with +:with_currency+.
170
+ #
171
+ # @example
172
+ # s = Money.ca_dollar(570).format(:html => true, :with_currency => true)
173
+ # s #=> "$5.70 <span class=\"currency\">CAD</span>"
174
+ #
175
+ # @option *rules [Boolean] :sign_before_symbol (false) Whether the sign should be
176
+ # before the currency symbol.
177
+ #
178
+ # @example
179
+ # # You can specify to display the sign before the symbol for negative numbers
180
+ # Money.new(-100, "GBP").format(:sign_before_symbol => true) #=> "-£1.00"
181
+ # Money.new(-100, "GBP").format(:sign_before_symbol => false) #=> "£-1.00"
182
+ # Money.new(-100, "GBP").format #=> "£-1.00"
183
+ def format(*rules)
184
+ # support for old format parameters
185
+ rules = normalize_formatting_rules(rules)
186
+ rules = localize_formatting_rules(rules)
187
+
188
+ if fractional == 0
189
+ if rules[:display_free].respond_to?(:to_str)
190
+ return rules[:display_free]
191
+ elsif rules[:display_free]
192
+ return "free"
193
+ end
194
+ end
195
+
196
+ symbol_value =
197
+ if rules.has_key?(:symbol)
198
+ if rules[:symbol] === true
199
+ symbol
200
+ elsif rules[:symbol]
201
+ rules[:symbol]
202
+ else
203
+ ""
204
+ end
205
+ elsif rules[:html]
206
+ currency.html_entity == '' ? currency.symbol : currency.html_entity
207
+ else
208
+ symbol
209
+ end
210
+
211
+ formatted = rules[:no_cents] ? "#{self.to_s.to_i}" : self.to_s
212
+
213
+ if rules[:no_cents_if_whole] && cents % currency.subunit_to_unit == 0
214
+ formatted = "#{self.to_s.to_i}"
215
+ end
216
+
217
+ thousands_separator_value = thousands_separator
218
+ # Determine thousands_separator
219
+ if rules.has_key?(:thousands_separator)
220
+ thousands_separator_value = rules[:thousands_separator] || ''
221
+ end
222
+
223
+ # Apply thousands_separator
224
+ formatted.gsub!(regexp_format(formatted, rules, decimal_mark, symbol_value),
225
+ "\\1#{thousands_separator_value}")
226
+
227
+ symbol_position =
228
+ if rules.has_key?(:symbol_position)
229
+ rules[:symbol_position]
230
+ elsif currency.symbol_first?
231
+ :before
232
+ else
233
+ :after
234
+ end
235
+
236
+ sign = ""
237
+ if rules[:sign_before_symbol] == true && self.negative?
238
+ formatted.tr!("-", "")
239
+ sign = "-"
240
+ end
241
+
242
+ if symbol_value && !symbol_value.empty?
243
+ symbol_value = "<span class=\"currency_symbol\">#{symbol_value}</span>" if rules[:html_wrap_symbol]
244
+
245
+ formatted = if symbol_position == :before
246
+ symbol_space = rules[:symbol_before_without_space] === false ? " " : ""
247
+ "#{sign}#{symbol_value}#{symbol_space}#{formatted}"
248
+ else
249
+ symbol_space = rules[:symbol_after_without_space] ? "" : " "
250
+ "#{sign}#{formatted}#{symbol_space}#{symbol_value}"
251
+ end
252
+ end
253
+
254
+ if rules.has_key?(:decimal_mark) && rules[:decimal_mark] &&
255
+ rules[:decimal_mark] != decimal_mark
256
+ formatted.sub!(decimal_mark, rules[:decimal_mark])
257
+ end
258
+
259
+ if rules[:with_currency]
260
+ formatted << " "
261
+ formatted << '<span class="currency">' if rules[:html]
262
+ formatted << currency.to_s
263
+ formatted << '</span>' if rules[:html]
264
+ end
265
+ formatted
266
+ end
267
+
268
+
269
+ private
270
+
271
+ # Cleans up formatting rules.
272
+ #
273
+ # @param [Hash] rules
274
+ #
275
+ # @return [Hash]
276
+ def normalize_formatting_rules(rules)
277
+ if rules.size == 0
278
+ rules = {}
279
+ elsif rules.size == 1
280
+ rules = rules.pop
281
+ rules = { rules => true } if rules.is_a?(Symbol)
282
+ end
283
+ if !rules.include?(:decimal_mark) && rules.include?(:separator)
284
+ rules[:decimal_mark] = rules[:separator]
285
+ end
286
+ if !rules.include?(:thousands_separator) && rules.include?(:delimiter)
287
+ rules[:thousands_separator] = rules[:delimiter]
288
+ end
289
+ rules
290
+ end
291
+ end
292
+
293
+ def regexp_format(formatted, rules, decimal_mark, symbol_value)
294
+ regexp_decimal = Regexp.escape(decimal_mark)
295
+ if rules[:south_asian_number_formatting]
296
+ /(\d+?)(?=(\d\d)+(\d)(?:\.))/
297
+ else
298
+ # Symbols may contain decimal marks (E.g "դր.")
299
+ if formatted.sub(symbol_value, "") =~ /#{regexp_decimal}/
300
+ /(\d)(?=(?:\d{3})+(?:#{regexp_decimal}))/
301
+ else
302
+ /(\d)(?=(?:\d{3})+(?:[^\d]{1}|$))/
303
+ end
304
+ end
305
+ end
306
+
307
+ def localize_formatting_rules(rules)
308
+ if currency.iso_code == "JPY" && I18n.locale == :ja
309
+ rules[:symbol] = "円"
310
+ rules[:symbol_position] = :after
311
+ rules[:symbol_after_without_space] = true
312
+ end
313
+ rules
314
+ end
315
+ end