Ruby Sending Email - SMTP
In modern applications, sending emails is a common requirement, such as user registration confirmation, password reset, notifications, etc. Ruby provides multiple ways to send emails, with the most common being through the SMTP (Simple Mail Transfer Protocol) protocol. This chapter will explain in detail how to send emails using SMTP in Ruby.
🎯 SMTP Basics
What is SMTP
SMTP (Simple Mail Transfer Protocol) is the standard protocol for sending emails. It defines how mail servers and clients transfer emails between each other. In Ruby, we can use the built-in [Net::SMTP](file:///D:/Workspace/Coding/VueProjects/tutorials-web/docs/ruby/../../../../../../Ruby30-x64/lib/ruby/3.0.0/net/smtp.rb#L89-L774) library to send emails.
How SMTP Works
The basic process of sending emails via SMTP:
- Connect to SMTP server
- Authenticate (if required)
- Specify sender and recipient
- Send email content
- Close connection
📧 Using Net::SMTP to Send Emails
Basic Email Sending
require 'net/smtp'
# SMTP server configuration
smtp_server = 'smtp.gmail.com'
port = 587
username = 'your_email@gmail.com'
password = 'your_password'
# Email content
from = 'your_email@gmail.com'
to = 'recipient@example.com'
subject = 'Test Email'
body = "This is a test email\n\nFrom Ruby application"
# Create email message
message = <<~MESSAGE
From: #{from}
To: #{to}
Subject: #{subject}
#{body}
MESSAGE
# Send email
Net::SMTP.start(smtp_server, port, 'localhost', username, password, :plain) do |smtp|
smtp.send_message(message, from, to)
end
puts "Email sent successfully!"Sending HTML Emails
require 'net/smtp'
def send_html_email(smtp_server, port, username, password, from, to, subject, html_body)
# Create HTML email message
message = <<~MESSAGE
From: #{from}
To: #{to}
Subject: #{subject}
Content-Type: text/html; charset=UTF-8
#{html_body}
MESSAGE
# Send email
Net::SMTP.start(smtp_server, port, 'localhost', username, password, :plain) do |smtp|
smtp.send_message(message, from, to)
end
end
# Usage example
smtp_server = 'smtp.gmail.com'
port = 587
username = 'your_email@gmail.com'
password = 'your_password'
from = 'your_email@gmail.com'
to = 'recipient@example.com'
subject = 'HTML Test Email'
html_body = <<~HTML
<html>
<body>
<h1>Welcome to our service</h1>
<p>This is an HTML formatted email</p>
<ul>
<li>Feature 1</li>
<li>Feature 2</li>
<li>Feature 3</li>
</ul>
<p>Thank you for using our service!</p>
</body>
</html>
HTML
send_html_email(smtp_server, port, username, password, from, to, subject, html_body)
puts "HTML email sent successfully!"Sending Emails with Attachments
require 'net/smtp'
require 'base64'
def send_email_with_attachment(smtp_server, port, username, password, from, to, subject, body, attachment_path)
# Read attachment
filename = File.basename(attachment_path)
file_content = File.read(attachment_path, mode: 'rb')
encoded_content = Base64.encode64(file_content).gsub(/\n/, "\n")
# Create email with attachment
boundary = "----=_NextPart_#{Time.now.to_i}_#{rand(1000000)}"
message = <<~MESSAGE
From: #{from}
To: #{to}
Subject: #{subject}
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="#{boundary}"
--#{boundary}
Content-Type: text/plain; charset=UTF-8
#{body}
--#{boundary}
Content-Type: application/octet-stream; name="#{filename}"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="#{filename}"
#{encoded_content}
--#{boundary}--
MESSAGE
# Send email
Net::SMTP.start(smtp_server, port, 'localhost', username, password, :plain) do |smtp|
smtp.send_message(message, from, to)
end
end
# Usage example
smtp_server = 'smtp.gmail.com'
port = 587
username = 'your_email@gmail.com'
password = 'your_password'
from = 'your_email@gmail.com'
to = 'recipient@example.com'
subject = 'Email with Attachment'
body = "This is an email with attachment\n\nPlease check the attachment."
attachment_path = 'path/to/your/file.pdf'
# send_email_with_attachment(smtp_server, port, username, password, from, to, subject, body, attachment_path)
puts "Email with attachment sent successfully!"🛠️ Email Configuration Management
Configuration Class
class EmailConfig
attr_accessor :smtp_server, :port, :username, :password, :domain
def initialize(smtp_server, port, username, password, domain = 'localhost')
@smtp_server = smtp_server
@port = port
@username = username
@password = password
@domain = domain
end
# Common email service provider configurations
def self.gmail(username, password)
new('smtp.gmail.com', 587, username, password, 'localhost')
end
def self.outlook(username, password)
new('smtp-mail.outlook.com', 587, username, password, 'localhost')
end
def self.yahoo(username, password)
new('smtp.mail.yahoo.com', 587, username, password, 'localhost')
end
def self.qq(username, password)
new('smtp.qq.com', 587, username, password, 'localhost')
end
end
# Usage example
gmail_config = EmailConfig.gmail('your_email@gmail.com', 'your_password')
puts gmail_config.smtp_server # smtp.gmail.com
puts gmail_config.port # 587Email Sender Class
require 'net/smtp'
require 'base64'
class EmailSender
def initialize(config)
@config = config
end
def send_email(to, subject, body, options = {})
from = options[:from] || @config.username
content_type = options[:content_type] || 'text/plain'
attachments = options[:attachments] || []
message = build_message(from, to, subject, body, content_type, attachments)
Net::SMTP.start(
@config.smtp_server,
@config.port,
@config.domain,
@config.username,
@config.password,
:plain
) do |smtp|
smtp.send_message(message, from, to)
end
end
private
def build_message(from, to, subject, body, content_type, attachments)
if attachments.empty?
build_simple_message(from, to, subject, body, content_type)
else
build_multipart_message(from, to, subject, body, content_type, attachments)
end
end
def build_simple_message(from, to, subject, body, content_type)
<<~MESSAGE
From: #{from}
To: #{to}
Subject: #{subject}
Content-Type: #{content_type}; charset=UTF-8
#{body}
MESSAGE
end
def build_multipart_message(from, to, subject, body, content_type, attachments)
boundary = "----=_NextPart_#{Time.now.to_i}_#{rand(1000000)}"
message = <<~MESSAGE
From: #{from}
To: #{to}
Subject: #{subject}
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="#{boundary}"
--#{boundary}
Content-Type: #{content_type}; charset=UTF-8
#{body}
MESSAGE
attachments.each do |attachment_path|
filename = File.basename(attachment_path)
file_content = File.read(attachment_path, mode: 'rb')
encoded_content = Base64.encode64(file_content).gsub(/\n/, "\n")
message += <<~ATTACHMENT
--#{boundary}
Content-Type: application/octet-stream; name="#{filename}"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="#{filename}"
#{encoded_content}
ATTACHMENT
end
message + "\n--#{boundary}--"
end
end
# Usage example
config = EmailConfig.gmail('your_email@gmail.com', 'your_password')
sender = EmailSender.new(config)
# Send simple email
sender.send_email(
'recipient@example.com',
'Test Email',
'This is a test email'
)
# Send HTML email
sender.send_email(
'recipient@example.com',
'HTML Test',
'<h1>Hello</h1><p>This is HTML</p>',
content_type: 'text/html'
)
# Send email with attachment
sender.send_email(
'recipient@example.com',
'With Attachment',
'Please check the attachment',
attachments: ['/path/to/file.pdf']
)🔒 Email Sending Best Practices
1. Use Environment Variables for Credentials
class SecureEmailConfig
def self.smtp_settings
{
address: ENV['SMTP_ADDRESS'] || 'smtp.gmail.com',
port: ENV['SMTP_PORT'] || 587,
username: ENV['SMTP_USERNAME'],
password: ENV['SMTP_PASSWORD'],
authentication: :plain,
enable_starttls_auto: true
}
end
end2. Handle Failures Gracefully
class ReliableEmailSender
def initialize(config)
@config = config
@max_retries = 3
end
def send_with_retry(to, subject, body, options = {})
retries = 0
begin
send_email(to, subject, body, options)
true
rescue => e
retries += 1
if retries < @max_retries
sleep(2 ** retries) # Exponential backoff
retry
else
log_failure(to, subject, e)
false
end
end
end
private
def send_email(to, subject, body, options)
# Email sending logic
end
def log_failure(to, subject, error)
# Log the failure
end
end3. Template-Based Emails
class TemplateEmailSender
TEMPLATES = {
welcome: {
subject: 'Welcome to Our Service!',
html_template: '<h1>Welcome, %s!</h1><p>Thank you for joining us.</p>'
},
password_reset: {
subject: 'Password Reset Request',
html_template: '<h1>Password Reset</h1><p>Click <a href="%s">here</a> to reset your password.</p>'
}
}
def send_template_email(template_name, to, template_params = {})
template = TEMPLATES[template_name]
return unless template
subject = template[:subject]
html_body = sprintf(template[:html_template], *template_params.values)
EmailSender.new(@config).send_email(to, subject, html_body, content_type: 'text/html')
end
end📚 Next Steps
After mastering Ruby SMTP email sending, we recommend continuing to learn:
- Ruby Socket Programming - Learn network programming
- Ruby Web Services - Learn web service development
- Ruby CGI Programming - Understand web application basics
Continue your Ruby learning journey!