vjt-email_validator 1.5.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.
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,45 @@
1
+ = CHANGELOG
2
+
3
+ == Version 1.4.4
4
+
5
+ * https://github.com/alexdunae/validates_email_format_of/compare/1.4.3...1.4.4
6
+
7
+ == Version 1.4.3
8
+
9
+ * https://github.com/alexdunae/validates_email_format_of/compare/1.4.2...1.4.3
10
+
11
+ == Version 1.4.2
12
+
13
+ * See https://github.com/alexdunae/validates_email_format_of/compare/1.4.1...1.4.2
14
+
15
+ == Version 1.4.1 (the George Anderson and 'history' edition)
16
+ * don't allow domains with underscores
17
+ * removed extra spaces in validation messages
18
+ * updated tests for Rails 2.3+
19
+
20
+ == Version 1.4 (the Denis Ahearn edition)
21
+ * added ability to run validation tests without touching ActiveRecord or a database
22
+
23
+ == Version 1.3.1 (the Github edition)
24
+ * updated for github
25
+
26
+ == Version 1.3 (the Travis Sinnott edition)
27
+ * added optional MX record check
28
+ * now available as a gem
29
+
30
+ == Version 1.2.1 (the RTFM edition)
31
+ * added support for quoted local parts
32
+ * added length checks for domain and local parts
33
+ * corrected escaped character support for RFC 3696 Errata
34
+ * added :allow_blank option
35
+ * added :unless option
36
+
37
+ == Version 1.2 (the Ismael Santos Kafeltz and Michael MacDonald edition)
38
+ * added support for un-escaped and escaped special characters in the local part, per RFC 3696
39
+ * added :allow_nil option
40
+
41
+ == Version 1.1 (the Francis Hwang edition)
42
+ * moved Regexp out of class methods into the ValidatesEmailFormatOf module
43
+
44
+ == Version 1.0
45
+ * initial version
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006-11 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.
data/README.rdoc ADDED
@@ -0,0 +1,67 @@
1
+ = email_validator ActiveModel::EachValidator
2
+
3
+ Validate e-mail addresses against RFC 2822 and RFC 3696.
4
+
5
+ == Installation
6
+
7
+ gem 'vjt-email_validator', :git => 'git://github.com/vjt/email_validator'
8
+
9
+ == Usage
10
+
11
+ Standalone:
12
+
13
+ EmailValidator.valid?(email) # => true or false
14
+
15
+ Inside an ActiveRecord model:
16
+
17
+ class Person < ActiveRecord::Base
18
+ validates :email, :email => true
19
+ end
20
+
21
+ === Options
22
+
23
+ Giving `:email => true` to the `validate` method uses the following default options.
24
+ Customize them using `:email => { .. }`; E.g. `:email => {:check_mx => true}`
25
+
26
+ :message
27
+ String. A custom error message (default is: "does not appear to be a valid e-mail address")
28
+ :mx_message
29
+ String. A custom error message displayed when there is no MX for the given e-mail address
30
+ (default is: "is not routable")
31
+ :multiple_message
32
+ String. A custom error message displayed when one of the e-mail addresses in the record
33
+ is invalid (default is "appears to contain an invalid e-mail address)
34
+ :multiple
35
+ Boolean. Allows multiple email addresses separated by space and/or comma/colon
36
+ :check_mx
37
+ Boolean. Check domain for a valid MX record (default is false)
38
+ :local_length
39
+ Maximum number of characters allowed in the local part (default is 64)
40
+ :domain_length
41
+ Maximum number of characters allowed in the domain part (default is 255)
42
+
43
+ == Testing
44
+
45
+ To execute the unit tests run <tt>rake test</tt>.
46
+
47
+ The unit tests for this plugin use an in-memory sqlite3 database.
48
+
49
+ == Resources
50
+
51
+ * Based on http://github.com/alexdunae/validates_email_format_of
52
+ * http://github.com/vjt/email_validator
53
+
54
+ == Original Credits
55
+
56
+ Written by Alex Dunae (dunae.ca), 2006-11.
57
+
58
+ Many thanks to the plugin's recent contributors: https://github.com/alexdunae/validates_email_format_of/contributors
59
+
60
+ Thanks to Francis Hwang (http://fhwang.net/) at Diversion Media for creating the 1.1 update.
61
+ Thanks to Travis Sinnott for creating the 1.3 update.
62
+ Thanks to Denis Ahearn at Riverock Technologies (http://www.riverocktech.com/) for creating the 1.4 update.
63
+ Thanks to George Anderson (http://github.com/george) and 'history' (http://github.com/history) for creating the 1.4.1 update.
64
+
65
+ == Rewrite
66
+
67
+ Rewrote by Marcello Barnaba (vjt@openssl.it), 2011-02 as an ActiveModel::EachValidator
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'email_validator'
@@ -0,0 +1,93 @@
1
+ # encoding: utf-8
2
+ require 'active_model'
3
+ require 'resolv'
4
+
5
+ class EmailValidator < ActiveModel::EachValidator
6
+
7
+ local_part_special_chars = Regexp.escape('!#$%&\'*-/=?+-^_`{|}~')
8
+ local_part_unquoted = '(?:[[:alnum:]' + local_part_special_chars + ']+[\.\+])*[[:alnum:]' + local_part_special_chars + '+]'
9
+ local_part_quoted = '\"(?:[[:alnum:]' + local_part_special_chars + '\.]|\\\\[\x00-\xFF])*\"'
10
+ domain_part = '@(?:(?:(?:\w+\-+[^_])|(?:\w+\.[a-z0-9-]*))*(?:[a-z0-9-]{1,63})\.[a-z]{2,6}(?:\.[a-z]{2,6})?'
11
+ Pattern = Regexp.new('\A(?:' + local_part_unquoted + '+|' + local_part_quoted + '+)' + domain_part + '\Z)', Regexp::EXTENDED | Regexp::IGNORECASE, 'n').freeze
12
+ Separator = /[;,\s]\s*/.freeze # for multiple e-mail addresses
13
+ Defaults = {
14
+ :message => I18n.t(:invalid_email_address, :scope => [:activerecord, :errors, :messages], :default => 'does not appear to be a valid e-mail address'),
15
+ :multiple_message => I18n.t(:invalid_multiple_email, :scope => [:activerecord, :errors, :messages], :default => 'appears to contain an invalid e-mail address'),
16
+ :mx_message => I18n.t(:unroutable_email_address, :scope => [:activerecord, :errors, :messages], :default => 'is not routable'),
17
+ :check_mx => false,
18
+ :with => Pattern,
19
+ :local_length => 64,
20
+ :domain_length => 255
21
+ }.freeze
22
+
23
+ # Validates whether the specified value is a valid email address,
24
+ # and uses record.errors.add() to add the error if the provided
25
+ # value is not valid.
26
+ #
27
+ # Configuration options:
28
+ # * <tt>message</tt> - A custom error message
29
+ # (default: "does not appear to be a valid e-mail address")
30
+ # * <tt>check_mx</tt> - Check for MX records
31
+ # (default: false)
32
+ # * <tt>mx_message</tt> - A custom error message when an MX record validation fails
33
+ # (default: "is not routable.")
34
+ # * <tt>with</tt> - The regex to use for validating the format of the email address
35
+ # (default: +Pattern+)</tt>
36
+ # * <tt>multiple</tt> - Allow multiple e-mail addresses, separated by +Separator+
37
+ # (default: false)
38
+ # * <tt>multiple_message</tt> - A custom error message shown when there are 2 or more addresses
39
+ # to validate and one or more is invalid
40
+ # (default: "appears to contain an invalid e-mail address)
41
+ # * <tt>local_length</tt> - Maximum number of characters allowed in the local part
42
+ # (default: 64)
43
+ # * <tt>domain_length</tt> - Maximum number of characters allowed in the domain part
44
+ # (default: 255)
45
+ def validate_each(record, attribute, value)
46
+ return if value.blank? # Use :presence => true
47
+ error = self.class.errors_on(value, self.options)
48
+ record.errors.add(attribute, :invalid, :message => error, :value => value) if error
49
+ end
50
+
51
+ class << self
52
+ def valid?(email, options = {})
53
+ errors_on(email, options).nil?
54
+ end
55
+
56
+ def errors_on(email, options)
57
+ options = Defaults.merge(options)
58
+ options[:multiple] ? validate_many(email, options) : validate_one(email, options)
59
+ end
60
+
61
+ private
62
+ def validate_many(value, options)
63
+ emails = value.split(Separator)
64
+ errors = emails.map {|addr| validate_one(addr, options)}
65
+ errors.compact!
66
+ options[emails.size == 1 ? :message : :multiple_message] unless errors.empty?
67
+ end
68
+
69
+ def validate_one(value, options)
70
+ local, domain = value.split('@', 2)
71
+ if local.nil? || local.length > options[:local_length] or
72
+ domain.nil? || domain.length > options[:domain_length] or
73
+ value !~ options[:with]
74
+ options[:message]
75
+
76
+ elsif options[:check_mx] && !validate_email_domain(domain)
77
+ options[:mx_message]
78
+
79
+ end
80
+ end
81
+
82
+ def validate_email_domain(domain)
83
+ Resolv::DNS.open do |dns|
84
+ dns.getresources(domain, Resolv::DNS::Resource::IN::MX).size > 0
85
+ end
86
+
87
+ rescue Errno::ECONNREFUSED, NoMethodError
88
+ # DNS is not available - thus return true
89
+ true
90
+ end
91
+
92
+ end
93
+ end
data/rakefile.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ require 'pathname'
6
+
7
+ desc 'Default: run unit tests.'
8
+ task :default => [:clean, :test]
9
+
10
+ desc 'Remove the old log file'
11
+ task :clean do
12
+ Pathname(__FILE__).dirname.join('test', 'debug.log').unlink rescue nil
13
+ end
14
+
15
+ desc 'Test the email validator.'
16
+ Rake::TestTask.new(:test) do |t|
17
+ t.libs << 'lib'
18
+ t.pattern = 'test/**/*_test.rb'
19
+ t.verbose = true
20
+ end
21
+
22
+ desc 'Generate documentation for the email validator.'
23
+ Rake::RDocTask.new(:rdoc) do |rdoc|
24
+ rdoc.rdoc_dir = 'rdoc'
25
+ rdoc.title = 'email_validator'
26
+ rdoc.options << '--line-numbers --inline-source'
27
+ rdoc.rdoc_files.include('README')
28
+ rdoc.rdoc_files.include('TODO')
29
+ rdoc.rdoc_files.include('lib/**/*.rb')
30
+ end
data/test/database.yml ADDED
@@ -0,0 +1,3 @@
1
+ plugin_test:
2
+ adapter: sqlite3
3
+ database: ":memory:"
@@ -0,0 +1,165 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ class EmailValidatorTest < TEST_CASE
4
+ fixtures :people
5
+
6
+ def setup
7
+ @valid_email = 'valid@example.com'
8
+ @invalid_email = 'invalid@example.'
9
+ end
10
+
11
+ def test_should_allow_valid_email_addresses
12
+ ['valid@example.com',
13
+ 'Valid@test.example.com',
14
+ 'valid+valid123@test.example.com',
15
+ 'valid_valid123@test.example.com',
16
+ 'valid-valid+123@test.example.co.uk',
17
+ 'valid-valid+1.23@test.example.com.au',
18
+ 'valid@example.co.uk',
19
+ 'v@example.com',
20
+ 'valid@example.ca',
21
+ 'valid_@example.com',
22
+ 'valid123.456@example.org',
23
+ 'valid123.456@example.travel',
24
+ 'valid123.456@example.museum',
25
+ 'valid@example.mobi',
26
+ 'valid@example.info',
27
+ 'valid-@example.com',
28
+ # allow single character domain parts
29
+ 'valid@mail.x.example.com',
30
+ 'valid@x.com',
31
+ 'valid@example.w-dash.sch.uk',
32
+ # from RFC 3696, page 6
33
+ 'customer/department=shipping@example.com',
34
+ '$A12345@example.com',
35
+ '!def!xyz%abc@example.com',
36
+ '_somename@example.com',
37
+ # apostrophes
38
+ "test'test@example.com",
39
+ ].each do |email|
40
+ p = create_person(:email => email)
41
+ save_passes(p)
42
+ end
43
+ end
44
+
45
+ def test_should_not_allow_invalid_email_addresses
46
+ ['invalid@example-com',
47
+ # period can not start local part
48
+ '.invalid@example.com',
49
+ # period can not end local part
50
+ 'invalid.@example.com',
51
+ # period can not appear twice consecutively in local part
52
+ 'invali..d@example.com',
53
+ # should not allow underscores in domain names
54
+ 'invalid@ex_mple.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
+ # unclosed quote
63
+ "\"a-17180061943-10618354-1993365053",
64
+ # too many special chars used to cause the regexp to hang
65
+ "-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@foo",
66
+ 'invalidexample.com',
67
+ # should not allow special chars after a period in the domain
68
+ 'local@sub.)domain.com',
69
+ 'local@sub.#domain.com',
70
+ # one at a time
71
+ "foo@example.com\nexample@gmail.com",
72
+ 'invalid@example.'].each do |email|
73
+ p = create_person(:email => email)
74
+ save_fails(p)
75
+ end
76
+ end
77
+
78
+ # from http://www.rfc-editor.org/errata_search.php?rfc=3696
79
+ def test_should_allow_quoted_characters
80
+ ['"Abc\@def"@example.com',
81
+ '"Fred\ Bloggs"@example.com',
82
+ '"Joe.\\Blow"@example.com',
83
+ ].each do |email|
84
+ p = create_person(:email => email)
85
+ save_passes(p)
86
+ end
87
+ end
88
+
89
+ # from http://tools.ietf.org/html/rfc3696, page 5
90
+ # corrected in http://www.rfc-editor.org/errata_search.php?rfc=3696
91
+ def test_should_not_allow_escaped_characters_without_quotes
92
+ ['Fred\ Bloggs_@example.com',
93
+ 'Abc\@def+@example.com',
94
+ 'Joe.\\Blow@example.com'
95
+ ].each do |email|
96
+ p = create_person(:email => email)
97
+ save_fails(p)
98
+ end
99
+ end
100
+
101
+ def test_should_check_length_limits
102
+ ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@example.com',
103
+ 'test@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com'
104
+ ].each do |email|
105
+ p = create_person(:email => email)
106
+ save_fails(p)
107
+ end
108
+ end
109
+
110
+ def test_should_respect_validate_on_option
111
+ p = create_person(:email => @valid_email)
112
+ save_passes(p)
113
+
114
+ # we only asked to validate on :create so this should fail
115
+ assert p.update_attributes(:email => @invalid_email)
116
+ assert_equal @invalid_email, p.email
117
+ end
118
+
119
+ def test_should_allow_custom_error_message
120
+ p = create_person(:email => @invalid_email)
121
+ save_fails(p)
122
+ assert_equal ['fails with custom message'], p.errors[:email]
123
+ end
124
+
125
+ def test_should_allow_nil
126
+ p = create_person(:email => nil)
127
+ save_passes(p)
128
+ end
129
+
130
+ # TODO: find a future-proof way to check DNS records
131
+ def test_check_mx
132
+ pmx = MxRecord.new(:email => 'test@dunae.ca')
133
+ save_passes(pmx)
134
+
135
+ pmx = MxRecord.new(:email => 'test@somethingthathasntbeenregistered.com')
136
+ save_fails(pmx)
137
+ end
138
+
139
+ def test_should_be_usable_standalone
140
+ assert_equal true, EmailValidator.valid?('vjt@openssl.it')
141
+ assert_equal false, EmailValidator.valid?('antani')
142
+ end
143
+
144
+ def test_overriding_length_checks
145
+ assert_equal false, EmailValidator.valid?('valid@example.com', :local_length => 1)
146
+ assert_equal false, EmailValidator.valid?('valid@example.com', :domain_length => 1)
147
+ end
148
+
149
+ protected
150
+ def create_person(params)
151
+ Person.new(params)
152
+ end
153
+
154
+ def save_passes(p)
155
+ assert p.valid?, " validating #{p.email}"
156
+ assert p.save
157
+ assert_equal [], p.errors[:email]
158
+ end
159
+
160
+ def save_fails(p)
161
+ assert !p.valid?, " validating #{p.email}"
162
+ assert !p.save
163
+ assert_equal 1, p.errors[:email].size
164
+ end
165
+ end
@@ -0,0 +1,3 @@
1
+ existing:
2
+ id: 1
3
+ email: test@example.com
@@ -0,0 +1,9 @@
1
+ class Person < ActiveRecord::Base
2
+ validates :email, :email => {:message => 'fails with custom message'},
3
+ :on => :create
4
+ end
5
+
6
+ class MxRecord < ActiveRecord::Base
7
+ validates :email, :email => {:check_mx => true},
8
+ :on => :create
9
+ end
data/test/schema.rb ADDED
@@ -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,38 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rubygems'
4
+ require 'test/unit'
5
+ require 'active_record'
6
+ require 'active_record/fixtures'
7
+ require "#{File.dirname(__FILE__)}/../init"
8
+
9
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
10
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
11
+ ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'plugin_test'])
12
+
13
+ load(File.dirname(__FILE__) + "/schema.rb") if File.exist?(File.dirname(__FILE__) + "/schema.rb")
14
+
15
+ if ActiveSupport.const_defined?(:TestCase)
16
+ ActiveSupport::TestCase.send(:include, ActiveRecord::TestFixtures)
17
+ TEST_CASE = ActiveSupport::TestCase
18
+ else
19
+ TEST_CASE = Test::Unit::TestCase
20
+ end
21
+
22
+ TEST_CASE.fixture_path = File.dirname(__FILE__) + "/fixtures/"
23
+ $:.unshift(TEST_CASE.fixture_path)
24
+
25
+ class TEST_CASE #:nodoc:
26
+ def create_fixtures(*table_names)
27
+ if block_given?
28
+ Fixtures.create_fixtures(TEST_CASE.fixture_path, table_names) { yield }
29
+ else
30
+ Fixtures.create_fixtures(TEST_CASE.fixture_path, table_names)
31
+ end
32
+ end
33
+
34
+ self.use_transactional_fixtures = false
35
+
36
+ self.use_instantiated_fixtures = false
37
+ end
38
+
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vjt-email_validator
3
+ version: !ruby/object:Gem::Version
4
+ hash: 1
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 5
9
+ - 1
10
+ version: 1.5.1
11
+ platform: ruby
12
+ authors:
13
+ - Marcello Barnaba
14
+ - Alex Dunae
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-02-28 00:00:00 +01:00
20
+ default_executable:
21
+ dependencies: []
22
+
23
+ description: ActiveModel::EachValidator to check for valid e-mail addresses
24
+ email: vjt@openssl.it
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files:
30
+ - README.rdoc
31
+ - CHANGELOG.rdoc
32
+ - MIT-LICENSE
33
+ files:
34
+ - MIT-LICENSE
35
+ - init.rb
36
+ - rakefile.rb
37
+ - CHANGELOG.rdoc
38
+ - README.rdoc
39
+ - lib/email_validator.rb
40
+ - test/email_validator_test.rb
41
+ - test/fixtures/person.rb
42
+ - test/schema.rb
43
+ - test/test_helper.rb
44
+ - test/database.yml
45
+ - test/fixtures/people.yml
46
+ has_rdoc: true
47
+ homepage: http://github.com/vjt/email_validator
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options:
52
+ - --title
53
+ - email_validator
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.4.1
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Validate e-mail addresses against RFC 2822 and RFC 3696.
81
+ test_files:
82
+ - test/email_validator_test.rb
83
+ - test/fixtures/person.rb
84
+ - test/schema.rb
85
+ - test/test_helper.rb
86
+ - test/database.yml
87
+ - test/fixtures/people.yml