bigonazzi / formatted_decimal

Extends an ActiveRecord object to handle number format translation trough I18n

formatted_decimal / formatted_decimal.rb
100644 86 lines (77 sloc) 2.404 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
# This module extends a model's decimal column with a virtual attributes to address Rails lack of
# internationalization support of form helpers. This modules provides a setter and a getter method
# that format numbers according to the current app's locale (google "I18n.locale" for more info).
#
# Usage:
#
# YourModel < < ActiveRecord::Base
# include FormattedDecimal
# formatted_decimal :total, :sub_total
# ...
#
# Then in your views use the formatted_total and formatted_sub_total attributes instead:
#
# <%= text_field :invoice, :formatted_total, :size => 15 %>
#
# Expects the locale file (app/config/locale/it.yml) to define a number format
# it:
# number:
# format:
# separator: ","
# delimiter: "'"
# precision: 2
 
module FormattedDecimal
  def self.included(base)
    base.extend(ClassMethods)
  end
  
  module ClassMethods
    # Define a new class method that adds the getter and setter methods.
    def formatted_decimal(*args)
      args.is_a?(Array) ? symbols = args : symbols = [args]
      
      symbols.each do |symbol|
        # getter
        method = "formatted_#{symbol.to_s}"
        self.send(:define_method, method) do
          formatNum(self.send(symbol))
        end
 
        # setter
        self.send(:define_method, method + '=') do |val|
          self.send("#{symbol}=", parseNum(val))
        end
      end
    end
  end
 
  protected
  # Returns the default number format from the current I18n locale.
  def getNumFormat
    begin
      I18n.translate(:'number.format', :locale => I18n.locale, :raise => true)
    rescue
      { :precision => 3, :separator => ".", :delimiter => "" }
    end
  end
  
  # Parses a formatted number into a BigDecimal.
  def parseNum(num)
    format = getNumFormat
    
    begin
      parts = num.to_s.split(format[:separator])
      parts[0].gsub!(format[:delimiter], '')
      parts[1] = parts[1][0,format[:precision]] if format[:precision]
      parts.join('.').to_d
    rescue
      num
    end
  end
  
  # Prints a number to a string, formatted using the correct locale.
  def formatNum(num)
    format = getNumFormat
    
    begin
      parts = num.to_s.split('.')
      parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{format[:delimiter]}")
      parts[1] = parts[1][0,format[:precision]] if format[:precision]
      parts.join(format[:separator])
    rescue
      num
    end
  end
end