Skip to content

Instantly share code, notes, and snippets.

@andynu
Created September 11, 2024 13:46
Show Gist options
  • Save andynu/cd468b2799701c16bae322b82c10495a to your computer and use it in GitHub Desktop.
Save andynu/cd468b2799701c16bae322b82c10495a to your computer and use it in GitHub Desktop.
Email merge script in ruby
#!/usr/bin/env ruby
# Encoding: utf-8
#
# This takes a csv file of values, and a txt file as a template
# and sends out a pile of customized emails.
#
require 'csv'
require 'mail'
class EmailMerge
def initialize(data_file, template_file)
@data = parse_data(data_file)
@subject, @body = parse_template(template_file)
@from = "example@example.com"
@to_field = "email"
end
def from=(email)
@from = email
end
def to_field=(field_name)
@to_field = field_name
end
def to(data)
data[@to_field]
end
def recipients
@data.map{|data| data[@to_field] }
end
def preview(n=1)
@data[0..n-1].each do |data|
puts "-"*80
puts "Subject: "+ render(@subject, data)
puts "-"*80
puts render(@body, data)
puts "-"*80
end
end
def render(template, data)
text = template.dup
text.gsub(/{{(.+?)}}/) do |match|
var = match.gsub(/{{/,'').gsub(/}}/,'')
data[var]
end
end
def deliver_all!(opts={})
@data.each do |data|
mail(data, opts)
end
end
def deliver_n!(n=1, opts={})
@data[0..n-1].each do |data|
mail(data, opts)
end
end
# you can override :from, :to, :subject, :body, with opts
def mail(data, opts={})
mail = Mail.new
mail.delivery_method :sendmail
mail.charset = 'UTF-8'
mail.content_transfer_encoding = "8bit"
mail.content_type = 'text/html'
mail.from = opts[:from] || @from
mail.to = opts[:to] || data[@to_field]
mail.subject = opts[:subject] || self.render(@subject, data)
mail.body = opts[:body] || self.render(@body, data)
ok = mail.deliver!
p [ok, mail]
end
private
def parse_template(template_file)
text = File.readlines(template_file)
[
text.shift.gsub(/Subject:\s+/i,'').strip,
text.join.strip
]
end
# reads file, returns array of hashes based on the header
def parse_data(data_file)
data = []
headers = nil
CSV.open(data_file).each do |row|
if headers.nil?
headers = row
else
data << Hash[*headers.zip(row).flatten]
end
end
data
end
end
if $0 == __FILE__
base_dir = "data/campaign_20140314"
data_file = "#{base_dir}/data.csv"
template_file = "#{base_dir}/template.html"
em = EmailMerge.new(data_file, template_file)
em.from = "example@example.com"
em.to_field = "email"
testing = true
if testing
em.preview 1 # show a few
puts em.recipients # show were they'd go if we ran it for real
em.deliver_n!(n=1, to: 'example@example.com') # test deliver a few
end
# DO IT FOR REAL, CAREFUL! TEST FIRST
#em.deliver_all!(to: 'example@example.com')
#em.deliver_all!
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment