monetize 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bdb994a311b7519b56c417e4496a36bd6e41c377
4
- data.tar.gz: f788b003d6412c6c3263b58736602f38be014c65
3
+ metadata.gz: 7f96f9f99c2fc04ab625a2977f370eff77d33696
4
+ data.tar.gz: 75817ce67a91ae35019f6ef3a3b290b937b64749
5
5
  SHA512:
6
- metadata.gz: 6c7435ede20d6b291da530a7c67876d347a2b484b46781ff5a7d040b4de6de6ce9c7f0378ef2179473e541c6b70223d6a793eb4c11911da0e38cc1e92a32e61a
7
- data.tar.gz: 12d53e41df08426527aceb44dd598bcf4dcdd926e6cf8d279ad8ce81e16f00b68a9b0503079df141dec6aa62de58e1f0ad7d3b4dee292a1b366f43df9c489571
6
+ metadata.gz: 657c4f4be74370f9a8b2b1f4fcb91e60a013e9d2e01f39f95258a48c47710c5418767770af32ca61c19d52b0a94ccaa875fd3a4b7d8f21023ff7f4b036700c90
7
+ data.tar.gz: 4cb6265937f209b0797cc4c0d2e78b4fa27a9f27f9f045cfac8d00812dcb2ccb86072672897b7fb36f7337dab52cbb62c3e58739b9cdcdc9ec32b73393bedd9a
@@ -0,0 +1,32 @@
1
+ Documentation:
2
+ Enabled: false
3
+ Metrics/AbcSize:
4
+ Enabled: false
5
+ Metrics/ClassLength:
6
+ Enabled: false
7
+ Metrics/MethodLength:
8
+ Enabled: false
9
+ Metrics/ModuleLength:
10
+ Enabled: false
11
+ Style/AndOr:
12
+ Enabled: false
13
+ Style/ClassAndModuleChildren:
14
+ Enabled: false
15
+ Style/PerlBackrefs:
16
+ Enabled: false
17
+
18
+ Style/CollectionMethods:
19
+ Enabled: yes
20
+ Lint/LiteralInInterpolation:
21
+ Enabled: yes
22
+
23
+ Metrics/LineLength:
24
+ Max: 120
25
+ Metrics/ParameterLists:
26
+ CountKeywordArgs: false
27
+ Style/DotPosition:
28
+ EnforcedStyle: trailing
29
+ Style/EmptyElse:
30
+ EnforcedStyle: empty
31
+ Style/SpaceInsideHashLiteralBraces:
32
+ EnforcedStyle: no_space
@@ -1,14 +1,21 @@
1
1
  # Changelog
2
2
 
3
- ## Master
3
+ ## 1.4.0
4
4
  - Required Forwardable on Collection to resolve NameError [\#44](https://github.com/RubyMoney/monetize/issues/44)
5
5
  - Add capability to parse currency amounts given with suffixes (K, M, B, and T)
6
6
 
7
7
  ## 1.3.0
8
8
  - Add NilClass extension
9
9
  - Added correct parsing of Brazilian Real $R symbol
10
- - Add testing task for Brazilian Real parsing
10
+ - Add testing task for Brazilian Real parsing
11
11
  - Add Lira Sign (₤) as a symbol for GBP
12
12
 
13
13
  ## 1.3.1
14
14
  - Updated Money version dependency to 6.6
15
+
16
+ ## master
17
+ - Fixed support for <code>Money.infinite_precision = true</code> in .to_money
18
+ - Add Rubocop config to project
19
+ - Reformat code to adapt to Rubocop guidelines
20
+ - Add config setting to always enforce currency delimiters
21
+ - Add rake console task
data/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
- gem "coveralls", :require => false
3
+ gem 'coveralls', require: false
4
4
 
5
5
  gemspec
data/Rakefile CHANGED
@@ -1,6 +1,18 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
3
 
4
4
  task default: :spec
5
5
 
6
6
  RSpec::Core::RakeTask.new
7
+
8
+ task :environment do
9
+ require_relative 'lib/monetize'
10
+ end
11
+
12
+ desc 'Start a console with library loaded'
13
+ task console: :environment do
14
+ require 'irb'
15
+ require 'irb/completion'
16
+ ARGV.clear
17
+ IRB.start
18
+ end
@@ -19,7 +19,7 @@ module Monetize
19
19
  if input.respond_to? :strip
20
20
  @input = input.clone.strip
21
21
  else
22
- raise ArgumentError 'Input must be a string'
22
+ fail ArgumentError, 'Input must be a string'
23
23
  end
24
24
 
25
25
  @currency = currency
@@ -47,13 +47,11 @@ module Monetize
47
47
  RANGE_SPLIT = /-/
48
48
 
49
49
  def split_list
50
- Array @input.split(LIST_SPLIT)
51
- .map(&:strip)
50
+ @input.split(LIST_SPLIT).map(&:strip)
52
51
  end
53
52
 
54
53
  def split_range
55
- Array @input.split(RANGE_SPLIT)
56
- .map(&:strip)
54
+ @input.split(RANGE_SPLIT).map(&:strip)
57
55
  end
58
56
  end
59
57
  end
@@ -1,37 +1,42 @@
1
1
  # encoding: utf-8
2
2
 
3
- require "money"
4
- require "monetize/core_extensions"
5
- require "monetize/version"
6
- require "collection"
3
+ require 'money'
4
+ require 'monetize/core_extensions'
5
+ require 'monetize/version'
6
+ require 'collection'
7
7
 
8
8
  module Monetize
9
-
10
9
  CURRENCY_SYMBOLS = {
11
- "$" => "USD",
12
- "" => "EUR",
13
- "£" => "GBP",
14
- "" => "GBP",
15
- "R$" => "BRL",
16
- "R" => "ZAR",
17
- "¥" => "JPY",
18
- "C$" => "CAD"
10
+ '$' => 'USD',
11
+ '' => 'EUR',
12
+ '£' => 'GBP',
13
+ '' => 'GBP',
14
+ 'R$' => 'BRL',
15
+ 'R' => 'ZAR',
16
+ '¥' => 'JPY',
17
+ 'C$' => 'CAD'
19
18
  }
20
19
 
21
20
  MULTIPLIER_SUFFIXES = {
22
- "K" => 3,
23
- "M" => 6,
24
- "B" => 9,
25
- "T" => 12
21
+ 'K' => 3,
22
+ 'M' => 6,
23
+ 'B' => 9,
24
+ 'T' => 12
26
25
  }
27
26
  MULTIPLIER_SUFFIXES.default = 0
28
- MULTIPLIER_REGEXP = Regexp.new('\d(%s)\b[^\d]*$' % MULTIPLIER_SUFFIXES.keys.join('|'), 'i')
27
+ MULTIPLIER_REGEXP = Regexp.new(format('^(.*?\d)(%s)\b([^\d]*)$', MULTIPLIER_SUFFIXES.keys.join('|')), 'i')
29
28
 
30
29
  # Class methods
31
30
  class << self
32
31
  # @attr_accessor [true, false] assume_from_symbol Use this to enable the
33
32
  # ability to assume the currency from a passed symbol
34
33
  attr_accessor :assume_from_symbol
34
+
35
+ # Monetize uses the delimiters set in the currency to separate integers from
36
+ # decimals, and to ignore thousands separators. In some corner cases,
37
+ # though, it will try to determine the correct separator by itself. Set this
38
+ # to true to enforce the delimiters set in the currency all the time.
39
+ attr_accessor :enforce_currency_delimiters
35
40
  end
36
41
 
37
42
  def self.parse(input, currency = Money.default_currency, options = {})
@@ -61,7 +66,7 @@ module Monetize
61
66
 
62
67
  def self.from_fixnum(value, currency = Money.default_currency)
63
68
  currency = Money::Currency.wrap(currency)
64
- value = value * currency.subunit_to_unit
69
+ value *= currency.subunit_to_unit
65
70
  Money.new(value, currency)
66
71
  end
67
72
 
@@ -72,7 +77,7 @@ module Monetize
72
77
 
73
78
  def self.from_bigdecimal(value, currency = Money.default_currency)
74
79
  currency = Money::Currency.wrap(currency)
75
- value = value * currency.subunit_to_unit
80
+ value *= currency.subunit_to_unit
76
81
  value = value.round unless Money.infinite_precision
77
82
  Money.new(value, currency)
78
83
  end
@@ -85,99 +90,47 @@ module Monetize
85
90
  value = BigDecimal.new(value.to_s)
86
91
  from_bigdecimal(value, currency)
87
92
  else
88
- raise ArgumentError, "'value' should be a type of Numeric"
93
+ fail ArgumentError, "'value' should be a type of Numeric"
89
94
  end
90
95
  end
91
96
 
92
97
  def self.extract_cents(input, currency = Money.default_currency)
93
- multiplier_suffix = (matches = MULTIPLIER_REGEXP.match(input)) ? matches[1].upcase : nil
94
- multiplier_exp = MULTIPLIER_SUFFIXES[multiplier_suffix]
98
+ multiplier_exp, input = extract_multiplier(input)
95
99
 
96
100
  num = input.gsub(/[^\d.,'-]/, '')
97
101
 
98
- negative = num =~ /^-|-$/ ? true : false
99
-
100
- decimal_char = currency.decimal_mark
101
-
102
- num = num.sub(/^-|-$/, '') if negative
103
-
104
- if num.include?('-')
105
- raise ArgumentError, "Invalid currency amount (hyphen)"
106
- end
102
+ negative, num = extract_sign(num)
107
103
 
108
104
  num.chop! if num.match(/[\.|,]$/)
109
105
 
110
- used_delimiters = num.scan(/[^\d]/)
106
+ major, minor = extract_major_minor(num, currency)
111
107
 
112
- case used_delimiters.uniq.length
113
- when 0
114
- major, minor = num, 0
115
- when 2
116
- thousands_separator, decimal_mark = used_delimiters.uniq
108
+ cents = major.to_i * currency.subunit_to_unit
117
109
 
118
- major, minor = num.gsub(thousands_separator, '').split(decimal_mark)
119
- min = 0 unless min
120
- when 1
121
- decimal_mark = used_delimiters.first
110
+ cents, minor = apply_multiplier(multiplier_exp, cents, minor)
122
111
 
123
- if decimal_char == decimal_mark
124
- major, minor = num.split(decimal_char)
125
- else
126
- if num.scan(decimal_mark).length > 1 # multiple matches; treat as decimal_mark
127
- major, minor = num.gsub(decimal_mark, ''), 0
128
- else
129
- possible_major, possible_minor = num.split(decimal_mark)
130
- possible_major ||= "0"
131
- possible_minor ||= "00"
132
-
133
- if possible_minor.length != 3 # thousands_separator
134
- major, minor = possible_major, possible_minor
135
- else
136
- if possible_major.length > 3
137
- major, minor = possible_major, possible_minor
138
- else
139
- if decimal_mark == '.'
140
- major, minor = possible_major, possible_minor
141
- else
142
- major, minor = "#{possible_major}#{possible_minor}", 0
143
- end
144
- end
145
- end
146
- end
147
- end
148
- else
149
- raise ArgumentError, "Invalid currency amount"
150
- end
112
+ cents += set_minor_precision(minor, currency)
151
113
 
152
- cents = major.to_i * currency.subunit_to_unit
114
+ apply_sign(negative, cents)
115
+ end
116
+
117
+ private
153
118
 
154
- cents *= (10 ** multiplier_exp)
119
+ def self.apply_multiplier(multiplier_exp, cents, minor)
120
+ cents *= (10**multiplier_exp)
155
121
  minor = minor.to_s + ('0' * multiplier_exp)
156
- shift = minor[0 ... multiplier_exp].to_i * 100
122
+ shift = minor[0...multiplier_exp].to_i * 100
157
123
  cents += shift
158
- minor = (minor[multiplier_exp .. -1] || '')
159
-
160
- minor = if minor.size < currency.decimal_places
161
- (minor + ("0" * currency.decimal_places))[0,currency.decimal_places].to_i
162
- elsif minor.size > currency.decimal_places
163
- if minor[currency.decimal_places,1].to_i >= 5
164
- minor[0,currency.decimal_places].to_i+1
165
- else
166
- minor[0,currency.decimal_places].to_i
167
- end
168
- else
169
- minor.to_i
170
- end
171
-
172
- cents += minor
124
+ minor = (minor[multiplier_exp..-1] || '')
125
+ [cents, minor]
126
+ end
173
127
 
128
+ def self.apply_sign(negative, cents)
174
129
  negative ? cents * -1 : cents
175
130
  end
176
131
 
177
- private
178
-
179
132
  def self.contains_currency_symbol?(amount)
180
- currency_symbol_regex === amount
133
+ amount =~ currency_symbol_regex
181
134
  end
182
135
 
183
136
  def self.compute_currency(amount)
@@ -189,10 +142,87 @@ module Monetize
189
142
  end
190
143
  end
191
144
 
145
+ def self.extract_major_minor(num, currency)
146
+ used_delimiters = num.scan(/[^\d]/).uniq
147
+
148
+ case used_delimiters.length
149
+ when 0
150
+ [num, 0]
151
+ when 2
152
+ thousands_separator, decimal_mark = used_delimiters
153
+ split_major_minor(num.gsub(thousands_separator, ''), decimal_mark)
154
+ when 1
155
+ extract_major_minor_with_single_delimiter(num, currency, used_delimiters.first)
156
+ else
157
+ fail ArgumentError, 'Invalid currency amount'
158
+ end
159
+ end
160
+
161
+ def self.extract_major_minor_with_single_delimiter(num, currency, delimiter)
162
+ if delimiter == currency.decimal_mark
163
+ split_major_minor(num, delimiter)
164
+ elsif enforce_currency_delimiters and delimiter == currency.thousands_separator
165
+ [num.gsub(delimiter, ''), 0]
166
+ else
167
+ extract_major_minor_with_tentative_delimiter(num, delimiter)
168
+ end
169
+ end
170
+
171
+ def self.extract_major_minor_with_tentative_delimiter(num, delimiter)
172
+ if num.scan(delimiter).length > 1
173
+ # Multiple matches; treat as thousands separator
174
+ [num.gsub(delimiter, ''), '00']
175
+ else
176
+ possible_major, possible_minor = split_major_minor(num, delimiter)
177
+
178
+ if possible_minor.length != 3 or possible_major.length > 3 or delimiter == '.'
179
+ # Doesn't look like thousands separator
180
+ [possible_major, possible_minor]
181
+ else
182
+ ["#{possible_major}#{possible_minor}", '00']
183
+ end
184
+ end
185
+ end
186
+
187
+ def self.extract_multiplier(input)
188
+ if (matches = MULTIPLIER_REGEXP.match(input))
189
+ multiplier_suffix = matches[2].upcase
190
+ [MULTIPLIER_SUFFIXES[multiplier_suffix], "#{$1}#{$3}"]
191
+ else
192
+ [0, input]
193
+ end
194
+ end
195
+
196
+ def self.extract_sign(input)
197
+ result = (input =~ /^-+(.*)$/ or input =~ /^(.*)-+$/) ? [true, $1] : [false, input]
198
+ fail ArgumentError, 'Invalid currency amount (hyphen)' if result[1].include?('-')
199
+ result
200
+ end
201
+
192
202
  def self.regex_safe_symbols
193
- CURRENCY_SYMBOLS.keys.map { |key|
194
- Regexp.escape(key)
195
- }.join('|')
203
+ CURRENCY_SYMBOLS.keys.map { |key| Regexp.escape(key) }.join('|')
204
+ end
205
+
206
+ def self.set_minor_precision(minor, currency)
207
+ if Money.infinite_precision
208
+ (BigDecimal.new(minor) / (10**minor.size)) * currency.subunit_to_unit
209
+ elsif minor.size < currency.decimal_places
210
+ (minor + ('0' * currency.decimal_places))[0, currency.decimal_places].to_i
211
+ elsif minor.size > currency.decimal_places
212
+ if minor[currency.decimal_places, 1].to_i >= 5
213
+ minor[0, currency.decimal_places].to_i + 1
214
+ else
215
+ minor[0, currency.decimal_places].to_i
216
+ end
217
+ else
218
+ minor.to_i
219
+ end
220
+ end
221
+
222
+ def self.split_major_minor(num, delimiter)
223
+ major, minor = num.split(delimiter)
224
+ minor = '00' unless minor
225
+ [major, minor]
196
226
  end
197
227
 
198
228
  def self.currency_symbol_regex
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
- require "monetize/core_extensions/nil_class"
4
- require "monetize/core_extensions/numeric"
5
- require "monetize/core_extensions/string"
6
- require "monetize/core_extensions/symbol"
7
- require "monetize/core_extensions/hash"
3
+ require 'monetize/core_extensions/nil_class'
4
+ require 'monetize/core_extensions/numeric'
5
+ require 'monetize/core_extensions/string'
6
+ require 'monetize/core_extensions/symbol'
7
+ require 'monetize/core_extensions/hash'
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Monetize
4
- VERSION = "1.3.1"
4
+ VERSION = '1.4.0'
5
5
  end
@@ -1,26 +1,27 @@
1
1
  # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
2
+ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "monetize/version"
4
+ require 'monetize/version'
5
+ require 'English'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "monetize"
8
+ spec.name = 'monetize'
8
9
  spec.version = Monetize::VERSION
9
- spec.authors = ["Shane Emmons"]
10
- spec.email = ["shane@emmons.io"]
11
- spec.description = "A library for converting various objects into `Money` objects."
12
- spec.summary = "A library for converting various objects into `Money` objects."
13
- spec.homepage = "https://github.com/RubyMoney/monetize"
14
- spec.license = "MIT"
10
+ spec.authors = ['Shane Emmons']
11
+ spec.email = ['shane@emmons.io']
12
+ spec.description = 'A library for converting various objects into `Money` objects.'
13
+ spec.summary = 'A library for converting various objects into `Money` objects.'
14
+ spec.homepage = 'https://github.com/RubyMoney/monetize'
15
+ spec.license = 'MIT'
15
16
 
16
- spec.files = `git ls-files`.split($/)
17
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
20
21
 
21
- spec.add_dependency "money", "~> 6.6"
22
+ spec.add_dependency 'money', '~> 6.7'
22
23
 
23
- spec.add_development_dependency "bundler", "~> 1.3"
24
- spec.add_development_dependency "rake"
25
- spec.add_development_dependency "rspec", "~> 3.0.0.beta1"
24
+ spec.add_development_dependency 'bundler', '~> 1.3'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'rspec', '~> 3.0.0.beta1'
26
27
  end