Class: Money

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/bd_money/rails.rb,
lib/bd_money/bd_money.rb

Defined Under Namespace

Classes: MoneyError

Constant Summary collapse

ROUND_MODES =
{
  :ceiling   => BigDecimal::ROUND_CEILING,
  :down      => BigDecimal::ROUND_DOWN,
  :floor     => BigDecimal::ROUND_FLOOR,
  :half_down => BigDecimal::ROUND_HALF_DOWN,
  :half_even => BigDecimal::ROUND_HALF_EVEN,
  :half_up   => BigDecimal::ROUND_HALF_UP,
  :up        => BigDecimal::ROUND_UP,
}
FORMATS =
{
  :default   => { :unit => "$", :spacer => " ", :delimiter => ",", :separator => ".", :precision => 2, :last => "" },
  :no_cents  => { :unit => "$", :spacer => " ", :delimiter => ",", :separator => ".", :precision => 0, :last => "" },
  :no_commas => { :unit => "$", :spacer => " ", :delimiter => "", :separator => ".", :precision => 2, :last => "" },
  :general   => { :unit => "", :spacer => "", :delimiter => "", :separator => ".", :precision => 2, :last => "" },
}
REMOVE_RE =
%r{[$,_ ]}
VALID_RE =
%r{^(-)?(\d)+(\.\d*(e-\d{1,10})?)?$}
YAML_TYPE_ROOT =
'npadv.com,2012-03-12'
YAML_TYPE_NAME =
'money'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value, precision = nil, round_mode = nil, format = nil) ⇒ Money

Returns a new instance of Money.



34
35
36
37
38
39
# File 'lib/bd_money/bd_money.rb', line 34

def initialize(value, precision = nil, round_mode = nil, format = nil)
  self.amount = value
  self.precision = precision if precision
  self.round_mode = round_mode if round_mode
  self.format = format if format
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &blk) ⇒ Object



226
227
228
229
230
231
232
233
# File 'lib/bd_money/bd_money.rb', line 226

def method_missing(meth, *args, &blk)
  if amount.respond_to? meth
    result = amount.send meth, *args, &blk
    result.is_a?(::BigDecimal) ? convert(result) : result
  else
    super
  end
end

Class Method Details

.[](value) ⇒ Object



295
296
297
# File 'lib/bd_money/bd_money.rb', line 295

def [](value)
  new value
end

.clean(value) ⇒ Object



283
284
285
# File 'lib/bd_money/bd_money.rb', line 283

def clean(value)
  value.to_s.gsub REMOVE_RE, ''
end

.convert(value, precision = nil, round_mode = nil) ⇒ Object



279
280
281
# File 'lib/bd_money/bd_money.rb', line 279

def convert(value, precision = nil, round_mode = nil)
  new value, precision, round_mode
end

.formatObject



275
276
277
# File 'lib/bd_money/bd_money.rb', line 275

def format
  @format || :default
end

.format=(value) ⇒ Object



270
271
272
273
# File 'lib/bd_money/bd_money.rb', line 270

def format=(value)
  raise "Unknown format options [#{value}]" unless FORMATS.key?(value)
  @format = value
end

.precisionObject



257
258
259
# File 'lib/bd_money/bd_money.rb', line 257

def precision
  @precision || 2
end

.precision=(value) ⇒ Object



252
253
254
255
# File 'lib/bd_money/bd_money.rb', line 252

def precision=(value)
  raise "Unknown precision [#{value}]" unless value.is_a?(Integer)
  @precision = value
end

.round_modeObject



266
267
268
# File 'lib/bd_money/bd_money.rb', line 266

def round_mode
  @round_mode || :half_up
end

.round_mode=(value) ⇒ Object



261
262
263
264
# File 'lib/bd_money/bd_money.rb', line 261

def round_mode=(value)
  raise "Unknown rounding mode [#{value}]" unless ROUND_MODES.key?(value)
  @round_mode = value
end

.valid?(value) ⇒ Boolean

Returns:

  • (Boolean)


287
288
289
# File 'lib/bd_money/bd_money.rb', line 287

def valid?(value)
  !!value.to_s.match(VALID_RE)
end

.zeroObject



291
292
293
# File 'lib/bd_money/bd_money.rb', line 291

def zero
  new 0
end

Instance Method Details

#%(other) ⇒ Object



116
117
118
# File 'lib/bd_money/bd_money.rb', line 116

def %(other)
  convert amount % convert(other).amount
end

#*(other) ⇒ Object



104
105
106
# File 'lib/bd_money/bd_money.rb', line 104

def *(other)
  convert amount * convert(other).amount
end

#**(other) ⇒ Object



112
113
114
# File 'lib/bd_money/bd_money.rb', line 112

def **(other)
  convert amount ** convert(other).amount.to_i
end

#+(other) ⇒ Object



96
97
98
# File 'lib/bd_money/bd_money.rb', line 96

def +(other)
  convert amount + convert(other).amount
end

#-(other) ⇒ Object



100
101
102
# File 'lib/bd_money/bd_money.rb', line 100

def -(other)
  convert amount - convert(other).amount
end

#/(other) ⇒ Object



108
109
110
# File 'lib/bd_money/bd_money.rb', line 108

def /(other)
  convert amount / convert(other).amount
end

#<=>(other) ⇒ Object



92
93
94
# File 'lib/bd_money/bd_money.rb', line 92

def <=>(other)
  amount <=> convert(other).amount
end

#^(other) ⇒ Object



120
121
122
# File 'lib/bd_money/bd_money.rb', line 120

def ^(other)
  convert amount ^ convert(other).amount
end

#amountObject



53
54
55
# File 'lib/bd_money/bd_money.rb', line 53

def amount
  @amount
end

#amount=(value) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
# File 'lib/bd_money/bd_money.rb', line 41

def amount=(value)
  if value.respond_to?(:to_big_decimal)
    @amount = value.to_big_decimal
  elsif value.is_a?(BigDecimal)
    @amount = value
  else
    str = self.class.clean value
    raise MoneyError, "Invalid value [#{str}] (#{value.class.name})" unless self.class.valid?(str)
    @amount = BigDecimal.new str.gsub REMOVE_RE, ''
  end
end

#as_json(options = nil) ⇒ Object

For better json decoding



16
17
18
# File 'lib/bd_money/rails.rb', line 16

def as_json(options = nil)
  to_s
end

#convert(value, this_precision = precision, this_round_mode = round_mode) ⇒ Object



84
85
86
# File 'lib/bd_money/bd_money.rb', line 84

def convert(value, this_precision = precision, this_round_mode = round_mode)
  self.class.convert value, this_precision, this_round_mode
end

#credit?Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/bd_money/bd_money.rb', line 133

def credit?
  amount >= 0
end

#debit?Boolean

Returns:

  • (Boolean)


146
147
148
# File 'lib/bd_money/bd_money.rb', line 146

def debit?
  amount < 0
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/bd_money/bd_money.rb', line 88

def eql?(other)
  amount == convert(other).amount
end

#formatObject



80
81
82
# File 'lib/bd_money/bd_money.rb', line 80

def format
  @format || self.class.format
end

#format=(value) ⇒ Object



75
76
77
78
# File 'lib/bd_money/bd_money.rb', line 75

def format=(value)
  raise "Unknown format options [#{value}]" unless FORMATS.key?(value)
  @format = value
end

#formatted(*args) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/bd_money/bd_money.rb', line 197

def formatted(*args)
  defaults = args.first.is_a?(::Symbol) ? FORMATS[args.shift] : FORMATS[:default]
  options  = args.last.is_a?(::Hash) ? args.pop : { }

  unit   = options[:unit] || defaults[:unit]
  spacer = options[:spacer] || defaults[:spacer]
  spacer = '' if unit.to_s.empty?
  delimiter = options[:delimiter] || defaults[:delimiter]
  separator = options[:separator] || defaults[:separator]
  separator = '' if precision == 0
  precision = options[:precision] || defaults[:precision]
  last      = options[:last] || defaults[:last]

  number = to_s precision
  return number if number == 'NaN'
  begin
    parts = number.to_s.split('.')
    parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
    number = parts.join(separator)
    "#{unit}#{spacer}#{number}#{last}"
  rescue
    number
  end
end

#precisionObject



62
63
64
# File 'lib/bd_money/bd_money.rb', line 62

def precision
  @precision || self.class.precision
end

#precision=(value) ⇒ Object



57
58
59
60
# File 'lib/bd_money/bd_money.rb', line 57

def precision=(value)
  raise "Unknown precision [#{value}]" unless value.is_a?(Integer)
  @precision = value
end

#quoted_idObject

Terrible hack to allow to quote money correctly



6
7
8
# File 'lib/bd_money/rails.rb', line 6

def quoted_id
  amount
end

#respond_to?(meth) ⇒ Boolean

Returns:

  • (Boolean)


222
223
224
# File 'lib/bd_money/bd_money.rb', line 222

def respond_to?(meth)
  amount.respond_to?(meth) || super
end

#round(this_precision = precision, this_round_mode = round_mode) ⇒ Object



167
168
169
# File 'lib/bd_money/bd_money.rb', line 167

def round(this_precision = precision, this_round_mode = round_mode)
  convert round_amount(this_precision, this_round_mode)
end

#round_amount(this_precision = precision, this_round_mode = round_mode) ⇒ Object



162
163
164
165
# File 'lib/bd_money/bd_money.rb', line 162

def round_amount(this_precision = precision, this_round_mode = round_mode)
  this_round_mode = BigDecimal.const_get("ROUND_#{this_round_mode.to_s.upcase}") if this_round_mode.is_a?(Symbol)
  amount.round this_precision, this_round_mode
end

#round_modeObject



71
72
73
# File 'lib/bd_money/bd_money.rb', line 71

def round_mode
  @round_mode || self.class.round_mode
end

#round_mode=(value) ⇒ Object



66
67
68
69
# File 'lib/bd_money/bd_money.rb', line 66

def round_mode=(value)
  raise "Unknown rounding mode [#{value}]" unless ROUND_MODES.key?(value)
  @round_mode = value
end

#to_big_decimalObject



158
159
160
# File 'lib/bd_money/bd_money.rb', line 158

def to_big_decimal
  amount
end

#to_creditObject



124
125
126
# File 'lib/bd_money/bd_money.rb', line 124

def to_credit
  convert amount.abs
end

#to_credit!Object



128
129
130
131
# File 'lib/bd_money/bd_money.rb', line 128

def to_credit!
  self.amount = amount.abs
  self
end

#to_dObject

This will help to save money objects correctly



11
12
13
# File 'lib/bd_money/rails.rb', line 11

def to_d
  amount
end

#to_debitObject



137
138
139
# File 'lib/bd_money/bd_money.rb', line 137

def to_debit
  convert amount.abs * -1
end

#to_debit!Object



141
142
143
144
# File 'lib/bd_money/bd_money.rb', line 141

def to_debit!
  self.amount = amount.abs * -1
  self
end

#to_f(this_precision = precision, this_round_mode = round_mode) ⇒ Object



175
176
177
# File 'lib/bd_money/bd_money.rb', line 175

def to_f(this_precision = precision, this_round_mode = round_mode)
  round_amount(this_precision, this_round_mode).to_f
end

#to_i(this_round_mode = round_mode) ⇒ Object



171
172
173
# File 'lib/bd_money/bd_money.rb', line 171

def to_i(this_round_mode = round_mode)
  round_amount(0, this_round_mode).to_i
end

#to_json(options = nil) ⇒ Object



193
194
195
# File 'lib/bd_money/bd_money.rb', line 193

def to_json(options = nil)
  to_s
end

#to_moneyObject



154
155
156
# File 'lib/bd_money/bd_money.rb', line 154

def to_money
  self
end

#to_s(this_precision = precision, this_round_mode = round_mode) ⇒ Object Also known as: inspect



179
180
181
182
183
184
185
186
187
188
189
# File 'lib/bd_money/bd_money.rb', line 179

def to_s(this_precision = precision, this_round_mode = round_mode)
  amount_str     = round_amount(this_precision, this_round_mode).to_s('F')
  return amount_str if amount_str == "NaN"
  dollars, cents = amount_str.split('.')
  return dollars if this_precision == 0 || cents.nil?
  if cents.size >= this_precision
    "#{dollars}.#{cents[0, this_precision]}"
  else
    "#{dollars}.#{cents}#{'0' * (this_precision - cents.size)}"
  end
end

#to_yaml(options = { }) ⇒ Object



239
240
241
242
243
244
245
246
247
248
# File 'lib/bd_money/bd_money.rb', line 239

def to_yaml(options = { })
  YAML.quick_emit(self.object_id, options) do |out|
    out.map(taguri, to_yaml_style) do |map|
      map.add 'amount', amount.to_s('F')
      map.add 'precision', @precision unless @precision.nil?
      map.add 'round_mode', @round_mode unless @round_mode.nil?
      map.add 'format', @format unless @format.nil?
    end
  end
end

#to_yaml_typeObject



235
236
237
# File 'lib/bd_money/bd_money.rb', line 235

def to_yaml_type
  "!#{YAML_TYPE_ROOT}/#{YAML_TYPE_NAME}"
end

#zero?Boolean

Returns:

  • (Boolean)


150
151
152
# File 'lib/bd_money/bd_money.rb', line 150

def zero?
  amount == 0
end