PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/padrino-helpers/lib/padrino-helpers/number_helpers.rb

https://github.com/cored/padrino-framework
Ruby | 288 lines | 113 code | 24 blank | 151 comment | 21 complexity | 18098546612151290efbc5b7b5022946 MD5 | raw file
  1. module Padrino
  2. module Helpers
  3. ##
  4. # Provides methods for converting numbers into formatted strings.
  5. # Methods are provided for phone numbers, currency, percentage,
  6. # precision, positional notation, and file size.
  7. #
  8. # Adapted from Rails Number Helpers.
  9. #
  10. module NumberHelpers
  11. ##
  12. # Formats a +number+ into a currency string (e.g., $13.65). You can customize the format
  13. # in the +options+ hash.
  14. #
  15. # @param [Float] number
  16. # Currency value to format.
  17. # @param [Hash] options
  18. # Options for currency conversion.
  19. # @option options [Fixnum] :precision (2)
  20. # Sets the level of precision.
  21. # @option options [String] :unit ("$")
  22. # Sets the denomination of the currency.
  23. # @option options [String] :separator (".")
  24. # Sets the separator between the units.
  25. # @option options [String] :delimiter (",")
  26. # Sets the thousands delimiter.
  27. # @option options [String] :format ("%u%n")
  28. # Sets the format of the output string. The field types are:
  29. # %u The currency unit
  30. # %n The number
  31. #
  32. # @return [String] The formatted representation of the currency
  33. #
  34. # @example
  35. # number_to_currency(1234567890.50) # => $1,234,567,890.50
  36. # number_to_currency(1234567890.506) # => $1,234,567,890.51
  37. # number_to_currency(1234567890.506, :precision => 3) # => $1,234,567,890.506
  38. # number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "")
  39. # # => £1234567890,50
  40. # number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "", :format => "%n %u")
  41. # # => 1234567890,50 £
  42. #
  43. # @api public
  44. def number_to_currency(number, options = {})
  45. options.symbolize_keys!
  46. defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
  47. currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :raise => true) rescue {}
  48. defaults = defaults.merge(currency)
  49. precision = options[:precision] || defaults[:precision]
  50. unit = options[:unit] || defaults[:unit]
  51. separator = options[:separator] || defaults[:separator]
  52. delimiter = options[:delimiter] || defaults[:delimiter]
  53. format = options[:format] || defaults[:format]
  54. separator = '' if precision == 0
  55. begin
  56. format.gsub(/%n/, number_with_precision(number,
  57. :precision => precision,
  58. :delimiter => delimiter,
  59. :separator => separator)
  60. ).gsub(/%u/, unit)
  61. rescue
  62. number
  63. end
  64. end
  65. ##
  66. # Formats a +number+ as a percentage string (e.g., 65%). You can customize the
  67. # format in the +options+ hash.
  68. #
  69. # @param [Fixnum, Float] number
  70. # Percentage value to format.
  71. # @param [Hash] options
  72. # Options for percentage conversion.
  73. # @option options [Fixnum] :precision (3)
  74. # Sets the level of precision.
  75. # @option options [String] :separator (".")
  76. # Sets the separator between the units.
  77. # @option options [String] :delimiter ("")
  78. # Sets the thousands delimiter
  79. #
  80. # @return [String] The formatted representation of the percentage
  81. #
  82. # @example
  83. # number_to_percentage(100) # => 100.000%
  84. # number_to_percentage(100, :precision => 0) # => 100%
  85. # number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000%
  86. # number_to_percentage(302.24398923423, :precision => 5) # => 302.24399%
  87. #
  88. # @api public
  89. def number_to_percentage(number, options = {})
  90. options.symbolize_keys!
  91. defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
  92. percentage = I18n.translate(:'number.percentage.format', :locale => options[:locale], :raise => true) rescue {}
  93. defaults = defaults.merge(percentage)
  94. precision = options[:precision] || defaults[:precision]
  95. separator = options[:separator] || defaults[:separator]
  96. delimiter = options[:delimiter] || defaults[:delimiter]
  97. begin
  98. number_with_precision(number,
  99. :precision => precision,
  100. :separator => separator,
  101. :delimiter => delimiter) + "%"
  102. rescue
  103. number
  104. end
  105. end
  106. ##
  107. # Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
  108. # customize the format in the +options+ hash.
  109. #
  110. # @overload number_with_delimiter(number, options={})
  111. # @param [Fixnum, Float] number
  112. # Number value to format.
  113. # @param [Hash] options
  114. # Options for formatter.
  115. # @option options [String] :delimiter (", ")
  116. # Sets the thousands delimiter
  117. # @option options [String] :separator (".")
  118. # Sets the separator between the units.
  119. #
  120. # @return [String] The formatted representation of the number
  121. #
  122. # @example
  123. # number_with_delimiter(12345678) # => 12,345,678
  124. # number_with_delimiter(12345678.05) # => 12,345,678.05
  125. # number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
  126. # number_with_delimiter(12345678, :separator => ",") # => 12,345,678
  127. # number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
  128. # # => 98 765 432,98
  129. #
  130. # @api public
  131. def number_with_delimiter(number, *args)
  132. options = args.extract_options!
  133. options.symbolize_keys!
  134. defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
  135. delimiter ||= (options[:delimiter] || defaults[:delimiter])
  136. separator ||= (options[:separator] || defaults[:separator])
  137. begin
  138. parts = number.to_s.split('.')
  139. parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
  140. parts.join(separator)
  141. rescue
  142. number
  143. end
  144. end
  145. ##
  146. # Formats a +number+ with the specified level of <tt>:precision</tt> (e.g., 112.32 has a precision of 2).
  147. # You can customize the format in the +options+ hash.
  148. #
  149. # @overload number_with_precision(number, options={})
  150. # @param [Fixnum, Float] number
  151. # Number value to format.
  152. # @param [Hash] options
  153. # Options for formatter.
  154. # @option options [Fixnum] :precision (3)
  155. # Sets the level of precision.
  156. # @option options [String] :separator (".")
  157. # Sets the separator between the units.
  158. # @option options [String] :delimiter ("")
  159. # Sets the thousands delimiter
  160. #
  161. # @return [String] The formatted representation of the number
  162. #
  163. # @example
  164. # number_with_precision(111.2345) # => 111.235
  165. # number_with_precision(111.2345, :precision => 2) # => 111.23
  166. # number_with_precision(13, :precision => 5) # => 13.00000
  167. # number_with_precision(389.32314, :precision => 0) # => 389
  168. # number_with_precision(1111.2345, :precision => 2, :separator => ',', :delimiter => '.')
  169. # # => 1.111,23
  170. #
  171. # @api public
  172. def number_with_precision(number, *args)
  173. options = args.extract_options!
  174. options.symbolize_keys!
  175. defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
  176. precision_defaults = I18n.translate(:'number.precision.format', :locale => options[:locale],
  177. :raise => true) rescue {}
  178. defaults = defaults.merge(precision_defaults)
  179. precision ||= (options[:precision] || defaults[:precision])
  180. separator ||= (options[:separator] || defaults[:separator])
  181. delimiter ||= (options[:delimiter] || defaults[:delimiter])
  182. begin
  183. rounded_number = (Float(number) * (10 ** precision)).round.to_f / 10 ** precision
  184. number_with_delimiter("%01.#{precision}f" % rounded_number,
  185. :separator => separator,
  186. :delimiter => delimiter)
  187. rescue
  188. number
  189. end
  190. end
  191. # The units available for storage formatting.
  192. STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
  193. ##
  194. # Formats the bytes in +size+ into a more understandable representation
  195. # (e.g., giving it 1500 yields 1.5 KB). This method is useful for
  196. # reporting file sizes to users. This method returns nil if
  197. # +size+ cannot be converted into a number. You can customize the
  198. # format in the +options+ hash.
  199. #
  200. #
  201. # @overload number_to_human_size(number, options={})
  202. # @param [Fixnum] number
  203. # Number value to format.
  204. # @param [Hash] options
  205. # Options for formatter.
  206. # @option options [Fixnum] :precision (1)
  207. # Sets the level of precision.
  208. # @option options [String] :separator (".")
  209. # Sets the separator between the units.
  210. # @option options [String] :delimiter ("")
  211. # Sets the thousands delimiter
  212. #
  213. # @return [String] The formatted representation of bytes
  214. #
  215. # @example
  216. # number_to_human_size(123) # => 123 Bytes
  217. # number_to_human_size(1234) # => 1.2 KB
  218. # number_to_human_size(12345) # => 12.1 KB
  219. # number_to_human_size(1234567) # => 1.2 MB
  220. # number_to_human_size(1234567890) # => 1.1 GB
  221. # number_to_human_size(1234567890123) # => 1.1 TB
  222. # number_to_human_size(1234567, :precision => 2) # => 1.18 MB
  223. # number_to_human_size(483989, :precision => 0) # => 473 KB
  224. # number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB
  225. #
  226. # @api public
  227. def number_to_human_size(number, *args)
  228. return nil if number.nil?
  229. options = args.extract_options!
  230. options.symbolize_keys!
  231. defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
  232. human = I18n.translate(:'number.human.format', :locale => options[:locale], :raise => true) rescue {}
  233. defaults = defaults.merge(human)
  234. precision ||= (options[:precision] || defaults[:precision])
  235. separator ||= (options[:separator] || defaults[:separator])
  236. delimiter ||= (options[:delimiter] || defaults[:delimiter])
  237. storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
  238. if number.to_i < 1024
  239. unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
  240. storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
  241. else
  242. max_exp = STORAGE_UNITS.size - 1
  243. number = Float(number)
  244. exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
  245. exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
  246. number /= 1024 ** exponent
  247. unit_key = STORAGE_UNITS[exponent]
  248. unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
  249. begin
  250. escaped_separator = Regexp.escape(separator)
  251. formatted_number = number_with_precision(number,
  252. :precision => precision,
  253. :separator => separator,
  254. :delimiter => delimiter
  255. ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
  256. storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
  257. rescue
  258. number
  259. end
  260. end
  261. end
  262. end # NumberHelpers
  263. end # Helpers
  264. end # Padrino