Class: Currency::Parser

Inherits:
Object show all
Defined in:
lib/currency/parser.rb

Overview

This class parses a Money value from a String. Each Currency has a default Parser.

Constant Summary collapse

@@default =
nil
@@empty_hash =
{ }

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opt = { }) ⇒ Parser

Returns a new instance of Parser.



39
40
41
42
43
44
45
# File 'lib/currency/parser.rb', line 39

def initialize(opt = { })
  @time =
    @enforce_currency =
    @currency =
    nil
  opt.each_pair{ | k, v | self.send("#{k}=", v) }
end

Instance Attribute Details

#currencyObject

The default Currency to use if no Currency is specified.



13
14
15
# File 'lib/currency/parser.rb', line 13

def currency
  @currency
end

#enforce_currencyObject

If true and a parsed string contains a ISO currency code that is not the same as currency, #parse() will raise IncompatibleCurrency. Defaults to false.



19
20
21
# File 'lib/currency/parser.rb', line 19

def enforce_currency
  @enforce_currency
end

#timeObject

The default Time to use if no Time is specified in the string. If :now, time is set to Time.new.



23
24
25
# File 'lib/currency/parser.rb', line 23

def time
  @time
end

Class Method Details

.defaultObject

Get the default Formatter.



27
28
29
30
# File 'lib/currency/parser.rb', line 27

def self.default
  @@default ||=
    self.new
end

.default=(x) ⇒ Object

Set the default Formatter.



34
35
36
# File 'lib/currency/parser.rb', line 34

def self.default=(x)
  @@default = x
end

Instance Method Details

#_parse(str) ⇒ Object

:nodoc:



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/currency/parser.rb', line 48

def _parse(str) # :nodoc:
  x = str

  # Get currency.
  # puts "str = #{str.inspect}, @currency = #{@currency}"

  md = nil # match data

  # $stderr.puts "'#{x}'.Money_rep(#{self})"
  
  # Parse time.
  time = nil
  if (md = /(\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?Z)/.match(x))
    time = Time.xmlschema(md[1])
    unless time
      raise ::Currency::Exception::InvalidMoneyString.new("time: #{str.inspect} #{currency} #{x.inspect}")
    end
    x = md.pre_match + md.post_match
  end
  # Default time
  time ||= @time
  time = Time.new if time == :now
  
  # $stderr.puts "x = #{x}"
  convert_currency = nil
  # Handle currency code in string.
  if (md = /([A-Z][A-Z][A-Z])/.match(x)) 
    curr = ::Currency::Currency.get(md[1])
    x = md.pre_match + md.post_match
    if @currency && @currency != curr
      if @enforce_currency
        raise ::Currency::Exception::IncompatibleCurrency.new("currency: #{str.inspect} #{@currency.code}")
      end
      convert_currency = @currency
    end
    currency = curr
  else
    currency = @currency || ::Currency::Currency.default
    currency = ::Currency::Currency.get(currency)
  end

  # Remove placeholders and symbol.
  x = x.gsub(/[, ]/, '')
  symbol = currency.symbol # FIXME
  x = x.gsub(symbol, '') if symbol
  
  # $stderr.puts "x = #{x.inspect}"
  # Match: whole Currency value.
  if md = /^([-+]?\d+)\.?$/.match(x)
    # $stderr.puts "'#{self}'.parse(#{str}) => EXACT"
    x = ::Currency::Money.new_rep(md[1].to_i * currency.scale, currency, @time)
    
    # Match: fractional Currency value.
  elsif md = /^([-+]?)(\d*)\.(\d+)$/.match(x)
    sign = md[1]
    whole = md[2]
    part = md[3]
    
    # $stderr.puts "'#{self}'.parse(#{str}) => DECIMAL (#{sign} #{whole} . #{part})"
    
    if part.length != currency.scale
      
      # Pad decimal places with additional '0'
      while part.length < currency.scale_exp
        part << '0'
      end
      
      # Truncate to Currency's decimal size. 
      part = part[0 ... currency.scale_exp]
      
      # $stderr.puts "  => INEXACT DECIMAL '#{whole}'"
    end
    
    # Put the string back together:
    #   #{sign}#{whole}#{part}
    whole = sign + whole + part
    # $stderr.puts "  => REP = #{whole}"
    
    x = whole.to_i
    
    x = ::Currency::Money.new_rep(x, currency, time)
  else
    # $stderr.puts "'#{self}'.parse(#{str}) => ??? '#{x}'"
    #x.to_f.Money_rep(self)
    raise ::Currency::Exception::InvalidMoneyString.new("#{str.inspect} #{currency} #{x.inspect}")
  end

  # Do conversion.
  if convert_currency
    x = x.convert(convert_currency)
  end


  x
end

#parse(str, opt = @@empty_hash) ⇒ Object

Parse a Money string in this Currency.

"123.45".money       # Using default Currency.
=> USD $123.45

"$123.45 USD".money   # Explicit Currency.
=> USD $123.45

"CAD 123.45".money
=> CAD $123.45

"123.45 CAD".money(:USD)  # Incompatible explicit Currency.
  !!! "123.45 CAD" USD (Currency::Exception::IncompatibleCurrency)


162
163
164
165
166
167
168
169
170
171
# File 'lib/currency/parser.rb', line 162

def parse(str, opt = @@empty_hash)
  prs = self

  unless opt.empty? 
    prs = prs.clone
    opt.each_pair{ | k, v | prs.send("#{k}=", v) }
  end
  
  prs._parse(str)
end