kafka_mailer 0.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +37 -0
- data/Rakefile +22 -0
- data/lib/kafka_mailer.rb +8 -0
- data/lib/kafka_mailer/delivery_method.rb +18 -0
- data/lib/kafka_mailer/message.html.erb +125 -0
- data/lib/kafka_mailer/message.rb +117 -0
- data/lib/kafka_mailer/railtie.rb +9 -0
- data/spec/letter_opener/delivery_method_spec.rb +277 -0
- data/spec/letter_opener/message_spec.rb +204 -0
- data/spec/spec_helper.rb +21 -0
- metadata +140 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3295f7b9bec090e10f821b20369619dd697ad861
|
4
|
+
data.tar.gz: cfc2e4da1a2e377b0b55965095c4b058729640a0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5231ebe7df6e96e3389bfc315ed84b113237128eaf2a31d9801452790f1cbde885de64a7ee20cb3d5919afa3e14d4c792066f22b8731146d2a8ec48d110decfc
|
7
|
+
data.tar.gz: 87dd4fce1c44543f412ed1b7ea9c6dc244b8c343e5c254bfe473367261c088ae1550fea03c6d991bd32614d87381311f0b9a3618083cd86dd529d9b0093df50d
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Eric Hayes
|
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.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# kafka_mailer [](https://travis-ci.org/ejhayes/kafka_mailer) [](http://badge.fury.io/rb/kafka_mailer) [](https://codeclimate.com/github/ejhayes/kafka_mailer) [](https://codeclimate.com/github/ejhayes/kafka_mailer) [](https://gemnasium.com/ejhayes/kafka_mailer)
|
2
|
+
|
3
|
+
Use [Apache Kafka](http://kafka.apache.org/) as a delivery_method in rails.
|
4
|
+
|
5
|
+
## Rails Setup
|
6
|
+
|
7
|
+
**IMPORTANT**: This is under development right now and is not yet ready for use in a production environment.
|
8
|
+
|
9
|
+
First add the gem to your development environment and run the +bundle+ command to install it.
|
10
|
+
|
11
|
+
gem "kafka_mailer"
|
12
|
+
|
13
|
+
Then set the delivery method in `config/environments/development.rb`
|
14
|
+
|
15
|
+
config.action_mailer.delivery_method = :kafka
|
16
|
+
|
17
|
+
Now your emails will be sent to a kafka message queue.
|
18
|
+
|
19
|
+
## Hacking on this gem
|
20
|
+
|
21
|
+
Once you have the source, you must run the following:
|
22
|
+
|
23
|
+
bundle
|
24
|
+
bundle exec rake spec
|
25
|
+
|
26
|
+
## TODO
|
27
|
+
|
28
|
+
Some items that will eventually need to be done:
|
29
|
+
|
30
|
+
- provide a config block that can specify the structure of the message being sent to kafka
|
31
|
+
|
32
|
+
|
33
|
+
## Thanks to
|
34
|
+
|
35
|
+
- Ryan Bates and his [letter_opener](https://github.com/ryanb/letter_opener) which was used as the base for this gem
|
36
|
+
- [poseidon](https://github.com/bpot/poseidon) gem for interacting with Apache Kafka
|
37
|
+
- [Apache kafka](http://kafka.apache.org/)
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
desc 'Interactive console with gem pre-loaded'
|
10
|
+
task :console do
|
11
|
+
require 'irb'
|
12
|
+
require 'irb/completion'
|
13
|
+
require 'ruby-debug'
|
14
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
15
|
+
|
16
|
+
# load the gem
|
17
|
+
require 'kafka_mailer'
|
18
|
+
|
19
|
+
ARGV.clear
|
20
|
+
puts "Console Ready!"
|
21
|
+
IRB.start
|
22
|
+
end
|
data/lib/kafka_mailer.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module KafkaMailer
|
2
|
+
class DeliveryMethod
|
3
|
+
class InvalidOption < StandardError; end
|
4
|
+
|
5
|
+
attr_accessor :settings
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
raise InvalidOption, "A location option is required when using the kafka delivery method" if options[:location].nil?
|
9
|
+
self.settings = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def deliver!(mail)
|
13
|
+
byebug
|
14
|
+
location = File.join(settings[:location], "#{Time.now.to_i}_#{Digest::SHA1.hexdigest(mail.encoded)[0..6]}")
|
15
|
+
messages = Message.rendered_messages(location, mail)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta http-equiv="Content-Type" content="text/html; charset=<%= encoding %>">
|
4
|
+
<% if mail.subject %>
|
5
|
+
<title><%= h mail.subject %></title>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<style type="text/css">
|
9
|
+
#container {
|
10
|
+
margin: 10px auto;
|
11
|
+
}
|
12
|
+
#message_headers {
|
13
|
+
background: #fff;
|
14
|
+
font-size: 12px;
|
15
|
+
font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
|
16
|
+
border-bottom: 1px solid #dedede;
|
17
|
+
margin-bottom: 10px;
|
18
|
+
overflow: auto;
|
19
|
+
}
|
20
|
+
|
21
|
+
#message_headers dl {
|
22
|
+
float: left;
|
23
|
+
line-height: 1.3em;
|
24
|
+
padding: 0;
|
25
|
+
}
|
26
|
+
|
27
|
+
#message_headers dt {
|
28
|
+
width: 92px;
|
29
|
+
margin: 0;
|
30
|
+
float: left;
|
31
|
+
text-align: right;
|
32
|
+
font-weight: bold;
|
33
|
+
color: #7f7f7f;
|
34
|
+
}
|
35
|
+
|
36
|
+
#message_headers dd {
|
37
|
+
margin: 0 0 0 102px;
|
38
|
+
}
|
39
|
+
|
40
|
+
#message_headers p.alternate {
|
41
|
+
float: right;
|
42
|
+
margin: 0;
|
43
|
+
}
|
44
|
+
|
45
|
+
#message_headers p.alternate a {
|
46
|
+
color: #09c;
|
47
|
+
}
|
48
|
+
|
49
|
+
pre#message_body {
|
50
|
+
padding: 4px;
|
51
|
+
white-space: pre-wrap;
|
52
|
+
border: 1px solid #eee;
|
53
|
+
background-color: #fcfcfc;
|
54
|
+
}
|
55
|
+
</style>
|
56
|
+
</head>
|
57
|
+
<body>
|
58
|
+
<div id="container">
|
59
|
+
<div id="message_headers">
|
60
|
+
<dl>
|
61
|
+
<dt>From:</dt>
|
62
|
+
<dd><%= h from %></dd>
|
63
|
+
|
64
|
+
<% unless sender.empty? %>
|
65
|
+
<dt>Sender:</dt>
|
66
|
+
<dd><%= h sender %></dd>
|
67
|
+
<% end %>
|
68
|
+
|
69
|
+
<% unless reply_to.empty? %>
|
70
|
+
<dt>Reply-To:</dt>
|
71
|
+
<dd><%= h reply_to %></dd>
|
72
|
+
<% end %>
|
73
|
+
|
74
|
+
<% if mail.subject %>
|
75
|
+
<dt>Subject:</dt>
|
76
|
+
<dd><strong><%= h mail.subject %></strong></dd>
|
77
|
+
<% end %>
|
78
|
+
|
79
|
+
<dt>Date:</dt>
|
80
|
+
<dd><%= Time.now.strftime("%b %e, %Y %I:%M:%S %p %Z") %></dd>
|
81
|
+
|
82
|
+
<% unless to.empty? %>
|
83
|
+
<dt>To:</dt>
|
84
|
+
<dd><%= h to %></dd>
|
85
|
+
<% end %>
|
86
|
+
|
87
|
+
<% unless cc.empty? %>
|
88
|
+
<dt>CC:</dt>
|
89
|
+
<dd><%= h cc %></dd>
|
90
|
+
<% end %>
|
91
|
+
|
92
|
+
<% unless bcc.empty? %>
|
93
|
+
<dt>BCC:</dt>
|
94
|
+
<dd><%= h bcc %></dd>
|
95
|
+
<% end %>
|
96
|
+
|
97
|
+
<% if @attachments.any? %>
|
98
|
+
<dt>Attachments:</dt>
|
99
|
+
<dd>
|
100
|
+
<% @attachments.each do |filename, path| %>
|
101
|
+
<a href="<%= path %>"><%= filename %></a>
|
102
|
+
<% end %>
|
103
|
+
</dd>
|
104
|
+
<% end %>
|
105
|
+
</dl>
|
106
|
+
|
107
|
+
<% if mail.multipart? %>
|
108
|
+
<p class="alternate">
|
109
|
+
<% if type == "plain" && mail.html_part %>
|
110
|
+
<a href="rich.html">View HTML version</a>
|
111
|
+
<% elsif type == "rich" && mail.text_part %>
|
112
|
+
<a href="plain.html">View plain text version</a>
|
113
|
+
<% end %>
|
114
|
+
</p>
|
115
|
+
<% end %>
|
116
|
+
</div>
|
117
|
+
|
118
|
+
<% if type == "plain" %>
|
119
|
+
<pre id="message_body"><%= auto_link(h(body)) %></pre>
|
120
|
+
<% else %>
|
121
|
+
<%= body %>
|
122
|
+
<% end %>
|
123
|
+
</div>
|
124
|
+
</body>
|
125
|
+
</html>
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "erb"
|
2
|
+
|
3
|
+
module KafkaMailer
|
4
|
+
class Message
|
5
|
+
attr_reader :mail
|
6
|
+
|
7
|
+
def self.rendered_messages(location, mail)
|
8
|
+
messages = []
|
9
|
+
messages << new(location, mail, mail.html_part) if mail.html_part
|
10
|
+
messages << new(location, mail, mail.text_part) if mail.text_part
|
11
|
+
messages << new(location, mail) if messages.empty?
|
12
|
+
messages.each(&:render)
|
13
|
+
messages.sort
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(location, mail, part = nil)
|
17
|
+
@location = location
|
18
|
+
@mail = mail
|
19
|
+
@part = part
|
20
|
+
@attachments = []
|
21
|
+
end
|
22
|
+
|
23
|
+
def render
|
24
|
+
FileUtils.mkdir_p(@location)
|
25
|
+
|
26
|
+
if mail.attachments.any?
|
27
|
+
attachments_dir = File.join(@location, 'attachments')
|
28
|
+
FileUtils.mkdir_p(attachments_dir)
|
29
|
+
mail.attachments.each do |attachment|
|
30
|
+
filename = attachment.filename.gsub(/[^\w.]/, '_')
|
31
|
+
path = File.join(attachments_dir, filename)
|
32
|
+
|
33
|
+
unless File.exists?(path) # true if other parts have already been rendered
|
34
|
+
File.open(path, 'wb') { |f| f.write(attachment.body.raw_source) }
|
35
|
+
end
|
36
|
+
|
37
|
+
@attachments << [attachment.filename, "attachments/#{URI.escape(filename)}"]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
File.open(filepath, 'w') do |f|
|
42
|
+
f.write ERB.new(template).result(binding)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def template
|
47
|
+
File.read(File.expand_path("../message.html.erb", __FILE__))
|
48
|
+
end
|
49
|
+
|
50
|
+
def filepath
|
51
|
+
File.join(@location, "#{type}.html")
|
52
|
+
end
|
53
|
+
|
54
|
+
def content_type
|
55
|
+
@part && @part.content_type || @mail.content_type
|
56
|
+
end
|
57
|
+
|
58
|
+
def body
|
59
|
+
@body ||= begin
|
60
|
+
body = (@part || @mail).decoded
|
61
|
+
|
62
|
+
mail.attachments.each do |attachment|
|
63
|
+
body.gsub!(attachment.url, "attachments/#{attachment.filename}")
|
64
|
+
end
|
65
|
+
|
66
|
+
body
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def from
|
71
|
+
@from ||= Array(@mail['from']).join(", ")
|
72
|
+
end
|
73
|
+
|
74
|
+
def sender
|
75
|
+
@sender ||= Array(@mail['sender']).join(", ")
|
76
|
+
end
|
77
|
+
|
78
|
+
def to
|
79
|
+
@to ||= Array(@mail['to']).join(", ")
|
80
|
+
end
|
81
|
+
|
82
|
+
def cc
|
83
|
+
@cc ||= Array(@mail['cc']).join(", ")
|
84
|
+
end
|
85
|
+
|
86
|
+
def bcc
|
87
|
+
@bcc ||= Array(@mail['bcc']).join(", ")
|
88
|
+
end
|
89
|
+
|
90
|
+
def reply_to
|
91
|
+
@reply_to ||= Array(@mail['reply-to']).join(", ")
|
92
|
+
end
|
93
|
+
|
94
|
+
def type
|
95
|
+
content_type =~ /html/ ? "rich" : "plain"
|
96
|
+
end
|
97
|
+
|
98
|
+
def encoding
|
99
|
+
body.respond_to?(:encoding) ? body.encoding : "utf-8"
|
100
|
+
end
|
101
|
+
|
102
|
+
def auto_link(text)
|
103
|
+
text.gsub(URI.regexp(%W[https http])) do |link|
|
104
|
+
"<a href=\"#{ link }\">#{ link }</a>"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def h(content)
|
109
|
+
CGI.escapeHTML(content)
|
110
|
+
end
|
111
|
+
|
112
|
+
def <=>(other)
|
113
|
+
order = %w[rich plain]
|
114
|
+
order.index(type) <=> order.index(other.type)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module KafkaMailer
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
initializer "letter_opener.add_delivery_method" do
|
4
|
+
ActiveSupport.on_load :action_mailer do
|
5
|
+
ActionMailer::Base.add_delivery_method :kafka, KafkaMailer::DeliveryMethod, :location => Rails.root.join("tmp", "kafka_mailer")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe KafkaMailer::DeliveryMethod do
|
4
|
+
let(:location) { File.expand_path('../../../tmp/kafka_mailer', __FILE__) }
|
5
|
+
|
6
|
+
let(:plain_file) { Dir["#{location}/*/plain.html"].first }
|
7
|
+
let(:plain) { CGI.unescape_html(File.read(plain_file)) }
|
8
|
+
|
9
|
+
before do
|
10
|
+
# TODO: STUB kafka connection
|
11
|
+
context = self
|
12
|
+
|
13
|
+
Mail.defaults do
|
14
|
+
delivery_method KafkaMailer::DeliveryMethod, :location => context.location
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'raises an exception if no location passed' do
|
19
|
+
# expect { KafkaMailer::DeliveryMethod.new }.to raise_exception(KafkaMailer::DeliveryMethod::InvalidOption)
|
20
|
+
# expect { KafkaMailer::DeliveryMethod.new(location: "foo") }.to_not raise_exception
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'integration' do
|
24
|
+
|
25
|
+
#context 'normal location path' do
|
26
|
+
# it 'opens email' do
|
27
|
+
# expect($stdout).to receive(:puts)
|
28
|
+
# expect {
|
29
|
+
# Mail.deliver do
|
30
|
+
# from 'Foo foo@example.com'
|
31
|
+
# body 'World! http://example.com'
|
32
|
+
# end
|
33
|
+
# }.not_to raise_error
|
34
|
+
# end
|
35
|
+
#end
|
36
|
+
|
37
|
+
#context 'with spaces in location path' do
|
38
|
+
# let(:location) { File.expand_path('../../../tmp/kafka_mailer with space', __FILE__) }
|
39
|
+
#
|
40
|
+
# it 'opens email' do
|
41
|
+
# expect($stdout).to receive(:puts)
|
42
|
+
# expect {
|
43
|
+
# Mail.deliver do
|
44
|
+
# from 'Foo foo@example.com'
|
45
|
+
# body 'World! http://example.com'
|
46
|
+
# end
|
47
|
+
# }.not_to raise_error
|
48
|
+
# end
|
49
|
+
#end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'content' do
|
53
|
+
context 'plain' do
|
54
|
+
before do
|
55
|
+
#expect(Launchy).to receive(:open)
|
56
|
+
|
57
|
+
Mail.deliver do
|
58
|
+
from 'Foo <foo@example.com>'
|
59
|
+
sender 'Baz <baz@example.com>'
|
60
|
+
reply_to 'No Reply <no-reply@example.com>'
|
61
|
+
to 'Bar <bar@example.com>'
|
62
|
+
cc 'Qux <qux@example.com>'
|
63
|
+
bcc 'Qux <qux@example.com>'
|
64
|
+
subject 'Hello'
|
65
|
+
body 'World! http://example.com'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# TODO: check message queue getting correct params
|
70
|
+
#it 'creates plain html document' do
|
71
|
+
# expect(File.exist?(plain_file)).to be_true
|
72
|
+
#end
|
73
|
+
#
|
74
|
+
#it 'saves From field' do
|
75
|
+
# expect(plain).to include("Foo <foo@example.com>")
|
76
|
+
#end
|
77
|
+
#
|
78
|
+
#it 'saves Sender field' do
|
79
|
+
# expect(plain).to include("Baz <baz@example.com>")
|
80
|
+
#end
|
81
|
+
#
|
82
|
+
#it 'saves Reply-to field' do
|
83
|
+
# expect(plain).to include("No Reply <no-reply@example.com>")
|
84
|
+
#end
|
85
|
+
#
|
86
|
+
#it 'saves To field' do
|
87
|
+
# expect(plain).to include("Bar <bar@example.com>")
|
88
|
+
#end
|
89
|
+
#
|
90
|
+
#it 'saves Subject field' do
|
91
|
+
# expect(plain).to include("Hello")
|
92
|
+
#end
|
93
|
+
#
|
94
|
+
#it 'saves Body with autolink' do
|
95
|
+
# expect(plain).to include('World! <a href="http://example.com">http://example.com</a>')
|
96
|
+
#end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'multipart' do
|
100
|
+
let(:rich_file) { Dir["#{location}/*/rich.html"].first }
|
101
|
+
let(:rich) { CGI.unescape_html(File.read(rich_file)) }
|
102
|
+
|
103
|
+
before do
|
104
|
+
#expect(Launchy).to receive(:open)
|
105
|
+
|
106
|
+
Mail.deliver do
|
107
|
+
from 'foo@example.com'
|
108
|
+
to 'bar@example.com'
|
109
|
+
subject 'Many parts with <html>'
|
110
|
+
text_part do
|
111
|
+
body 'This is <plain> text'
|
112
|
+
end
|
113
|
+
html_part do
|
114
|
+
content_type 'text/html; charset=UTF-8'
|
115
|
+
body '<h1>This is HTML</h1>'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
#it 'creates plain html document' do
|
121
|
+
# expect(File.exist?(plain_file)).to be_true
|
122
|
+
#end
|
123
|
+
#
|
124
|
+
#it 'creates rich html document' do
|
125
|
+
# expect(File.exist?(rich_file)).to be_true
|
126
|
+
#end
|
127
|
+
#
|
128
|
+
#it 'shows link to rich html version' do
|
129
|
+
# expect(plain).to include("View HTML version")
|
130
|
+
#end
|
131
|
+
#
|
132
|
+
#it 'saves text part' do
|
133
|
+
# expect(plain).to include("This is <plain> text")
|
134
|
+
#end
|
135
|
+
#
|
136
|
+
#it 'saves html part' do
|
137
|
+
# expect(rich).to include("<h1>This is HTML</h1>")
|
138
|
+
#end
|
139
|
+
#
|
140
|
+
#it 'saves escaped Subject field' do
|
141
|
+
# expect(plain).to include("Many parts with <html>")
|
142
|
+
#end
|
143
|
+
#
|
144
|
+
#it 'shows subject as title' do
|
145
|
+
# expect(rich).to include("<title>Many parts with <html></title>")
|
146
|
+
#end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'using deliver! method' do
|
151
|
+
before do
|
152
|
+
#expect(Launchy).to receive(:open)
|
153
|
+
Mail.new do
|
154
|
+
from 'foo@example.com'
|
155
|
+
to 'bar@example.com'
|
156
|
+
subject 'Hello'
|
157
|
+
body 'World!'
|
158
|
+
end.deliver!
|
159
|
+
end
|
160
|
+
|
161
|
+
#it 'creates plain html document' do
|
162
|
+
# expect(File.exist?(plain_file)).to be_true
|
163
|
+
#end
|
164
|
+
#
|
165
|
+
#it 'saves From field' do
|
166
|
+
# expect(plain).to include("foo@example.com")
|
167
|
+
#end
|
168
|
+
#
|
169
|
+
#it 'saves To field' do
|
170
|
+
# expect(plain).to include("bar@example.com")
|
171
|
+
#end
|
172
|
+
#
|
173
|
+
#it 'saves Subject field' do
|
174
|
+
# expect(plain).to include("Hello")
|
175
|
+
#end
|
176
|
+
#
|
177
|
+
#it 'saves Body field' do
|
178
|
+
# expect(plain).to include("World!")
|
179
|
+
#end
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'attachments in plain text mail' do
|
183
|
+
before do
|
184
|
+
Mail.deliver do
|
185
|
+
from 'foo@example.com'
|
186
|
+
to 'bar@example.com'
|
187
|
+
subject 'With attachments'
|
188
|
+
text_part do
|
189
|
+
body 'This is <plain> text'
|
190
|
+
end
|
191
|
+
attachments[File.basename(__FILE__)] = File.read(__FILE__)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
#it 'creates attachments dir with attachment' do
|
196
|
+
# attachment = Dir["#{location}/*/attachments/#{File.basename(__FILE__)}"].first
|
197
|
+
# expect(File.exists?(attachment)).to be_true
|
198
|
+
#end
|
199
|
+
#
|
200
|
+
#it 'saves attachment name' do
|
201
|
+
# plain = File.read(Dir["#{location}/*/plain.html"].first)
|
202
|
+
# expect(plain).to include(File.basename(__FILE__))
|
203
|
+
#end
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'attachments in rich mail' do
|
207
|
+
let(:url) { mail.attachments[0].url }
|
208
|
+
|
209
|
+
let!(:mail) do
|
210
|
+
Mail.deliver do
|
211
|
+
from 'foo@example.com'
|
212
|
+
to 'bar@example.com'
|
213
|
+
subject 'With attachments'
|
214
|
+
attachments[File.basename(__FILE__)] = File.read(__FILE__)
|
215
|
+
url = attachments[0].url
|
216
|
+
html_part do
|
217
|
+
content_type 'text/html; charset=UTF-8'
|
218
|
+
body "Here's an image: <img src='#{url}' />"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
#it 'creates attachments dir with attachment' do
|
224
|
+
# attachment = Dir["#{location}/*/attachments/#{File.basename(__FILE__)}"].first
|
225
|
+
# expect(File.exists?(attachment)).to be_true
|
226
|
+
#end
|
227
|
+
#
|
228
|
+
#it 'replaces inline attachment urls' do
|
229
|
+
# text = File.read(Dir["#{location}/*/rich.html"].first)
|
230
|
+
# expect(mail.parts[0].body).to include(url)
|
231
|
+
# expect(text).to_not include(url)
|
232
|
+
# expect(text).to include("attachments/#{File.basename(__FILE__)}")
|
233
|
+
#end
|
234
|
+
end
|
235
|
+
|
236
|
+
context 'attachments with non-word characters in the filename' do
|
237
|
+
before do
|
238
|
+
Mail.deliver do
|
239
|
+
from 'foo@example.com'
|
240
|
+
to 'bar@example.com'
|
241
|
+
subject 'With attachments'
|
242
|
+
text_part do
|
243
|
+
body 'This is <plain> text'
|
244
|
+
end
|
245
|
+
attachments['non word:chars/used,01.txt'] = File.read(__FILE__)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
#it 'creates attachments dir with attachment' do
|
250
|
+
# attachment = Dir["#{location}/*/attachments/non_word_chars_used_01.txt"].first
|
251
|
+
# expect(File.exists?(attachment)).to be_true
|
252
|
+
#end
|
253
|
+
#
|
254
|
+
#it 'saves attachment name' do
|
255
|
+
# plain = File.read(Dir["#{location}/*/plain.html"].first)
|
256
|
+
# expect(plain).to include('non_word_chars_used_01.txt')
|
257
|
+
#end
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
context 'subjectless mail' do
|
262
|
+
before do
|
263
|
+
#expect(Launchy).to receive(:open)
|
264
|
+
|
265
|
+
Mail.deliver do
|
266
|
+
from 'Foo foo@example.com'
|
267
|
+
reply_to 'No Reply no-reply@example.com'
|
268
|
+
to 'Bar bar@example.com'
|
269
|
+
body 'World! http://example.com'
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
#it 'creates plain html document' do
|
274
|
+
# expect(File.exist?(plain_file)).to be_true
|
275
|
+
#end
|
276
|
+
end
|
277
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe KafkaMailer::Message do
|
5
|
+
let(:location) { File.expand_path('../../../tmp/letter_opener', __FILE__) }
|
6
|
+
|
7
|
+
def mail(options={}, &blk)
|
8
|
+
Mail.new(options, &blk)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#reply_to' do
|
12
|
+
it 'handles one email as a string' do
|
13
|
+
mail = mail(:reply_to => 'test@example.com')
|
14
|
+
message = described_class.new(location, mail)
|
15
|
+
expect(message.reply_to).to eq('test@example.com')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'handles one email with display names' do
|
19
|
+
mail = mail(:reply_to => 'test <test@example.com>')
|
20
|
+
message = described_class.new(location, mail)
|
21
|
+
expect(message.reply_to).to eq('test <test@example.com>')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'handles array of emails' do
|
25
|
+
mail = mail(:reply_to => ['test1@example.com', 'test2@example.com'])
|
26
|
+
message = described_class.new(location, mail)
|
27
|
+
expect(message.reply_to).to eq('test1@example.com, test2@example.com')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'handles array of emails with display names' do
|
31
|
+
mail = mail(:reply_to => ['test1 <test1@example.com>', 'test2 <test2@example.com>'])
|
32
|
+
message = described_class.new(location, mail)
|
33
|
+
expect(message.reply_to).to eq('test1 <test1@example.com>, test2 <test2@example.com>')
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#to' do
|
39
|
+
it 'handles one email as a string' do
|
40
|
+
mail = mail(:to => 'test@example.com')
|
41
|
+
message = described_class.new(location, mail)
|
42
|
+
expect(message.to).to eq('test@example.com')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'handles one email with display names' do
|
46
|
+
mail = mail(:to => 'test <test@example.com>')
|
47
|
+
message = described_class.new(location, mail)
|
48
|
+
expect(message.to).to eq('test <test@example.com>')
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'handles array of emails' do
|
52
|
+
mail = mail(:to => ['test1@example.com', 'test2@example.com'])
|
53
|
+
message = described_class.new(location, mail)
|
54
|
+
expect(message.to).to eq('test1@example.com, test2@example.com')
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'handles array of emails with display names' do
|
58
|
+
mail = mail(:to => ['test1 <test1@example.com>', 'test2 <test2@example.com>'])
|
59
|
+
message = described_class.new(location, mail)
|
60
|
+
expect(message.to).to eq('test1 <test1@example.com>, test2 <test2@example.com>')
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#cc' do
|
66
|
+
it 'handles one cc email as a string' do
|
67
|
+
mail = mail(:cc => 'test@example.com')
|
68
|
+
message = described_class.new(location, mail)
|
69
|
+
expect(message.cc).to eq('test@example.com')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'handles one cc email with display name' do
|
73
|
+
mail = mail(:cc => ['test <test1@example.com>', 'test2 <test2@example.com>'])
|
74
|
+
message = described_class.new(location, mail)
|
75
|
+
expect(message.cc).to eq('test <test1@example.com>, test2 <test2@example.com>')
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'handles array of cc emails' do
|
79
|
+
mail = mail(:cc => ['test1@example.com', 'test2@example.com'])
|
80
|
+
message = described_class.new(location, mail)
|
81
|
+
expect(message.cc).to eq('test1@example.com, test2@example.com')
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'handles array of cc emails with display names' do
|
85
|
+
mail = mail(:cc => ['test <test1@example.com>', 'test2 <test2@example.com>'])
|
86
|
+
message = described_class.new(location, mail)
|
87
|
+
expect(message.cc).to eq('test <test1@example.com>, test2 <test2@example.com>')
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#bcc' do
|
93
|
+
it 'handles one bcc email as a string' do
|
94
|
+
mail = mail(:bcc => 'test@example.com')
|
95
|
+
message = described_class.new(location, mail)
|
96
|
+
expect(message.bcc).to eq('test@example.com')
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'handles one bcc email with display name' do
|
100
|
+
mail = mail(:bcc => ['test <test1@example.com>', 'test2 <test2@example.com>'])
|
101
|
+
message = described_class.new(location, mail)
|
102
|
+
expect(message.bcc).to eq('test <test1@example.com>, test2 <test2@example.com>')
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'handles array of bcc emails' do
|
106
|
+
mail = mail(:bcc => ['test1@example.com', 'test2@example.com'])
|
107
|
+
message = described_class.new(location, mail)
|
108
|
+
expect(message.bcc).to eq('test1@example.com, test2@example.com')
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'handles array of bcc emails with display names' do
|
112
|
+
mail = mail(:bcc => ['test <test1@example.com>', 'test2 <test2@example.com>'])
|
113
|
+
message = described_class.new(location, mail)
|
114
|
+
expect(message.bcc).to eq('test <test1@example.com>, test2 <test2@example.com>')
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#sender' do
|
120
|
+
it 'handles one email as a string' do
|
121
|
+
mail = mail(:sender => 'sender@example.com')
|
122
|
+
message = described_class.new(location, mail)
|
123
|
+
expect(message.sender).to eq('sender@example.com')
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'handles one email as a string with display name' do
|
127
|
+
mail = mail(:sender => 'test <test@example.com>')
|
128
|
+
message = described_class.new(location, mail)
|
129
|
+
expect(message.sender).to eq('test <test@example.com>')
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'handles array of emails' do
|
133
|
+
mail = mail(:sender => ['sender1@example.com', 'sender2@example.com'])
|
134
|
+
message = described_class.new(location, mail)
|
135
|
+
expect(message.sender).to eq('sender1@example.com, sender2@example.com')
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'handles array of emails with display names' do
|
139
|
+
mail = mail(:sender => ['test <test1@example.com>', 'test2 <test2@example.com>'])
|
140
|
+
message = described_class.new(location, mail)
|
141
|
+
expect(message.sender).to eq('test <test1@example.com>, test2 <test2@example.com>')
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#<=>' do
|
147
|
+
it 'sorts rich type before plain type' do
|
148
|
+
plain = described_class.new(location, double(content_type: 'text/plain'))
|
149
|
+
rich = described_class.new(location, double(content_type: 'text/html'))
|
150
|
+
expect([plain, rich].sort).to eq([rich, plain])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe '#auto_link' do
|
155
|
+
let(:message){ described_class.new(location, mail) }
|
156
|
+
|
157
|
+
it 'does not modify unlinkable text' do
|
158
|
+
text = 'the quick brown fox jumped over the lazy dog'
|
159
|
+
expect(message.auto_link(text)).to eq(text)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'adds links for http' do
|
163
|
+
raw = "Link to http://localhost:3000/example/path path"
|
164
|
+
linked = "Link to <a href=\"http://localhost:3000/example/path\">http://localhost:3000/example/path</a> path"
|
165
|
+
expect(message.auto_link(raw)).to eq(linked)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#body' do
|
170
|
+
it 'handles UTF-8 charset body correctly, with QP CTE, for a non-multipart message' do
|
171
|
+
mail = mail(:sender => 'sender@example.com') do
|
172
|
+
content_type "text/html; charset=UTF-8"
|
173
|
+
content_transfer_encoding 'quoted-printable'
|
174
|
+
body "☃"
|
175
|
+
end
|
176
|
+
message = described_class.new(location, mail)
|
177
|
+
expect(message.body.encoding.name).to eq('UTF-8')
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'handles UTF-8 charset HTML part body correctly, with QP CTE, for a multipart message' do
|
181
|
+
mail = mail(:sender => 'sender@example.com') do
|
182
|
+
html_part do
|
183
|
+
content_type "text/html; charset=UTF-8"
|
184
|
+
content_transfer_encoding 'quoted-printable'
|
185
|
+
body "☃"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
message = described_class.new(location, mail, mail.html_part)
|
189
|
+
expect(message.body.encoding.name).to eq('UTF-8')
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'handles UTF-8 charset text part body correctly, with QP CTE, for a multipart message' do
|
193
|
+
mail = mail(:sender => 'sender@example.com') do
|
194
|
+
text_part do
|
195
|
+
content_type "text/plain; charset=UTF-8"
|
196
|
+
content_transfer_encoding 'quoted-printable'
|
197
|
+
body "☃"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
message = described_class.new(location, mail, mail.text_part)
|
201
|
+
expect(message.body.encoding.name).to eq('UTF-8')
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# Code coverage
|
2
|
+
# Travis: use code climate
|
3
|
+
# Everything else: simplecov
|
4
|
+
if ENV['RAILS_ENV'] == 'travis'
|
5
|
+
require "codeclimate-test-reporter"
|
6
|
+
CodeClimate::TestReporter.start
|
7
|
+
else
|
8
|
+
require 'simplecov'
|
9
|
+
SimpleCov.start
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'rubygems'
|
13
|
+
require 'bundler/setup'
|
14
|
+
Bundler.require(:default)
|
15
|
+
|
16
|
+
require "mail"
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
20
|
+
config.run_all_when_everything_filtered = true
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kafka_mailer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eric Hayes
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: poseidon
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: simplecov
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.7.1
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.7.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mail
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.5.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.5.0
|
97
|
+
description: When mail is sent from your application, kafka_mailer will put the message
|
98
|
+
contents in a kafka queue.
|
99
|
+
email: eric@deployfx.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- CHANGELOG.md
|
105
|
+
- Gemfile
|
106
|
+
- LICENSE
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- lib/kafka_mailer.rb
|
110
|
+
- lib/kafka_mailer/delivery_method.rb
|
111
|
+
- lib/kafka_mailer/message.html.erb
|
112
|
+
- lib/kafka_mailer/message.rb
|
113
|
+
- lib/kafka_mailer/railtie.rb
|
114
|
+
- spec/letter_opener/delivery_method_spec.rb
|
115
|
+
- spec/letter_opener/message_spec.rb
|
116
|
+
- spec/spec_helper.rb
|
117
|
+
homepage: http://github.com/ejhayes/kafka_mailer
|
118
|
+
licenses: []
|
119
|
+
metadata: {}
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 2.2.2
|
137
|
+
signing_key:
|
138
|
+
specification_version: 4
|
139
|
+
summary: Send email to apache kafka message queue.
|
140
|
+
test_files: []
|