money2 7.0.0.rc1

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,150 @@
1
+ # encoding: utf-8
2
+ require 'money/unaccent'
3
+
4
+ class Money
5
+ class Currency
6
+ # Using this module requires `sixarm_ruby_unaccent` gem.
7
+ # Add it to your Gemfile manually.
8
+ module Heuristics
9
+ module_function
10
+
11
+ # An robust and efficient algorithm for finding currencies in
12
+ # text. Using several algorithms it can find symbols, iso codes and
13
+ # even names of currencies.
14
+ # Although not recommendable, it can also attempt to find the given
15
+ # currency in an entire sentence
16
+ #
17
+ # Returns: Array (matched results)
18
+ def analyze(str, klass)
19
+ Analyzer.new(str, SearchTree.cache[klass]).process
20
+ end
21
+
22
+ class SearchTree
23
+ class << self
24
+ def cache
25
+ @cache ||= Hash.new { |h, k| h[k] = new(k) }
26
+ end
27
+ end
28
+
29
+ attr_reader :currency_class
30
+
31
+ def initialize(currency_class)
32
+ @currency_class = currency_class
33
+ end
34
+
35
+ def table
36
+ currency_class.table
37
+ end
38
+
39
+ def by_symbol
40
+ @by_symbol ||= table.each_with_object({}) do |(_, c), r|
41
+ symbol = (c[:symbol]||"").downcase
42
+ symbol.chomp!('.')
43
+ (r[symbol] ||= []) << c
44
+
45
+ (c[:alternate_symbols]||[]).each do |ac|
46
+ ac = ac.downcase
47
+ ac.chomp!('.')
48
+ (r[ac] ||= []) << c
49
+ end
50
+ end
51
+ end
52
+
53
+ def by_code
54
+ @by_code ||= table.each_with_object({}) do |(k, c), r|
55
+ (r[k.downcase] ||= []) << c
56
+ end
57
+ end
58
+
59
+ def by_name
60
+ @by_name ||= table.each_with_object({}) do |(_, c), r|
61
+ name_parts = c[:name].unaccent.downcase.split
62
+ name_parts.each {|part| part.chomp!('.')}
63
+
64
+ # construct one branch per word
65
+ root = r
66
+ while name_part = name_parts.shift
67
+ root = (root[name_part] ||= {})
68
+ end
69
+
70
+ # the leaf is a currency
71
+ (root[:value] ||= []) << c
72
+ end
73
+ end
74
+ end
75
+
76
+ class Analyzer
77
+ attr_reader :search_tree, :words
78
+ attr_accessor :str, :currencies
79
+
80
+ def initialize(str, search_tree)
81
+ @str = (str || '').dup
82
+ @search_tree = search_tree
83
+ end
84
+
85
+ def process
86
+ format
87
+ return [] if str.empty?
88
+
89
+ @currencies = []
90
+ search_by_symbol
91
+ search_by_code
92
+ search_by_name
93
+
94
+ currencies.map { |x| x[:code] }.tap(&:uniq!).tap(&:sort!)
95
+ end
96
+
97
+ def format
98
+ str.gsub!(/[\r\n\t]/,'')
99
+ str.gsub!(/[0-9][\.,:0-9]*[0-9]/,'')
100
+ str.gsub!(/[0-9]/, '')
101
+ str.downcase!
102
+ @words = str.unaccent.split
103
+ @words.each {|word| word.chomp!('.'); word.chomp!(',') }
104
+ end
105
+
106
+ def search_by_symbol
107
+ words.each do |word|
108
+ if found = search_tree.by_symbol[word]
109
+ currencies.concat(found)
110
+ end
111
+ end
112
+ end
113
+
114
+ def search_by_code
115
+ words.each do |word|
116
+ if found = search_tree.by_code[word]
117
+ currencies.concat(found)
118
+ end
119
+ end
120
+ end
121
+
122
+ def search_by_name
123
+ # remember, the search tree by name is a construct of branches and leaf!
124
+ # We need to try every combination of words within the sentence, so we
125
+ # end up with a x^2 equation, which should be fine as most names are either
126
+ # one or two words, and this is multiplied with the words of given sentence
127
+
128
+ search_words = words.dup
129
+
130
+ while search_words.length > 0
131
+ root = search_tree.by_name
132
+
133
+ search_words.each do |word|
134
+ if root = root[word]
135
+ if root[:value]
136
+ currencies.concat(root[:value])
137
+ end
138
+ else
139
+ break
140
+ end
141
+ end
142
+
143
+ search_words.delete_at(0)
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+
@@ -0,0 +1,29 @@
1
+ class Money
2
+ class Currency
3
+ module Loader
4
+ DATA_PATH = File.expand_path("../../../../config", __FILE__).freeze
5
+ FILES = %w(
6
+ currency_iso.json
7
+ currency_non_iso.json
8
+ currency_backwards_compatible.json
9
+ )
10
+
11
+ extend self
12
+
13
+ # Loads and returns the currencies stored in JSON files in the config directory.
14
+ #
15
+ # @return [Hash]
16
+ def load_all
17
+ FILES.inject({}) { |acc, x| acc.merge!(load(x)) }
18
+ end
19
+
20
+ def load(filename)
21
+ json = File.read("#{DATA_PATH}/#{filename}")
22
+ json.force_encoding(::Encoding::UTF_8) if defined?(::Encoding)
23
+ JSON.parse(json, symbolize_names: true).each_with_object({}) do |x, acc|
24
+ acc[x[:code]] = x
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,139 @@
1
+ class Money
2
+ module CurrencyMethods
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ # Creates a new Money object of the given value, using the Canadian
9
+ # dollar currency.
10
+ #
11
+ # @param [Integer] cents The cents value.
12
+ #
13
+ # @return [Money]
14
+ #
15
+ # @example
16
+ # n = Money.ca_dollar(100)
17
+ # n.cents #=> 100
18
+ # n.currency #=> #<Money::Currency id: cad>
19
+ def ca_dollar(cents)
20
+ new(cents, "CAD")
21
+ end
22
+ alias_method :cad, :ca_dollar
23
+
24
+
25
+ # Creates a new Money object of the given value, using the American dollar
26
+ # currency.
27
+ #
28
+ # @param [Integer] cents The cents value.
29
+ #
30
+ # @return [Money]
31
+ #
32
+ # @example
33
+ # n = Money.us_dollar(100)
34
+ # n.cents #=> 100
35
+ # n.currency #=> #<Money::Currency id: usd>
36
+ def us_dollar(cents)
37
+ new(cents, "USD")
38
+ end
39
+ alias_method :usd, :us_dollar
40
+
41
+
42
+ # Creates a new Money object of the given value, using the Euro currency.
43
+ #
44
+ # @param [Integer] cents The cents value.
45
+ #
46
+ # @return [Money]
47
+ #
48
+ # @example
49
+ # n = Money.euro(100)
50
+ # n.cents #=> 100
51
+ # n.currency #=> #<Money::Currency id: eur>
52
+ def euro(cents)
53
+ new(cents, "EUR")
54
+ end
55
+ alias_method :eur, :euro
56
+
57
+
58
+ # Creates a new Money object of the given value, in British pounds.
59
+ #
60
+ # @param [Integer] pence The pence value.
61
+ #
62
+ # @return [Money]
63
+ #
64
+ # @example
65
+ # n = Money.pound_sterling(100)
66
+ # n.fractional #=> 100
67
+ # n.currency #=> #<Money::Currency id: gbp>
68
+ def pound_sterling(pence)
69
+ new(pence, "GBP")
70
+ end
71
+ alias_method :gbp, :pound_sterling
72
+ end
73
+
74
+ # Assuming using a currency using dollars:
75
+ # Returns the value of the money in dollars,
76
+ # instead of in the fractional unit cents.
77
+ #
78
+ # Synonym of #amount
79
+ #
80
+ # @return [BigDecimal]
81
+ #
82
+ # @example
83
+ # Money.new(1_00, "USD").dollars # => BigDecimal.new("1.00")
84
+ #
85
+ # @see #amount
86
+ # @see #to_d
87
+ # @see #cents
88
+ #
89
+ def dollars
90
+ amount
91
+ end
92
+
93
+ # Convenience method for fractional part of the amount. Synonym of #fractional
94
+ #
95
+ # @return [Integer] when infinite_precision is false
96
+ # @return [BigDecimal] when infinite_precision is true
97
+ #
98
+ # @see infinite_precision
99
+ def cents
100
+ fractional
101
+ end
102
+
103
+ # Receive a money object with the same amount as the current Money object
104
+ # in United States dollar.
105
+ #
106
+ # @return [Money]
107
+ #
108
+ # @example
109
+ # n = Money.new(100, "CAD").as_us_dollar
110
+ # n.currency #=> #<Money::Currency id: usd>
111
+ def as_us_dollar
112
+ exchange_to("USD")
113
+ end
114
+
115
+ # Receive a money object with the same amount as the current Money object
116
+ # in Canadian dollar.
117
+ #
118
+ # @return [Money]
119
+ #
120
+ # @example
121
+ # n = Money.new(100, "USD").as_ca_dollar
122
+ # n.currency #=> #<Money::Currency id: cad>
123
+ def as_ca_dollar
124
+ exchange_to("CAD")
125
+ end
126
+
127
+ # Receive a money object with the same amount as the current Money object
128
+ # in euro.
129
+ #
130
+ # @return [Money]
131
+ #
132
+ # @example
133
+ # n = Money.new(100, "USD").as_euro
134
+ # n.currency #=> #<Money::Currency id: eur>
135
+ def as_euro
136
+ exchange_to("EUR")
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,404 @@
1
+ # encoding: UTF-8
2
+ class Money
3
+ class Formatter
4
+ extend ClassAttribute
5
+
6
+ # @!attribute [rw] use_i18n
7
+ # @return [Boolean] Use this to disable i18n even if it's used by other
8
+ # objects in your app.
9
+ class_attribute :use_i18n
10
+ self.use_i18n = true
11
+
12
+ # @!attribute default_rules
13
+ # @return [Hash] Use this to define a default hash of rules for every time
14
+ # +Money#format+ is called. Rules provided on method call will be
15
+ # merged with the default ones. To overwrite a rule, just provide the
16
+ # intended value while calling +format+.
17
+ #
18
+ # @see +#format+ for more details.
19
+ #
20
+ # @example
21
+ # Money.formatter.default_rules = { :display_free => true }
22
+ # Money.new(0, "USD").format # => "free"
23
+ # Money.new(0, "USD").format(:display_free => false) # => "$0.00"
24
+ class_attribute :default_rules
25
+ self.default_rules = {}
26
+
27
+ class << self
28
+ def format(*args)
29
+ new(*args).format
30
+ end
31
+
32
+ def decimal_str(money, decimal_places = money.currency.decimal_places)
33
+ str = money.to_d.to_s('F')
34
+ units, fractional = str.split('.')
35
+ if decimal_places == 0 && fractional == '0'
36
+ units
37
+ else
38
+ pad = decimal_places - fractional.size
39
+ str << '0' * pad if pad > 0
40
+ str
41
+ end
42
+ end
43
+
44
+ # Splits string into chunks fullfilling the rightmost first:
45
+ #
46
+ # rsplit_str_by('12345') # => ['12', '345']
47
+ def rsplit_str_by(str, count)
48
+ size = str.size
49
+ i = size % count
50
+ parts = i > 0 ? [str.slice(0, i)] : []
51
+ while i < size
52
+ parts << str.slice(i, count)
53
+ i += count
54
+ end
55
+ parts
56
+ end
57
+ end
58
+
59
+ attr_reader :money, :currency, :rules
60
+
61
+ def initialize(money, **rules)
62
+ @money = money
63
+ @currency = money.currency
64
+ @rules = rules
65
+ end
66
+
67
+ # Creates a formatted price string according to several rules.
68
+ #
69
+ # @param [Hash] rules The options used to format the string.
70
+ #
71
+ # @return [String]
72
+ #
73
+ # @option rules [Boolean, String] :display_free (false) Whether a zero
74
+ # amount of money should be formatted of "free" or as the supplied string.
75
+ #
76
+ # @example
77
+ # Money.us_dollar(0).format(:display_free => true) #=> "free"
78
+ # Money.us_dollar(0).format(:display_free => "gratis") #=> "gratis"
79
+ # Money.us_dollar(0).format #=> "$0.00"
80
+ #
81
+ # @option rules [Boolean] :with_currency (false) Whether the currency name
82
+ # should be appended to the result string.
83
+ #
84
+ # @example
85
+ # Money.ca_dollar(100).format #=> "$1.00"
86
+ # Money.ca_dollar(100).format(:with_currency => true) #=> "$1.00 CAD"
87
+ # Money.us_dollar(85).format(:with_currency => true) #=> "$0.85 USD"
88
+ #
89
+ # @option rules [Boolean, Integer] :round (false) Force rounding.
90
+ # Specify number of digits after decimal point. When true is given
91
+ # it uses currency's default decimal places count.
92
+ #
93
+ # @example
94
+ # Money.us_dollar(100.1).format # => "$1.001"
95
+ # Money.us_dollar(100.1).format(round: true) # => "$1"
96
+ # Money.us_dollar(100.9).format(round: true) # => "$1.01"
97
+ # Money.us_dollar(100.9).format(round: 1) # => "$1.00"
98
+ #
99
+ # @option rules [Boolean] :no_cents (false) Whether cents should be omitted.
100
+ #
101
+ # @example
102
+ # Money.ca_dollar(100).format(:no_cents => true) #=> "$1"
103
+ # Money.ca_dollar(599).format(:no_cents => true) #=> "$5"
104
+ #
105
+ # @option rules [Boolean] :no_cents_if_whole (false) Whether cents should be
106
+ # omitted if the cent value is zero
107
+ #
108
+ # @example
109
+ # Money.ca_dollar(10000).format(:no_cents_if_whole => true) #=> "$100"
110
+ # Money.ca_dollar(10034).format(:no_cents_if_whole => true) #=> "$100.34"
111
+ #
112
+ # @option rules [Boolean, String, nil] :symbol (true) Whether a money symbol
113
+ # should be prepended to the result string. The default is true. This method
114
+ # attempts to pick a symbol that's suitable for the given currency.
115
+ #
116
+ # @example
117
+ # Money.new(100, "USD") #=> "$1.00"
118
+ # Money.new(100, "GBP") #=> "£1.00"
119
+ # Money.new(100, "EUR") #=> "€1.00"
120
+ #
121
+ # # Same thing.
122
+ # Money.new(100, "USD").format(:symbol => true) #=> "$1.00"
123
+ # Money.new(100, "GBP").format(:symbol => true) #=> "£1.00"
124
+ # Money.new(100, "EUR").format(:symbol => true) #=> "€1.00"
125
+ #
126
+ # # You can specify a false expression or an empty string to disable
127
+ # # prepending a money symbol.§
128
+ # Money.new(100, "USD").format(:symbol => false) #=> "1.00"
129
+ # Money.new(100, "GBP").format(:symbol => nil) #=> "1.00"
130
+ # Money.new(100, "EUR").format(:symbol => "") #=> "1.00"
131
+ #
132
+ # # If the symbol for the given currency isn't known, then it will default
133
+ # # to "¤" as symbol.
134
+ # Money.new(100, "AWG").format(:symbol => true) #=> "¤1.00"
135
+ #
136
+ # # You can specify a string as value to enforce using a particular symbol.
137
+ # Money.new(100, "AWG").format(:symbol => "ƒ") #=> "ƒ1.00"
138
+ #
139
+ # # You can specify a indian currency format
140
+ # Money.new(10000000, "INR").format(:south_asian => true) #=> "1,00,000.00"
141
+ # Money.new(10000000).format(:south_asian => true) #=> "$1,00,000.00"
142
+ #
143
+ # @option rules [Boolean, nil] :symbol_space (true) Whether
144
+ # a space between the money symbol and the amount should be inserted when
145
+ # +:symbol_position+ is +:before+.
146
+ # The default is false when +:symbol_position+ is +:before+,
147
+ # and true when +:symbol_position+ is +:after+.
148
+ # Ignored if +:symbol+ is false.
149
+ #
150
+ # @example
151
+ # # Default is to not insert a space.
152
+ # Money.new(100, "USD").format #=> "$1.00"
153
+ #
154
+ # # Same thing.
155
+ # Money.new(100, "USD").format(:symbol_space => true) #=> "$1.00"
156
+ #
157
+ # # If set to false, will insert a space.
158
+ # Money.new(100, "USD").format(:symbol_space => false) #=> "$ 1.00"
159
+ #
160
+ # # Default is to insert a space.
161
+ # Money.new(100, "USD").format(:symbol_position => :after) #=> "1.00 $"
162
+ #
163
+ # # If set to true, will not insert a space.
164
+ # Money.new(100, "USD").format(:symbol_position => :after, :symbol_space => true) #=> "1.00$"
165
+ #
166
+ # @example
167
+ #
168
+ # @option rules [Boolean, String, nil] :separator (true) Whether the
169
+ # currency should be separated by the specified character or '.'
170
+ #
171
+ # @example
172
+ # # If a string is specified, it's value is used.
173
+ # Money.new(100, "USD").format(:separator => ",") #=> "$1,00"
174
+ #
175
+ # # If the separator for a given currency isn't known, then it will default
176
+ # # to "." as separator.
177
+ # Money.new(100, "FOO").format #=> "$1.00"
178
+ #
179
+ # @option rules [Boolean, String, nil] :delimiter (true) Whether
180
+ # the currency should be delimited by the specified character or ','
181
+ #
182
+ # @example
183
+ # # If false is specified, no delimiter is used.
184
+ # Money.new(100000, "USD").format(:delimiter => false) #=> "1000.00"
185
+ # Money.new(100000, "USD").format(:delimiter => nil) #=> "1000.00"
186
+ # Money.new(100000, "USD").format(:delimiter => "") #=> "1000.00"
187
+ #
188
+ # # If a string is specified, it's value is used.
189
+ # Money.new(100000, "USD").format(:delimiter => ".") #=> "$1.000.00"
190
+ #
191
+ # # If the delimiter for a given currency isn't known, then it will
192
+ # # default to "," as delimiter.
193
+ # Money.new(100000, "FOO").format #=> "$1,000.00"
194
+ #
195
+ # @option rules [Boolean] :html (false) Whether the currency should be
196
+ # HTML-formatted. Only useful in combination with +:with_currency+.
197
+ #
198
+ # @example
199
+ # Money.ca_dollar(570).format(:html => true, :with_currency => true)
200
+ # #=> "$5.70 <span class=\"currency\">CAD</span>"
201
+ #
202
+ # @option rules [Boolean] :sign_before_symbol (false) Whether the sign should be
203
+ # before the currency symbol.
204
+ #
205
+ # @example
206
+ # # You can specify to display the sign before the symbol for negative numbers
207
+ # Money.new(-100, "GBP").format(:sign_before_symbol => true) #=> "-£1.00"
208
+ # Money.new(-100, "GBP").format(:sign_before_symbol => false) #=> "£-1.00"
209
+ # Money.new(-100, "GBP").format #=> "£-1.00"
210
+ #
211
+ # @option rules [Boolean] :sign_positive (false) Whether positive numbers should be
212
+ # signed, too.
213
+ #
214
+ # @example
215
+ # # You can specify to display the sign with positive numbers
216
+ # Money.new(100, "GBP").format(:sign_positive => true, :sign_before_symbol => true) #=> "+£1.00"
217
+ # Money.new(100, "GBP").format(:sign_positive => true, :sign_before_symbol => false) #=> "£+1.00"
218
+ # Money.new(100, "GBP").format(:sign_positive => false, :sign_before_symbol => true) #=> "£1.00"
219
+ # Money.new(100, "GBP").format(:sign_positive => false, :sign_before_symbol => false) #=> "£1.00"
220
+ # Money.new(100, "GBP").format #=> "£+1.00"
221
+ #
222
+ # @option rules [Boolean] :disambiguate (false) Prevents the result from being ambiguous
223
+ # due to equal symbols for different currencies. Uses the `disambiguate_symbol`.
224
+ #
225
+ # @example
226
+ # Money.new(10000, "USD").format(:disambiguate => false) #=> "$100.00"
227
+ # Money.new(10000, "CAD").format(:disambiguate => false) #=> "$100.00"
228
+ # Money.new(10000, "USD").format(:disambiguate => true) #=> "$100.00"
229
+ # Money.new(10000, "CAD").format(:disambiguate => true) #=> "C$100.00"
230
+ #
231
+ # @option rules [Boolean] :html_wrap_symbol (false) Wraps the currency symbol
232
+ # in a html <span> tag.
233
+ #
234
+ # @example
235
+ # Money.new(10000, "USD").format(:disambiguate => false)
236
+ # #=> "<span class=\"currency_symbol\">$100.00</span>
237
+ #
238
+ # @option rules [Symbol] :symbol_position (:before) `:before` if the currency
239
+ # symbol goes before the amount, `:after` if it goes after.
240
+ #
241
+ # @example
242
+ # Money.new(10000, "USD").format(:symbol_position => :before) #=> "$100.00"
243
+ # Money.new(10000, "USD").format(:symbol_position => :after) #=> "100.00 $"
244
+ #
245
+ # @option rules [Boolean] :translate_symbol (true) `true` Checks for custom
246
+ # symbol definitions using I18n.
247
+ #
248
+ # @example
249
+ # # With the following entry in the translation files:
250
+ # # en:
251
+ # # number:
252
+ # # currency:
253
+ # # symbol:
254
+ # # CAD: "CAD$"
255
+ # Money.new(10000, "CAD").format(:translate_symbol => true) #=> "CAD$100.00"
256
+ #
257
+ # @example
258
+ # Money.new(89000, :btc).format(:drop_trailing_zeros => true) #=> B⃦0.00089
259
+ # Money.new(110, :usd).format(:drop_trailing_zeros => true) #=> $1.1
260
+ #
261
+ # Note that the default rules can be defined through {Money.default_rules} hash.
262
+ #
263
+ # @see Money.default_rules Money.default_rules for more information.
264
+ def format
265
+ prepare_rules
266
+ return display_free if money.to_d == 0 && rules[:display_free]
267
+ str = format_number(money.to_d)
268
+ str = add_symbol_and_sign(str)
269
+ add_currency(str)
270
+ end
271
+
272
+ def format_number(val)
273
+ number_str =
274
+ if rules[:no_cents] || (rules[:no_cents_if_whole] && val % 1 == 0)
275
+ val.to_i.to_s
276
+ else
277
+ round = rules[:round]
278
+ if round
279
+ decimal_places = round == true ? currency.decimal_places : round
280
+ val = val.round(decimal_places)
281
+ end
282
+ self.class.decimal_str(val.abs, decimal_places || currency.decimal_places)
283
+ end
284
+
285
+ units, fractions = number_str.split('.')
286
+ if rules[:drop_trailing_zeros]
287
+ fractions.sub!(/0+\z/, '')
288
+ fractions = nil if fractions.empty?
289
+ end
290
+
291
+ units = apply_delimiter(units)
292
+ separator = rules[:separator] || self.separator
293
+ fractions ? "#{units}#{separator}#{fractions}" : units
294
+ end
295
+
296
+ def add_symbol_and_sign(number_str)
297
+ symbol_position = self.symbol_position
298
+ symbol = self.symbol
299
+ sign =
300
+ if money.negative?
301
+ '-'
302
+ elsif rules[:sign_positive] && money.positive?
303
+ '+'
304
+ end
305
+
306
+ if rules[:sign_before_symbol]
307
+ sign_before = sign
308
+ sign = nil
309
+ end
310
+
311
+ if symbol && !symbol.empty?
312
+ symbol = "<span class=\"currency_symbol\">#{symbol}</span>" if rules[:html_wrap_symbol]
313
+ case symbol_position
314
+ when :before
315
+ symbol_space = rules[:symbol_space] ? ' ' : nil
316
+ "#{sign_before}#{symbol}#{symbol_space}#{sign}#{number_str}"
317
+ when :after
318
+ symbol_space = rules.fetch(:symbol_space) { true } ? ' ' : nil
319
+ "#{sign_before}#{sign}#{number_str}#{symbol_space}#{symbol}"
320
+ else
321
+ raise ArgumentError, ':symbol_position must be :before or :after'
322
+ end
323
+ else
324
+ "#{sign_before}#{sign}#{number_str}"
325
+ end
326
+ end
327
+
328
+ def add_currency(str)
329
+ return str unless rules[:with_currency]
330
+ currency_str = currency.to_s
331
+ currency_str = "<span class=\"currency\">#{currency_str}</span>" if rules[:html]
332
+ str << ' ' << currency_str
333
+ end
334
+
335
+ def display_free
336
+ rules[:display_free].respond_to?(:to_str) ? rules[:display_free] : 'free'
337
+ end
338
+
339
+ def delimiter
340
+ val_from_i18n(:delimiter, ',')
341
+ end
342
+
343
+ def separator
344
+ val_from_i18n(:separator, '.')
345
+ end
346
+
347
+ def symbol
348
+ if rules[:translate_symbol] && rules[:symbol] != false
349
+ val = symbol_from_i18n
350
+ return val if val
351
+ end
352
+ if rules.key?(:symbol)
353
+ symbol = rules[:symbol]
354
+ if symbol == true
355
+ rules[:disambiguate] && currency.disambiguate_symbol || money.symbol
356
+ else
357
+ symbol
358
+ end
359
+ elsif rules[:html]
360
+ currency.html_entity || currency.symbol
361
+ else
362
+ rules[:disambiguate] && currency.disambiguate_symbol || money.symbol
363
+ end
364
+ end
365
+
366
+ def symbol_position
367
+ rules[:symbol_position] || (currency.symbol_first? ? :before : :after)
368
+ end
369
+
370
+ private
371
+
372
+ def val_from_i18n(name, default)
373
+ if self.class.use_i18n
374
+ I18n.t name, scope: 'number.currency.format', default: ->(*) do
375
+ I18n.t name, scope: 'number.format',
376
+ default: ->(*) { currency.public_send(name) || default }
377
+ end
378
+ else
379
+ currency.public_send(name) || default
380
+ end
381
+ end
382
+
383
+ def symbol_from_i18n
384
+ I18n.t currency.code, scope: 'number.currency.symbol', raise: true
385
+ rescue I18n::MissingTranslationData
386
+ end
387
+
388
+ def apply_delimiter(units)
389
+ parts =
390
+ if rules[:south_asian]
391
+ self.class.rsplit_str_by(units[0...-3], 2) + [[units[-3..-1]]]
392
+ else
393
+ self.class.rsplit_str_by(units, 3)
394
+ end
395
+ return parts.first if parts.one?
396
+ delimiter = rules.key?(:delimiter) ? rules[:delimiter] || '' : self.delimiter
397
+ parts.join(delimiter)
398
+ end
399
+
400
+ def prepare_rules
401
+ @rules = self.class.default_rules.merge(rules)
402
+ end
403
+ end
404
+ end