Skip to content
This repository
Browse code

Upgraded NumberHelper with number_to_phone support international form…

…ats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) (closes #6421) [BobSilva]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@5336 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 1c71a5d25fff323eef25ca3f744151e59c1ee86b 1 parent afd288c
David Heinemeier Hansson authored October 22, 2006
2  actionpack/CHANGELOG
... ...
@@ -1,5 +1,7 @@
1 1
 *SVN*
2 2
 
  3
+* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 [BobSilva]
  4
+
3 5
 * Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 [BobSilva/chrismear]
4 6
 
5 7
 * Force *_url named routes to show the host in ActionView [Rick]
164  actionpack/lib/action_view/helpers/number_helper.rb
... ...
@@ -1,42 +1,64 @@
1 1
 module ActionView
2  
-  module Helpers
3  
-    # Provides methods for converting a number into a formatted string that currently represents
4  
-    # one of the following forms: phone number, percentage, money, or precision level.
  2
+  module Helpers #:nodoc:
  3
+    # Provides methods for converting a numbers into formatted strings.
  4
+    # Methods are provided for phone numbers, currency, percentage,
  5
+    # precision, positional notation, and file size.
5 6
     module NumberHelper
6  
-      # Formats a +number+ into a US phone number string. The +options+ can be a hash used to customize the format of the output.
7  
-      # The area code can be surrounded by parentheses by setting +:area_code+ to true; default is false
8  
-      # The delimiter can be set using +:delimiter+; default is "-"
9  
-      # Examples:
10  
-      #   number_to_phone(1235551234)   => 123-555-1234
11  
-      #   number_to_phone(1235551234, {:area_code => true})   => (123) 555-1234
12  
-      #   number_to_phone(1235551234, {:delimiter => " "})    => 123 555 1234
13  
-      #   number_to_phone(1235551234, {:area_code => true, :extension => 555})  => (123) 555-1234 x 555
  7
+      # Formats a +number+ into a US phone number. You can customize the format
  8
+      # in the +options+ hash.
  9
+      # * <tt>:area_code</tt>  - Adds parentheses around the area code.
  10
+      # * <tt>:delimiter</tt>  - Specifies the delimiter to use, defaults to "-".
  11
+      # * <tt>:extension</tt>  - Specifies an extension to add to the end of the
  12
+      #   generated number
  13
+      # * <tt>:country_code</tt>  - Sets the country code for the phone number.
  14
+      #
  15
+      #  number_to_phone(1235551234)   => 123-555-1234
  16
+      #  number_to_phone(1235551234, :area_code => true)   => (123) 555-1234
  17
+      #  number_to_phone(1235551234, :delimiter => " ")    => 123 555 1234
  18
+      #  number_to_phone(1235551234, :area_code => true, :extension => 555)  => (123) 555-1234 x 555
  19
+      #  number_to_phone(1235551234, :country_code => 1)
14 20
       def number_to_phone(number, options = {})
15  
-        options   = options.stringify_keys
16  
-        area_code = options.delete("area_code") { false }
17  
-        delimiter = options.delete("delimiter") { "-" }
18  
-        extension = options.delete("extension") { "" }
  21
+        number       = number.to_s.strip unless number.nil?
  22
+        options      = options.stringify_keys
  23
+        area_code    = options["area_code"] || nil
  24
+        delimiter    = options["delimiter"] || "-"
  25
+        extension    = options["extension"].to_s.strip || nil
  26
+        country_code = options["country_code"] || nil
  27
+
19 28
         begin
20  
-          str = area_code == true ? number.to_s.gsub(/([0-9]{3})([0-9]{3})([0-9]{4})/,"(\\1) \\2#{delimiter}\\3") : number.to_s.gsub(/([0-9]{3})([0-9]{3})([0-9]{4})/,"\\1#{delimiter}\\2#{delimiter}\\3")
21  
-          extension.to_s.strip.empty? ? str : "#{str} x #{extension.to_s.strip}"
  29
+          str = ""
  30
+          str << "+#{country_code}#{delimiter}" unless country_code.blank?
  31
+          str << if area_code
  32
+            number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
  33
+          else
  34
+            number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
  35
+          end
  36
+          str << " x #{extension}" unless extension.blank?
  37
+          str
22 38
         rescue
23 39
           number
24 40
         end
25 41
       end
26 42
 
27  
-      # Formats a +number+ into a currency string. The +options+ hash can be used to customize the format of the output.
28  
-      # The +number+ can contain a level of precision using the +precision+ key; default is 2
29  
-      # The currency type can be set using the +unit+ key; default is "$"
30  
-      # The unit separator can be set using the +separator+ key; default is "."
31  
-      # The delimiter can be set using the +delimiter+ key; default is ","
32  
-      # Examples:
33  
-      #    number_to_currency(1234567890.50)     => $1,234,567,890.50
34  
-      #    number_to_currency(1234567890.506)    => $1,234,567,890.51
35  
-      #    number_to_currency(1234567890.50, {:unit => "&pound;", :separator => ",", :delimiter => ""}) => &pound;1234567890,50
  43
+      # Formats a +number+ into a currency string. You can customize the format
  44
+      # in the +options+ hash.
  45
+      # * <tt>:precision</tt>  -  Sets the level of precision, defaults to 2
  46
+      # * <tt>:unit</tt>  - Sets the denomination of the currency, defaults to "$"
  47
+      # * <tt>:separator</tt>  - Sets the separator between the units, defaults to "."
  48
+      # * <tt>:delimiter</tt>  - Sets the thousands delimiter, defaults to ","
  49
+      #
  50
+      #  number_to_currency(1234567890.50)     => $1,234,567,890.50
  51
+      #  number_to_currency(1234567890.506)    => $1,234,567,890.51
  52
+      #  number_to_currency(1234567890.506, :precision => 3)    => $1,234,567,890.506
  53
+      #  number_to_currency(1234567890.50, :unit => "&pound;", :separator => ",", :delimiter => "") 
  54
+      #     => &pound;1234567890,50
36 55
       def number_to_currency(number, options = {})
37  
-        options = options.stringify_keys
38  
-        precision, unit, separator, delimiter = options.delete("precision") { 2 }, options.delete("unit") { "$" }, options.delete("separator") { "." }, options.delete("delimiter") { "," }
39  
-        separator = "" unless precision > 0
  56
+        options   = options.stringify_keys
  57
+        precision = options["precision"] || 2
  58
+        unit      = options["unit"] || "$"
  59
+        separator = precision > 0 ? options["separator"] || "." : ""
  60
+        delimiter = options["delimiter"] || ","
  61
+        
40 62
         begin
41 63
           parts = number_with_precision(number, precision).split('.')
42 64
           unit + number_with_delimiter(parts[0], delimiter) + separator + parts[1].to_s
@@ -45,16 +67,19 @@ def number_to_currency(number, options = {})
45 67
         end
46 68
       end
47 69
 
48  
-      # Formats a +number+ as into a percentage string. The +options+ hash can be used to customize the format of the output.
49  
-      # The +number+ can contain a level of precision using the +precision+ key; default is 3
50  
-      # The unit separator can be set using the +separator+ key; default is "."
51  
-      # Examples:
52  
-      #   number_to_percentage(100)    => 100.000%
53  
-      #   number_to_percentage(100, {:precision => 0}) => 100%
54  
-      #   number_to_percentage(302.0574, {:precision => 2})  => 302.06%
  70
+      # Formats a +number+ as a percentage string. You can customize the
  71
+      # format in the +options+ hash.
  72
+      # * <tt>:precision</tt>  - Sets the level of precision, defaults to 3
  73
+      # * <tt>:separator</tt>  - Sets the separator between the units, defaults to "."
  74
+      #
  75
+      #  number_to_percentage(100)    => 100.000%
  76
+      #  number_to_percentage(100, {:precision => 0})   => 100%
  77
+      #  number_to_percentage(302.0574, {:precision => 2})   => 302.06%
55 78
       def number_to_percentage(number, options = {})
56  
-        options = options.stringify_keys
57  
-        precision, separator = options.delete("precision") { 3 }, options.delete("separator") { "." }
  79
+        options   = options.stringify_keys
  80
+        precision = options["precision"] || 3
  81
+        separator = options["separator"] || "."
  82
+        
58 83
         begin
59 84
           number = number_with_precision(number, precision)
60 85
           parts = number.split('.')
@@ -68,9 +93,14 @@ def number_to_percentage(number, options = {})
68 93
         end
69 94
       end
70 95
 
71  
-      # Formats a +number+ with a +delimiter+.
72  
-      # Example:
73  
-      #    number_with_delimiter(12345678) => 12,345,678
  96
+      # Formats a +number+ with grouped thousands using +delimiter+. You
  97
+      # can customize the format in the +options+ hash.
  98
+      # * <tt>:delimiter</tt>  - Sets the thousands delimiter, defaults to ","
  99
+      # * <tt>:separator</tt>  - Sets the separator between the units, defaults to "."
  100
+      #
  101
+      #  number_with_delimiter(12345678)      => 12,345,678
  102
+      #  number_with_delimiter(12345678.05)   => 12,345,678.05
  103
+      #  number_with_delimiter(12345678, :delimiter => ".")   => 12.345.678
74 104
       def number_with_delimiter(number, delimiter=",", separator=".")
75 105
         begin
76 106
           parts = number.to_s.split(separator)
@@ -81,35 +111,45 @@ def number_with_delimiter(number, delimiter=",", separator=".")
81 111
         end
82 112
       end
83 113
       
84  
-      # Returns a formatted-for-humans file size.
  114
+      # Formats a +number+ with the specified level of +precision+. The default
  115
+      # level of precision is 3.
  116
+      #
  117
+      #  number_with_precision(111.2345)    => 111.235
  118
+      #  number_with_precision(111.2345, 2) => 111.24
  119
+      def number_with_precision(number, precision=3)
  120
+        "%01.#{precision}f" % number
  121
+      rescue
  122
+        number
  123
+      end
  124
+      
  125
+      # Formats the bytes in +size+ into a more understandable representation.
  126
+      # Useful for reporting file sizes to users. This method returns nil if 
  127
+      # +size+ cannot be converted into a number. You can change the default 
  128
+      # precision of 1 in +precision+.
85 129
       # 
86  
-      # Examples:
87  
-      #   human_size(123)          => 123 Bytes
88  
-      #   human_size(1234)         => 1.2 KB
89  
-      #   human_size(12345)        => 12.1 KB
90  
-      #   human_size(1234567)      => 1.2 MB
91  
-      #   human_size(1234567890)   => 1.1 GB
92  
-      def number_to_human_size(size)
  130
+      #  number_to_human_size(123)           => 123 Bytes
  131
+      #  number_to_human_size(1234)          => 1.2 KB
  132
+      #  number_to_human_size(12345)         => 12.1 KB
  133
+      #  number_to_human_size(1234567)       => 1.2 MB
  134
+      #  number_to_human_size(1234567890)    => 1.1 GB
  135
+      #  number_to_human_size(1234567890123) => 1.1 TB
  136
+      #  number_to_human_size(1234567, 2)    => 1.18 MB
  137
+      def number_to_human_size(size, precision=1)
  138
+        size = Kernel.Float(size)
93 139
         case 
94  
-          when size == 1        : '1 Byte'
95  
-          when size < 1.kilobyte: '%d Bytes' % size
96  
-          when size < 1.megabyte: '%.1f KB'  % (size / 1.0.kilobyte)
97  
-          when size < 1.gigabyte: '%.1f MB'  % (size / 1.0.megabyte)
98  
-          when size < 1.terabyte: '%.1f GB'  % (size / 1.0.gigabyte)
99  
-          else                    '%.1f TB'  % (size / 1.0.terabyte)
  140
+          when size == 1        : "1 Byte"
  141
+          when size < 1.kilobyte: "%d Bytes" % size
  142
+          when size < 1.megabyte: "%.#{precision}f KB"  % (size / 1.0.kilobyte)
  143
+          when size < 1.gigabyte: "%.#{precision}f MB"  % (size / 1.0.megabyte)
  144
+          when size < 1.terabyte: "%.#{precision}f GB"  % (size / 1.0.gigabyte)
  145
+          else                    "%.#{precision}f TB"  % (size / 1.0.terabyte)
100 146
         end.sub('.0', '')
101 147
       rescue
102 148
         nil
103 149
       end
104 150
       
105 151
       alias_method :human_size, :number_to_human_size # deprecated alias
106  
-
107  
-      # Formats a +number+ with a level of +precision+.
108  
-      # Example:
109  
-      #    number_with_precision(111.2345) => 111.235
110  
-      def number_with_precision(number, precision=3)
111  
-        sprintf("%01.#{precision}f", number)
112  
-      end
  152
+      deprecate :human_size
113 153
     end
114 154
   end
115 155
 end
84  actionpack/test/template/number_helper_test.rb
... ...
@@ -1,18 +1,22 @@
1  
-require 'test/unit'
  1
+require File.dirname(__FILE__) + '/../abstract_unit'
2 2
 require File.dirname(__FILE__) + '/../../lib/action_view/helpers/number_helper'
3  
-require File.dirname(__FILE__) + '/../../../activesupport/lib/active_support/core_ext/hash' # for stringify_keys
4  
-require File.dirname(__FILE__) + '/../../../activesupport/lib/active_support/core_ext/numeric'  # for human_size
5 3
 
6 4
 class NumberHelperTest < Test::Unit::TestCase
7 5
   include ActionView::Helpers::NumberHelper
8  
-  include ActiveSupport::CoreExtensions::Hash
9 6
 
10 7
   def test_number_to_phone
11  
-    assert_equal("123-555-1234", number_to_phone(1235551234))
12  
-    assert_equal("(123) 555-1234", number_to_phone(1235551234, {:area_code => true}))
13  
-    assert_equal("123 555 1234", number_to_phone(1235551234, {:delimiter => " "}))
14  
-    assert_equal("(123) 555-1234 x 555", number_to_phone(1235551234, {:area_code => true, :extension => 555}))
15  
-    assert_equal("123-555-1234", number_to_phone(1235551234, :extension => "   "))
  8
+    assert_equal("800-555-1212", number_to_phone(8005551212))
  9
+    assert_equal("(800) 555-1212", number_to_phone(8005551212, {:area_code => true}))
  10
+    assert_equal("800 555 1212", number_to_phone(8005551212, {:delimiter => " "}))
  11
+    assert_equal("(800) 555-1212 x 123", number_to_phone(8005551212, {:area_code => true, :extension => 123}))
  12
+    assert_equal("800-555-1212", number_to_phone(8005551212, :extension => "  "))
  13
+    assert_equal("800-555-1212", number_to_phone("8005551212"))
  14
+    assert_equal("+1-800-555-1212", number_to_phone(8005551212, :country_code => 1))
  15
+    assert_equal("+18005551212", number_to_phone(8005551212, :country_code => 1, :delimiter => ''))
  16
+    assert_equal("22-555-1212", number_to_phone(225551212))
  17
+    assert_equal("+45-22-555-1212", number_to_phone(225551212, :country_code => 45))
  18
+    assert_equal("x", number_to_phone("x"))
  19
+    assert_nil number_to_phone(nil)
16 20
   end
17 21
 
18 22
   def test_number_to_currency
@@ -21,17 +25,22 @@ def test_number_to_currency
21 25
     assert_equal("$1,234,567,890", number_to_currency(1234567890.50, {:precision => 0}))
22 26
     assert_equal("$1,234,567,890.5", number_to_currency(1234567890.50, {:precision => 1}))
23 27
     assert_equal("&pound;1234567890,50", number_to_currency(1234567890.50, {:unit => "&pound;", :separator => ",", :delimiter => ""}))
  28
+    assert_equal("$1,234,567,890.50", number_to_currency("1234567890.50"))
  29
+    assert_equal("$x.", number_to_currency("x"))
  30
+    assert_nil number_to_currency(nil)
24 31
   end
25 32
 
26 33
   def test_number_to_percentage
27 34
     assert_equal("100.000%", number_to_percentage(100))
28 35
     assert_equal("100%", number_to_percentage(100, {:precision => 0}))
29 36
     assert_equal("302.06%", number_to_percentage(302.0574, {:precision => 2}))
  37
+    assert_equal("100.000%", number_to_percentage("100"))
  38
+    assert_equal("x%", number_to_percentage("x"))
  39
+    assert_nil number_to_percentage(nil)
30 40
   end
31 41
 
32 42
   def test_number_with_delimiter
33 43
     assert_equal("12,345,678", number_with_delimiter(12345678))
34  
-    assert_equal(nil, number_with_delimiter(nil))
35 44
     assert_equal("0", number_with_delimiter(0))
36 45
     assert_equal("123", number_with_delimiter(123))
37 46
     assert_equal("123,456", number_with_delimiter(123456))
@@ -40,27 +49,44 @@ def test_number_with_delimiter
40 49
     assert_equal("123,456.78901", number_with_delimiter(123456.78901))
41 50
     assert_equal("123,456,789.78901", number_with_delimiter(123456789.78901))
42 51
     assert_equal("0.78901", number_with_delimiter(0.78901))
  52
+    assert_equal("123,456.78", number_with_delimiter("123456.78"))
  53
+    assert_equal("x", number_with_delimiter("x"))
  54
+    assert_nil number_with_delimiter(nil)
43 55
   end
44  
-
45  
-  def test_number_to_human_size
46  
-    assert_equal '0 Bytes',   human_size(0)
47  
-    assert_equal '1 Byte',    human_size(1)
48  
-    assert_equal '3 Bytes',   human_size(3.14159265)
49  
-    assert_equal '123 Bytes', human_size(123.0)
50  
-    assert_equal '123 Bytes', human_size(123)
51  
-    assert_equal '1.2 KB',    human_size(1234)
52  
-    assert_equal '12.1 KB',   human_size(12345)
53  
-    assert_equal '1.2 MB',    human_size(1234567)
54  
-    assert_equal '1.1 GB',    human_size(1234567890)
55  
-    assert_equal '1.1 TB',    human_size(1234567890123)
56  
-    assert_equal '444 KB',    human_size(444.kilobytes)
57  
-    assert_equal '1023 MB',   human_size(1023.megabytes)
58  
-    assert_equal '3 TB',      human_size(3.terabytes)
59  
-    assert_nil human_size('x')
60  
-    assert_nil human_size(nil)
61  
-  end
62  
-
  56
+  
63 57
   def test_number_with_precision
64 58
     assert_equal("111.235", number_with_precision(111.2346))
  59
+    assert_equal("111.23", number_with_precision(111.2346, 2))
  60
+    assert_equal("111.00", number_with_precision(111, 2))
  61
+    assert_equal("111.235", number_with_precision("111.2346"))
  62
+    assert_equal("x", number_with_precision("x"))
  63
+    assert_nil number_with_precision(nil)
  64
+  end
  65
+  
  66
+  def test_number_to_human_size
  67
+    assert_equal '0 Bytes',   number_to_human_size(0)
  68
+    assert_equal '1 Byte',    number_to_human_size(1)
  69
+    assert_equal '3 Bytes',   number_to_human_size(3.14159265)
  70
+    assert_equal '123 Bytes', number_to_human_size(123.0)
  71
+    assert_equal '123 Bytes', number_to_human_size(123)
  72
+    assert_equal '1.2 KB',    number_to_human_size(1234)
  73
+    assert_equal '12.1 KB',   number_to_human_size(12345)
  74
+    assert_equal '1.2 MB',    number_to_human_size(1234567)
  75
+    assert_equal '1.1 GB',    number_to_human_size(1234567890)
  76
+    assert_equal '1.1 TB',    number_to_human_size(1234567890123)
  77
+    assert_equal '444 KB',    number_to_human_size(444.kilobytes)
  78
+    assert_equal '1023 MB',   number_to_human_size(1023.megabytes)
  79
+    assert_equal '3 TB',      number_to_human_size(3.terabytes)
  80
+    assert_equal '1.18 MB',   number_to_human_size(1234567, 2)
  81
+    assert_equal '3 Bytes',   number_to_human_size(3.14159265, 4)
  82
+    assert_equal("123 Bytes", number_to_human_size("123"))
  83
+    assert_nil number_to_human_size('x')
  84
+    assert_nil number_to_human_size(nil)
  85
+  end
  86
+  
  87
+  def test_human_size_alias_is_deprecated
  88
+    assert_deprecated 'human_size' do
  89
+      assert_equal '0 Bytes', human_size(0)
  90
+    end
65 91
   end
66 92
 end

0 notes on commit 1c71a5d

Please sign in to comment.
Something went wrong with that request. Please try again.