email_address 0.1.16 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -29,20 +29,22 @@
29
29
  # user.canonical_email #=> "patsmith@gmail.com"
30
30
  ################################################################################
31
31
 
32
- class EmailAddress::CanonicalEmailAddressType < ActiveRecord::Type::Value
32
+ module EmailAddress
33
+ class CanonicalEmailAddressType < ActiveRecord::Type::Value
33
34
 
34
- # From user input, setter
35
- def cast(value)
36
- super(EmailAddress.canonical(value))
37
- end
35
+ # From user input, setter
36
+ def cast(value)
37
+ super(Address.new(value).canonical)
38
+ end
38
39
 
39
- # From a database value
40
- def deserialize(value)
41
- value && EmailAddress.normal(value)
42
- end
40
+ # From a database value
41
+ def deserialize(value)
42
+ value && Address.new(value).normal
43
+ end
43
44
 
44
- # To a database value (string)
45
- def serialize(value)
46
- value && EmailAddress.normal(value)
45
+ # To a database value (string)
46
+ def serialize(value)
47
+ value && Address.new(value).normal
48
+ end
47
49
  end
48
50
  end
@@ -17,6 +17,11 @@ module EmailAddress
17
17
  # the SHA1 Digest, making it unique to your application so it can't easily be
18
18
  # discovered by comparing against a known list of email/sha1 pairs.
19
19
  #
20
+ # * sha256_secret ""
21
+ # This application-level secret is appended to the email_address to compute
22
+ # the SHA256 Digest, making it unique to your application so it can't easily be
23
+ # discovered by comparing against a known list of email/sha256 pairs.
24
+ #
20
25
  # For local part configuration:
21
26
  # * local_downcase: true
22
27
  # Downcase the local part. You probably want this for uniqueness.
@@ -104,6 +109,7 @@ module EmailAddress
104
109
  dns_lookup: :mx, # :mx, :a, :off
105
110
  dns_timeout: nil,
106
111
  sha1_secret: "",
112
+ sha256_secret: "",
107
113
  munge_string: "*****",
108
114
 
109
115
  local_downcase: true,
@@ -143,6 +149,7 @@ module EmailAddress
143
149
  },
144
150
  msn: {
145
151
  host_match: %w[msn. hotmail. outlook. live.],
152
+ exchanger_match: %w[outlook.com],
146
153
  mailbox_validator: ->(m, t) { m =~ /\A\w[\-\w]*(?:\.[\-\w]+)*\z/i }
147
154
  },
148
155
  yahoo: {
@@ -187,9 +194,14 @@ module EmailAddress
187
194
  # Customize your own error message text.
188
195
  def self.error_messages(hash = {}, locale = "en", *extra)
189
196
  hash = extra.first if extra.first.is_a? Hash
190
- unless hash.empty?
197
+
198
+ @errors[locale] ||= {}
199
+ @errors[locale]["email_address"] ||= {}
200
+
201
+ unless hash.nil? || hash.empty?
191
202
  @errors[locale]["email_address"] = @errors[locale]["email_address"].merge(hash)
192
203
  end
204
+
193
205
  @errors[locale]["email_address"]
194
206
  end
195
207
 
@@ -200,7 +212,7 @@ module EmailAddress
200
212
  end
201
213
 
202
214
  def initialize(overrides = {})
203
- @config = EmailAddress::Config.all_settings(overrides)
215
+ @config = Config.all_settings(overrides)
204
216
  end
205
217
 
206
218
  def []=(setting, value)
@@ -29,20 +29,22 @@
29
29
  # user.canonical_email #=> "patsmith@gmail.com"
30
30
  ################################################################################
31
31
 
32
- class EmailAddress::EmailAddressType < ActiveRecord::Type::Value
32
+ module EmailAddress
33
+ class EmailAddressType < ActiveRecord::Type::Value
33
34
 
34
- # From user input, setter
35
- def cast(value)
36
- super(EmailAddress.normal(value))
37
- end
35
+ # From user input, setter
36
+ def cast(value)
37
+ super(Address.new(value).normal)
38
+ end
38
39
 
39
- # From a database value
40
- def deserialize(value)
41
- value && EmailAddress.normal(value)
42
- end
43
- #
44
- # To a database value (string)
45
- def serialize(value)
46
- value && EmailAddress.normal(value)
40
+ # From a database value
41
+ def deserialize(value)
42
+ value && Address.new(value).normal
43
+ end
44
+
45
+ # To a database value (string)
46
+ def serialize(value)
47
+ value && Address.new(value).normal
48
+ end
47
49
  end
48
50
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "resolv"
4
- require "netaddr"
5
4
  require "socket"
6
5
 
7
6
  module EmailAddress
@@ -24,7 +23,7 @@ module EmailAddress
24
23
 
25
24
  def initialize(host, config = {})
26
25
  @host = host
27
- @config = config.is_a?(Hash) ? EmailAddress::Config.new(config) : config
26
+ @config = config.is_a?(Hash) ? Config.new(config) : config
28
27
  @dns_disabled = @config[:host_validation] == :syntax || @config[:dns_lookup] == :off
29
28
  end
30
29
 
@@ -38,7 +37,7 @@ module EmailAddress
38
37
  # Returns the provider name based on the MX-er host names, or nil if not matched
39
38
  def provider
40
39
  return @provider if defined? @provider
41
- EmailAddress::Config.providers.each do |provider, config|
40
+ Config.providers.each do |provider, config|
42
41
  if config[:exchanger_match] && matches?(config[:exchanger_match])
43
42
  return @provider = provider
44
43
  end
@@ -58,8 +57,8 @@ module EmailAddress
58
57
 
59
58
  ress = begin
60
59
  dns.getresources(@host, Resolv::DNS::Resource::IN::MX)
61
- rescue Resolv::ResolvTimeout
62
- []
60
+ rescue Resolv::ResolvTimeout
61
+ []
63
62
  end
64
63
 
65
64
  records = ress.map { |r|
@@ -76,7 +75,7 @@ module EmailAddress
76
75
 
77
76
  # Returns Array of domain names for the MX'ers, used to determine the Provider
78
77
  def domains
79
- @_domains ||= mxers.map { |m| EmailAddress::Host.new(m.first).domain_name }.sort.uniq
78
+ @_domains ||= mxers.map { |m| Host.new(m.first).domain_name }.sort.uniq
80
79
  end
81
80
 
82
81
  # Returns an array of MX IP address (String) for the given email domain
@@ -105,22 +104,9 @@ module EmailAddress
105
104
 
106
105
  # Given a cidr (ip/bits) and ip address, returns true on match. Caches cidr object.
107
106
  def in_cidr?(cidr)
108
- if cidr.include?(":")
109
- c = NetAddr::IPv6Net.parse(cidr)
110
- return true if mx_ips.find do |ip|
111
- next unless ip.include?(":")
112
- rel = c.rel NetAddr::IPv6Net.parse(ip)
113
- !rel.nil? && rel >= 0
114
- end
115
- elsif cidr.include?(".")
116
- c = NetAddr::IPv4Net.parse(cidr)
117
- return true if mx_ips.find do |ip|
118
- next if ip.include?(":")
119
- rel = c.rel NetAddr::IPv4Net.parse(ip)
120
- !rel.nil? && rel >= 0
121
- end
122
- end
123
- false
107
+ net = IPAddr.new(cidr)
108
+ found = mx_ips.detect { |ip| net.include?(IPAddr.new(ip)) }
109
+ !!found
124
110
  end
125
111
  end
126
112
  end
@@ -1,6 +1,5 @@
1
1
  require "simpleidn"
2
2
  require "resolv"
3
- require "netaddr"
4
3
  require "net/smtp"
5
4
 
6
5
  module EmailAddress
@@ -34,15 +33,15 @@ module EmailAddress
34
33
  attr_reader :host_name
35
34
  attr_accessor :dns_name, :domain_name, :registration_name,
36
35
  :tld, :tld2, :subdomains, :ip_address, :config, :provider,
37
- :comment, :error_message, :reason
36
+ :comment, :error_message, :reason, :locale
38
37
  MAX_HOST_LENGTH = 255
39
38
 
40
39
  # Sometimes, you just need a Regexp...
41
- DNS_HOST_REGEX = / [\p{L}\p{N}]+ (?: (?: \-{1,2} | \.) [\p{L}\p{N}]+ )*/x
40
+ DNS_HOST_REGEX = / [\p{L}\p{N}]+ (?: (?: -{1,2} | \.) [\p{L}\p{N}]+ )*/x
42
41
 
43
42
  # The IPv4 and IPv6 were lifted from Resolv::IPv?::Regex and tweaked to not
44
43
  # \A...\z anchor at the edges.
45
- IPv6_HOST_REGEX = /\[IPv6:
44
+ IPV6_HOST_REGEX = /\[IPv6:
46
45
  (?: (?:(?x-mi:
47
46
  (?:[0-9A-Fa-f]{1,4}:){7}
48
47
  [0-9A-Fa-f]{1,4}
@@ -61,7 +60,7 @@ module EmailAddress
61
60
  (?: \d+)\.(?: \d+)\.(?: \d+)\.(?: \d+)
62
61
  )))\]/ix
63
62
 
64
- IPv4_HOST_REGEX = /\[((?x-mi:0
63
+ IPV4_HOST_REGEX = /\[((?x-mi:0
65
64
  |1(?:[0-9][0-9]?)?
66
65
  |2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
67
66
  |[3-9][0-9]?))\.((?x-mi:0
@@ -80,14 +79,15 @@ module EmailAddress
80
79
 
81
80
  # Matches Host forms: DNS name, IPv4, or IPv6 formats
82
81
  STANDARD_HOST_REGEX = /\A (?: #{DNS_HOST_REGEX}
83
- | #{IPv4_HOST_REGEX} | #{IPv6_HOST_REGEX}) \z/ix
82
+ | #{IPV4_HOST_REGEX} | #{IPV6_HOST_REGEX}) \z/ix
84
83
 
85
84
  # host name -
86
85
  # * host type - :email for an email host, :mx for exchanger host
87
- def initialize(host_name, config = {})
86
+ def initialize(host_name, config = {}, locale = "en")
88
87
  @original = host_name ||= ""
88
+ @locale = locale
89
89
  config[:host_type] ||= :email
90
- @config = config.is_a?(Hash) ? EmailAddress::Config.new(config) : config
90
+ @config = config.is_a?(Hash) ? Config.new(config) : config
91
91
  @error = @error_message = nil
92
92
  parse(host_name)
93
93
  end
@@ -104,7 +104,7 @@ module EmailAddress
104
104
  dns_name
105
105
  end
106
106
  end
107
- alias to_s name
107
+ alias_method :to_s, :name
108
108
 
109
109
  # The canonical host name is the simplified, DNS host name
110
110
  def canonical
@@ -149,7 +149,7 @@ module EmailAddress
149
149
  if @config[:host_remove_spaces]
150
150
  @host_name = @host_name.delete(" ")
151
151
  end
152
- @dns_name = if /[^[:ascii:]]/.match(host_name)
152
+ @dns_name = if /[^[:ascii:]]/.match?(host_name)
153
153
  ::SimpleIDN.to_ascii(host_name)
154
154
  else
155
155
  host_name
@@ -209,7 +209,7 @@ module EmailAddress
209
209
  def find_provider # :nodoc:
210
210
  return provider if provider
211
211
 
212
- EmailAddress::Config.providers.each do |provider, config|
212
+ Config.providers.each do |provider, config|
213
213
  if config[:host_match] && matches?(config[:host_match])
214
214
  return set_provider(provider, config)
215
215
  end
@@ -217,12 +217,6 @@ module EmailAddress
217
217
 
218
218
  return set_provider(:default) unless dns_enabled?
219
219
 
220
- provider = exchangers.provider
221
- if provider != :default
222
- set_provider(provider,
223
- EmailAddress::Config.provider(provider))
224
- end
225
-
226
220
  self.provider ||= set_provider(:default)
227
221
  end
228
222
 
@@ -235,7 +229,11 @@ module EmailAddress
235
229
  def parts
236
230
  {host_name: host_name, dns_name: dns_name, subdomain: subdomains,
237
231
  registration_name: registration_name, domain_name: domain_name,
238
- tld2: tld2, tld: tld, ip_address: ip_address,}
232
+ tld2: tld2, tld: tld, ip_address: ip_address}
233
+ end
234
+
235
+ def hosted_provider
236
+ Exchanger.cached(dns_name).provider
239
237
  end
240
238
 
241
239
  ############################################################################
@@ -248,7 +246,7 @@ module EmailAddress
248
246
  end
249
247
 
250
248
  def ip?
251
- ip_address.nil? ? false : true
249
+ !!ip_address
252
250
  end
253
251
 
254
252
  def ipv4?
@@ -292,7 +290,7 @@ module EmailAddress
292
290
  # Does "sub.example.com" match ".com" and ".example.com" top level names?
293
291
  # Matches TLD (uk) or TLD2 (co.uk)
294
292
  def tld_matches?(rule)
295
- rule.match(/\A\.(.+)\z/) && ($1 == tld || $1 == tld2) ? true : false
293
+ rule.match(/\A\.(.+)\z/) && ($1 == tld || $1 == tld2) # ? true : false
296
294
  end
297
295
 
298
296
  def provider_matches?(rule)
@@ -312,13 +310,8 @@ module EmailAddress
312
310
  # the passed CIDR string ("10.9.8.0/24" or "2001:..../64")
313
311
  def ip_matches?(cidr)
314
312
  return false unless ip_address
315
- return cidr if !cidr.include?("/") && cidr == ip_address
316
- if cidr.include?(":") && ip_address.include?(":")
317
- return cidr if NetAddr::IPv6Net.parse(cidr).contains(NetAddr::IPv6.parse(ip_address))
318
- elsif cidr.include?(".") && ip_address.include?(".")
319
- return cidr if NetAddr::IPv4Net.parse(cidr).contains(NetAddr::IPv4.parse(ip_address))
320
- end
321
- false
313
+ net = IPAddr.new(cidr)
314
+ net.include?(IPAddr.new(ip_address))
322
315
  end
323
316
 
324
317
  ############################################################################
@@ -335,16 +328,16 @@ module EmailAddress
335
328
  # Returns: [official_hostname, alias_hostnames, address_family, *address_list]
336
329
  def dns_a_record
337
330
  @_dns_a_record = "0.0.0.0" if @config[:dns_lookup] == :off
338
- @_dns_a_record ||= Socket.gethostbyname(dns_name)
331
+ @_dns_a_record ||= Addrinfo.getaddrinfo(dns_name, 80) # Port 80 for A rec, 25 for MX
339
332
  rescue SocketError # not found, but could also mean network not work
340
333
  @_dns_a_record ||= []
341
334
  end
342
335
 
343
- # Returns an array of EmailAddress::Exchanger hosts configured in DNS.
336
+ # Returns an array of Exchanger hosts configured in DNS.
344
337
  # The array will be empty if none are configured.
345
338
  def exchangers
346
339
  # return nil if @config[:host_type] != :email || !self.dns_enabled?
347
- @_exchangers ||= EmailAddress::Exchanger.cached(dns_name, @config)
340
+ @_exchangers ||= Exchanger.cached(dns_name, @config)
348
341
  end
349
342
 
350
343
  # Returns a DNS TXT Record
@@ -355,8 +348,8 @@ module EmailAddress
355
348
  records = begin
356
349
  dns.getresources(alternate_host || dns_name,
357
350
  Resolv::DNS::Resource::IN::TXT)
358
- rescue Resolv::ResolvTimeout
359
- []
351
+ rescue Resolv::ResolvTimeout
352
+ []
360
353
  end
361
354
 
362
355
  records.empty? ? nil : records.map(&:data).join(" ")
@@ -461,21 +454,9 @@ module EmailAddress
461
454
  end
462
455
 
463
456
  def localhost?
464
- if ip_address
465
- rel =
466
- if ip_address.include?(":")
467
- NetAddr::IPv6Net.parse("" + "::1").rel(
468
- NetAddr::IPv6Net.parse(ip_address)
469
- )
470
- else
471
- NetAddr::IPv4Net.parse("" + "127.0.0.0/8").rel(
472
- NetAddr::IPv4Net.parse(ip_address)
473
- )
474
- end
475
- !rel.nil? && rel >= 0
476
- else
477
- host_name == "localhost"
478
- end
457
+ return true if host_name == "localhost"
458
+ return false unless ip_address
459
+ IPAddr.new(ip_address).loopback?
479
460
  end
480
461
 
481
462
  # Connects to host to test it can receive email. This should NOT be performed
@@ -499,7 +480,7 @@ module EmailAddress
499
480
  def set_error(err, reason = nil)
500
481
  @error = err
501
482
  @reason = reason
502
- @error_message = EmailAddress::Config.error_message(err)
483
+ @error_message = Config.error_message(err, locale)
503
484
  false
504
485
  end
505
486
 
@@ -67,50 +67,51 @@ module EmailAddress
67
67
  # [CFWS]
68
68
  ############################################################################
69
69
  class Local
70
- attr_reader :local
70
+ attr_reader :local
71
71
  attr_accessor :mailbox, :comment, :tag, :config, :original
72
- attr_accessor :syntax
72
+ attr_accessor :syntax, :locale
73
73
 
74
74
  # RFC-2142: MAILBOX NAMES FOR COMMON SERVICES, ROLES AND FUNCTIONS
75
- BUSINESS_MAILBOXES = %w(info marketing sales support)
76
- NETWORK_MAILBOXES = %w(abuse noc security)
77
- SERVICE_MAILBOXES = %w(postmaster hostmaster usenet news webmaster www uucp ftp)
78
- SYSTEM_MAILBOXES = %w(help mailer-daemon root) # Not from RFC-2142
79
- ROLE_MAILBOXES = %w(staff office orders billing careers jobs) # Not from RFC-2142
80
- SPECIAL_MAILBOXES = BUSINESS_MAILBOXES + NETWORK_MAILBOXES + SERVICE_MAILBOXES +
81
- SYSTEM_MAILBOXES + ROLE_MAILBOXES
82
- STANDARD_MAX_SIZE = 64
75
+ BUSINESS_MAILBOXES = %w[info marketing sales support]
76
+ NETWORK_MAILBOXES = %w[abuse noc security]
77
+ SERVICE_MAILBOXES = %w[postmaster hostmaster usenet news webmaster www uucp ftp]
78
+ SYSTEM_MAILBOXES = %w[help mailer-daemon root] # Not from RFC-2142
79
+ ROLE_MAILBOXES = %w[staff office orders billing careers jobs] # Not from RFC-2142
80
+ SPECIAL_MAILBOXES = BUSINESS_MAILBOXES + NETWORK_MAILBOXES + SERVICE_MAILBOXES +
81
+ SYSTEM_MAILBOXES + ROLE_MAILBOXES
82
+ STANDARD_MAX_SIZE = 64
83
83
 
84
84
  # Conventional : word([.-+'_]word)*
85
- CONVENTIONAL_MAILBOX_REGEX = /\A [\p{L}\p{N}_]+ (?: [\.\-\+\'_] [\p{L}\p{N}_]+ )* \z/x
86
- CONVENTIONAL_MAILBOX_WITHIN = /[\p{L}\p{N}_]+ (?: [\.\-\+\'_] [\p{L}\p{N}_]+ )*/x
85
+ CONVENTIONAL_MAILBOX_REGEX = /\A [\p{L}\p{N}_]+ (?: [.\-+'_] [\p{L}\p{N}_]+ )* \z/x
86
+ CONVENTIONAL_MAILBOX_WITHIN = /[\p{L}\p{N}_]+ (?: [.\-+'_] [\p{L}\p{N}_]+ )*/x
87
87
 
88
88
  # Relaxed: same characters, relaxed order
89
- RELAXED_MAILBOX_WITHIN = /[\p{L}\p{N}_]+ (?: [\.\-\+\'_]+ [\p{L}\p{N}_]+ )*/x
90
- RELAXED_MAILBOX_REGEX = /\A [\p{L}\p{N}_]+ (?: [\.\-\+\'_]+ [\p{L}\p{N}_]+ )* \z/x
89
+ RELAXED_MAILBOX_WITHIN = /[\p{L}\p{N}_]+ (?: [.\-+'_]+ [\p{L}\p{N}_]+ )*/x
90
+ RELAXED_MAILBOX_REGEX = /\A [\p{L}\p{N}_]+ (?: [.\-+'_]+ [\p{L}\p{N}_]+ )* \z/x
91
91
 
92
92
  # RFC5322 Token: token."token".token (dot-separated tokens)
93
93
  # Quoted Token can also have: SPACE \" \\ ( ) , : ; < > @ [ \ ] .
94
94
  STANDARD_LOCAL_WITHIN = /
95
- (?: [\p{L}\p{N}\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\(\)]+
96
- | \" (?: \\[\" \\] | [\x20-\x21\x23-\x2F\x3A-\x40\x5B\x5D-\x60\x7B-\x7E\p{L}\p{N}] )+ \" )
97
- (?: \. (?: [\p{L}\p{N}\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\(\)]+
98
- | \" (?: \\[\" \\] | [\x20-\x21\x23-\x2F\x3A-\x40\x5B\x5D-\x60\x7B-\x7E\p{L}\p{N}] )+ \" ) )* /x
95
+ (?: [\p{L}\p{N}!\#$%&'*+\-\/=?\^_`{|}~()]+
96
+ | " (?: \\[" \\] | [\x20-\x21\x23-\x2F\x3A-\x40\x5B\x5D-\x60\x7B-\x7E\p{L}\p{N}] )+ " )
97
+ (?: \. (?: [\p{L}\p{N}!\#$%&'*+\-\/=?\^_`{|}~()]+
98
+ | " (?: \\[" \\] | [\x20-\x21\x23-\x2F\x3A-\x40\x5B\x5D-\x60\x7B-\x7E\p{L}\p{N}] )+ " ) )* /x
99
99
 
100
100
  STANDARD_LOCAL_REGEX = /\A #{STANDARD_LOCAL_WITHIN} \z/x
101
101
 
102
102
  REDACTED_REGEX = /\A \{ [0-9a-f]{40} \} \z/x # {sha1}
103
103
 
104
- CONVENTIONAL_TAG_REGEX = # AZaz09_!'+-/=
105
- %r/^([\w\!\'\+\-\/\=]+)$/i.freeze
106
- RELAXED_TAG_REGEX = # AZaz09_!#$%&'*+-/=?^`{|}~
107
- %r/^([\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+)$/i.freeze
104
+ CONVENTIONAL_TAG_REGEX = # AZaz09_!'+-/=
105
+ %r{^([\w!'+\-/=.]+)$}i
106
+ RELAXED_TAG_REGEX = # AZaz09_!#$%&'*+-/=?^`{|}~
107
+ %r/^([\w.!\#$%&'*+\-\/=?\^`{|}~]+)$/i
108
108
 
109
- def initialize(local, config={}, host=nil)
110
- @config = config.is_a?(Hash) ? EmailAddress::Config.new(config) : config
111
- self.local = local
112
- @host = host
113
- @error = @error_message = nil
109
+ def initialize(local, config = {}, host = nil, locale = "en")
110
+ @config = config.is_a?(Hash) ? Config.new(config) : config
111
+ self.local = local
112
+ @host = host
113
+ @locale = locale
114
+ @error = @error_message = nil
114
115
  end
115
116
 
116
117
  def local=(raw)
@@ -121,23 +122,23 @@ module EmailAddress
121
122
  if @config[:local_parse].is_a?(Proc)
122
123
  self.mailbox, self.tag, self.comment = @config[:local_parse].call(raw)
123
124
  else
124
- self.mailbox, self.tag, self.comment = self.parse(raw)
125
+ self.mailbox, self.tag, self.comment = parse(raw)
125
126
  end
126
127
 
127
128
  self.format
128
129
  end
129
130
 
130
131
  def parse(raw)
131
- if raw =~ /\A\"(.*)\"\z/ # Quoted
132
+ if raw =~ /\A"(.*)"\z/ # Quoted
132
133
  raw = $1
133
134
  raw = raw.gsub(/\\(.)/, '\1') # Unescape
134
135
  elsif @config[:local_fix] && @config[:local_format] != :standard
135
- raw = raw.gsub(' ','')
136
- raw = raw.gsub(',','.')
137
- #raw.gsub!(/([^\p{L}\p{N}]{2,10})/) {|s| s[0] } # Stutter punctuation typo
136
+ raw = raw.delete(" ")
137
+ raw = raw.tr(",", ".")
138
+ # raw.gsub!(/([^\p{L}\p{N}]{2,10})/) {|s| s[0] } # Stutter punctuation typo
138
139
  end
139
- raw, comment = self.parse_comment(raw)
140
- mailbox, tag = self.parse_tag(raw)
140
+ raw, comment = parse_comment(raw)
141
+ mailbox, tag = parse_tag(raw)
141
142
  mailbox ||= ""
142
143
  [mailbox, tag, comment]
143
144
  end
@@ -156,28 +157,28 @@ module EmailAddress
156
157
  end
157
158
 
158
159
  def parse_tag(raw)
159
- separator = @config[:tag_separator] ||= '+'
160
+ separator = @config[:tag_separator] ||= "+"
160
161
  raw.split(separator, 2)
161
162
  end
162
163
 
163
164
  # True if the the value contains only Latin characters (7-bit ASCII)
164
165
  def ascii?
165
- ! self.unicode?
166
+ !unicode?
166
167
  end
167
168
 
168
169
  # True if the the value contains non-Latin Unicde characters
169
170
  def unicode?
170
- self.local =~ /[^\p{InBasicLatin}]/ ? true : false
171
+ /[^\p{InBasicLatin}]/.match?(local)
171
172
  end
172
173
 
173
174
  # Returns true if the value matches the Redacted format
174
175
  def redacted?
175
- self.local =~ REDACTED_REGEX ? true : false
176
+ REDACTED_REGEX.match?(local)
176
177
  end
177
178
 
178
179
  # Returns true if the value matches the Redacted format
179
180
  def self.redacted?(local)
180
- local =~ REDACTED_REGEX ? true : false
181
+ REDACTED_REGEX.match?(local)
181
182
  end
182
183
 
183
184
  # Is the address for a common system or business role account?
@@ -190,81 +191,80 @@ module EmailAddress
190
191
  end
191
192
 
192
193
  # Builds the local string according to configurations
193
- def format(form=@config[:local_format]||:conventional)
194
+ def format(form = @config[:local_format] || :conventional)
194
195
  if @config[:local_format].is_a?(Proc)
195
196
  @config[:local_format].call(self)
196
197
  elsif form == :conventional
197
- self.conventional
198
+ conventional
198
199
  elsif form == :canonical
199
- self.canonical
200
+ canonical
200
201
  elsif form == :relaxed
201
- self.relax
202
+ relax
202
203
  elsif form == :standard
203
- self.standard
204
+ standard
204
205
  end
205
206
  end
206
207
 
207
208
  # Returns a conventional form of the address
208
209
  def conventional
209
- if self.tag
210
- [self.mailbox, self.tag].join(@config[:tag_separator])
210
+ if tag
211
+ [mailbox, tag].join(@config[:tag_separator])
211
212
  else
212
- self.mailbox
213
+ mailbox
213
214
  end
214
215
  end
215
216
 
216
217
  # Returns a canonical form of the address
217
218
  def canonical
218
219
  if @config[:mailbox_canonical]
219
- @config[:mailbox_canonical].call(self.mailbox)
220
+ @config[:mailbox_canonical].call(mailbox)
220
221
  else
221
- self.mailbox.downcase
222
+ mailbox.downcase
222
223
  end
223
224
  end
224
225
 
225
226
  # Relaxed format: mailbox and tag, no comment, no extended character set
226
227
  def relax
227
- form = self.mailbox
228
- form += @config[:tag_separator] + self.tag if self.tag
229
- form = form.gsub(/[ \"\(\),:<>@\[\]\\]/,'')
230
- form
228
+ form = mailbox
229
+ form += @config[:tag_separator] + tag if tag
230
+ form.gsub(/[ "(),:<>@\[\]\\]/, "")
231
231
  end
232
232
 
233
233
  # Returns a normalized version of the standard address parts.
234
234
  def standard
235
- form = self.mailbox
236
- form += @config[:tag_separator] + self.tag if self.tag
237
- form += "(" + self.comment + ")" if self.comment
238
- form = form.gsub(/([\\\"])/, '\\\1') # Escape \ and "
239
- if form =~ /[ \"\(\),:<>@\[\\\]]/ # Space and "(),:;<>@[\]
240
- form = %Q("#{form}")
235
+ form = mailbox
236
+ form += @config[:tag_separator] + tag if tag
237
+ form += "(" + comment + ")" if comment
238
+ form = form.gsub(/([\\"])/, '\\\1') # Escape \ and "
239
+ if /[ "(),:<>@\[\\\]]/.match?(form) # Space and "(),:;<>@[\]
240
+ form = %("#{form}")
241
241
  end
242
242
  form
243
243
  end
244
244
 
245
245
  # Sets the part to be the conventional form
246
246
  def conventional!
247
- self.local = self.conventional
247
+ self.local = conventional
248
248
  end
249
249
 
250
250
  # Sets the part to be the canonical form
251
251
  def canonical!
252
- self.local = self.canonical
252
+ self.local = canonical
253
253
  end
254
254
 
255
255
  # Dropps unusual parts of Standard form to form a relaxed version.
256
256
  def relax!
257
- self.local = self.relax
257
+ self.local = relax
258
258
  end
259
259
 
260
260
  # Returns the munged form of the address, like "ma*****"
261
261
  def munge
262
- self.to_s.sub(/\A(.{1,2}).*/) { |m| $1 + @config[:munge_string] }
262
+ to_s.sub(/\A(.{1,2}).*/) { |m| $1 + @config[:munge_string] }
263
263
  end
264
264
 
265
265
  # Mailbox with trailing numbers removed
266
266
  def root_name
267
- self.mailbox =~ /\A(.+?)\d+\z/ ? $1 : self.mailbox
267
+ mailbox =~ /\A(.+?)\d+\z/ ? $1 : mailbox
268
268
  end
269
269
 
270
270
  ############################################################################
@@ -272,19 +272,19 @@ module EmailAddress
272
272
  ############################################################################
273
273
 
274
274
  # True if the part is valid according to the configurations
275
- def valid?(format=@config[:local_format]||:conventional)
275
+ def valid?(format = @config[:local_format] || :conventional)
276
276
  if @config[:mailbox_validator].is_a?(Proc)
277
- @config[:mailbox_validator].call(self.mailbox, self.tag)
277
+ @config[:mailbox_validator].call(mailbox, tag)
278
278
  elsif format.is_a?(Proc)
279
279
  format.call(self)
280
280
  elsif format == :conventional
281
- self.conventional?
281
+ conventional?
282
282
  elsif format == :relaxed
283
- self.relaxed?
283
+ relaxed?
284
284
  elsif format == :redacted
285
- self.redacted?
285
+ redacted?
286
286
  elsif format == :standard
287
- self.standard?
287
+ standard?
288
288
  elsif format == :none
289
289
  true
290
290
  else
@@ -295,13 +295,13 @@ module EmailAddress
295
295
  # Returns the format of the address
296
296
  def format?
297
297
  # if :custom
298
- if self.conventional?
298
+ if conventional?
299
299
  :conventional
300
- elsif self.relaxed?
300
+ elsif relaxed?
301
301
  :relax
302
- elsif self.redacted?
302
+ elsif redacted?
303
303
  :redacted
304
- elsif self.standard?
304
+ elsif standard?
305
305
  :standard
306
306
  else
307
307
  :invalid
@@ -309,38 +309,38 @@ module EmailAddress
309
309
  end
310
310
 
311
311
  def valid_size?
312
- return set_error(:local_size_long) if self.local.size > STANDARD_MAX_SIZE
313
- if @host && @host.hosted_service?
312
+ return set_error(:local_size_long) if local.size > STANDARD_MAX_SIZE
313
+ if @host&.hosted_service?
314
314
  return false if @config[:local_private_size] && !valid_size_checks(@config[:local_private_size])
315
- else
316
- return false if @config[:local_size] && !valid_size_checks(@config[:local_size])
315
+ elsif @config[:local_size] && !valid_size_checks(@config[:local_size])
316
+ return false
317
317
  end
318
318
  return false if @config[:mailbox_size] && !valid_size_checks(@config[:mailbox_size])
319
319
  true
320
320
  end
321
321
 
322
322
  def valid_size_checks(range)
323
- return set_error(:local_size_short) if self.mailbox.size < range.first
324
- return set_error(:local_size_long) if self.mailbox.size > range.last
323
+ return set_error(:local_size_short) if mailbox.size < range.first
324
+ return set_error(:local_size_long) if mailbox.size > range.last
325
325
  true
326
326
  end
327
327
 
328
- def valid_encoding?(enc=@config[:local_encoding]||:ascii)
329
- return false if enc == :ascii && self.unicode?
328
+ def valid_encoding?(enc = @config[:local_encoding] || :ascii)
329
+ return false if enc == :ascii && unicode?
330
330
  true
331
331
  end
332
332
 
333
333
  # True if the part matches the conventional format
334
334
  def conventional?
335
335
  self.syntax = :invalid
336
- if self.tag
337
- return false unless self.mailbox =~ CONVENTIONAL_MAILBOX_REGEX &&
338
- self.tag =~ CONVENTIONAL_TAG_REGEX
336
+ if tag
337
+ return false unless mailbox =~ CONVENTIONAL_MAILBOX_REGEX &&
338
+ tag =~ CONVENTIONAL_TAG_REGEX
339
339
  else
340
- return false unless self.local =~ CONVENTIONAL_MAILBOX_REGEX
340
+ return false unless CONVENTIONAL_MAILBOX_REGEX.match?(local)
341
341
  end
342
- self.valid_size? or return false
343
- self.valid_encoding? or return false
342
+ valid_size? or return false
343
+ valid_encoding? or return false
344
344
  self.syntax = :conventional
345
345
  true
346
346
  end
@@ -348,12 +348,12 @@ module EmailAddress
348
348
  # Relaxed conventional is not so strict about character order.
349
349
  def relaxed?
350
350
  self.syntax = :invalid
351
- self.valid_size? or return false
352
- self.valid_encoding? or return false
353
- if self.tag
354
- return false unless self.mailbox =~ RELAXED_MAILBOX_REGEX &&
355
- self.tag =~ RELAXED_TAG_REGEX
356
- elsif self.local =~ RELAXED_MAILBOX_REGEX
351
+ valid_size? or return false
352
+ valid_encoding? or return false
353
+ if tag
354
+ return false unless mailbox =~ RELAXED_MAILBOX_REGEX &&
355
+ tag =~ RELAXED_TAG_REGEX
356
+ elsif RELAXED_MAILBOX_REGEX.match?(local)
357
357
  self.syntax = :relaxed
358
358
  true
359
359
  else
@@ -364,9 +364,9 @@ module EmailAddress
364
364
  # True if the part matches the RFC standard format
365
365
  def standard?
366
366
  self.syntax = :invalid
367
- self.valid_size? or return false
368
- self.valid_encoding? or return false
369
- if self.local =~ STANDARD_LOCAL_REGEX
367
+ valid_size? or return false
368
+ valid_encoding? or return false
369
+ if STANDARD_LOCAL_REGEX.match?(local)
370
370
  self.syntax = :standard
371
371
  true
372
372
  else
@@ -379,26 +379,23 @@ module EmailAddress
379
379
  def matches?(*rules)
380
380
  rules.flatten.each do |r|
381
381
  if r =~ /(.+)@\z/
382
- return r if File.fnmatch?($1, self.local)
382
+ return r if File.fnmatch?($1, local)
383
383
  end
384
384
  end
385
385
  false
386
386
  end
387
387
 
388
- def set_error(err, reason=nil)
388
+ def set_error(err, reason = nil)
389
389
  @error = err
390
- @reason= reason
391
- @error_message = EmailAddress::Config.error_message(err)
390
+ @reason = reason
391
+ @error_message = Config.error_message(err, locale)
392
392
  false
393
393
  end
394
394
 
395
- def error_message
396
- @error_message
397
- end
395
+ attr_reader :error_message
398
396
 
399
397
  def error
400
- self.valid? ? nil : ( @error || :local_invalid)
398
+ valid? ? nil : (@error || :local_invalid)
401
399
  end
402
-
403
400
  end
404
401
  end