em-mailer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm_gemset_create_on_use_flag=1
2
+ rvm gemset use em-mailer
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in em-mailer.gemspec
4
+ gemspec
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
+
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new do |t|
5
+ t.rspec_opts = ['-c', '-f progress', '-r ./spec/spec_helper.rb']
6
+ t.pattern = 'spec/**/*_spec.rb'
7
+ end
8
+
9
+ task :default => :spec
10
+
@@ -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
+ }
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ module Em
2
+ module Mailer
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+ require 'em-spec/rspec'
4
+
5
+ require 'em-mailer'
6
+
7
+ RSpec.configure do |c|
8
+ c.mock_with :rspec
9
+ end
10
+
@@ -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