String substitution and fixing the send email with csv code

July 22, 2014

I am busy learning Ruby each day I write some Ruby no matter what. Well, except on Sundays I don’t write code on Sundays just because I need to rest my brain. Yesterday I wrote this bit of code and to tell you the truth I didn’t like it one bit.

require 'net/smtp'

addresses = File.read('addresses.csv').split(',').map(&:strip)

from = "example@example.com"

smtp = Net::SMTP.new('smtp.gmail.com', 587)
smtp.enable_starttls

smtp.start(Socket.gethostname, "#{from}", 'a_password', :login) do |smtp|

	addresses.each do |to|
		message = <<EOM

From: Someone <#{from}>
To: Someone2 <#{to}>
Subject: This is from Ruby
Date: #{Time.now.strftime("%d/%m/%Y %H:%M")}

This email was sent using Ruby
EOM

	
		begin
			smtp.send_message(message, "#{from}", "#{to}")
		rescue Exception => e
			puts e
		end
	end
end

So tell me what is wrong with this code? The biggest problem I see is reassigning to the message variable in the each loop (I am sure there is much more wrong, but right now this is the most obvious).

When sending 1 or 2 emails, this might not be a big issue but when you start sending hundreds or thousands, I am pretty sure there will be some cost involved.

Today I did some digging and found that you can do string substitution using the ‘%’ symbol. So instead of using #{varible_name} you can use %{variable_name}

So now my code looks like this:

require 'net/smtp'

addresses = File.read('addresses.csv').split(',').map(&:strip)

from = "example@example.com"

message = <<EOM

From: Someone <#{from}>
To: Someone2 <%{to}>
Subject: This is from Ruby
Date: #{Time.now.strftime("%d/%m/%Y %H:%M")}

This email was sent using Ruby
EOM


smtp = Net::SMTP.new('smtp.gmail.com', 587)
smtp.enable_starttls

smtp.start(Socket.gethostname, "#{from}", 'a_password', :login) do |smtp|
	addresses.each do |to|	
		begin
			smtp.send_message(message % {to: to}, "#{from}", "#{to}")
		rescue Exception => e
			puts e
		end
	end
end

So rule of thumb is if you want to reuse a string template by assigning it to a variable and change items within the template in a loop. Or if you want to pass a string template around you should do so using the ‘%’ symbol for string substitution instead.

There are 2 ways to do string substitution using the ‘%’ symbol. You can use an array like below:

message = "Hello %s, welcome to %s."
puts message % ['Guy', 'The Boom Boom Room']

Where ‘%s’ is for a string, ‘%f’ would be for float and ‘%d’ is for integer.

The second way and my preference is using a Hash. I prefer this as you can use named placements and there is no need to worry about potential data type errors.

message = "Hello %{name}, welcome to %{address}."
puts message % {name: 'Guy', address:'The Boom Boom Room'}

Pretty sweet right?


Discussion, links, and tweets

My name is Deon Heyns and I am a developer learning things and documenting them in realtime. Python, Ruby, Scala, .NET, and Groovy are all languages I have written code in. I appeared in the New York Post once. I host my code up at GitHub and Bitbucket so have a look at my code, fork it and send those pull requests.

comments powered by Disqus