alexdunae-validates_email_format_of 1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ = CHANGELOG
2
+
3
+ == Version 1.4 (the Denis Ahearn edition)
4
+ * added ability to run validation tests without touching ActiveRecord or a database
5
+
6
+ == Version 1.3.1 (the Github edition)
7
+ * updated for github
8
+
9
+ == Version 1.3 (the Travis Sinnott edition)
10
+ * added optional MX record check
11
+ * now available as a gem
12
+
13
+ == Version 1.2.1 (the RTFM edition)
14
+ * added support for quoted local parts
15
+ * added length checks for domain and local parts
16
+ * corrected escaped character support for RFC 3696 Errata
17
+ * added :allow_blank option
18
+ * added :unless option
19
+
20
+ == Version 1.2 (the Ismael Santos Kafeltz and Michael MacDonald edition)
21
+ * added support for un-escaped and escaped special characters in the local part, per RFC 3696
22
+ * added :allow_nil option
23
+
24
+ == Version 1.1 (the Francis Hwang edition)
25
+ * moved Regexp out of class methods into the ValidatesEmailFormatOf module
26
+
27
+ == Version 1.0
28
+ * initial version
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006-09 Alex Dunae
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,59 @@
1
+ = validates_email_format_of Gem and Rails Plugin
2
+
3
+ Validate e-mail addresses against RFC 2822 and RFC 3696.
4
+
5
+ == Installation
6
+
7
+ Installing as a gem:
8
+
9
+ gem sources -a http://gems.github.com
10
+ gem install alexdunae-validates_email_format_of
11
+
12
+ Installing as a Ruby on Rails plugin:
13
+
14
+ ./script/plugin install git://github.com/alexdunae/validates_email_format_of.git
15
+
16
+
17
+ == Usage
18
+
19
+ class Person < ActiveRecord::Base
20
+ validates_email_format_of :email
21
+ end
22
+
23
+ === Options
24
+
25
+ :message
26
+ String. A custom error message (default is: " does not appear to be a valid e-mail address")
27
+ :on
28
+ Symbol. Specifies when this validation is active (default is :save, other options :create, :update)
29
+ :allow_nil
30
+ Boolean. Allow nil values (default is false)
31
+ :allow_blank
32
+ Boolean. Allow blank values (default is false)
33
+ :check_mx
34
+ Boolean. Check domain for a valid MX record (default is false)
35
+ :if
36
+ Specifies a method, proc or string to call to determine if the validation should occur
37
+ (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The method,
38
+ proc or string should return or evaluate to a true or false value.
39
+ :unless
40
+ See :if option.
41
+
42
+ == Testing
43
+
44
+ To execute the unit tests run <tt>rake test</tt>.
45
+
46
+ The unit tests for this plugin use an in-memory sqlite3 database.
47
+
48
+ == Resources
49
+
50
+ * http://github.com/alexdunae/validates_email_format_of
51
+ * http://code.dunae.ca/validates_email_format_of.html
52
+
53
+ == Credits
54
+
55
+ Written by Alex Dunae (dunae.ca), 2006-09.
56
+
57
+ Thanks to Francis Hwang (http://fhwang.net/) at Diversion Media for creating the 1.1 update.
58
+
59
+ Thanks to Travis Sinnott for creating the 1.3 update.
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/rails/init'
@@ -0,0 +1,88 @@
1
+ module ValidatesEmailFormatOf
2
+ require 'resolv'
3
+
4
+ LocalPartSpecialChars = Regexp.escape('!#$%&\'*-/=?+-^_`{|}~')
5
+ LocalPartUnquoted = '(([[:alnum:]' + LocalPartSpecialChars + ']+[\.\+]+))*[[:alnum:]' + LocalPartSpecialChars + '+]+'
6
+ LocalPartQuoted = '\"(([[:alnum:]' + LocalPartSpecialChars + '\.\+]*|(\\\\[\x00-\xFF]))*)\"'
7
+ Regex = Regexp.new('^((' + LocalPartUnquoted + ')|(' + LocalPartQuoted + ')+)@(((\w+\-+)|(\w+\.))*\w{1,63}\.[a-z]{2,6}$)', Regexp::EXTENDED | Regexp::IGNORECASE)
8
+
9
+ def self.validate_email_domain(email)
10
+ domain = email.match(/\@(.+)/)[1]
11
+ Resolv::DNS.open do |dns|
12
+ @mx = dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
13
+ end
14
+ @mx.size > 0 ? true : false
15
+ end
16
+
17
+ # Validates whether the specified value is a valid email address. Returns nil if the value is valid, otherwise returns an array
18
+ # containing one or more validation error messages.
19
+ #
20
+ # Configuration options:
21
+ # * <tt>message</tt> - A custom error message (default is: " does not appear to be a valid e-mail address")
22
+ # * <tt>check_mx</tt> - Check for MX records (default is false)
23
+ # * <tt>mx_message</tt> - A custom error message when an MX record validation fails (default is: " is not routable.")
24
+ # * <tt>with</tt> The regex to use for validating the format of the email address (default is ValidatesEmailFormatOf::Regex)</tt>
25
+ def self.validate_email_format(email, options={})
26
+ default_options = { :message => ' does not appear to be a valid e-mail address',
27
+ :check_mx => false,
28
+ :mx_message => ' is not routable.',
29
+ :with => ValidatesEmailFormatOf::Regex }
30
+ options.merge!(default_options) {|key, old, new| old} # merge the default options into the specified options, retaining all specified options
31
+
32
+ # local part max is 64 chars, domain part max is 255 chars
33
+ # TODO: should this decode escaped entities before counting?
34
+ begin
35
+ domain, local = email.reverse.split('@', 2)
36
+ rescue
37
+ return [ options[:message] ]
38
+ end
39
+
40
+ unless email =~ options[:with] and not email =~ /\.\./ and domain.length <= 255 and local.length <= 64
41
+ return [ options[:message] ]
42
+ end
43
+
44
+ if options[:check_mx] and !ValidatesEmailFormatOf::validate_email_domain(email)
45
+ return [ options[:mx_message] ]
46
+ end
47
+
48
+ return nil # represents no validation errors
49
+ end
50
+ end
51
+
52
+ module ActiveRecord
53
+ module Validations
54
+ module ClassMethods
55
+ # Validates whether the value of the specified attribute is a valid email address
56
+ #
57
+ # class User < ActiveRecord::Base
58
+ # validates_email_format_of :email, :on => :create
59
+ # end
60
+ #
61
+ # Configuration options:
62
+ # * <tt>message</tt> - A custom error message (default is: " does not appear to be a valid e-mail address")
63
+ # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
64
+ # * <tt>allow_nil</tt> - Allow nil values (default is false)
65
+ # * <tt>allow_blank</tt> - Allow blank values (default is false)
66
+ # * <tt>check_mx</tt> - Check for MX records (default is false)
67
+ # * <tt>mx_message</tt> - A custom error message when an MX record validation fails (default is: " is not routable.")
68
+ # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
69
+ # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
70
+ # method, proc or string should return or evaluate to a true or false value.
71
+ # * <tt>unless</tt> - See <tt>:if</tt>
72
+ def validates_email_format_of(*attr_names)
73
+ options = { :on => :save,
74
+ :allow_nil => false,
75
+ :allow_blank => false }
76
+ options.update(attr_names.pop) if attr_names.last.is_a?(Hash)
77
+
78
+ validates_each(attr_names, options) do |record, attr_name, value|
79
+ v = value.to_s
80
+ errors = ValidatesEmailFormatOf::validate_email_format(v, options)
81
+ errors.each do |error|
82
+ record.errors.add(attr_name, error)
83
+ end unless errors.nil?
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1 @@
1
+ require 'validates_email_format_of'
@@ -0,0 +1,28 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => [:clean_log, :test]
7
+
8
+ desc 'Remove the old log file'
9
+ task :clean_log do
10
+ "rm -f #{File.dirname(__FILE__)}/test/debug.log" if File.exists?(File.dirname(__FILE__) + '/test/debug.log')
11
+ end
12
+
13
+ desc 'Test the validates_email_format_of plugin.'
14
+ Rake::TestTask.new(:test) do |t|
15
+ t.libs << 'lib'
16
+ t.pattern = 'test/**/*_test.rb'
17
+ t.verbose = true
18
+ end
19
+
20
+ desc 'Generate documentation for the validates_email_format_of plugin.'
21
+ Rake::RDocTask.new(:rdoc) do |rdoc|
22
+ rdoc.rdoc_dir = 'rdoc'
23
+ rdoc.title = 'validates_email_format_of plugin'
24
+ rdoc.options << '--line-numbers --inline-source'
25
+ rdoc.rdoc_files.include('README')
26
+ rdoc.rdoc_files.include('TODO')
27
+ rdoc.rdoc_files.include('lib/**/*.rb')
28
+ end
@@ -0,0 +1,3 @@
1
+ existing:
2
+ id: 1
3
+ email: test@example.com
@@ -0,0 +1,12 @@
1
+ class Person < ActiveRecord::Base
2
+ validates_email_format_of :email,
3
+ :on => :create,
4
+ :message => 'fails with custom message',
5
+ :allow_nil => true
6
+ end
7
+
8
+ class MxRecord < ActiveRecord::Base
9
+ validates_email_format_of :email,
10
+ :on => :create,
11
+ :check_mx => true
12
+ end
@@ -0,0 +1,10 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :people, :force => true do |t|
3
+ t.column "email", :string
4
+ end
5
+
6
+ create_table :mx_records, :force => true do |t|
7
+ t.column "email", :string
8
+ end
9
+
10
+ end
@@ -0,0 +1,33 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+ RAILS_ROOT = File.dirname(__FILE__)
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'active_record'
7
+ require 'active_record/fixtures'
8
+ require "#{File.dirname(__FILE__)}/../init"
9
+
10
+
11
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
12
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
13
+ ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'plugin_test'])
14
+
15
+ load(File.dirname(__FILE__) + "/schema.rb") if File.exist?(File.dirname(__FILE__) + "/schema.rb")
16
+
17
+ Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
18
+ $LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
19
+
20
+ class Test::Unit::TestCase #:nodoc:
21
+ def create_fixtures(*table_names)
22
+ if block_given?
23
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
24
+ else
25
+ Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
26
+ end
27
+ end
28
+
29
+ self.use_transactional_fixtures = false
30
+
31
+ self.use_instantiated_fixtures = false
32
+ end
33
+
@@ -0,0 +1,145 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ValidatesEmailFormatOfTest < Test::Unit::TestCase
4
+ fixtures :people, :peoplemx
5
+
6
+ def setup
7
+ @valid_email = 'valid@example.com'
8
+ @invalid_email = 'invalid@example.'
9
+ end
10
+
11
+ def test_without_activerecord
12
+ assert_nil ValidatesEmailFormatOf::validate_email_format('valid@example.com')
13
+ err = ValidatesEmailFormatOf::validate_email_format('valid@example-com')
14
+ assert_equal 1, err.size
15
+ end
16
+
17
+ def test_should_allow_valid_email_addresses
18
+ ['valid@example.com',
19
+ 'Valid@test.example.com',
20
+ 'valid+valid123@test.example.com',
21
+ 'valid_valid123@test.example.com',
22
+ 'valid-valid+123@test.example.co.uk',
23
+ 'valid-valid+1.23@test.example.com.au',
24
+ 'valid@example.co.uk',
25
+ 'v@example.com',
26
+ 'valid@example.ca',
27
+ 'valid_@example.com',
28
+ 'valid123.456@example.org',
29
+ 'valid123.456@example.travel',
30
+ 'valid123.456@example.museum',
31
+ 'valid@example.mobi',
32
+ 'valid@example.info',
33
+ 'valid-@example.com',
34
+ # from RFC 3696, page 6
35
+ 'customer/department=shipping@example.com',
36
+ '$A12345@example.com',
37
+ '!def!xyz%abc@example.com',
38
+ '_somename@example.com',
39
+ # apostrophes
40
+ "test'test@example.com",
41
+ ].each do |email|
42
+ p = create_person(:email => email)
43
+ save_passes(p, email)
44
+ end
45
+ end
46
+
47
+ def test_should_not_allow_invalid_email_addresses
48
+ ['invalid@example-com',
49
+ # period can not start local part
50
+ '.invalid@example.com',
51
+ # period can not end local part
52
+ 'invalid.@example.com',
53
+ # period can not appear twice consecutively in local part
54
+ 'invali..d@example.com',
55
+ 'invalid@example.com.',
56
+ 'invalid@example.com_',
57
+ 'invalid@example.com-',
58
+ 'invalid-example.com',
59
+ 'invalid@example.b#r.com',
60
+ 'invalid@example.c',
61
+ 'invali d@example.com',
62
+ 'invalidexample.com',
63
+ 'invalid@example.'].each do |email|
64
+ p = create_person(:email => email)
65
+ save_fails(p, email)
66
+ end
67
+ end
68
+
69
+ # from http://www.rfc-editor.org/errata_search.php?rfc=3696
70
+ def test_should_allow_quoted_characters
71
+ ['"Abc\@def"@example.com',
72
+ '"Fred\ Bloggs"@example.com',
73
+ '"Joe.\\Blow"@example.com',
74
+ ].each do |email|
75
+ p = create_person(:email => email)
76
+ save_passes(p, email)
77
+ end
78
+ end
79
+
80
+ # from http://tools.ietf.org/html/rfc3696, page 5
81
+ # corrected in http://www.rfc-editor.org/errata_search.php?rfc=3696
82
+ def test_should_not_allow_escaped_characters_without_quotes
83
+ ['Fred\ Bloggs_@example.com',
84
+ 'Abc\@def+@example.com',
85
+ 'Joe.\\Blow@example.com'
86
+ ].each do |email|
87
+ p = create_person(:email => email)
88
+ save_fails(p, email)
89
+ end
90
+ end
91
+
92
+ def test_should_check_length_limits
93
+ ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@example.com',
94
+ 'test@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com'
95
+ ].each do |email|
96
+ p = create_person(:email => email)
97
+ save_fails(p, email)
98
+ end
99
+ end
100
+
101
+ def test_should_respect_validate_on_option
102
+ p = create_person(:email => @valid_email)
103
+ save_passes(p)
104
+
105
+ # we only asked to validate on :create so this should fail
106
+ assert p.update_attributes(:email => @invalid_email)
107
+ assert_equal @invalid_email, p.email
108
+ end
109
+
110
+ def test_should_allow_custom_error_message
111
+ p = create_person(:email => @invalid_email)
112
+ save_fails(p)
113
+ assert_equal 'fails with custom message', p.errors.on(:email)
114
+ end
115
+
116
+ def test_should_allow_nil
117
+ p = create_person(:email => nil)
118
+ save_passes(p)
119
+ end
120
+
121
+ def test_check_mx
122
+ pmx = MxRecord.new(:email => 'test@dunae.ca')
123
+ save_passes(pmx)
124
+
125
+ pmx = MxRecord.new(:email => 'test@example.com')
126
+ save_fails(pmx)
127
+ end
128
+
129
+ protected
130
+ def create_person(params)
131
+ Person.new(params)
132
+ end
133
+
134
+ def save_passes(p, email = '')
135
+ assert p.valid?, " validating #{email}"
136
+ assert p.save
137
+ assert_nil p.errors.on(:email)
138
+ end
139
+
140
+ def save_fails(p, email = '')
141
+ assert !p.valid?, " validating #{email}"
142
+ assert !p.save
143
+ assert p.errors.on(:email)
144
+ end
145
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alexdunae-validates_email_format_of
3
+ version: !ruby/object:Gem::Version
4
+ version: "1.4"
5
+ platform: ruby
6
+ authors:
7
+ - Alex Dunae
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-22 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Validate e-mail addresses against RFC 2822 and RFC 3696.
17
+ email: code@dunae.ca
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - CHANGELOG.rdoc
25
+ - MIT-LICENSE
26
+ files:
27
+ - init.rb
28
+ - rakefile.rb
29
+ - lib/validates_email_format_of.rb
30
+ - rails/init.rb
31
+ - README.rdoc
32
+ - CHANGELOG.rdoc
33
+ - MIT-LICENSE
34
+ has_rdoc: true
35
+ homepage: http://code.dunae.ca/validates_email_format_of.html
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --title
39
+ - validates_email_format_of
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: Validate e-mail addresses against RFC 2822 and RFC 3696.
61
+ test_files:
62
+ - test/validates_email_format_of_test.rb
63
+ - test/test_helper.rb
64
+ - test/schema.rb
65
+ - test/fixtures/person.rb
66
+ - test/fixtures/people.yml