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.
@@ -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