├── spec ├── base.rb └── pony_spec.rb ├── README.rdoc ├── lib └── pony.rb └── Rakefile /spec/base.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'spec' 3 | 4 | require File.dirname(__FILE__) + '/../lib/pony' 5 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | 2 | This fork is no longer maintained. Please visit: http://github.com/benprew/pony 3 | 4 | -------------------------------------------------------- 5 | 6 | = Pony, the express way to send email in Ruby 7 | 8 | == Overview 9 | 10 | Ruby no longer has to be jealous of PHP's mail() function, which can send an email in a single command. 11 | 12 | Pony.mail(:to => 'you@example.com', :from => 'me@example.com', :subject => 'hi', :body => 'Hello there.') 13 | 14 | Any option key may be omitted except for :to. 15 | 16 | == Transport 17 | 18 | Pony uses /usr/sbin/sendmail to send mail if it is available, otherwise it uses SMTP to localhost. 19 | 20 | This can be over-ridden if you specify a via option 21 | 22 | Pony.mail(:to => 'you@example.com', :via => :smtp) # sends via SMTP 23 | 24 | Pony.mail(:to => 'you@example.com', :via => :sendmail) # sends via sendmail 25 | 26 | You can also specify options for SMTP: 27 | 28 | Pony.mail(:to => 'you@example.com', :via => :smtp, :smtp => { 29 | :host => 'smtp.yourserver.com', 30 | :port => '25', 31 | :user => 'user', 32 | :password => 'pass', 33 | :auth => :plain, # :plain, :login, :cram_md5, no auth by default 34 | :domain => "example.com" # the HELO domain provided by the client to the server 35 | } 36 | 37 | == Meta 38 | 39 | Written by Adam Wiggins 40 | 41 | Patches contributed by: Mathieu Martin, Arun Thampi, Thomas Hurst, Stephen 42 | Celis, Othmane Benkirane, and Neil Mock 43 | 44 | Released under the MIT License: http://www.opensource.org/licenses/mit-license.php 45 | 46 | http://github.com/adamwiggins/pony 47 | 48 | -------------------------------------------------------------------------------- /lib/pony.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'net/smtp' 3 | begin 4 | require 'tmail' 5 | rescue LoadError 6 | require 'actionmailer' 7 | end 8 | 9 | module Pony 10 | def self.mail(options) 11 | raise(ArgumentError, ":to is required") unless options[:to] 12 | 13 | via = options.delete(:via) 14 | if via.nil? 15 | transport build_tmail(options) 16 | else 17 | if via_options.include?(via.to_s) 18 | send("transport_via_#{via}", build_tmail(options), options) 19 | else 20 | raise(ArgumentError, ":via must be either smtp or sendmail") 21 | end 22 | end 23 | end 24 | 25 | def self.build_tmail(options) 26 | mail = TMail::Mail.new 27 | mail.to = options[:to] 28 | mail.from = options[:from] || 'pony@unknown' 29 | mail.subject = options[:subject] 30 | mail.body = options[:body] || "" 31 | mail 32 | end 33 | 34 | def self.sendmail_binary 35 | @sendmail_binary ||= `which sendmail`.chomp 36 | end 37 | 38 | def self.transport(tmail) 39 | if File.executable? sendmail_binary 40 | transport_via_sendmail(tmail) 41 | else 42 | transport_via_smtp(tmail) 43 | end 44 | end 45 | 46 | def self.via_options 47 | %w(sendmail smtp) 48 | end 49 | 50 | def self.transport_via_sendmail(tmail, options={}) 51 | IO.popen('-', 'w+') do |pipe| 52 | if pipe 53 | pipe.write(tmail.to_s) 54 | else 55 | exec(sendmail_binary, *tmail.to) 56 | end 57 | end 58 | end 59 | 60 | def self.transport_via_smtp(tmail, options={:smtp => {}}) 61 | default_options = {:smtp => { :host => 'localhost', :port => '25', :domain => 'localhost.localdomain' }} 62 | o = default_options[:smtp].merge(options[:smtp]) 63 | smtp = Net::SMTP.new(o[:host], o[:port]) 64 | if o.include?(:auth) 65 | smtp.start(o[:domain], o[:user], o[:password], o[:auth]) 66 | else 67 | smtp.start(o[:domain]) 68 | end 69 | smtp.send_message tmail.to_s, tmail.from, tmail.to 70 | smtp.finish 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'spec/rake/spectask' 3 | 4 | desc "Run all specs" 5 | Spec::Rake::SpecTask.new('spec') do |t| 6 | t.spec_files = FileList['spec/*_spec.rb'] 7 | end 8 | 9 | desc "Print specdocs" 10 | Spec::Rake::SpecTask.new(:doc) do |t| 11 | t.spec_opts = ["--format", "specdoc", "--dry-run"] 12 | t.spec_files = FileList['spec/*_spec.rb'] 13 | end 14 | 15 | desc "Run all examples with RCov" 16 | Spec::Rake::SpecTask.new('rcov') do |t| 17 | t.spec_files = FileList['spec/*_spec.rb'] 18 | t.rcov = true 19 | t.rcov_opts = ['--exclude', 'examples'] 20 | end 21 | 22 | task :default => :spec 23 | 24 | ###################################################### 25 | 26 | require 'rake' 27 | require 'rake/testtask' 28 | require 'rake/clean' 29 | require 'rake/gempackagetask' 30 | require 'fileutils' 31 | 32 | version = "0.3" 33 | name = "pony" 34 | 35 | spec = Gem::Specification.new do |s| 36 | s.name = name 37 | s.version = version 38 | s.summary = "Send email in one command: Pony.mail(:to => 'someone@example.com', :body => 'hello')" 39 | s.description = "Send email in one command: Pony.mail(:to => 'someone@example.com', :body => 'hello')" 40 | s.author = "Adam Wiggins" 41 | s.email = "adam@heroku.com" 42 | s.homepage = "http://github.com/adamwiggins/pony" 43 | s.rubyforge_project = "pony" 44 | 45 | s.platform = Gem::Platform::RUBY 46 | s.has_rdoc = false 47 | 48 | s.files = %w(Rakefile) + Dir.glob("{lib,spec}/**/*") 49 | 50 | s.require_path = "lib" 51 | s.add_dependency( 'tmail', '~> 1.0' ) 52 | end 53 | 54 | Rake::GemPackageTask.new(spec) do |p| 55 | p.need_tar = true if RUBY_PLATFORM !~ /mswin/ 56 | end 57 | 58 | task :install => [ :package ] do 59 | sh %{sudo gem install pkg/#{name}-#{version}.gem} 60 | end 61 | 62 | task :uninstall => [ :clean ] do 63 | sh %{sudo gem uninstall #{name}} 64 | end 65 | 66 | Rake::TestTask.new do |t| 67 | t.libs << "spec" 68 | t.test_files = FileList['spec/*_spec.rb'] 69 | t.verbose = true 70 | end 71 | 72 | CLEAN.include [ 'pkg', '*.gem', '.config' ] 73 | 74 | -------------------------------------------------------------------------------- /spec/pony_spec.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/base' 2 | 3 | describe Pony do 4 | it "sends mail" do 5 | Pony.should_receive(:transport) do |tmail| 6 | tmail.to.should == [ 'joe@example.com' ] 7 | tmail.from.should == [ 'sender@example.com' ] 8 | tmail.subject.should == 'hi' 9 | tmail.body.should == 'Hello, Joe.' 10 | end 11 | Pony.mail(:to => 'joe@example.com', :from => 'sender@example.com', :subject => 'hi', :body => 'Hello, Joe.') 12 | end 13 | 14 | it "requires :to param" do 15 | Pony.stub!(:transport) 16 | lambda { Pony.mail({}) }.should raise_error(ArgumentError) 17 | end 18 | 19 | it "doesn't require any other param" do 20 | Pony.stub!(:transport) 21 | lambda { Pony.mail(:to => 'joe@example.com') }.should_not raise_error 22 | end 23 | 24 | #################### 25 | 26 | describe "builds a TMail object with field:" do 27 | it "to" do 28 | Pony.build_tmail(:to => 'joe@example.com').to.should == [ 'joe@example.com' ] 29 | end 30 | 31 | it "from" do 32 | Pony.build_tmail(:from => 'joe@example.com').from.should == [ 'joe@example.com' ] 33 | end 34 | 35 | it "from (default)" do 36 | Pony.build_tmail({}).from.should == [ 'pony@unknown' ] 37 | end 38 | 39 | it "subject" do 40 | Pony.build_tmail(:subject => 'hello').subject.should == 'hello' 41 | end 42 | 43 | it "body" do 44 | Pony.build_tmail(:body => 'What do you know, Joe?').body.should == 'What do you know, Joe?' 45 | end 46 | end 47 | 48 | describe "transport" do 49 | it "transports via the sendmail binary if it exists" do 50 | File.stub!(:executable?).and_return(true) 51 | Pony.should_receive(:transport_via_sendmail).with(:tmail) 52 | Pony.transport(:tmail) 53 | end 54 | 55 | it "transports via smtp if no sendmail binary" do 56 | Pony.stub!(:sendmail_binary).and_return('/does/not/exist') 57 | Pony.should_receive(:transport_via_smtp).with(:tmail) 58 | Pony.transport(:tmail) 59 | end 60 | 61 | it "transports mail via /usr/sbin/sendmail binary" do 62 | pipe = mock('sendmail pipe') 63 | IO.should_receive(:popen).with('-',"w+").and_yield(pipe) 64 | pipe.should_receive(:write).with('message') 65 | Pony.transport_via_sendmail(mock('tmail', :to => 'to', :from => 'from', :to_s => 'message')) 66 | end 67 | 68 | describe "SMTP transport" do 69 | before do 70 | @smtp = mock('net::smtp object') 71 | @smtp.stub!(:start) 72 | @smtp.stub!(:send_message) 73 | @smtp.stub!(:finish) 74 | Net::SMTP.stub!(:new).and_return(@smtp) 75 | end 76 | 77 | it "defaults to localhost as the SMTP server" do 78 | Net::SMTP.should_receive(:new).with('localhost', '25').and_return(@smtp) 79 | Pony.transport_via_smtp(mock('tmail', :to => 'to', :from => 'from', :to_s => 'message')) 80 | end 81 | 82 | it "uses SMTP authorization when auth key is provided" do 83 | o = { :smtp => { :user => 'user', :password => 'password', :auth => 'plain'}} 84 | @smtp.should_receive(:start).with('localhost.localdomain', 'user', 'password', 'plain') 85 | Pony.transport_via_smtp(mock('tmail', :to => 'to', :from => 'from', :to_s => 'message'), o) 86 | end 87 | 88 | it "starts the job" do 89 | @smtp.should_receive(:start) 90 | Pony.transport_via_smtp(mock('tmail', :to => 'to', :from => 'from', :to_s => 'message')) 91 | end 92 | 93 | it "sends a tmail message" do 94 | @smtp.should_receive(:send_message) 95 | Pony.transport_via_smtp(mock('tmail', :to => 'to', :from => 'from', :to_s => 'message')) 96 | end 97 | 98 | it "finishes the job" do 99 | @smtp.should_receive(:finish) 100 | Pony.transport_via_smtp(mock('tmail', :to => 'to', :from => 'from', :to_s => 'message')) 101 | end 102 | 103 | end 104 | end 105 | 106 | describe ":via option should over-ride the default transport mechanism" do 107 | it "should send via sendmail if :via => sendmail" do 108 | Pony.should_receive(:transport_via_sendmail) 109 | Pony.mail(:to => 'joe@example.com', :via => :sendmail) 110 | end 111 | 112 | it "should send via smtp if :via => smtp" do 113 | Pony.should_receive(:transport_via_smtp) 114 | Pony.mail(:to => 'joe@example.com', :via => :smtp) 115 | end 116 | 117 | it "should raise an error if via is neither smtp nor sendmail" do 118 | lambda { Pony.mail(:to => 'joe@plumber.com', :via => :pigeon) }.should raise_error(ArgumentError) 119 | end 120 | end 121 | 122 | end 123 | --------------------------------------------------------------------------------