kennethkalmer-daemon-kit 0.1.7.3 → 0.1.7.4
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/History.txt +8 -0
- data/Manifest.txt +30 -0
- data/app_generators/daemon_kit/templates/bin/daemon.erb +1 -1
- data/daemon_generators/amqp/templates/config/amqp.yml +5 -5
- data/daemon_generators/deploy_capistrano/templates/config/deploy.rb +3 -1
- data/lib/daemon_kit/amqp.rb +2 -1
- data/lib/daemon_kit/application.rb +1 -0
- data/lib/daemon_kit/deployment/capistrano.rb +6 -9
- data/lib/daemon_kit/error_handlers/mail.rb +52 -15
- data/lib/daemon_kit/initializer.rb +14 -22
- data/lib/daemon_kit/tasks/log.rake +8 -0
- data/lib/daemon_kit.rb +23 -1
- data/spec/error_handlers_spec.rb +23 -0
- data/vendor/tmail-1.2.3/tmail/address.rb +426 -0
- data/vendor/tmail-1.2.3/tmail/attachments.rb +46 -0
- data/vendor/tmail-1.2.3/tmail/base64.rb +46 -0
- data/vendor/tmail-1.2.3/tmail/compat.rb +41 -0
- data/vendor/tmail-1.2.3/tmail/config.rb +67 -0
- data/vendor/tmail-1.2.3/tmail/core_extensions.rb +63 -0
- data/vendor/tmail-1.2.3/tmail/encode.rb +581 -0
- data/vendor/tmail-1.2.3/tmail/header.rb +960 -0
- data/vendor/tmail-1.2.3/tmail/index.rb +9 -0
- data/vendor/tmail-1.2.3/tmail/interface.rb +1130 -0
- data/vendor/tmail-1.2.3/tmail/loader.rb +3 -0
- data/vendor/tmail-1.2.3/tmail/mail.rb +578 -0
- data/vendor/tmail-1.2.3/tmail/mailbox.rb +495 -0
- data/vendor/tmail-1.2.3/tmail/main.rb +6 -0
- data/vendor/tmail-1.2.3/tmail/mbox.rb +3 -0
- data/vendor/tmail-1.2.3/tmail/net.rb +248 -0
- data/vendor/tmail-1.2.3/tmail/obsolete.rb +132 -0
- data/vendor/tmail-1.2.3/tmail/parser.rb +1476 -0
- data/vendor/tmail-1.2.3/tmail/port.rb +379 -0
- data/vendor/tmail-1.2.3/tmail/quoting.rb +118 -0
- data/vendor/tmail-1.2.3/tmail/require_arch.rb +58 -0
- data/vendor/tmail-1.2.3/tmail/scanner.rb +49 -0
- data/vendor/tmail-1.2.3/tmail/scanner_r.rb +261 -0
- data/vendor/tmail-1.2.3/tmail/stringio.rb +280 -0
- data/vendor/tmail-1.2.3/tmail/utils.rb +337 -0
- data/vendor/tmail-1.2.3/tmail/version.rb +39 -0
- data/vendor/tmail-1.2.3/tmail.rb +5 -0
- data/vendor/tmail.rb +13 -0
- metadata +33 -3
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 0.1.7.4 (Not released)
|
2
|
+
|
3
|
+
* Enhanced deploy.rb template to check for current dk gem verion,
|
4
|
+
unless vendored
|
5
|
+
* Fix bug in capistrano recipe for restarting daemons
|
6
|
+
* Added log:truncate rake task
|
7
|
+
* Error mails now handled by TMail
|
8
|
+
|
1
9
|
== 0.1.7.3 2009-05-31
|
2
10
|
|
3
11
|
* Removed dependency on daemons gem, now handled in house
|
data/Manifest.txt
CHANGED
@@ -75,6 +75,7 @@ lib/daemon_kit/tasks.rb
|
|
75
75
|
lib/daemon_kit/tasks/environment.rake
|
76
76
|
lib/daemon_kit/tasks/framework.rake
|
77
77
|
lib/daemon_kit/tasks/god.rake
|
78
|
+
lib/daemon_kit/tasks/log.rake
|
78
79
|
lib/daemon_kit/tasks/monit.rake
|
79
80
|
rubygems_generators/install_rspec/USAGE
|
80
81
|
rubygems_generators/install_rspec/install_rspec_generator.rb
|
@@ -89,6 +90,7 @@ script/txt2html
|
|
89
90
|
spec/argument_spec.rb
|
90
91
|
spec/config_spec.rb
|
91
92
|
spec/daemon_kit_spec.rb
|
93
|
+
spec/error_handlers_spec.rb
|
92
94
|
spec/fixtures/env.yml
|
93
95
|
spec/fixtures/noenv.yml
|
94
96
|
spec/initializer_spec.rb
|
@@ -106,3 +108,31 @@ test/test_generator_helper.rb
|
|
106
108
|
test/test_helper.rb
|
107
109
|
test/test_jabber_generator.rb
|
108
110
|
test/test_nanite_agent_generator.rb
|
111
|
+
vendor/tmail-1.2.3/tmail.rb
|
112
|
+
vendor/tmail-1.2.3/tmail/address.rb
|
113
|
+
vendor/tmail-1.2.3/tmail/attachments.rb
|
114
|
+
vendor/tmail-1.2.3/tmail/base64.rb
|
115
|
+
vendor/tmail-1.2.3/tmail/compat.rb
|
116
|
+
vendor/tmail-1.2.3/tmail/config.rb
|
117
|
+
vendor/tmail-1.2.3/tmail/core_extensions.rb
|
118
|
+
vendor/tmail-1.2.3/tmail/encode.rb
|
119
|
+
vendor/tmail-1.2.3/tmail/header.rb
|
120
|
+
vendor/tmail-1.2.3/tmail/index.rb
|
121
|
+
vendor/tmail-1.2.3/tmail/interface.rb
|
122
|
+
vendor/tmail-1.2.3/tmail/loader.rb
|
123
|
+
vendor/tmail-1.2.3/tmail/mail.rb
|
124
|
+
vendor/tmail-1.2.3/tmail/mailbox.rb
|
125
|
+
vendor/tmail-1.2.3/tmail/main.rb
|
126
|
+
vendor/tmail-1.2.3/tmail/mbox.rb
|
127
|
+
vendor/tmail-1.2.3/tmail/net.rb
|
128
|
+
vendor/tmail-1.2.3/tmail/obsolete.rb
|
129
|
+
vendor/tmail-1.2.3/tmail/parser.rb
|
130
|
+
vendor/tmail-1.2.3/tmail/port.rb
|
131
|
+
vendor/tmail-1.2.3/tmail/quoting.rb
|
132
|
+
vendor/tmail-1.2.3/tmail/require_arch.rb
|
133
|
+
vendor/tmail-1.2.3/tmail/scanner.rb
|
134
|
+
vendor/tmail-1.2.3/tmail/scanner_r.rb
|
135
|
+
vendor/tmail-1.2.3/tmail/stringio.rb
|
136
|
+
vendor/tmail-1.2.3/tmail/utils.rb
|
137
|
+
vendor/tmail-1.2.3/tmail/version.rb
|
138
|
+
vendor/tmail.rb
|
@@ -4,8 +4,8 @@
|
|
4
4
|
# omitted will let the gem use it's own defaults.
|
5
5
|
|
6
6
|
# The configuration specifies the following keys:
|
7
|
-
# *
|
8
|
-
# *
|
7
|
+
# * user - Username for the broker
|
8
|
+
# * pass - Password for the broker
|
9
9
|
# * host - Hostname where the broker is running
|
10
10
|
# * vhost - Vhost to connect to
|
11
11
|
# * port - Port where the broker is running
|
@@ -13,8 +13,8 @@
|
|
13
13
|
# * timeout - Timeout
|
14
14
|
|
15
15
|
defaults: &defaults
|
16
|
-
|
17
|
-
|
16
|
+
user: guest
|
17
|
+
pass: guest
|
18
18
|
host: localhost
|
19
19
|
vhost: /
|
20
20
|
|
@@ -25,4 +25,4 @@ test:
|
|
25
25
|
<<: *defaults
|
26
26
|
|
27
27
|
production:
|
28
|
-
<<: *defaults
|
28
|
+
<<: *defaults
|
@@ -34,7 +34,9 @@ set :config_files, %w{}
|
|
34
34
|
set :shared_children, %w{log tmp}
|
35
35
|
|
36
36
|
# Record our dependencies
|
37
|
-
|
37
|
+
unless File.directory?( "#{DaemonKit.root}/vendor/daemon_kit" )
|
38
|
+
depend :remote, :gem, "daemon-kit", ">=#{DaemonKit::VERSION}"
|
39
|
+
end
|
38
40
|
|
39
41
|
# Hook into capistrano's events
|
40
42
|
before "deploy:update_code", "deploy:check"
|
data/lib/daemon_kit/amqp.rb
CHANGED
@@ -30,7 +30,8 @@ module DaemonKit
|
|
30
30
|
DaemonKit.trap('INT') { ::AMQP.stop { ::EM.stop } }
|
31
31
|
DaemonKit.trap('TERM') { ::AMQP.stop { ::EM.stop } }
|
32
32
|
|
33
|
-
# Start our event loop
|
33
|
+
# Start our event loop and AMQP client
|
34
|
+
DaemonKit.logger.debug("AMQP.start(#{@config.inspect})")
|
34
35
|
::AMQP.start(@config, &block)
|
35
36
|
end
|
36
37
|
end
|
@@ -305,18 +305,15 @@ namespace :deploy do
|
|
305
305
|
end
|
306
306
|
|
307
307
|
desc <<-DESC
|
308
|
-
Restarts your application. This works by calling
|
309
|
-
|
308
|
+
Restarts your application. This works by calling 'stop' task, \
|
309
|
+
followed by the 'start' task.
|
310
310
|
|
311
|
-
|
312
|
-
|
313
|
-
that user. If you are in an environment where you can't use sudo, set \
|
314
|
-
the :use_sudo variable to false:
|
315
|
-
|
316
|
-
set :use_sudo, false
|
311
|
+
See the descriptions for the 'start' and 'stop' tasks for any \
|
312
|
+
additional info.
|
317
313
|
DESC
|
318
314
|
task :restart, :except => { :no_release => true } do
|
319
|
-
|
315
|
+
stop
|
316
|
+
start
|
320
317
|
end
|
321
318
|
|
322
319
|
namespace :rollback do
|
@@ -1,33 +1,66 @@
|
|
1
|
+
require DaemonKit.framework_root + '/vendor/tmail'
|
1
2
|
require 'net/smtp'
|
2
3
|
|
3
4
|
module DaemonKit
|
4
5
|
module ErrorHandlers
|
5
|
-
# Send an email notification of the exception via SMTP
|
6
|
+
# Send an email notification of the exception via SMTP.
|
6
7
|
class Mail < Base
|
7
|
-
|
8
|
+
|
8
9
|
# SMTP hostname
|
9
10
|
@host = 'localhost'
|
10
|
-
|
11
|
-
|
11
|
+
|
12
|
+
# SMTP port
|
13
|
+
@port = 25
|
14
|
+
|
12
15
|
# Recipients of the notification
|
13
16
|
@recipients = []
|
14
|
-
attr_accessor :recipients
|
15
17
|
|
16
18
|
# Subject prefix
|
17
19
|
@prefix = '[DAEMON-KIT]'
|
18
|
-
attr_accessor :prefix
|
19
20
|
|
20
21
|
# Sender address
|
21
22
|
@sender = 'daemon-kit'
|
22
|
-
|
23
|
+
|
24
|
+
# SMTP username
|
25
|
+
@username = nil
|
26
|
+
|
27
|
+
# SMTP password
|
28
|
+
@password = nil
|
29
|
+
|
30
|
+
# Authentication mechanism (:plain, :login, or :cram_md5)
|
31
|
+
@authentication = nil
|
32
|
+
|
33
|
+
# Use TLS?
|
34
|
+
@tls = false
|
35
|
+
|
36
|
+
# Domain used when talking to SMTP server
|
37
|
+
@domain = 'localhost.localdomain'
|
38
|
+
|
39
|
+
class << self
|
40
|
+
attr_accessor :host, :port, :recipients, :prefix, :sender, :username,
|
41
|
+
:password, :authentication, :tls, :domain
|
42
|
+
end
|
43
|
+
|
44
|
+
[ :host, :port, :recipients, :prefix, :sender, :username, :password,
|
45
|
+
:authentication, :tls, :domain ].each do |cm|
|
46
|
+
class_eval(<<-EOM, __FILE__, __LINE__)
|
47
|
+
def #{cm}=( val )
|
48
|
+
self.class.#{cm} = val
|
49
|
+
end
|
50
|
+
EOM
|
51
|
+
end
|
23
52
|
|
24
53
|
def handle_exception( exception )
|
25
|
-
email = <<EOF
|
26
|
-
To: #{self.recipients.map { |r| '<' + r + '>' }.join(', ')}
|
27
|
-
From: <#{self.sender}>
|
28
|
-
Subject: #{self.prefix} #{exception.message}
|
29
|
-
Date: #{Time.now}
|
30
54
|
|
55
|
+
mail = TMail::Mail.new
|
56
|
+
mail.to = self.class.recipients
|
57
|
+
mail.from = self.class.sender
|
58
|
+
mail.subject = "#{self.class.prefix} #{exception.message}"
|
59
|
+
mail.set_content_type 'text', 'plain'
|
60
|
+
mail.mime_version = '1.0'
|
61
|
+
mail.date = Time.now
|
62
|
+
|
63
|
+
mail.body = <<EOF
|
31
64
|
DaemonKit caught an exception inside #{DaemonKit.configuration.daemon_name}.
|
32
65
|
|
33
66
|
Message: #{exception.message}
|
@@ -37,10 +70,14 @@ Backtrace:
|
|
37
70
|
Environment: #{ENV.inspect}
|
38
71
|
EOF
|
39
72
|
begin
|
40
|
-
Net::SMTP.
|
41
|
-
|
73
|
+
smtp = Net::SMTP.new( self.class.host, self.class.port )
|
74
|
+
smtp.enable_starttls_auto if self.class.tls && smtp.respond_to?(:enable_starttls_auto)
|
75
|
+
smtp.start( self.class.domain, self.class.username, self.class.password,
|
76
|
+
self.class.authentication ) do |smtp|
|
77
|
+
smtp.sendmail( mail.to_s, mail.from, mail.to )
|
42
78
|
end
|
43
|
-
rescue
|
79
|
+
rescue => e
|
80
|
+
DaemonKit.logger.error "Failed to send exception mail: #{e.message}" if DaemonKit.logger
|
44
81
|
end
|
45
82
|
end
|
46
83
|
end
|
@@ -18,14 +18,6 @@ module DaemonKit
|
|
18
18
|
|
19
19
|
class << self
|
20
20
|
|
21
|
-
def logger
|
22
|
-
@logger
|
23
|
-
end
|
24
|
-
|
25
|
-
def logger=( logger )
|
26
|
-
@logger = logger
|
27
|
-
end
|
28
|
-
|
29
21
|
def configuration
|
30
22
|
@configuration
|
31
23
|
end
|
@@ -46,17 +38,6 @@ module DaemonKit
|
|
46
38
|
self.configuration.trap( *args, &block )
|
47
39
|
end
|
48
40
|
|
49
|
-
def framework_root
|
50
|
-
@framework_root ||= File.join( File.dirname(__FILE__), '..', '..' ).to_absolute_path
|
51
|
-
end
|
52
|
-
|
53
|
-
def root
|
54
|
-
DAEMON_ROOT
|
55
|
-
end
|
56
|
-
|
57
|
-
def env
|
58
|
-
DAEMON_ENV
|
59
|
-
end
|
60
41
|
end
|
61
42
|
|
62
43
|
|
@@ -81,7 +62,7 @@ module DaemonKit
|
|
81
62
|
end
|
82
63
|
|
83
64
|
def self.shutdown
|
84
|
-
DaemonKit.logger.warn "Shutting down"
|
65
|
+
DaemonKit.logger.warn "Shutting down #{DaemonKit.configuration.daemon_name}"
|
85
66
|
exit
|
86
67
|
end
|
87
68
|
|
@@ -107,6 +88,8 @@ module DaemonKit
|
|
107
88
|
load_postdaemonize_configs
|
108
89
|
|
109
90
|
set_process_name
|
91
|
+
|
92
|
+
DaemonKit.logger.info( "DaemonKit (#{DaemonKit::VERSION}) booted, now running #{DaemonKit.configuration.daemon_name}" )
|
110
93
|
end
|
111
94
|
|
112
95
|
def set_load_path
|
@@ -158,6 +141,8 @@ module DaemonKit
|
|
158
141
|
|
159
142
|
DaemonKit.logger = logger
|
160
143
|
|
144
|
+
DaemonKit.logger.info "DaemonKit (#{DaemonKit::VERSION}) booting in #{DAEMON_ENV} mode"
|
145
|
+
|
161
146
|
configuration.trap("USR1") {
|
162
147
|
DaemonKit.logger.level = DaemonKit.logger.debug? ? Logger::INFO : Logger::DEBUG
|
163
148
|
DaemonKit.logger.info "Log level changed to #{DaemonKit.logger.debug? ? 'DEBUG' : 'INFO' }"
|
@@ -166,8 +151,6 @@ module DaemonKit
|
|
166
151
|
DaemonKit.logger.level = Logger::DEBUG
|
167
152
|
DaemonKit.logger.info "Log level changed to DEBUG"
|
168
153
|
}
|
169
|
-
|
170
|
-
DaemonKit.logger.info "DaemonKit up and running in #{DAEMON_ENV} mode"
|
171
154
|
end
|
172
155
|
|
173
156
|
def initialize_signal_traps
|
@@ -281,6 +264,8 @@ module DaemonKit
|
|
281
264
|
end
|
282
265
|
|
283
266
|
def parse_arguments!
|
267
|
+
return unless own_args?
|
268
|
+
|
284
269
|
configs = Arguments.configuration( ARGV ).first
|
285
270
|
@unused_arguments = {}
|
286
271
|
|
@@ -352,6 +337,13 @@ module DaemonKit
|
|
352
337
|
STDERR.puts msg
|
353
338
|
end
|
354
339
|
end
|
340
|
+
|
341
|
+
# If we are executed with any of these commands, don't allow
|
342
|
+
# arguments to be parsed cause they will interfere with the
|
343
|
+
# script encapsulating DaemonKit, like capistrano
|
344
|
+
def own_args?
|
345
|
+
![ 'cap' ].include?( File.basename( $0 ) )
|
346
|
+
end
|
355
347
|
end
|
356
348
|
|
357
349
|
|
data/lib/daemon_kit.rb
CHANGED
@@ -7,7 +7,7 @@ $:.unshift( File.dirname(__FILE__).to_absolute_path ) unless
|
|
7
7
|
$:.include?( File.dirname(__FILE__).to_absolute_path )
|
8
8
|
|
9
9
|
module DaemonKit
|
10
|
-
VERSION = '0.1.7.
|
10
|
+
VERSION = '0.1.7.4'
|
11
11
|
|
12
12
|
autoload :Initializer, 'daemon_kit/initializer'
|
13
13
|
autoload :Application, 'daemon_kit/application'
|
@@ -20,4 +20,26 @@ module DaemonKit
|
|
20
20
|
autoload :Jabber, 'daemon_kit/jabber'
|
21
21
|
autoload :AMQP, 'daemon_kit/amqp'
|
22
22
|
autoload :Nanite, 'daemon_kit/nanite'
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def logger
|
26
|
+
@logger
|
27
|
+
end
|
28
|
+
|
29
|
+
def logger=( logger )
|
30
|
+
@logger = logger
|
31
|
+
end
|
32
|
+
|
33
|
+
def root
|
34
|
+
DAEMON_ROOT
|
35
|
+
end
|
36
|
+
|
37
|
+
def env
|
38
|
+
DAEMON_ENV
|
39
|
+
end
|
40
|
+
|
41
|
+
def framework_root
|
42
|
+
@framework_root ||= File.join( File.dirname(__FILE__), '..' ).to_absolute_path
|
43
|
+
end
|
44
|
+
end
|
23
45
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe DaemonKit::Safety do
|
4
|
+
end
|
5
|
+
|
6
|
+
describe DaemonKit::ErrorHandlers::Mail do
|
7
|
+
it "should send an email report" do
|
8
|
+
conf = Object.new
|
9
|
+
conf.stubs(:daemon_name).returns('test')
|
10
|
+
DaemonKit.stubs(:configuration).returns(conf)
|
11
|
+
|
12
|
+
fake_smtp = Object.new
|
13
|
+
fake_smtp.expects(:start).with('localhost.localdomain', nil, nil, nil)
|
14
|
+
Net::SMTP.expects(:new).with('localhost', 25).returns(fake_smtp)
|
15
|
+
|
16
|
+
begin
|
17
|
+
raise RuntimeError, "specs don't fail :)"
|
18
|
+
rescue => e
|
19
|
+
handler = DaemonKit::ErrorHandlers::Mail.instance
|
20
|
+
handler.handle_exception( e )
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,426 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
= Address handling class
|
4
|
+
|
5
|
+
=end
|
6
|
+
#--
|
7
|
+
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
#
|
28
|
+
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
29
|
+
# with permission of Minero Aoki.
|
30
|
+
#++
|
31
|
+
|
32
|
+
require 'tmail/encode'
|
33
|
+
require 'tmail/parser'
|
34
|
+
|
35
|
+
|
36
|
+
module TMail
|
37
|
+
|
38
|
+
# = Class Address
|
39
|
+
#
|
40
|
+
# Provides a complete handling library for email addresses. Can parse a string of an
|
41
|
+
# address directly or take in preformatted addresses themselves. Allows you to add
|
42
|
+
# and remove phrases from the front of the address and provides a compare function for
|
43
|
+
# email addresses.
|
44
|
+
#
|
45
|
+
# == Parsing and Handling a Valid Address:
|
46
|
+
#
|
47
|
+
# Just pass the email address in as a string to Address.parse:
|
48
|
+
#
|
49
|
+
# email = TMail::Address.parse('Mikel Lindsaar <mikel@lindsaar.net>)
|
50
|
+
# #=> #<TMail::Address mikel@lindsaar.net>
|
51
|
+
# email.address
|
52
|
+
# #=> "mikel@lindsaar.net"
|
53
|
+
# email.local
|
54
|
+
# #=> "mikel"
|
55
|
+
# email.domain
|
56
|
+
# #=> "lindsaar.net"
|
57
|
+
# email.name # Aliased as phrase as well
|
58
|
+
# #=> "Mikel Lindsaar"
|
59
|
+
#
|
60
|
+
# == Detecting an Invalid Address
|
61
|
+
#
|
62
|
+
# If you want to check the syntactical validity of an email address, just pass it to
|
63
|
+
# Address.parse and catch any SyntaxError:
|
64
|
+
#
|
65
|
+
# begin
|
66
|
+
# TMail::Mail.parse("mikel 2@@@@@ me .com")
|
67
|
+
# rescue TMail::SyntaxError
|
68
|
+
# puts("Invalid Email Address Detected")
|
69
|
+
# else
|
70
|
+
# puts("Address is valid")
|
71
|
+
# end
|
72
|
+
# #=> "Invalid Email Address Detected"
|
73
|
+
class Address
|
74
|
+
|
75
|
+
include TextUtils #:nodoc:
|
76
|
+
|
77
|
+
# Sometimes you need to parse an address, TMail can do it for you and provide you with
|
78
|
+
# a fairly robust method of detecting a valid address.
|
79
|
+
#
|
80
|
+
# Takes in a string, returns a TMail::Address object.
|
81
|
+
#
|
82
|
+
# Raises a TMail::SyntaxError on invalid email format
|
83
|
+
def Address.parse( str )
|
84
|
+
Parser.parse :ADDRESS, special_quote_address(str)
|
85
|
+
end
|
86
|
+
|
87
|
+
def Address.special_quote_address(str) #:nodoc:
|
88
|
+
# Takes a string which is an address and adds quotation marks to special
|
89
|
+
# edge case methods that the RACC parser can not handle.
|
90
|
+
#
|
91
|
+
# Right now just handles two edge cases:
|
92
|
+
#
|
93
|
+
# Full stop as the last character of the display name:
|
94
|
+
# Mikel L. <mikel@me.com>
|
95
|
+
# Returns:
|
96
|
+
# "Mikel L." <mikel@me.com>
|
97
|
+
#
|
98
|
+
# Unquoted @ symbol in the display name:
|
99
|
+
# mikel@me.com <mikel@me.com>
|
100
|
+
# Returns:
|
101
|
+
# "mikel@me.com" <mikel@me.com>
|
102
|
+
#
|
103
|
+
# Any other address not matching these patterns just gets returned as is.
|
104
|
+
case
|
105
|
+
# This handles the missing "" in an older version of Apple Mail.app
|
106
|
+
# around the display name when the display name contains a '@'
|
107
|
+
# like 'mikel@me.com <mikel@me.com>'
|
108
|
+
# Just quotes it to: '"mikel@me.com" <mikel@me.com>'
|
109
|
+
when str =~ /\A([^"].+@.+[^"])\s(<.*?>)\Z/
|
110
|
+
return "\"#{$1}\" #{$2}"
|
111
|
+
# This handles cases where 'Mikel A. <mikel@me.com>' which is a trailing
|
112
|
+
# full stop before the address section. Just quotes it to
|
113
|
+
# '"Mikel A. <mikel@me.com>"
|
114
|
+
when str =~ /\A(.*?\.)\s(<.*?>)\Z/
|
115
|
+
return "\"#{$1}\" #{$2}"
|
116
|
+
else
|
117
|
+
str
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def address_group? #:nodoc:
|
122
|
+
false
|
123
|
+
end
|
124
|
+
|
125
|
+
# Address.new(local, domain)
|
126
|
+
#
|
127
|
+
# Accepts:
|
128
|
+
#
|
129
|
+
# * local - Left of the at symbol
|
130
|
+
#
|
131
|
+
# * domain - Array of the domain split at the periods.
|
132
|
+
#
|
133
|
+
# For example:
|
134
|
+
#
|
135
|
+
# Address.new("mikel", ["lindsaar", "net"])
|
136
|
+
# #=> "#<TMail::Address mikel@lindsaar.net>"
|
137
|
+
def initialize( local, domain )
|
138
|
+
if domain
|
139
|
+
domain.each do |s|
|
140
|
+
raise SyntaxError, 'empty word in domain' if s.empty?
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# This is to catch an unquoted "@" symbol in the local part of the
|
145
|
+
# address. Handles addresses like <"@"@me.com> and makes sure they
|
146
|
+
# stay like <"@"@me.com> (previously were becoming <@@me.com>)
|
147
|
+
if local && (local.join == '@' || local.join =~ /\A[^"].*?@.*?[^"]\Z/)
|
148
|
+
@local = "\"#{local.join}\""
|
149
|
+
else
|
150
|
+
@local = local
|
151
|
+
end
|
152
|
+
|
153
|
+
@domain = domain
|
154
|
+
@name = nil
|
155
|
+
@routes = []
|
156
|
+
end
|
157
|
+
|
158
|
+
# Provides the name or 'phrase' of the email address.
|
159
|
+
#
|
160
|
+
# For Example:
|
161
|
+
#
|
162
|
+
# email = TMail::Address.parse("Mikel Lindsaar <mikel@lindsaar.net>")
|
163
|
+
# email.name
|
164
|
+
# #=> "Mikel Lindsaar"
|
165
|
+
def name
|
166
|
+
@name
|
167
|
+
end
|
168
|
+
|
169
|
+
# Setter method for the name or phrase of the email
|
170
|
+
#
|
171
|
+
# For Example:
|
172
|
+
#
|
173
|
+
# email = TMail::Address.parse("mikel@lindsaar.net")
|
174
|
+
# email.name
|
175
|
+
# #=> nil
|
176
|
+
# email.name = "Mikel Lindsaar"
|
177
|
+
# email.to_s
|
178
|
+
# #=> "Mikel Lindsaar <mikel@me.com>"
|
179
|
+
def name=( str )
|
180
|
+
@name = str
|
181
|
+
@name = nil if str and str.empty?
|
182
|
+
end
|
183
|
+
|
184
|
+
#:stopdoc:
|
185
|
+
alias phrase name
|
186
|
+
alias phrase= name=
|
187
|
+
#:startdoc:
|
188
|
+
|
189
|
+
# This is still here from RFC 822, and is now obsolete per RFC2822 Section 4.
|
190
|
+
#
|
191
|
+
# "When interpreting addresses, the route portion SHOULD be ignored."
|
192
|
+
#
|
193
|
+
# It is still here, so you can access it.
|
194
|
+
#
|
195
|
+
# Routes return the route portion at the front of the email address, if any.
|
196
|
+
#
|
197
|
+
# For Example:
|
198
|
+
# email = TMail::Address.parse( "<@sa,@another:Mikel@me.com>")
|
199
|
+
# => #<TMail::Address Mikel@me.com>
|
200
|
+
# email.to_s
|
201
|
+
# => "<@sa,@another:Mikel@me.com>"
|
202
|
+
# email.routes
|
203
|
+
# => ["sa", "another"]
|
204
|
+
def routes
|
205
|
+
@routes
|
206
|
+
end
|
207
|
+
|
208
|
+
def inspect #:nodoc:
|
209
|
+
"#<#{self.class} #{address()}>"
|
210
|
+
end
|
211
|
+
|
212
|
+
# Returns the local part of the email address
|
213
|
+
#
|
214
|
+
# For Example:
|
215
|
+
#
|
216
|
+
# email = TMail::Address.parse("mikel@lindsaar.net")
|
217
|
+
# email.local
|
218
|
+
# #=> "mikel"
|
219
|
+
def local
|
220
|
+
return nil unless @local
|
221
|
+
return '""' if @local.size == 1 and @local[0].empty?
|
222
|
+
# Check to see if it is an array before trying to map it
|
223
|
+
if @local.respond_to?(:map)
|
224
|
+
@local.map {|i| quote_atom(i) }.join('.')
|
225
|
+
else
|
226
|
+
quote_atom(@local)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Returns the domain part of the email address
|
231
|
+
#
|
232
|
+
# For Example:
|
233
|
+
#
|
234
|
+
# email = TMail::Address.parse("mikel@lindsaar.net")
|
235
|
+
# email.local
|
236
|
+
# #=> "lindsaar.net"
|
237
|
+
def domain
|
238
|
+
return nil unless @domain
|
239
|
+
join_domain(@domain)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Returns the full specific address itself
|
243
|
+
#
|
244
|
+
# For Example:
|
245
|
+
#
|
246
|
+
# email = TMail::Address.parse("mikel@lindsaar.net")
|
247
|
+
# email.address
|
248
|
+
# #=> "mikel@lindsaar.net"
|
249
|
+
def spec
|
250
|
+
s = self.local
|
251
|
+
d = self.domain
|
252
|
+
if s and d
|
253
|
+
s + '@' + d
|
254
|
+
else
|
255
|
+
s
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
alias address spec
|
260
|
+
|
261
|
+
# Provides == function to the email. Only checks the actual address
|
262
|
+
# and ignores the name/phrase component
|
263
|
+
#
|
264
|
+
# For Example
|
265
|
+
#
|
266
|
+
# addr1 = TMail::Address.parse("My Address <mikel@lindsaar.net>")
|
267
|
+
# #=> "#<TMail::Address mikel@lindsaar.net>"
|
268
|
+
# addr2 = TMail::Address.parse("Another <mikel@lindsaar.net>")
|
269
|
+
# #=> "#<TMail::Address mikel@lindsaar.net>"
|
270
|
+
# addr1 == addr2
|
271
|
+
# #=> true
|
272
|
+
def ==( other )
|
273
|
+
other.respond_to? :spec and self.spec == other.spec
|
274
|
+
end
|
275
|
+
|
276
|
+
alias eql? ==
|
277
|
+
|
278
|
+
# Provides a unique hash value for this record against the local and domain
|
279
|
+
# parts, ignores the name/phrase value
|
280
|
+
#
|
281
|
+
# email = TMail::Address.parse("mikel@lindsaar.net")
|
282
|
+
# email.hash
|
283
|
+
# #=> 18767598
|
284
|
+
def hash
|
285
|
+
@local.hash ^ @domain.hash
|
286
|
+
end
|
287
|
+
|
288
|
+
# Duplicates a TMail::Address object returning the duplicate
|
289
|
+
#
|
290
|
+
# addr1 = TMail::Address.parse("mikel@lindsaar.net")
|
291
|
+
# addr2 = addr1.dup
|
292
|
+
# addr1.id == addr2.id
|
293
|
+
# #=> false
|
294
|
+
def dup
|
295
|
+
obj = self.class.new(@local.dup, @domain.dup)
|
296
|
+
obj.name = @name.dup if @name
|
297
|
+
obj.routes.replace @routes
|
298
|
+
obj
|
299
|
+
end
|
300
|
+
|
301
|
+
include StrategyInterface #:nodoc:
|
302
|
+
|
303
|
+
def accept( strategy, dummy1 = nil, dummy2 = nil ) #:nodoc:
|
304
|
+
unless @local
|
305
|
+
strategy.meta '<>' # empty return-path
|
306
|
+
return
|
307
|
+
end
|
308
|
+
|
309
|
+
spec_p = (not @name and @routes.empty?)
|
310
|
+
if @name
|
311
|
+
strategy.phrase @name
|
312
|
+
strategy.space
|
313
|
+
end
|
314
|
+
tmp = spec_p ? '' : '<'
|
315
|
+
unless @routes.empty?
|
316
|
+
tmp << @routes.map {|i| '@' + i }.join(',') << ':'
|
317
|
+
end
|
318
|
+
tmp << self.spec
|
319
|
+
tmp << '>' unless spec_p
|
320
|
+
strategy.meta tmp
|
321
|
+
strategy.lwsp ''
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
class AddressGroup
|
328
|
+
|
329
|
+
include Enumerable
|
330
|
+
|
331
|
+
def address_group?
|
332
|
+
true
|
333
|
+
end
|
334
|
+
|
335
|
+
def initialize( name, addrs )
|
336
|
+
@name = name
|
337
|
+
@addresses = addrs
|
338
|
+
end
|
339
|
+
|
340
|
+
attr_reader :name
|
341
|
+
|
342
|
+
def ==( other )
|
343
|
+
other.respond_to? :to_a and @addresses == other.to_a
|
344
|
+
end
|
345
|
+
|
346
|
+
alias eql? ==
|
347
|
+
|
348
|
+
def hash
|
349
|
+
map {|i| i.hash }.hash
|
350
|
+
end
|
351
|
+
|
352
|
+
def []( idx )
|
353
|
+
@addresses[idx]
|
354
|
+
end
|
355
|
+
|
356
|
+
def size
|
357
|
+
@addresses.size
|
358
|
+
end
|
359
|
+
|
360
|
+
def empty?
|
361
|
+
@addresses.empty?
|
362
|
+
end
|
363
|
+
|
364
|
+
def each( &block )
|
365
|
+
@addresses.each(&block)
|
366
|
+
end
|
367
|
+
|
368
|
+
def to_a
|
369
|
+
@addresses.dup
|
370
|
+
end
|
371
|
+
|
372
|
+
alias to_ary to_a
|
373
|
+
|
374
|
+
def include?( a )
|
375
|
+
@addresses.include? a
|
376
|
+
end
|
377
|
+
|
378
|
+
def flatten
|
379
|
+
set = []
|
380
|
+
@addresses.each do |a|
|
381
|
+
if a.respond_to? :flatten
|
382
|
+
set.concat a.flatten
|
383
|
+
else
|
384
|
+
set.push a
|
385
|
+
end
|
386
|
+
end
|
387
|
+
set
|
388
|
+
end
|
389
|
+
|
390
|
+
def each_address( &block )
|
391
|
+
flatten.each(&block)
|
392
|
+
end
|
393
|
+
|
394
|
+
def add( a )
|
395
|
+
@addresses.push a
|
396
|
+
end
|
397
|
+
|
398
|
+
alias push add
|
399
|
+
|
400
|
+
def delete( a )
|
401
|
+
@addresses.delete a
|
402
|
+
end
|
403
|
+
|
404
|
+
include StrategyInterface
|
405
|
+
|
406
|
+
def accept( strategy, dummy1 = nil, dummy2 = nil )
|
407
|
+
strategy.phrase @name
|
408
|
+
strategy.meta ':'
|
409
|
+
strategy.space
|
410
|
+
first = true
|
411
|
+
each do |mbox|
|
412
|
+
if first
|
413
|
+
first = false
|
414
|
+
else
|
415
|
+
strategy.meta ','
|
416
|
+
end
|
417
|
+
strategy.space
|
418
|
+
mbox.accept strategy
|
419
|
+
end
|
420
|
+
strategy.meta ';'
|
421
|
+
strategy.lwsp ''
|
422
|
+
end
|
423
|
+
|
424
|
+
end
|
425
|
+
|
426
|
+
end # module TMail
|