#!/usr/local/bin/ruby -w # # == extensions/integer.rb # # Adds methods to the builtin Numeric and Integer classes. # require "extensions/_base" # # * Integer#even? # ExtensionsProject.implement(Integer, :even?) do class Integer # # Returns true if this integer is even, false otherwise. # 14.even? # -> true # 15.even? # -> false # def even? self % 2 == 0 end end end # # * Integer#odd? # ExtensionsProject.implement(Integer, :odd?) do class Integer # # Returns true if this integer is odd, false otherwise. # -99.odd? # -> true # -98.odd? # -> false # def odd? self % 2 == 1 end end end # # This code arose from discussions with Francis Hwang. Leaving it here for future work. # # class Numeric # def precision_format(nplaces, flag = :pad) # format = "%.#{nplaces}f" # result = sprintf(format, self) # case flag # when :pad # when :nopad # result.sub!(/\.?0*$/, '') # else # raise ArgumentError, "Invalid value for flag: #{flag.inspect}" # end # result # end # end # # 100.precision_format(2) # -> "100.00" # 100.precision_format(2, :nopad) # -> "100" # 100.1.precision_format(2) # -> "100.10" # 100.1.precision_format(2, :nopad) # -> "100.1" # 100.1.precision_format(2, false) # # -> "ArgumentError: Invalid value for flag: false" # ExtensionsProject.implement(Numeric, :format_s) do #-- # Copyright © 2003 Austin Ziegler # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. #++ class Numeric # # Provides the base formatting styles for #format_s. See #format_s for # more details. Two keys provided that are not supported in the # #format_s arguments are: # # :style:: Allows a style to inherit from other styles. Styles # will be applied in oldest-first order in the event # of multiple inheritance layers. # :id:: This must be provided on any default style created # or provided so as to provide a stop marker so that # recursive styles do not result in an infinite loop. # # This is an implementation detail, not important for users of the class. # FORMAT_STYLES = { :us => { :sep => ',', :dec => '.', :id => :us }, :usd => { :style => :us, :currency => { :id => "$", :pos => :before }, :id => :usd }, :eu => { :sep => ' ', :dec => ',', :id => :us }, :euro => { :style => :eu, :currency => { :id => "€", :pos => :before }, :id => :euro }, :percent => { :style => :us, :currency => { :id => "%%", :pos => :after }, :id => :percent } } # # Format a number as a string, using US or European conventions, and # allowing for the accounting format of representing negative numbers. # Optionally, currency formatting options can be provided. # # For example: # x = -10259.8937 # x.format_s # => "-10,259.8937" # x.format_s(:us) # => "-10,259.8937" # x.format_s(:usd) # => "$-10,259.8937" # x.format_s(:eu) # => "-10 259,8937" # x.format_s(:euro) # => "€-10 259,8937" # x.format_s(:us, :acct => true) # => "(10,259.8937)" # x.format_s(:eu, :acct => true) # => "(10 259,8937)" # x.format_s(:usd, :acct => true) # => "$(10,259.8937)" # x.format_s(:euro, :acct => true) # => "€(10 259,8937)" # x.format_s(:percent) # => "-10,259.8937%" # # You may configure several aspects of the formatting by providing keyword # arguments after the country and accounting arguments. One example of that # is the :acct keyword. A more insane example is: # # x = -10259.8937 # x.format_s(:us, # :sep => ' ', :dec => ',', # :neg => '<%s>', :size => 2, # :fd => true) # -> "<1 02 59,89 37>" # # The keyword parameters are as follows: # # :acct:: If +true+, then use accounting style for negative # numbers. This overrides any value for # :neg. # :sep:: Default "," for US, " " for Euro. Separate the # number groups from each other with this string. # :dec:: Default "." for US, "," for Euro. Separate the # number's integer part from the fractional part # with this string. # :neg:: Default "-%s". The format string used to # represent negative numbers. If :acct is # +true+, this is set to "(%s)". # :size:: The number of digits per group. Defaults to # thousands (3). # :fd:: Indicates whether the decimal portion of the # number should be formatted the same way as the # integer portion of the number. ("fd" == "format # decimal".) Defaults to +false+. # :currency:: This is an optional hash with two keys, # :id and :pos. :id is # the string value of the currency (e.g., # "$", "€", "USD "); # :pos is either :before or # :after, referring to the position of the # currency indicator. The default :pos is # :before. # def format_s(style = :us, configs={}) style = FORMAT_STYLES[style].dup # Adopt US style by default. # Deal with recursive styles. if style[:style] styles = [] s = style while s[:style] s = FORMAT_STYLES[s[:style]].dup styles << s break if s[:style] = s[:id] end styles.reverse_each { |s| style.merge!(s) } end # Merge the configured style. style.merge!(configs) sm = style[:sep] || ',' dp = style[:dec] || '.' if style[:acct] fmt = '(%s)' else fmt = style[:neg] || '-%s' end sz = style[:size] || 3 format_decimal = style[:fd] ng = (self < 0) fmt = "%s" if not ng dec, frac = self.abs.to_s.split(/\./) dec.reverse! dec.gsub!(/\d{#{sz}}/) { |m| "#{m}#{sm}" } dec.gsub!(/#{sm}$/, '') dec.reverse! if format_decimal and not frac.nil? frac.gsub!(/\d{#{sz}}/) { |m| "#{m}#{sm}" } frac.gsub!(/#{sm}$/, '') end if frac.nil? val = dec else val = "#{dec}#{dp}#{frac}" end if style[:currency] if style[:currency][:pos].nil? or style[:currency][:pos] == :before fmt = "#{style[:currency][:id]}#{fmt}" elsif style[:currency][:pos] == :after fmt = "#{fmt}#{style[:currency][:id]}" end end fmt % val end end # class Numeric end # ExtensionsProject.implement