require 'phony' require 'countries' require 'phony_rails/string_extensions' require 'validators/phony_validator' require 'phony_rails/version' module PhonyRails def self.country_number_for(country_code) ISO3166::Country::Data[country_code].try(:[], 'country_code') end # This method requires a country_code attribute (eg. NL) and phone_number to be set. # Options: # :country_code => The country code we should use. # :default_country_code => Some fallback code (eg. 'NL') that can be used as default (comes from phony_normalize_numbers method). # This idea came from: # http://www.redguava.com.au/2011/06/rails-convert-phone-numbers-to-international-format-for-sms/ def self.normalize_number(number, options = {}) return if number.nil? number = number.clone # Just to be sure, we don't want to change the original. number.gsub!(/[^\d\+]/, '') # Strips weird stuff from the number return if number.blank? if country_number = country_number_for(options[:country_code] || options[:default_country_code]) # Add country_number if missing number = "#{country_number}#{number}" if not number =~ /^(00|\+)?#{country_number}/ end number = Phony.normalize(number) return number.to_s rescue number # If all goes wrong .. we still return the original input. end # This module is added to AR. module ActiveRecordExtension def self.extended(base) base.send :include, InstanceMethods base.extend ClassMethods end module InstanceMethods private # This methods sets the attribute to the normalized version. # It also adds the country_code (number), eg. 31 for NL numbers. def set_phony_normalized_numbers(attributes, options = {}) options = options.clone options[:country_code] ||= self.country_code if self.respond_to?(:country_code) attributes.each do |attribute| attribute_name = options[:as] || attribute write_attribute(attribute_name, PhonyRails.normalize_number(read_attribute(attribute), options)) end end end module ClassMethods # Use this method on the class level like: # phony_normalize :phone_number, :fax_number, :default_country_code => 'NL' # # It checks your model object for a a country_code attribute (eg. 'NL') to do the normalizing so make sure # you've geocoded before calling this method! def phony_normalize(*attributes) options = attributes.last.is_a?(Hash) ? attributes.pop : {} options.assert_valid_keys :country_code, :default_country_code, :as raise ArgumentError, ':as option can not be used on phony_normalize with multiple attribute names! (PhonyRails)' if !options[:as].blank? && attributes.size > 1 attributes.each do |attribute| raise ArgumentError, "No attribute #{attribute} found on #{self.class.name} (PhonyRails)" if not self.attribute_method?(attribute) # Add before validation that saves a normalized version of the phone number self.before_validation do set_phony_normalized_numbers(attributes, options) end end end # Usage: # phony_normalized_method :fax_number, :default_country_code => 'US' # Creates a normalized_fax_number method. def phony_normalized_method(*attributes) main_options = attributes.last.is_a?(Hash) ? attributes.pop : {} main_options.assert_valid_keys :country_code, :default_country_code attributes.each do |attribute| raise StandardError, "Instance method normalized_#{attribute} already exists on #{self.name} (PhonyRails)" if self.instance_methods.include?(:"normalized_#{attribute}") define_method :"normalized_#{attribute}" do |options = {}| raise ArgumentError, "No attribute/method #{attribute} found on #{self.class.name} (PhonyRails)" if not self.respond_to?(attribute) options[:country_code] ||= self.country_code if self.respond_to?(:country_code) PhonyRails.normalize_number(self.send(attribute), main_options.merge(options)) end end end end end end ActiveRecord::Base.extend PhonyRails::ActiveRecordExtension