has_phone_number 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Dallas Reedy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,42 @@
1
+ == HasPhoneNumber
2
+
3
+ A fairly simple plugin for dealing with the commonalities of phone number (and fax number) attributes.
4
+ I wanted an easy way to set them (as digit-only strings no matter what other junk got passed in) and to display them as desired using a custom getter (with +number_to_phone+ baked right in).
5
+
6
+
7
+ == Example
8
+
9
+ Setting +has_phone_number+ will give you all the default options and assumes an attribute named <tt>:phone_number</tt>.
10
+ Setting +has_fax_number+ just makes a call to +has_phone_number+ but assumes an attribute named <tt>:fax_number</tt>.
11
+
12
+ has_phone_number
13
+ has_fax_number
14
+
15
+ You can tell +has_phone_number+ what attribute you want it set on, or pass it as many attributes as you want!
16
+
17
+ has_phone_number :home_phone, :cell_phone, :other_phone
18
+
19
+ You can also pass some simple options in:
20
+
21
+ has_phone_number :validate => false # skip validation!
22
+ has_phone_number :validate_on => :update, :validate_if => proc{|record| record.something?}
23
+ has_phone_number :validation_message => "that doesn't even <em>look</em> like a phone number!"
24
+
25
+ Or, you can use the +validates_as_phone_number+ method to validate specific numbers when and where you want!
26
+
27
+ validates_as_phone_number :cell_phone, :if => proc{|record| record.home_phone.blank?}
28
+
29
+ I even went as far as allowing +method_missing+ to respond to validation methods corresponding to defined phone number attributes:
30
+
31
+ validates_other_phone :on => :update, :if => proc{|record| ![record.home_phone, record.cell_phone].any?}
32
+
33
+
34
+ == To Do
35
+
36
+ * Add support for phone extensions
37
+ * Add support for custom validations (like specifying the regular expression to check against)
38
+ * Add support for customizing the output of the getter (setting +number_to_phone+ options)
39
+
40
+
41
+ ---
42
+ Copyright (c) 2009 Dallas Reedy, released under the MIT license
@@ -0,0 +1,37 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the has_phone_number plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the has_phone_number plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'HasPhoneNumber'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ begin
26
+ require 'jeweler'
27
+ Jeweler::Tasks.new do |gemspec|
28
+ gemspec.name = "has_phone_number"
29
+ gemspec.summary = (summary = "HasPhoneNumber is a Ruby on Rails gem for setting up phone number attributes.")
30
+ gemspec.email = "code@dallasreedy.com"
31
+ gemspec.homepage = "http://github.com/dallas/has_phone_number"
32
+ gemspec.description = summary
33
+ gemspec.authors = ["Dallas Reedy"]
34
+ end
35
+ rescue LoadError
36
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
37
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.1
@@ -0,0 +1,53 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{has_phone_number}
8
+ s.version = "1.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Dallas Reedy"]
12
+ s.date = %q{2009-08-25}
13
+ s.description = %q{HasPhoneNumber is a Ruby on Rails gem for setting up phone number attributes.}
14
+ s.email = %q{code@dallasreedy.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ "MIT-LICENSE",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "has_phone_number.gemspec",
24
+ "init.rb",
25
+ "lib/has_phone_number.rb",
26
+ "test/attributes_test.rb",
27
+ "test/schema.rb",
28
+ "test/test.db",
29
+ "test/test_helper.rb",
30
+ "test/validations_test.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/dallas/has_phone_number}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.5}
36
+ s.summary = %q{HasPhoneNumber is a Ruby on Rails gem for setting up phone number attributes.}
37
+ s.test_files = [
38
+ "test/attributes_test.rb",
39
+ "test/schema.rb",
40
+ "test/test_helper.rb",
41
+ "test/validations_test.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
49
+ else
50
+ end
51
+ else
52
+ end
53
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'has_phone_number'
@@ -0,0 +1,104 @@
1
+ module Dallas #:nodoc: all
2
+ module HasPhoneNumber
3
+ include ActionView::Helpers::NumberHelper
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+ attr_accessor :phone_number_attributes
11
+
12
+ # the main method
13
+ def has_phone_number(*args)
14
+ @phone_number_attributes = [] unless @phone_number_attributes.is_a? Array
15
+
16
+ options = args.extract_options!
17
+ attributes = args
18
+ attributes << :phone_number if attributes.empty?
19
+
20
+ unless options[:validate] === false
21
+ validates_as_phone_number attributes, options
22
+ end
23
+
24
+ attributes.each do |attribute|
25
+ # setter method to filter non-digit characters
26
+ define_method "#{attribute}=" do |value|
27
+ write_attribute(attribute, value.to_s.gsub(/\D+/, ''))
28
+ end
29
+
30
+ # getter method which automatically applies number_to_phone helper
31
+ define_method "#{attribute}" do |*from_database|
32
+ return nil if self[attribute].nil?
33
+ !! from_database.first ? self[attribute] : number_to_phone(self[attribute], :area_code => true)
34
+ end
35
+
36
+ # override the default #{attribute}_before_type_cast so that it is
37
+ # formatted using number_to_phone in form fields also
38
+ define_method "#{attribute}_before_type_cast" do |*from_database|
39
+ __send__(attribute, !! from_database.first)
40
+ end
41
+
42
+ @phone_number_attributes << attribute
43
+ end
44
+
45
+ include Dallas::HasPhoneNumber::InstanceMethods
46
+ end
47
+
48
+ # define convenience method for setting the common attribute "fax_number"
49
+ def has_fax_number(*args)
50
+ options = args.extract_options!
51
+ args << :fax_number if args.empty?
52
+ has_phone_number(*args << options)
53
+ end
54
+
55
+ def phone_number_attributes
56
+ Array(@phone_number_attributes)
57
+ end
58
+
59
+ # simple length and format validation
60
+ # using validates_each to bypass ActiveRecord's odd propensity to use record.attribute
61
+ # rather than record.read_attribute(attribute) or record[attribute]
62
+ def validates_as_phone_number(*args)
63
+ options = clean_has_phone_number_options(args.extract_options!)
64
+ message = options.delete(:message)
65
+ attributes = args
66
+
67
+ validates_each attributes, options do |record, attr_name, value|
68
+ record.errors.add attr_name, message || 'should be a 10-digit phone number' unless record[attr_name] =~ /^\d{10}$/
69
+ end
70
+ end
71
+
72
+ # alias the method_missing method chain for customized validation methods
73
+ alias_method :method_missing_without_phone_number_validations, :method_missing
74
+
75
+ def method_missing_with_phone_number_validations(name, *args, &block)
76
+ return validates_as_phone_number($1, *args) if name.to_s =~ /^validates_(#{phone_number_attributes.join('|')})$/
77
+ method_missing_without_phone_number_validations(name, *args, &block)
78
+ end
79
+
80
+ alias_method :method_missing, :method_missing_with_phone_number_validations
81
+
82
+ private
83
+
84
+ # look out for keys set in the original has_(phone|fax)_number like the following:
85
+ # :validate_on => :update
86
+ # :validates_if => proc{…}
87
+ # :validation_message
88
+ def clean_has_phone_number_options(options = {})
89
+ options.inject({}) do |opts, (key, value)|
90
+ opts[key.to_s.sub(/^validat(es?|ion)_/, '').to_sym] = value
91
+ opts
92
+ end
93
+ end
94
+ end # ClassMethods
95
+
96
+ module InstanceMethods
97
+ def phone_numbers
98
+ self.class.phone_number_attributes.map {|a| [a, read_attribute(a)]}
99
+ end
100
+ end # InstanceMethods
101
+ end # HasPhoneNumber
102
+ end # Dallas
103
+
104
+ ActiveRecord::Base.send(:include, Dallas::HasPhoneNumber)
@@ -0,0 +1,71 @@
1
+ require 'test_helper'
2
+
3
+ class AttributesTest < Test::Unit::TestCase
4
+ context 'Setting has_phone_number or has_fax_number' do
5
+ setup do
6
+ @attribute_names = %w(phone_number fax_number telephone facsimile phone_not_validated fax_not_validated phone_with_validation_options phone_validated_elsewhere fax_validated_elsewhere)
7
+ @user = User.new(:name => 'Dallas Reedy')
8
+
9
+ # generates a phone number in the form: nnn-nnn-nnnn
10
+ def generate_phone_number
11
+ "#{(x = ('000'..'999').to_a)[rand(x.length - 1)]}-#{x[rand(x.length - 1)]}-#{(y = ('0000'..'9999').to_a)[rand(y.length - 1)]}"
12
+ end
13
+ end
14
+
15
+ should 'add attributes names to the phone_number_attributes array' do
16
+ assert_same_elements @user.class.phone_number_attributes.map {|a| a.to_s}, @attribute_names
17
+ end
18
+
19
+ should 'automatically use :phone_number or :fax_number if no attribute names are given' do
20
+ %w(phone_number fax_number).each do |name|
21
+ assert_contains @user.class.phone_number_attributes, name.to_sym
22
+ assert_respond_to @user, "#{name}="
23
+ assert_respond_to @user, name
24
+ assert_nothing_raised do
25
+ @user.send(name, true)
26
+ end
27
+ end
28
+ end
29
+
30
+ should 'create a setter method' do
31
+ @attribute_names.each do |name|
32
+ assert_respond_to @user, "#{name}="
33
+ end
34
+ end
35
+
36
+ should 'create a getter method' do
37
+ @attribute_names.each do |name|
38
+ assert_respond_to @user, name
39
+ end
40
+ end
41
+
42
+ should 'properly set a phone number using setter method' do
43
+ @attribute_names.each do |name|
44
+ assert_send [@user, "#{name}=", number = generate_phone_number]
45
+ assert_equal number.gsub(/\D+/, ''), @user.send("#{name}", true)
46
+ end
47
+ end
48
+
49
+ should 'properly return a phone number using getter method' do
50
+ # need to run the setters again…
51
+ @attribute_names.each do |name|
52
+ assert_send [@user, "#{name}=", generate_phone_number]
53
+ end
54
+
55
+ @attribute_names.each do |name|
56
+ assert_match /^\(\d{3}\) \d{3}\-\d{4}$/, @user.send(name)
57
+ end
58
+ end
59
+
60
+ should 'return the "database stored" value if argument is passed to getter' do
61
+ # need to run the setters again…
62
+ @attribute_names.each do |name|
63
+ assert_send [@user, "#{name}=", generate_phone_number]
64
+ end
65
+
66
+ @attribute_names.each do |name|
67
+ assert_match /^\d{10}$/, @user.send(name, true)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,53 @@
1
+ ActiveRecord::Base.establish_connection(
2
+ :adapter => defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' ? 'jdbcsqlite3' : 'sqlite3',
3
+ :database => File.join(File.dirname(__FILE__), 'test.db')
4
+ )
5
+
6
+ class CreateSchema < ActiveRecord::Migration
7
+ def self.up
8
+ create_table :users, :force => true do |t|
9
+ t.string :name
10
+ t.string :phone_number, :fax_number
11
+ t.string :telephone, :facsimile
12
+ t.string :phone_not_validated, :fax_not_validated
13
+ t.string :phone_with_validation_options
14
+ t.string :phone_validated_elsewhere, :fax_validated_elsewhere
15
+ t.timestamps
16
+ end
17
+ end
18
+ end
19
+
20
+ CreateSchema.suppress_messages do
21
+ CreateSchema.migrate(:up)
22
+ end
23
+
24
+ # Define our User model
25
+ class User < ActiveRecord::Base
26
+ # defaults
27
+ has_phone_number
28
+ has_fax_number
29
+
30
+ # custom column names
31
+ has_phone_number :telephone
32
+ has_fax_number :facsimile
33
+
34
+ # validation turned off
35
+ has_phone_number :phone_not_validated, :validate => false
36
+ has_fax_number :fax_not_validated, :validate => false
37
+
38
+ # more validation options
39
+ has_phone_number :phone_with_validation_options,
40
+ :validate_if => proc{|user| !user.name.nil?},
41
+ :validates_on => :update,
42
+ :validation_message => 'is so not a valid phone number!'
43
+
44
+ # handle validation elsewhere
45
+ has_phone_number :phone_validated_elsewhere, :validate => false
46
+ has_fax_number :fax_validated_elsewhere, :validate => false
47
+
48
+ # default validation method
49
+ validates_as_phone_number :phone_validated_elsewhere, :message => "didn't pass validations elsewhere"
50
+
51
+ # auto-defined validation method
52
+ validates_fax_validated_elsewhere :on => :update, :message => "didn't pass validations from auto-defined validation method"
53
+ end
Binary file
@@ -0,0 +1,11 @@
1
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
2
+ $: << File.dirname(__FILE__)
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'activerecord'
7
+ require 'shoulda'
8
+ require 'action_view'
9
+ require 'has_phone_number'
10
+ require 'schema'
11
+ begin; require 'redgreen'; rescue LoadError; end
@@ -0,0 +1,108 @@
1
+ require 'test_helper'
2
+
3
+ class ValidationsTest < Test::Unit::TestCase
4
+ context 'Setting has_phone_number or has_fax_number' do
5
+ setup do
6
+ @attribute_names = %w(phone_number fax_number telephone facsimile phone_not_validated fax_not_validated phone_with_validation_options phone_validated_elsewhere fax_validated_elsewhere)
7
+ @not_validated = %w(phone_not_validated fax_not_validated)
8
+ @validated_on_update = %w(phone_with_validation_options fax_validated_elsewhere)
9
+ @auto_validated = @attribute_names - @not_validated - @validated_on_update
10
+
11
+ @user = User.new(:name => 'Dallas Reedy')
12
+
13
+ # generates a phone number in the form: nnn-nnn-nnnn
14
+ def generate_phone_number
15
+ "#{(x = ('000'..'999').to_a)[rand(x.length - 1)]}-#{x[rand(x.length - 1)]}-#{(y = ('0000'..'9999').to_a)[rand(y.length - 1)]}"
16
+ end
17
+
18
+ def set_fields(fields)
19
+ fields.each do |name|
20
+ @user.send("#{name}=", generate_phone_number)
21
+ end
22
+ end
23
+ end
24
+
25
+ should 'automatically add a validation unless told not to' do
26
+ @auto_validated.each do |name|
27
+ assert_equal false, @user.valid?
28
+ assert @user.errors.invalid?(name.to_s)
29
+
30
+ @user.send("#{name}=", 'gibberish.')
31
+ assert_equal false, @user.valid?
32
+ assert @user.errors.invalid?(name.to_s)
33
+
34
+ @user.send("#{name}=", generate_phone_number)
35
+ end
36
+ assert @user.save
37
+ end
38
+
39
+ should 'not validate if told not to' do
40
+ set_fields @auto_validated
41
+
42
+ @not_validated.each do |name|
43
+ assert @user.valid?
44
+ assert @user.errors.empty?
45
+
46
+ @user.send("#{name}=", 'gibberish.')
47
+ @user.valid?
48
+ assert @user.errors.empty?
49
+ end
50
+ assert @user.save
51
+ end
52
+
53
+ should 'apply and adhere to validation-specific options set for the default validation' do
54
+ set_fields @auto_validated + ['fax_validated_elsewhere']
55
+
56
+ # test that validation fails on :update
57
+ assert @user.save
58
+ assert_equal false, @user.valid?
59
+ assert @user.errors.invalid?(:phone_with_validation_options)
60
+
61
+ # test invalid data
62
+ @user.phone_with_validation_options = 'gibberish.'
63
+ assert_equal false, @user.valid?
64
+ assert @user.errors.invalid?(:phone_with_validation_options)
65
+
66
+ # test our custom error message
67
+ assert_equal 'is so not a valid phone number!', @user.errors.on(:phone_with_validation_options)
68
+
69
+ # test our custom error :if option
70
+ @user.name = nil
71
+ assert @user.valid?
72
+
73
+ # test saving valid record
74
+ @user.name = 'Dallas Reedy'
75
+ @user.phone_with_validation_options = generate_phone_number
76
+ assert @user.save
77
+ end
78
+
79
+ should 'respond to validates_as_phone_number and adhere to specified options' do
80
+ set_fields @auto_validated - ['phone_validated_elsewhere']
81
+
82
+ # test our custom error message
83
+ assert_equal false, @user.valid?
84
+ assert @user.errors.invalid?(:phone_validated_elsewhere)
85
+ assert_equal "didn't pass validations elsewhere", @user.errors.on(:phone_validated_elsewhere)
86
+
87
+ # test saving valid record
88
+ @user.phone_validated_elsewhere = generate_phone_number
89
+ assert @user.save
90
+ end
91
+
92
+ should 'respond to validates_#{attribute_name} and adhere to specified options' do
93
+ set_fields @auto_validated + ['phone_with_validation_options']
94
+
95
+ # test that validation fails on :update
96
+ assert @user.save
97
+ assert_equal false, @user.valid?
98
+ assert @user.errors.invalid?(:fax_validated_elsewhere)
99
+
100
+ # test for our custom error message
101
+ assert_equal "didn't pass validations from auto-defined validation method", @user.errors.on(:fax_validated_elsewhere)
102
+
103
+ # test saving valid record
104
+ @user.fax_validated_elsewhere = generate_phone_number
105
+ assert @user.save
106
+ end
107
+ end
108
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_phone_number
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Dallas Reedy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-25 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: HasPhoneNumber is a Ruby on Rails gem for setting up phone number attributes.
17
+ email: code@dallasreedy.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - MIT-LICENSE
26
+ - README.rdoc
27
+ - Rakefile
28
+ - VERSION
29
+ - has_phone_number.gemspec
30
+ - init.rb
31
+ - lib/has_phone_number.rb
32
+ - test/attributes_test.rb
33
+ - test/schema.rb
34
+ - test/test.db
35
+ - test/test_helper.rb
36
+ - test/validations_test.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/dallas/has_phone_number
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --charset=UTF-8
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.5
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: HasPhoneNumber is a Ruby on Rails gem for setting up phone number attributes.
65
+ test_files:
66
+ - test/attributes_test.rb
67
+ - test/schema.rb
68
+ - test/test_helper.rb
69
+ - test/validations_test.rb