Sunday, October 5, 2008

Formatting phone numbers, Fax numbers

I created a plug-in act_as_formatted to format numbers, phone/fax numbers and zip codes. If there is number field (stored as integer in DB) and user formats it using "," for example 10,010, this gets saved as 10 (without special handling). To solve this problem I created a plug-in act_as_formatted. This plug-in creates *_ui and *_ui= methods on ActiveRecord models. attrubute_ui does the formatting and attribute_ui= ensures, right data is being saved. Example usage:
In model
acts_as_number :revenue
acts_phone_number :phone
In view use attribute_ui
<%= company.revenue_ui %>
<%= company.phone_ui %>
---------------------------------------
Here is the code. Ideally model should not be using ActionView::Helpers::NumberHelper but for now this seems to be working fine for e.

class ActiveRecord::Base
include ActionView::Helpers::NumberHelper
include ERB::Util

def self.format_as_number(*args)

args.each do |arg|

define_method "#{arg.to_s}_ui=" do |number_string|
if number_string
# If a valid number, sanitize to digits only
if valid_number? number_string
number_string.gsub!(/[^0-9]/, "")
end
end
write_attribute(arg, number_string)
end
end

args.each do |arg|
define_method "#{arg.to_s}_ui" do
val = read_attribute(arg)
begin
number_with_delimiter val if val
rescue
val
end
end
end

define_method 'validate' do
args.each { |arg|
error_message = 'is invalid. It can contain only digits'
if !eval(arg.to_s).nil?
errors.add(arg, error_message) unless valid_number? eval(arg.to_s)
end
}
end
end

def self.format_as_phone_number(*args)

args.each do |arg|

define_method "#{arg.to_s}=" do |number_string|
if number_string
# If a valid phone number(can contain only 0-9/-()+xX),
# sanitize it to "digits only" only if 10 digits long
# If number has more that 10 digits, store it as is
if valid_phone? number_string
numbers = number_string.gsub(/[^0-9]/, "")
if numbers.size == 10
number_string = numbers
end
end
end
write_attribute(arg, number_string)
end
end

args.each do |arg|
define_method "#{arg.to_s}_ui" do
val = read_attribute(arg)
begin
if val
numbers = val.gsub(/[^0-9]/, "")
#Format 10 digits phone number only
if numbers.size == 10
return number_to_phone(numbers,:area_code => true)
end
end
val
rescue
val
end
end
end

define_method 'validate' do
args.each { |arg|
error_message = 'is invalid. It must contain at least 5 digits, only the following characters are allowed: 0-9/-()+x'
if !eval(arg.to_s).nil?
errors.add(arg, error_message) unless valid_phone? eval(arg.to_s)
end
}
end
end

# Valid phone number can contain only 0-9/-()+xX and whitespace.
def valid_phone?(number)
return true if( number.nil? || number.strip.size == 0)
n_digits = number.scan(/[0-9]/).size
valid_chars = (number =~ /^[xX.+\/\-() 0-9]+$/)
return n_digits >= 5 && valid_chars
end

# Valid number can contain only digits and ,.
def valid_number?(number)
return true if( number.nil? || number.strip.size == 0)
valid_chars = (number =~ /^[\,\ 0-9]+$/)
return valid_chars
end

end

No comments: