em-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.
- data/.gitignore +4 -0
- data/.rvmrc +2 -0
- data/Gemfile +4 -0
- data/README +120 -0
- data/Rakefile +10 -0
- data/bin/em_mailer_server +76 -0
- data/em-mailer.gemspec +30 -0
- data/lib/em-mailer.rb +25 -0
- data/lib/em-mailer/client.rb +33 -0
- data/lib/em-mailer/version.rb +5 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/client_spec.rb +78 -0
- metadata +127 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
data/Gemfile
ADDED
data/README
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
#README
|
2
|
+
|
3
|
+
WARNING: This is an experiment.
|
4
|
+
|
5
|
+
You probably don't want to use this (quite yet). I haven't even begun
|
6
|
+
to see if it works on Rails 3. But, I can assure you it *is* working
|
7
|
+
in production for an old Rails 2 customer.
|
8
|
+
|
9
|
+
For those that have read the aforementioned caveats and are gluttons
|
10
|
+
for punishment, please read on.
|
11
|
+
|
12
|
+
##Why?
|
13
|
+
I wrote em-mailer to solve a problem that I was having. I wanted to
|
14
|
+
send e-mails (to a fair # of subscribers) asynchronously. I didn't
|
15
|
+
want to use Resque or another background job / worker solution for
|
16
|
+
this, I wanted a drop-dead simple mail server process whose sole job
|
17
|
+
was to accept Mail messages encoded as JSON over a socket, and then
|
18
|
+
deliver them according to the delivery options (I've tested both
|
19
|
+
:sendmail and :smtp configurations).
|
20
|
+
|
21
|
+
##Rails 2
|
22
|
+
I originally wrote em_mailer for an old Rails 2 app that I hadn't
|
23
|
+
bothered getting around to migrating, so there's built-in support
|
24
|
+
for similarly lazy-minded individuals.
|
25
|
+
|
26
|
+
In config/environment.rb:
|
27
|
+
|
28
|
+
config.gem 'em_mailer'
|
29
|
+
|
30
|
+
In config/environments/production.rb
|
31
|
+
|
32
|
+
config.action_mailer.delivery_method = :em_mailer
|
33
|
+
|
34
|
+
Out of the box it works with :sendmail delivery as the default.
|
35
|
+
|
36
|
+
If you wish to use SMTP you can do something like this:
|
37
|
+
|
38
|
+
In config/initializers/em_mailer.rb
|
39
|
+
|
40
|
+
ActionMailer::Base.em_mailer_settings[:delivery_method] = :smtp
|
41
|
+
ActionMailer::Base.em_mailer_settings[:delivery_options] = {
|
42
|
+
:address => '<< your smtp server name>>',
|
43
|
+
:port => 25,
|
44
|
+
:domain => '<< your domain >>',
|
45
|
+
:authentication => :plain,
|
46
|
+
:user_name => '<< user with access to smtp host >>',
|
47
|
+
:password => '<< their password >>'
|
48
|
+
}
|
49
|
+
|
50
|
+
##Rails 3
|
51
|
+
In your Gemfile
|
52
|
+
|
53
|
+
gem 'em_mailer'
|
54
|
+
|
55
|
+
That should be all that's necessary. Again, haven't experimented (yet).
|
56
|
+
|
57
|
+
##Running the Mail Server
|
58
|
+
You'll need a running instance of the mail server running. The gem
|
59
|
+
comes bundled with a binary em_mailer_server. You can run it from
|
60
|
+
a command line thusly:
|
61
|
+
|
62
|
+
em_mailer_server
|
63
|
+
|
64
|
+
To run it daemonized it's as easy as:
|
65
|
+
|
66
|
+
em_mailer_server -D
|
67
|
+
|
68
|
+
You can also specify the port that you want the server to listen on
|
69
|
+
(default is 5600)
|
70
|
+
|
71
|
+
em_mailer_server -p 5600 -D
|
72
|
+
|
73
|
+
For a full list of configuration options pass it a -h, or --help.
|
74
|
+
|
75
|
+
##Start/Stop Script
|
76
|
+
I happen to be running em_mailer_server on a CentOS box. Here's my
|
77
|
+
/etc/init.d/em_mailer script that I use to start|stop|restart the
|
78
|
+
process on the host server
|
79
|
+
|
80
|
+
#!/bin/bash
|
81
|
+
#
|
82
|
+
# EM Mailer
|
83
|
+
#
|
84
|
+
# chkconfig: - 85 15
|
85
|
+
# description: start, stop, restart EM Mailer Server
|
86
|
+
#
|
87
|
+
RETVAL=0
|
88
|
+
|
89
|
+
case "$1" in
|
90
|
+
start)
|
91
|
+
/usr/local/bin/em_mailer_server -P /var/run/em_mailer.pid -D
|
92
|
+
RETVAL=$?
|
93
|
+
;;
|
94
|
+
stop)
|
95
|
+
kill `cat /var/run/em_mailer.pid`
|
96
|
+
RETVAL=$?
|
97
|
+
;;
|
98
|
+
restart)
|
99
|
+
kill `cat /var/run/em_mailer.pid`
|
100
|
+
/usr/local/bin/em_mailer_server -P /var/run/em_mailer.pid -D
|
101
|
+
RETVAL=$?
|
102
|
+
;;
|
103
|
+
*)
|
104
|
+
echo "Usage: em_mailer {start|stop|restart}"
|
105
|
+
exit 1
|
106
|
+
;;
|
107
|
+
esac
|
108
|
+
|
109
|
+
exit $RETVAL
|
110
|
+
|
111
|
+
##Monitoring
|
112
|
+
Lastly, I like to use monit to make sure my daemon processes are up and
|
113
|
+
running. Here's my sample monit script:
|
114
|
+
|
115
|
+
check process em_mailer with pidfile /var/run/em_mailer.pid
|
116
|
+
start program = "/etc/init.d/em_mailer start"
|
117
|
+
stop program = "/etc/init.d/em_mailer stop"
|
118
|
+
if failed port 5600 then restart
|
119
|
+
group mail
|
120
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'eventmachine'
|
5
|
+
require 'optparse'
|
6
|
+
require 'mail'
|
7
|
+
require 'yajl'
|
8
|
+
|
9
|
+
options = {}
|
10
|
+
|
11
|
+
optparse = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: em_mailer_server [options]"
|
13
|
+
|
14
|
+
options[:host] = '127.0.0.1'
|
15
|
+
opts.on('-o', '--host HOST', 'listen on HOST (default: 127.0.0.1)') do |host|
|
16
|
+
options[:host] = host
|
17
|
+
end
|
18
|
+
|
19
|
+
options[:port] = 5600
|
20
|
+
opts.on('-p', '--port PORT', 'use PORT (default: 5600)') do |port|
|
21
|
+
options[:port] = port
|
22
|
+
end
|
23
|
+
|
24
|
+
options[:pid] = 'tmp/pids/em_mailer.pid'
|
25
|
+
opts.on('-P', '--pid PID', 'specify path to write PID (default: tmp/pids/em_mailer.pid)') do |pid|
|
26
|
+
options[:pid] = pid
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-D', '--daemonize', 'run daemonized in the background') { |d|
|
30
|
+
options[:daemonize] = d ? true : false
|
31
|
+
}
|
32
|
+
|
33
|
+
opts.on('-h', '--help', 'display this screen') do
|
34
|
+
puts opts
|
35
|
+
exit 0
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.parse! ARGV
|
39
|
+
end
|
40
|
+
|
41
|
+
module MailServer
|
42
|
+
def post_init
|
43
|
+
@parser = Yajl::Parser.new(:symbolize_keys => true)
|
44
|
+
@parser.on_parse_complete = Proc.new { |obj| object_parsed(obj) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def object_parsed(obj)
|
48
|
+
puts "delivering #{obj[:message]}"
|
49
|
+
mail = Mail.new(obj[:message])
|
50
|
+
mail.delivery_method obj[:delivery_method].to_sym, obj[:delivery_options]
|
51
|
+
mail.deliver
|
52
|
+
end
|
53
|
+
|
54
|
+
def receive_data(data)
|
55
|
+
@parser << data
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
EM.run{
|
60
|
+
EM.start_server options[:host], options[:port], MailServer
|
61
|
+
if options[:daemonize]
|
62
|
+
if RUBY_VERSION < "1.9"
|
63
|
+
exit if fork
|
64
|
+
Process.setsid
|
65
|
+
exit if fork
|
66
|
+
Dir.chdir "/"
|
67
|
+
File.umask 0000
|
68
|
+
STDIN.reopen "/dev/null"
|
69
|
+
STDOUT.reopen "/dev/null", "a"
|
70
|
+
STDERR.reopen "/dev/null", "a"
|
71
|
+
else
|
72
|
+
Process.daemon
|
73
|
+
end
|
74
|
+
end
|
75
|
+
File.open(options[:pid], 'w') { |f| f << Process.pid }
|
76
|
+
}
|
data/em-mailer.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "em-mailer/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "em-mailer"
|
7
|
+
s.version = Em::Mailer::VERSION
|
8
|
+
s.authors = ["Chris Kraybill"]
|
9
|
+
s.email = ["ckraybill@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Asynchronous mail client and server}
|
12
|
+
s.description = %q{Asynchronous mail client and server written in ruby,
|
13
|
+
eventmachine to replace synchronous mail implementation
|
14
|
+
in Rails 2 and 3}
|
15
|
+
|
16
|
+
s.rubyforge_project = "em-mailer"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_dependency "eventmachine"
|
24
|
+
s.add_dependency "mail"
|
25
|
+
s.add_dependency "yajl-ruby"
|
26
|
+
s.add_dependency "actionmailer"
|
27
|
+
|
28
|
+
s.add_development_dependency "rspec"
|
29
|
+
s.add_development_dependency "em-spec"
|
30
|
+
end
|
data/lib/em-mailer.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "action_mailer"
|
2
|
+
require "em-mailer/version"
|
3
|
+
require "em-mailer/client"
|
4
|
+
|
5
|
+
if defined?(Rails) && Rails::VERSION::MAJOR == 2
|
6
|
+
module ActionMailer
|
7
|
+
class Base
|
8
|
+
@@em_mailer_settings = {
|
9
|
+
:host => 'localhost',
|
10
|
+
:port => 5600,
|
11
|
+
:delivery_method => :sendmail,
|
12
|
+
:delivery_options => {}
|
13
|
+
}
|
14
|
+
cattr_accessor :em_mailer_settings
|
15
|
+
|
16
|
+
def perform_delivery_em_mailer(mail)
|
17
|
+
em_client = Em::Mailer::Client.new(em_mailer_settings)
|
18
|
+
em_client.deliver!(mail)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
ActionMailer::Base.add_delivery_method :em_mailer, Em::Mailer::Client
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'yajl'
|
3
|
+
|
4
|
+
module Em
|
5
|
+
module Mailer
|
6
|
+
class Client
|
7
|
+
def initialize(values)
|
8
|
+
self.settings = {
|
9
|
+
:host => 'localhost',
|
10
|
+
:port => 5600,
|
11
|
+
:delivery_method => :sendmail,
|
12
|
+
:delivery_options => {}
|
13
|
+
}.merge!(values)
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :settings
|
17
|
+
|
18
|
+
def deliver!(mail)
|
19
|
+
socket = open_socket
|
20
|
+
Yajl::Encoder.encode({
|
21
|
+
:message => mail.to_s,
|
22
|
+
:delivery_method => settings[:delivery_method],
|
23
|
+
:delivery_options => settings[:delivery_options]
|
24
|
+
}, socket)
|
25
|
+
socket.close
|
26
|
+
end
|
27
|
+
|
28
|
+
def open_socket
|
29
|
+
TCPSocket.new(settings[:host], settings[:port])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Em::Mailer::Client do
|
4
|
+
describe "#initialize" do
|
5
|
+
before do
|
6
|
+
@client = Em::Mailer::Client.new({})
|
7
|
+
end
|
8
|
+
|
9
|
+
it "default's host to localhost" do
|
10
|
+
@client.settings[:host].should == 'localhost'
|
11
|
+
end
|
12
|
+
|
13
|
+
it "default's port to 5600" do
|
14
|
+
@client.settings[:port].should == 5600
|
15
|
+
end
|
16
|
+
|
17
|
+
it "default's delivery method to :sendmail" do
|
18
|
+
@client.settings[:delivery_method].should == :sendmail
|
19
|
+
end
|
20
|
+
|
21
|
+
it "default's delivery options to an empty hash" do
|
22
|
+
@client.settings[:delivery_options].should == {}
|
23
|
+
end
|
24
|
+
|
25
|
+
it "allows overridding the port" do
|
26
|
+
@client = Em::Mailer::Client.new({:port => 5700})
|
27
|
+
@client.settings[:port].should == 5700
|
28
|
+
end
|
29
|
+
|
30
|
+
it "allows overridding the delivery method" do
|
31
|
+
@client = Em::Mailer::Client.new({:delivery_method => :smtp})
|
32
|
+
@client.settings[:delivery_method].should == :smtp
|
33
|
+
end
|
34
|
+
|
35
|
+
it "allows overridding the delivery options" do
|
36
|
+
@client = Em::Mailer::Client.new({:delivery_options => {:username => 'meh'}})
|
37
|
+
@client.settings[:delivery_options][:username].should == 'meh'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#deliver!" do
|
42
|
+
before do
|
43
|
+
@mail = { :to => 'bleh@foo.com', :subject => 'Hai!' }
|
44
|
+
@socket = mock(TCPSocket, :close => true)
|
45
|
+
@client = Em::Mailer::Client.new({:host => 'foo', :port => 42})
|
46
|
+
@client.stub(:open_socket).and_return(@socket)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "opens a socket" do
|
50
|
+
@client.should_receive(:open_socket)
|
51
|
+
@client.deliver!(@mail)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "encodes a hash, along with the socket" do
|
55
|
+
Yajl::Encoder.should_receive(:encode).with(an_instance_of(Hash),@socket)
|
56
|
+
@client.deliver!(@mail)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "converts the mail message to a String" do
|
60
|
+
@mail.should_receive(:to_s)
|
61
|
+
@client.deliver!(@mail)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "closes the socket" do
|
65
|
+
@socket.should_receive(:close)
|
66
|
+
@client.deliver!(@mail)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#open_socket" do
|
71
|
+
it "instantiates a socket on the correct host and port" do
|
72
|
+
@client = Em::Mailer::Client.new({:host => 'foo', :port => 42})
|
73
|
+
TCPSocket.should_receive(:new).with('foo',42)
|
74
|
+
@client.open_socket
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: em-mailer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Chris Kraybill
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-01 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: eventmachine
|
16
|
+
requirement: &70103820724380 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70103820724380
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mail
|
27
|
+
requirement: &70103820723920 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70103820723920
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: yajl-ruby
|
38
|
+
requirement: &70103820723500 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70103820723500
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: actionmailer
|
49
|
+
requirement: &70103820723080 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70103820723080
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rspec
|
60
|
+
requirement: &70103820722660 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70103820722660
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: em-spec
|
71
|
+
requirement: &70103820722240 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70103820722240
|
80
|
+
description: ! "Asynchronous mail client and server written in ruby,\n eventmachine
|
81
|
+
to replace synchronous mail implementation\n in Rails 2 and 3"
|
82
|
+
email:
|
83
|
+
- ckraybill@gmail.com
|
84
|
+
executables:
|
85
|
+
- em_mailer_server
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- .gitignore
|
90
|
+
- .rvmrc
|
91
|
+
- Gemfile
|
92
|
+
- README
|
93
|
+
- Rakefile
|
94
|
+
- bin/em_mailer_server
|
95
|
+
- em-mailer.gemspec
|
96
|
+
- lib/em-mailer.rb
|
97
|
+
- lib/em-mailer/client.rb
|
98
|
+
- lib/em-mailer/version.rb
|
99
|
+
- spec/spec_helper.rb
|
100
|
+
- spec/unit/client_spec.rb
|
101
|
+
homepage: ''
|
102
|
+
licenses: []
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project: em-mailer
|
121
|
+
rubygems_version: 1.8.10
|
122
|
+
signing_key:
|
123
|
+
specification_version: 3
|
124
|
+
summary: Asynchronous mail client and server
|
125
|
+
test_files:
|
126
|
+
- spec/spec_helper.rb
|
127
|
+
- spec/unit/client_spec.rb
|