mailit 2009.08
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/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
|