tuktuk 0.5.1 → 0.5.2
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.
- checksums.yaml +7 -0
- data/README.md +46 -9
- data/lib/tuktuk/package.rb +17 -6
- data/lib/tuktuk/rails.rb +30 -0
- data/lib/tuktuk/tuktuk.rb +17 -7
- data/lib/tuktuk/version.rb +1 -1
- metadata +10 -21
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f62d4bf43a7a59fc76ce9fec973740c593a40914
|
4
|
+
data.tar.gz: dd1bc1790077737e8d9c7c7b05f838bf8e2f2fed
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 067e0525fcf6209b3a7023c123a7d7972cf7587b46c73a2e7100dde5e7cba23175e0f5a9afc9cbf73cce6a6b7de5da6355be06e1bf6f9895b7b1c9a65304fc1b
|
7
|
+
data.tar.gz: 454def2bcb6549622b77bc7c1668240d87176c98efca5e178a2d3a6f8c7f8e6b64f65d487d999236aa49a767d669a6618200660fd832b18c54a0dc36778e0df0
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
Tuktuk - SMTP client for Ruby
|
2
2
|
=============================
|
3
3
|
|
4
|
-
Unlike Pony (which is friggin' awesome by the way) Tuktuk does not rely on
|
5
|
-
`sendmail` or a separate SMTP server in order to
|
4
|
+
Unlike famous ol' Pony gem (which is friggin' awesome by the way), Tuktuk does not rely on
|
5
|
+
`sendmail` or a separate SMTP server in order to deliver email. Tuktuk looks up the
|
6
6
|
MX servers of the destination address and connects directly using Net::SMTP.
|
7
7
|
This way you don't need to install Exim or Postfix and you can actually handle
|
8
8
|
response status codes -- like bounces, 5xx -- within your application.
|
@@ -32,7 +32,7 @@ The `response` is either a Net::SMTP::Response object, or a Bounce exception (Ha
|
|
32
32
|
|
33
33
|
response, email = Tuktuk.deliver(message)
|
34
34
|
|
35
|
-
if response.is_a?(Bounce)
|
35
|
+
if response.is_a?(Tuktuk::Bounce)
|
36
36
|
puts 'Email bounced. Type: ' + response.class.name # => HardBounce or SoftBounce
|
37
37
|
else
|
38
38
|
puts 'Email delivered!'
|
@@ -55,7 +55,7 @@ result = Tuktuk.deliver_many(messages)
|
|
55
55
|
|
56
56
|
result.each do |response, email|
|
57
57
|
|
58
|
-
if response.is_a?(Bounce)
|
58
|
+
if response.is_a?(Tuktuk::Bounce)
|
59
59
|
puts 'Email bounced. Type: ' + response.class.name
|
60
60
|
else
|
61
61
|
puts 'Email delivered!'
|
@@ -87,18 +87,55 @@ response, email = Tuktuk.deliver(message)
|
|
87
87
|
|
88
88
|
For DKIM to work, you need to set up a TXT record in your domain's DNS.
|
89
89
|
|
90
|
-
|
90
|
+
All available options, with their defaults:
|
91
91
|
|
92
92
|
``` ruby
|
93
93
|
Tuktuk.options = {
|
94
|
-
:log_to
|
95
|
-
:helo_domain
|
96
|
-
:max_workers
|
94
|
+
:log_to => nil, # e.g. log/mailer.log
|
95
|
+
:helo_domain => nil, # your server's domain goes here
|
96
|
+
:max_workers => 0, # controls number of threads for delivering_many emails (read below)
|
97
|
+
:open_timeout => 20, # max seconds to wait for opening a connection
|
98
|
+
:read_timeout => 20, # 20 seconds to wait for a response, once connected
|
99
|
+
:verify_ssl => true, # whether to skip SSL keys verification or not
|
100
|
+
:debug => false, # connects and delivers email to localhost, instead of real target server. CAUTION!
|
97
101
|
:dkim => { ... }
|
98
102
|
}
|
99
103
|
```
|
100
104
|
|
101
|
-
|
105
|
+
You can set the `max_threads` option to `auto`, which will spawn the necessary threads to connect in paralell to all target MX servers when delivering multiple messages. When set to `0`, these batches will be delivered sequentially.
|
106
|
+
|
107
|
+
In other words, if you have three emails targeted to Gmail users and two for Hotmail users, using `auto` Tuktuk will spawn two threads and connect to both servers at once. Using `0` will have email deliveried to one host and then the other.
|
108
|
+
|
109
|
+
Using with Rails
|
110
|
+
----------------
|
111
|
+
|
112
|
+
Tuktuk comes with ActionMailer support out of the box. In your environment.rb or environments/{env}.rb:
|
113
|
+
|
114
|
+
``` ruby
|
115
|
+
require 'tuktuk/rails'
|
116
|
+
|
117
|
+
[...]
|
118
|
+
|
119
|
+
config.action_mailer.delivery_method = :tuktuk
|
120
|
+
```
|
121
|
+
|
122
|
+
Since Tuktuk delivers email directly to the user's MX servers, it's probably a good idea to set `config.action_mailer.raise_delivery_errors` to true. That way you can actually know if an email couldn't make it to its destination.
|
123
|
+
|
124
|
+
When used with ActionMailer, you can pass options using ActionMailer's interface, like this:
|
125
|
+
|
126
|
+
``` ruby
|
127
|
+
|
128
|
+
config.action_mailer.delivery_method = :tuktuk
|
129
|
+
|
130
|
+
config.action_mailer.tuktuk_settings = {
|
131
|
+
:log_to => 'log/mailer.log', # when not set, Tuktuk will use Rails.logger
|
132
|
+
:dkim => {
|
133
|
+
:domain => 'yoursite.com',
|
134
|
+
:selector => 'mailer',
|
135
|
+
:private_key => IO.read('ssl/yoursite.com.key')
|
136
|
+
}
|
137
|
+
}
|
138
|
+
```
|
102
139
|
|
103
140
|
--
|
104
141
|
|
data/lib/tuktuk/package.rb
CHANGED
@@ -9,22 +9,33 @@ module Tuktuk
|
|
9
9
|
module Package
|
10
10
|
|
11
11
|
class << self
|
12
|
+
|
13
|
+
def build(message, index = nil)
|
14
|
+
mail = message.is_a?(Hash) ? new(message) : message.is_a?(Mail) ? message : Mail.read_from_string(message.to_s)
|
15
|
+
mail.array_index = index if index
|
16
|
+
mail
|
17
|
+
end
|
12
18
|
|
13
|
-
def new(message
|
19
|
+
def new(message)
|
14
20
|
mail = message[:html_body] ? mixed(message) : plain(message)
|
15
|
-
mail.array_index = index if index
|
16
21
|
mail.charset = 'UTF-8'
|
17
22
|
|
18
|
-
mail['In-Reply-To']
|
19
|
-
mail['List-Archive']
|
20
|
-
mail['List-Id']
|
21
|
-
mail['X-Mailer']
|
23
|
+
mail['In-Reply-To'] = message[:in_reply_to] if message[:in_reply_to]
|
24
|
+
mail['List-Archive'] = message[:list_archive] if message[:list_archive]
|
25
|
+
mail['List-Id'] = message[:list_id] if message[:list_id]
|
26
|
+
mail['X-Mailer'] = "Tuktuk SMTP v#{Tuktuk::VERSION}"
|
22
27
|
|
23
28
|
if message[:return_path]
|
24
29
|
mail['Return-Path'] = message[:return_path]
|
25
30
|
mail['Bounces-To'] = message[:return_path]
|
26
31
|
mail['Errors-To'] = message[:return_path]
|
27
32
|
end
|
33
|
+
|
34
|
+
if message[:attachments].any?
|
35
|
+
message[:attachments].each do |file|
|
36
|
+
mail.add_file(file)
|
37
|
+
end
|
38
|
+
end
|
28
39
|
|
29
40
|
mail
|
30
41
|
end
|
data/lib/tuktuk/rails.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
if ActionMailer::Base.respond_to?(:add_delivery_method)
|
2
|
+
|
3
|
+
ActionMailer::Base.add_delivery_method :tuktuk, Tuktuk
|
4
|
+
|
5
|
+
module Tuktuk
|
6
|
+
|
7
|
+
def self.new(options)
|
8
|
+
self.options = options
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
else
|
15
|
+
|
16
|
+
require 'tuktuk'
|
17
|
+
|
18
|
+
class ActionMailer::Base
|
19
|
+
|
20
|
+
def self.tuktuk_settings=(opts)
|
21
|
+
Tuktuk.options = opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def perform_delivery_tuktuk(mail)
|
25
|
+
Tuktuk.deliver!(mail)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/lib/tuktuk/tuktuk.rb
CHANGED
@@ -14,6 +14,7 @@ DEFAULTS = {
|
|
14
14
|
:read_timeout => 20,
|
15
15
|
:open_timeout => 20,
|
16
16
|
:verify_ssl => true,
|
17
|
+
:debug => false,
|
17
18
|
:log_to => nil # $stdout,
|
18
19
|
}
|
19
20
|
|
@@ -26,13 +27,20 @@ module Tuktuk
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def deliver(message, opts = {})
|
29
|
-
raise 'Please pass a valid message object.' unless
|
30
|
+
# raise 'Please pass a valid message object.' unless message[:to]
|
30
31
|
self.options = opts if opts.any?
|
31
|
-
mail = Package.
|
32
|
+
mail = Package.build(message)
|
32
33
|
response = lookup_and_deliver(mail)
|
33
34
|
return response, mail
|
34
35
|
end
|
35
36
|
|
37
|
+
# same as deliver but raises error. used by ActionMailer
|
38
|
+
def deliver!(mail)
|
39
|
+
@logger = Rails.logger if defined?(Rails) and !config[:log_to]
|
40
|
+
resp, email = deliver(mail)
|
41
|
+
raise resp if resp.is_a?(Exception)
|
42
|
+
end
|
43
|
+
|
36
44
|
def deliver_many(messages, opts = {})
|
37
45
|
raise 'Please pass an array of messages.' unless messages.any?
|
38
46
|
self.options = opts if opts.any?
|
@@ -74,7 +82,7 @@ module Tuktuk
|
|
74
82
|
def reorder_by_domain(array)
|
75
83
|
hash = {}
|
76
84
|
array.each_with_index do |message, i|
|
77
|
-
mail = Package.
|
85
|
+
mail = Package.build(message, i)
|
78
86
|
raise "Invalid destination count: #{mail.destinations.count}" if mail.destinations.count != 1
|
79
87
|
|
80
88
|
if to = mail.destinations.first and domain = get_domain(to)
|
@@ -199,7 +207,6 @@ module Tuktuk
|
|
199
207
|
from = get_from(mail)
|
200
208
|
|
201
209
|
response = nil
|
202
|
-
server = 'localhost' if ENV['DEBUG']
|
203
210
|
socket = init_connection(server)
|
204
211
|
socket.start(get_helo_domain(from), nil, nil, nil) do |smtp|
|
205
212
|
response = smtp.send_message(get_raw_mail(mail), from, to)
|
@@ -213,7 +220,6 @@ module Tuktuk
|
|
213
220
|
logger.info "Delivering #{mails.count} mails at #{server}..."
|
214
221
|
responses = {}
|
215
222
|
|
216
|
-
server = 'localhost' if ENV['DEBUG']
|
217
223
|
socket = init_connection(server)
|
218
224
|
socket.start(get_helo_domain, nil, nil, nil) do |smtp|
|
219
225
|
mails.each do |mail|
|
@@ -230,8 +236,8 @@ module Tuktuk
|
|
230
236
|
|
231
237
|
responses
|
232
238
|
rescue => e # SMTPServerBusy, SMTPSyntaxError, SMTPUnsupportedCommand, SMTPUnknownError (unexpected reply code)
|
233
|
-
|
234
|
-
|
239
|
+
logger.error "[SERVER ERROR: #{server}] #{e.message}"
|
240
|
+
@last_error = e
|
235
241
|
responses
|
236
242
|
end
|
237
243
|
|
@@ -252,6 +258,10 @@ module Tuktuk
|
|
252
258
|
context.verify_mode = config[:verify_ssl] ?
|
253
259
|
OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
254
260
|
|
261
|
+
if config[:debug]
|
262
|
+
logger.warn "Debug option enabled. Connecting to localhost!"
|
263
|
+
server = 'localhost'
|
264
|
+
end
|
255
265
|
smtp = Net::SMTP.new(server, nil)
|
256
266
|
smtp.enable_starttls_auto(context)
|
257
267
|
smtp.read_timeout = config[:read_timeout] if config[:read_timeout]
|
data/lib/tuktuk/version.rb
CHANGED
metadata
CHANGED
@@ -1,36 +1,32 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tuktuk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
5
|
-
prerelease:
|
4
|
+
version: 0.5.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Tomás Pollak
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-08-06 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: bundler
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: 1.0.0
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: 1.0.0
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: net-dns
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - '='
|
36
32
|
- !ruby/object:Gem::Version
|
@@ -38,7 +34,6 @@ dependencies:
|
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - '='
|
44
39
|
- !ruby/object:Gem::Version
|
@@ -46,7 +41,6 @@ dependencies:
|
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: mail
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - ~>
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,6 @@ dependencies:
|
|
54
48
|
type: :runtime
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
52
|
- - ~>
|
60
53
|
- !ruby/object:Gem::Version
|
@@ -62,7 +55,6 @@ dependencies:
|
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: dkim
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
59
|
- - ~>
|
68
60
|
- !ruby/object:Gem::Version
|
@@ -70,7 +62,6 @@ dependencies:
|
|
70
62
|
type: :runtime
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
66
|
- - ~>
|
76
67
|
- !ruby/object:Gem::Version
|
@@ -78,7 +69,6 @@ dependencies:
|
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: work_queue
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
73
|
- - ~>
|
84
74
|
- !ruby/object:Gem::Version
|
@@ -86,7 +76,6 @@ dependencies:
|
|
86
76
|
type: :runtime
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
80
|
- - ~>
|
92
81
|
- !ruby/object:Gem::Version
|
@@ -107,32 +96,32 @@ files:
|
|
107
96
|
- lib/tuktuk/cache.rb
|
108
97
|
- lib/tuktuk/dns.rb
|
109
98
|
- lib/tuktuk/package.rb
|
99
|
+
- lib/tuktuk/rails.rb
|
110
100
|
- lib/tuktuk/tuktuk.rb
|
111
101
|
- lib/tuktuk/version.rb
|
112
102
|
- spec/deliver_spec.rb
|
113
103
|
- tuktuk.gemspec
|
114
104
|
homepage: https://github.com/tomas/tuktuk
|
115
105
|
licenses: []
|
106
|
+
metadata: {}
|
116
107
|
post_install_message:
|
117
108
|
rdoc_options: []
|
118
109
|
require_paths:
|
119
110
|
- lib
|
120
111
|
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
112
|
requirements:
|
123
|
-
- -
|
113
|
+
- - '>='
|
124
114
|
- !ruby/object:Gem::Version
|
125
115
|
version: '0'
|
126
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
-
none: false
|
128
117
|
requirements:
|
129
|
-
- -
|
118
|
+
- - '>='
|
130
119
|
- !ruby/object:Gem::Version
|
131
120
|
version: 1.3.6
|
132
121
|
requirements: []
|
133
122
|
rubyforge_project: tuktuk
|
134
|
-
rubygems_version:
|
123
|
+
rubygems_version: 2.0.2
|
135
124
|
signing_key:
|
136
|
-
specification_version:
|
125
|
+
specification_version: 4
|
137
126
|
summary: SMTP client for Ruby with DKIM support.
|
138
127
|
test_files: []
|