mailit 2009.08
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +6 -0
- data/CHANGELOG +76 -0
- data/MANIFEST +28 -0
- data/README.md +63 -0
- data/Rakefile +84 -0
- data/lib/mailit.rb +8 -0
- data/lib/mailit/mail.rb +367 -0
- data/lib/mailit/mailer.rb +131 -0
- data/lib/mailit/mime.rb +49 -0
- data/lib/mailit/version.rb +3 -0
- data/lib/version.rb +3 -0
- data/mailit.gemspec +27 -0
- data/spec/helper.rb +4 -0
- data/spec/mailit/mail.rb +108 -0
- data/spec/mailit/mailer.rb +48 -0
- data/tasks/authors.rake +33 -0
- data/tasks/bacon.rake +70 -0
- data/tasks/changelog.rake +18 -0
- data/tasks/copyright.rake +38 -0
- data/tasks/gem.rake +23 -0
- data/tasks/gem_installer.rake +76 -0
- data/tasks/install_dependencies.rake +6 -0
- data/tasks/manifest.rake +4 -0
- data/tasks/rcov.rake +23 -0
- data/tasks/release.rake +52 -0
- data/tasks/reversion.rake +8 -0
- data/tasks/setup.rake +15 -0
- data/tasks/yard.rake +4 -0
- metadata +82 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
module Mailit
|
2
|
+
# The Mailer is an abstraction layer for different SMTP clients, it provides
|
3
|
+
# #send and #defer_send methods
|
4
|
+
#
|
5
|
+
# At the time of writing, Net::SMTP and EventMachine::Protocols::SmtpClient are
|
6
|
+
# supported, it should be trivial to add support for any other client.
|
7
|
+
#
|
8
|
+
# The difference between #send and #defer_send depends on the backend, but
|
9
|
+
# usually #send will block until the mail was sent while #defer_send does it
|
10
|
+
# in the background and allows you to continue execution immediately.
|
11
|
+
#
|
12
|
+
# @example Usage
|
13
|
+
#
|
14
|
+
# mail = Mailit::Mail.new
|
15
|
+
# mail.to = 'test@test.com'
|
16
|
+
# mail.from = 'sender@sender.com'
|
17
|
+
# mail.subject 'Here are some files for you!'
|
18
|
+
# mail.text = 'This is what you see with a plaintext mail reader'
|
19
|
+
# mail.attach('/home/manveru/.vimrc')
|
20
|
+
#
|
21
|
+
# # Send and wait until sending finished
|
22
|
+
# Mailit::Mailer.send(mail)
|
23
|
+
#
|
24
|
+
# # Send in background thread and continue doing other things
|
25
|
+
# Mailit::Mailer.defer_send(mail)
|
26
|
+
#
|
27
|
+
# The default Mailer backend is Net::SMTP, you can change the
|
28
|
+
# default by including another module into Mailit::mailer
|
29
|
+
#
|
30
|
+
# @example Using Mailt::Mailer::EventMachine by inclusion
|
31
|
+
#
|
32
|
+
# class Mailit::Mailer
|
33
|
+
# include Mailit::Mailer::EventMachine
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
class Mailer
|
37
|
+
OPTIONS = {
|
38
|
+
:server => 'smtp.localhost',
|
39
|
+
:port => 25,
|
40
|
+
:domain => 'localhost',
|
41
|
+
:username => 'foo',
|
42
|
+
:password => 'foo',
|
43
|
+
:noop => false,
|
44
|
+
:auth_type => :login, # :plain, :login, :cram_md5
|
45
|
+
:starttls => false, # only useful for EventMachine::SmtpClient
|
46
|
+
}
|
47
|
+
|
48
|
+
def self.send(mail, options = {})
|
49
|
+
new.send(mail, options)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.defer_send(mail, options = {})
|
53
|
+
new.defer_send(mail, options)
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_reader :options
|
57
|
+
|
58
|
+
def initialize(options = {})
|
59
|
+
@options = OPTIONS.merge(options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def send(mail, override = {})
|
63
|
+
require 'net/smtp'
|
64
|
+
|
65
|
+
server, port, domain, username, password, auth_type, noop =
|
66
|
+
settings(override, :server, :port, :domain, :username, :password, :auth_type, :noop)
|
67
|
+
|
68
|
+
::Net::SMTP.start(server, port, domain, username, password, auth_type) do |smtp|
|
69
|
+
return if noop
|
70
|
+
smtp.send_message(mail.to_s, mail.from, mail.to)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def defer_send(mail, override = {})
|
75
|
+
Thread.new{ send(mail, override) }
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def settings(override, *keys)
|
81
|
+
options.merge(override).values_at(*keys)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Allows you to comfortably use the EventMachine::Protocols::SmtpClient.
|
85
|
+
# In order to use it, you have to first include this module into Mailit::Mailer
|
86
|
+
module EventMachine
|
87
|
+
# This assumes that EventMachine was required and we are inside the
|
88
|
+
# EventMachine::run block.
|
89
|
+
#
|
90
|
+
# Since EM implements some parts of the mail building we'll have to
|
91
|
+
# deconstruct our mail a bit.
|
92
|
+
# On the upside, it seems like it supports STARTTLS (without certificate
|
93
|
+
# options though)
|
94
|
+
def self.included(base)
|
95
|
+
require 'em/protocols/smtpclient'
|
96
|
+
base.module_eval do
|
97
|
+
def send(mail, override = {})
|
98
|
+
server, port, domain, username, password, auth_type =
|
99
|
+
settings(override, :server, :port, :domain, :username, :password, :auth_type)
|
100
|
+
|
101
|
+
mail.construct # prepare headers and body
|
102
|
+
|
103
|
+
em_options = { :port => port, :host => server, :domain => domain,
|
104
|
+
:from => mail.from, :to => mail.to, :header => mail.header_string,
|
105
|
+
:body => mail.body_string }
|
106
|
+
|
107
|
+
if auth_type
|
108
|
+
em_options[:auth] = {
|
109
|
+
:type => auth_type, :username => username, :password => password }
|
110
|
+
end
|
111
|
+
|
112
|
+
email = EM::Protocols::SmtpClient.send(em_options)
|
113
|
+
email.callback &@callback if @callback
|
114
|
+
email.errback &@errback if @errback
|
115
|
+
end
|
116
|
+
|
117
|
+
def callback(proc=nil, &blk)
|
118
|
+
@callback = proc || blk
|
119
|
+
end
|
120
|
+
|
121
|
+
def errback(proc=nil, &blk)
|
122
|
+
@errback = proc || blk
|
123
|
+
end
|
124
|
+
|
125
|
+
alias defer_send send
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end # module EventMachine
|
130
|
+
end # class Mailer
|
131
|
+
end # module Mailit
|
data/lib/mailit/mime.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Mailit
|
2
|
+
module Mime
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def mime_for(filename)
|
6
|
+
detect_handler unless defined?(@mime_handler)
|
7
|
+
send(@mime_handler, filename)
|
8
|
+
end
|
9
|
+
|
10
|
+
def detect_handler
|
11
|
+
try_require('rubygems')
|
12
|
+
|
13
|
+
if try_require('mime/types')
|
14
|
+
@mime_handler = :from_mime_types
|
15
|
+
elsif try_require('rack') and try_require('rack/mime')
|
16
|
+
@mime_handler = :from_rack
|
17
|
+
else
|
18
|
+
require 'webrick/httputils'
|
19
|
+
@webrick_types = WEBrick::HTTPUtils::DefaultMimeTypes.dup
|
20
|
+
try_extend_webrick('/etc/mime.types')
|
21
|
+
@mime_handler = :from_webrick
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def from_mime_types(filename)
|
26
|
+
MIME::Types.type_for(filename) || 'application/octet-stream'
|
27
|
+
end
|
28
|
+
|
29
|
+
def from_rack(filename)
|
30
|
+
Rack::Mime.mime_type(File.extname(filename))
|
31
|
+
end
|
32
|
+
|
33
|
+
def from_webrick(filename)
|
34
|
+
WEBrick::HTTPUtils.mime_type(filename, @webrick_types)
|
35
|
+
end
|
36
|
+
|
37
|
+
def try_extend_webrick(file)
|
38
|
+
hash = WEBrick::HTTPUtils.load_mime_types(file)
|
39
|
+
@webrick_types.merge!(hash)
|
40
|
+
rescue
|
41
|
+
end
|
42
|
+
|
43
|
+
def try_require(lib)
|
44
|
+
require lib
|
45
|
+
true
|
46
|
+
rescue LoadError
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/version.rb
ADDED
data/mailit.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{mailit}
|
5
|
+
s.version = "2009.08"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Kevin Berry"]
|
9
|
+
s.date = %q{2009-08-25}
|
10
|
+
s.description = %q{The Mailit library, by Kevin Berry}
|
11
|
+
s.email = %q{kevinberry@nrs.us}
|
12
|
+
s.files = ["AUTHORS", "CHANGELOG", "MANIFEST", "README.md", "Rakefile", "lib/mailit.rb", "lib/mailit/mail.rb", "lib/mailit/mailer.rb", "lib/mailit/mime.rb", "lib/mailit/version.rb", "lib/version.rb", "mailit.gemspec", "spec/helper.rb", "spec/mailit/mail.rb", "spec/mailit/mailer.rb", "tasks/authors.rake", "tasks/bacon.rake", "tasks/changelog.rake", "tasks/copyright.rake", "tasks/gem.rake", "tasks/gem_installer.rake", "tasks/install_dependencies.rake", "tasks/manifest.rake", "tasks/rcov.rake", "tasks/release.rake", "tasks/reversion.rake", "tasks/setup.rake", "tasks/yard.rake"]
|
13
|
+
s.homepage = %q{http://github.com/manveru/mailit}
|
14
|
+
s.require_paths = ["lib"]
|
15
|
+
s.rubygems_version = %q{1.3.4}
|
16
|
+
s.summary = %q{The Mailit library, by Kevin Berry}
|
17
|
+
|
18
|
+
if s.respond_to? :specification_version then
|
19
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
20
|
+
s.specification_version = 3
|
21
|
+
|
22
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
23
|
+
else
|
24
|
+
end
|
25
|
+
else
|
26
|
+
end
|
27
|
+
end
|
data/spec/helper.rb
ADDED
data/spec/mailit/mail.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# Encoding: UTF-8
|
2
|
+
require 'spec/helper'
|
3
|
+
|
4
|
+
# The specs are translated from the Test::Unit tests of MailFactory.
|
5
|
+
#
|
6
|
+
# TODO:
|
7
|
+
# * test_attach_as
|
8
|
+
# * test_email
|
9
|
+
|
10
|
+
describe Mailit::Mail do
|
11
|
+
should 'set and get headers' do
|
12
|
+
mail = Mailit::Mail.new
|
13
|
+
|
14
|
+
mail.set_header('arbitrary', 'some value')
|
15
|
+
mail.get_header('arbitrary').should == ['some value']
|
16
|
+
|
17
|
+
mail.set_header('arbitrary-header', 'some _ value')
|
18
|
+
mail.get_header('arbitrary-header').should == ['some _ value']
|
19
|
+
end
|
20
|
+
|
21
|
+
should 'generate valid boundaries' do
|
22
|
+
50.times do
|
23
|
+
boundary = Mailit::Mail.generate_boundary
|
24
|
+
boundary.should =~ /^----=_NextPart_[a-zA-Z0-9_.]{25}$/
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
should 'make mail with recipient' do
|
29
|
+
mail = Mailit::Mail.new
|
30
|
+
|
31
|
+
mail.to = 'test@test.com'
|
32
|
+
mail.to.should == 'test@test.com'
|
33
|
+
|
34
|
+
mail.to = 'test@test2.com'
|
35
|
+
mail.to.should == 'test@test2.com'
|
36
|
+
|
37
|
+
mail.headers.size.should == 1 # make sure the previous was deleted
|
38
|
+
end
|
39
|
+
|
40
|
+
should 'make mail with sender' do
|
41
|
+
mail = Mailit::Mail.new
|
42
|
+
|
43
|
+
mail.from = 'test@test.com'
|
44
|
+
mail.from.should == 'test@test.com'
|
45
|
+
|
46
|
+
mail.from = 'test@test2.com'
|
47
|
+
mail.from.should == 'test@test2.com'
|
48
|
+
|
49
|
+
mail.headers.size.should == 1 # make sure the previous was deleted
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'set correct subject' do
|
53
|
+
mail = Mailit::Mail.new
|
54
|
+
|
55
|
+
mail.subject = 'Test Subject'
|
56
|
+
mail.subject.should == '=?utf-8?Q?Test_Subject?='
|
57
|
+
|
58
|
+
mail.subject = 'A Different Subject'
|
59
|
+
mail.subject.should == '=?utf-8?Q?A_Different_Subject?='
|
60
|
+
|
61
|
+
mail.headers.size.should == 1 # make sure the previous was deleted
|
62
|
+
end
|
63
|
+
|
64
|
+
should 'use quoted printable with instruction' do
|
65
|
+
mail = Mailit::Mail.new
|
66
|
+
|
67
|
+
mail.to = 'test@test.com'
|
68
|
+
mail.from = 'test@othertest.com'
|
69
|
+
mail.subject = "My email subject has a ? in it and also an = and a _ too... Also some non-quoted junk ()!@\#\{\$\%\}"
|
70
|
+
mail.text = "This is a test message with\na few\n\nlines."
|
71
|
+
|
72
|
+
mail.subject.should == "=?utf-8?Q?My_email_subject_has_a_=3F_in_it_and_also_an_=3D_and_a_=5F_too..._Also_some_non-quoted_junk_()!@\#\{\$\%\}?="
|
73
|
+
end
|
74
|
+
|
75
|
+
should 'use subject quoting for scandinavian string' do
|
76
|
+
mail = Mailit::Mail.new
|
77
|
+
|
78
|
+
mail.to = "test@test.com"
|
79
|
+
mail.from = "test@othertest.com"
|
80
|
+
# Three a with dots and three o with dots.
|
81
|
+
mail.subject = "\303\244\303\244\303\244\303\266\303\266\303\266"
|
82
|
+
mail.text = "This is a test message with\na few\n\nlines."
|
83
|
+
|
84
|
+
mail.subject.should == "=?utf-8?Q?=C3=A4=C3=A4=C3=A4=C3=B6=C3=B6=C3=B6?="
|
85
|
+
end
|
86
|
+
|
87
|
+
should 'use subject quoting for utf-8 string' do
|
88
|
+
mail = Mailit::Mail.new
|
89
|
+
|
90
|
+
mail.to = "test@test.com"
|
91
|
+
mail.from = "test@othertest.com"
|
92
|
+
mail.subject = "My email subject has a à which is utf8."
|
93
|
+
mail.text = "This is a test message with\na few\n\nlines."
|
94
|
+
|
95
|
+
mail.subject.should == "=?utf-8?Q?My_email_subject_has_a_=C3=83_which_is_utf8.?="
|
96
|
+
end
|
97
|
+
|
98
|
+
should 'encode html as quoted printable' do
|
99
|
+
mail = Mailit::Mail.new
|
100
|
+
|
101
|
+
mail.to = "test@test.com"
|
102
|
+
mail.from = "test@othertest.com"
|
103
|
+
mail.subject = "some html"
|
104
|
+
mail.html = "<a href=\"http://google.com\">click here</a>"
|
105
|
+
|
106
|
+
mail.to_s.should.include('<a href=3D"http://google.com">click here</a>')
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec/helper'
|
2
|
+
|
3
|
+
class MockSMTP
|
4
|
+
INSTANCES = []
|
5
|
+
|
6
|
+
def self.start(*args, &block)
|
7
|
+
INSTANCES << new(*args, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :start_args, :result, :send_message_args
|
11
|
+
|
12
|
+
def initialize(*args, &block)
|
13
|
+
@start_args = args
|
14
|
+
yield(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_message(*args)
|
18
|
+
@send_message_args = args
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Mailit::Mail::OPTIONS[:message_id] = lambda{|mail| '1234' }
|
23
|
+
|
24
|
+
describe Mailit::Mailer do
|
25
|
+
it 'sends a mail' do
|
26
|
+
mail = Mailit::Mail.new(
|
27
|
+
:to => 'test@example.com',
|
28
|
+
:from => 'sender@example.com',
|
29
|
+
:subject => 'Here are some files for you!',
|
30
|
+
:text => 'Some text about that')
|
31
|
+
|
32
|
+
mailer = Mailit::Mailer.new
|
33
|
+
|
34
|
+
mailer.send(mail, :server => 'smtp.example.com', :port => 25,
|
35
|
+
:domain => 'example.com', :password => 'foo',
|
36
|
+
:mailer => MockSMTP)
|
37
|
+
|
38
|
+
mock = MockSMTP::INSTANCES.last
|
39
|
+
mock.start_args.should == [
|
40
|
+
'smtp.example.com', 25,
|
41
|
+
'example.com',
|
42
|
+
'sender@example.com',
|
43
|
+
'foo',
|
44
|
+
:cram_md5
|
45
|
+
]
|
46
|
+
mock.send_message_args.should == [mail.to_s, mail.from, mail.to]
|
47
|
+
end
|
48
|
+
end
|
data/tasks/authors.rake
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Once git has a fix for the glibc in handling .mailmap and another fix for
|
2
|
+
# allowing empty mail address to be mapped in .mailmap we won't have to handle
|
3
|
+
# them manually.
|
4
|
+
|
5
|
+
desc 'Update AUTHORS'
|
6
|
+
task :authors do
|
7
|
+
authors = Hash.new(0)
|
8
|
+
|
9
|
+
`git shortlog -nse`.scan(/(\d+)\s(.+)\s<(.*)>$/) do |count, name, email|
|
10
|
+
# Examples of mappping, replace with your own or comment this out/delete it
|
11
|
+
case name
|
12
|
+
when /^(?:bougyman$|TJ Vanderpoel)/
|
13
|
+
name, email = "TJ Vanderpoel", "tj@rubyists.com"
|
14
|
+
when /^(?:manveru$|Michael Fellinger)/
|
15
|
+
name, email = "Michael Fellinger", "mf@rubyists.com"
|
16
|
+
when /^(?:deathsyn$|Kevin Berry)/
|
17
|
+
name, email = "Kevin Berry", "kb@rubyists.com"
|
18
|
+
when /^(?:(?:jayson|thedonvaughn|jvaughn)$|Jayson Vaughn)/
|
19
|
+
name, email = "Jayson Vaughn", "jv@rubyists.com"
|
20
|
+
end
|
21
|
+
|
22
|
+
authors[[name, email]] += count.to_i
|
23
|
+
end
|
24
|
+
|
25
|
+
File.open('AUTHORS', 'w+') do |io|
|
26
|
+
io.puts "Following persons have contributed to #{GEMSPEC.name}."
|
27
|
+
io.puts '(Sorted by number of submitted patches, then alphabetically)'
|
28
|
+
io.puts ''
|
29
|
+
authors.sort_by{|(n,e),c| [-c, n.downcase] }.each do |(name, email), count|
|
30
|
+
io.puts("%6d %s <%s>" % [count, name, email])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/tasks/bacon.rake
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
desc 'Run all bacon specs with pretty output'
|
2
|
+
task :bacon => :install_dependencies do
|
3
|
+
require 'open3'
|
4
|
+
require 'scanf'
|
5
|
+
require 'matrix'
|
6
|
+
|
7
|
+
specs = PROJECT_SPECS
|
8
|
+
|
9
|
+
some_failed = false
|
10
|
+
specs_size = specs.size
|
11
|
+
if specs.size == 0
|
12
|
+
$stderr.puts "You have no specs! Put a spec in spec/ before running this task"
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
len = specs.map{|s| s.size }.sort.last
|
16
|
+
total_tests = total_assertions = total_failures = total_errors = 0
|
17
|
+
totals = Vector[0, 0, 0, 0]
|
18
|
+
|
19
|
+
red, yellow, green = "\e[31m%s\e[0m", "\e[33m%s\e[0m", "\e[32m%s\e[0m"
|
20
|
+
left_format = "%4d/%d: %-#{len + 11}s"
|
21
|
+
spec_format = "%d specifications (%d requirements), %d failures, %d errors"
|
22
|
+
|
23
|
+
specs.each_with_index do |spec, idx|
|
24
|
+
print(left_format % [idx + 1, specs_size, spec])
|
25
|
+
|
26
|
+
Open3.popen3(RUBY, spec) do |sin, sout, serr|
|
27
|
+
out = sout.read.strip
|
28
|
+
err = serr.read.strip
|
29
|
+
|
30
|
+
# this is conventional, see spec/innate/state/fiber.rb for usage
|
31
|
+
if out =~ /^Bacon::Error: (needed .*)/
|
32
|
+
puts(yellow % ("%6s %s" % ['', $1]))
|
33
|
+
else
|
34
|
+
total = nil
|
35
|
+
|
36
|
+
out.each_line do |line|
|
37
|
+
scanned = line.scanf(spec_format)
|
38
|
+
|
39
|
+
next unless scanned.size == 4
|
40
|
+
|
41
|
+
total = Vector[*scanned]
|
42
|
+
break
|
43
|
+
end
|
44
|
+
|
45
|
+
if total
|
46
|
+
totals += total
|
47
|
+
tests, assertions, failures, errors = total_array = total.to_a
|
48
|
+
|
49
|
+
if tests > 0 && failures + errors == 0
|
50
|
+
puts((green % "%6d passed") % tests)
|
51
|
+
else
|
52
|
+
some_failed = true
|
53
|
+
puts(red % " failed")
|
54
|
+
puts out unless out.empty?
|
55
|
+
puts err unless err.empty?
|
56
|
+
end
|
57
|
+
else
|
58
|
+
some_failed = true
|
59
|
+
puts(red % " failed")
|
60
|
+
puts out unless out.empty?
|
61
|
+
puts err unless err.empty?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
total_color = some_failed ? red : green
|
68
|
+
puts(total_color % (spec_format % totals.to_a))
|
69
|
+
exit 1 if some_failed
|
70
|
+
end
|