Sending Gmail Emails With Rails

As part of our meetup project, I needed to figure out how to send email via Rails. Our website allows a user to select a favorite photo and then receive a link to that photo via email.

Rails sends email through something called the Action Mailer. The Rails Guides explain how to set up Action Mailer, but I needed to take some additional steps that the documentation didn’t include. For instance, the guide explains how to send a welcome email after a user signs up, but we wanted to send an email later on – after the user selects a favorite photo. Here is how I got it working for Gmail.

As the guide points out, mailers are conceptually similar to controllers. We generate a mailer via the command line:

rails generate mailer UserMailer

This creates a file called app/mailers/user_mailer.rb. More on this file later.

In our project, the user enters an email address via a form. In the code below, the form_tag directs to the users/create action – the create method in the User controller – and the e-mail field passes an :email_address to the params. And since we want to send the user a photo URL, we pass the photo URL as a hidden field:

<%= form_tag("users/create", method: "get") do %>
<img src="<%=@picture_chosen.photo_url%>">
<div>
<%= hidden_field_tag(:photo_url, @picture_chosen.photo_url) %>
<%= email_field(:user, :email_address) %>
<%= submit_tag("Submit")%>
<% end %>
</div>

On submit, the data passes to the User controller’s create method:

class UsersController < ApplicationController

def create
  @user = User.new(user_params)
  respond_to do |format|
    @user.email_address = params[:user][:email_address]
    @user.photo_url = params[:photo_url]
    if @user.save
      UserMailer.result_email(@user).deliver_now
      format.html { redirect_to(root_path, notice: 'Favorite photo chosen') }
      format.json { render json: root_path, status: :created, location: @user }
    else
      format.html { render action: 'new' }
      format.json { render json: @user.errors, status: :unprocessable_entity }
    end
  end
end

private

def user_params
  params.require(:user).permit(user: [:email_address, :photo_url] )
end

end

The email address gets saved as @user.email_address, which we must add to the permitted user params. Upon @user.save, we get directed to:

UserMailer.result_email(@user).deliver_now

UserMailer is the mailer we created at the beginning via rails generate mailer UserMailer. (The deliver_now at the end sends the email immediately; there is also an option for deliver_later.)

So we're directed to the UserMailer, and we need to put the following code in there:

class UserMailer < ApplicationMailer
  def result_email(user)
    @user = user
    @url = user.photo_url
    mail(to: @user.email_address, subject: 'Your favorite photo')
  end
end

We're setting the individual user and the photo URL as objects so we can pass them, and the mail command specifies the email address and the email subject.

The UserMailer inherits from ApplicationMailer, which, like UserMailer, lives in the app/mailers/ folder. ApplicationMailer needs to contain the email address from which the email will be sent:

class ApplicationMailer < ActionMailer::Base
  default from: "email_sender@gmail.com"
  layout 'mailer'
end

(Replace "email_sender" with the actual sending account.)

To go along with the UserMailer, there is a corresponding user_mailer folder under app/views/. The user_mailer folder contains two files, result_email.html.erb and result_email.text.erb. The former sends an HTML-formatted email; the latter contains a text-formatted email, which will be sent if the user's email client doesn't accept HTML-formatted email.

Here's our HTML-formatted email in our result_email.html.erb file:

<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<h1>Hello!</h1>
<p>
Thanks for picking your favorite photo! Here is a link to the photo you chose as your favorite:
</p>
<p>
<%= @url %>
</p>
</body>
</html>

Note that since we created a @url object in the UserMailer, we can call it in the email above.

This sets everything up, but the email won't actually get sent unless we add the following code to the three files under config/environments/ -- development.rb, production.rb, and test.rb:

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.delivery_method = :smtp
config.action_mailer.raise_delivery_errors = true
config.action_mailer.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => 'localhost',
:user_name => ENV["gmail_username"],
:password => ENV["gmail_password"],
:authentication => :login,
:enable_starttls_auto => true
}

This works for Gmail. Other email clients will require slightly different settings.

Note that :user_name and :password are ENV keys, because these are the sender's username and password, and we don't want the password to be visible to others. We should also install the Figaro gem and require it in the Gemfile. This creates a file called config/application.yml, which gets added to the .gitignore file and will keep the email name and password hidden. Open this file and add the following (replacing the sample user name and password with the real ones):

gmail_username: "emailer_sender@gmail.com"
gmail_password: "123456789"

Make sure "gmail_username" and "gmail_password" are spelled the same in both the config/environments files and the config/application.yml file.

And we're done. Your application can now send email.

Refactoring Your Code After Adding a Feature

This has been project week at Flatiron, and while designing our websites we’ve been learning to follow the principle of minimum viable product, or MVP. This basically means “the smallest thing you can build that lets you quickly make it around the build/measure/learn loop” or “the smallest thing you can build that delivers customer value (and as a bonus captures some of that value back).”

The idea is to start by building a simple product that works before adding new features. Here’s the image we were shown in class that illustrates MVP:

how-to-build-a-minimum-viable-product

Our group decided to build a site that maps out public restrooms in New York City so users can easily find a place to answer nature’s call. Users can review existing restrooms and even add restrooms to the site’s database. In creating our site, we found it useful to follow the MVP principle, but this created some issues we had to fix later on.

There’s an existing database of public restrooms in New York City public parks, so we decided that our MVP was to build a map showing all these restrooms. Only after we’d built out this minimum product and had it working would we build the next feature: letting users add restrooms to the database.

mvp_boat
credit

When we built out our MVP, we created a table in our database called public_parks. We also had a PublicParks controller, a PublicParks model, PublicParks views, and PublicParks routes. We had a public_park_id as a foreign key in another of our tables, and we had public_parks objects throughout our code. This was all fine when we were working on our MVP, but it created problems when we expanded our project to let users add public restrooms in other types of venues such as restaurants and stores. Each of our parks had a “show” page with the path “/public_parks.” But this URL wouldn’t make sense for a restaurant or bookstore. And anyone looking at our code would be confused by a public parks table that also included stores and restaurants, or public_park objects that weren’t parks. What to do?

We realized we had a couple of options. One, we could create a new table called location types and refactor from there. The other option was just to give the public parks table, files, objects, and routes a new name. Which choice was better?

cookie
credit

We realized that, given the features we were planning, there was no need to create a separate table of location types. There was nothing that separated one type of location from another: whether the restroom was in a park, store, or restaurant, our users would be able to rate it on a scale from 1 to 10 and write a review. Therefore, we decided to rename our public parks table, controllers, views, models, and routes as restrooms. Had we wanted to add any features that applied only to stores or restaurants — such as operating hours — it might have made more sense to create separate location types. But since we were creating a fairly simple website, it made more sense to put every location in one table and not differentiate.

But we actually did add a column for location type to our parks table, which gets assigned when a user adds a restroom to the site. The user selects a choice from a dropdown table to categorize the restroom as a park, restaurant, or store. If, in the future, we decide to do different things for these different location types, we have a way to distinguish them without tediously going through all our locations and assigning them location types, one by one.

Depending on your MVP and the features you expect to add later, you’ll have to figure out the best way to structure your code from the start and what you’ll have to do to refactor your code later. As I go through my career and work on more complicated projects, I look forward to learning more about how to plan my code and refactor it. Revising code can be just as fun and challenging as writing it.

tree
credit

Back to the Future With Cron Jobs and Ruby Gems

It’s Back to the Future Day!

Back to the Future is my all-time favorite movie — and the sequels are a close second and third — so this is a pretty special day. To celebrate, I’ll discuss time travel!

Sort of. I’ll discuss cron jobs.

A cron job is a way to schedule tasks ahead of time in Unix-type command-line systems such as the Mac Terminal — for example, sending out an email, updating a file at the same time every day, and so on. Tasks can be scheduled for a particular time or at regular intervals — daily, weekly, etc.

I decided I wanted to post the following tweet on October 21, 2015, at 4:29 pm, to celebrate Marty McFly’s arrival from 1985:

Yes, it's October 21, 2015 at 4:29 pm...

...on the East Coast.

But Hill Valley is in California. Three hours to go.

#BackToTheFuture

I wanted to preschedule my tweet, so it could go out even if I’m away from my computer at the time (as long as my computer is running). First I tried some web apps, but I couldn’t get tweets to appear at the exact time I wanted. So I turned to the command line. Here’s how this works.

(1) Using the “t” Ruby gem

The first step is to install a Ruby gem called t, which provides command-line access to Twitter:

gem install t

After connecting your Twitter account to the gem by following the instructions on the gem’s GitHub page, you can tweet directly from the command line by typing

t update "[text of your tweet]"

The text of your tweet will immediately show up on Twitter as a tweet from your account.

But what if you want to pre-schedule a tweet? What if you want to run the above command not now, but later? You’ll need to set up a cron job.

(2) Setting up a cron job

This involves two steps: (a) setting up a file that contains your “t” command, and (b) setting up the actual cron job.

(a) Setting up the file containing your “t” command

The best place to create this file (or shell script) is in your home directory. To find out where this is, type:

echo $HOME

My home directory is “Users/[myname].”

You can go there directly by typing

cd $HOME

In your home directory, create a new file with a .sh extension, which is the extension for a shell script:

touch [filename].sh

Open this empty file in your text editor. At the top of the empty file, type

#!/bin/bash

(The exact path might be different on different computers, but this worked for me on a Mac.)

Next, you can type the “t” update command, but you have to preface the “t” with something. Ruby Version Manager (rvm) creates wrappers for gems, and you need to preface the “t” with the path for the wrappers. This link helped me figure that path out. For me, that path is /Users/jeffslutzky/.rvm/wrappers/ruby-2.2.3, so the full path to “t” is:

/Users/jeffslutzky/.rvm/wrappers/ruby-2.2.3/t

And therefore the full command is:

/Users/jeffslutzky/.rvm/wrappers/ruby-2.2.3/t update "[tweet text]"

So the full file is these two lines:

#!/bin/bash

/Users/jeffslutzky/.rvm/wrappers/ruby-2.2.3/t update "[tweet text]"

After creating the shell script, we have to change its permissions, by going back to the command line and typing

chmod +x [filename].sh

After creating the shell script and setting its permissions, the next step is to set up the cron job.

(b) Setting up the cron job

We’re actually setting up a crontab, which is the text file that contains the schedule of tasks. I was having problems using my default text editor, Sublime, for crontabs, so I had to set my default crontab editor to Nano by typing this on the command line:

export VISUAL=nano; crontab -e

After that, open the crontab file by typing:

crontab -e

This file requires one line containing two things: a series of five numbers corresponding to the time we want the task to run, and the path to the shell script we just wrote.

The numbers come in this order: minutes, hours (in 24-hour format), days of month, months, days of week. I don’t care about the day of the week, so I can replace that with an asterisk (*). For example, October 21 at 4:29 pm (i.e. 10/21 at 16:29) is:

29 16 21 10 *

That’s just 10/21 – 16:29 in reverse order.

The path to my shell script is:

/Users/jeffslutzky/bttf.sh

So, adding this to the time, the full line to type in for this task is:

29 16 21 10 * /Users/jeffslutzky/bttf.sh

Save the file. That’s it. The tweet is now pre-scheduled. We’re all done!

To see the contents of the crontab, type:

crontab -l

To edit it again, type:

crontab -e

You can even add other tasks to the crontab file by adding a new line with a new time and shell script path.

I’ve actually pre-scheduled two tweets. If my calculations are correct, when this baby hits 88 miles per hour, you’re gonna see some serious tweets (at twitter.com/tinmanic).

To recap:

(1) Create a file with an .sh extension containing a command-line action, and set the file permissions.

#!/bin/bash

/Users/jeffslutzky/.rvm/wrappers/ruby-2.2.3/t update "[tweet text]"

(2) Create a new task in the crontab to execute the above file at a chosen time.

29 16 21 10 * /Users/jeffslutzky/bttf.sh

Helpful links

delorean

Sublime Hints and Shortcuts

I’ve completed my first week as a student in the Flatiron School’s Web Development Immersive program. It’s been exciting and fun and busy and I’ve met friendly, interesting people. It’s the best experience I’ve had in ages.

In this blog post I’ll cover Sublime. Sublime is the default text editor we use at the Flatiron School; we use it to write all our Ruby programs, and I’m using it to write this very blog post. Besides Terminal, it’s probably the program we use most often. Therefore, knowing some Sublime tips and shortcuts can help a programmer become much more efficient. One of our instructors sent us a link to a whole bunch of Sublime shortcuts. I thought I’d cover some of those, and some other tips I’ve found most useful so far:

Setting up your visual environment. Since you’ll be staring at the Sublime interface all the time, you’ll want the screen to look pleasant. The default environment for Sublime is light text on a black background. I find that to be hard on my eyes, but fortunately you can change this. In the menu, go to Sublime Text > Preferences > Color Scheme. Under this menu, Flatiron’s Sublime installation has an option called Colorsublime-Themes, where you can pick Solarized Dark or Solarized Light. I use Solarized Light, which gives you a soothing beige background:

solarized_light

There are a ton of other options in the menu under Color Scheme – Default.

Multiple column view. Most Flatiron labs require getting certain tests to pass. It can be helpful to be able to look at your code and the test at the same time, especally when the test includes some data, such as a sample hash that you need to transform into another hash, or some supplied variables. To do this, you can press OPTION+CONTROL plus the number of columns you want. Pressing OPTION+CTRL+2 will give you two columns, and then you can drag any of the open tabs to any column:

two_column_view

Since this will give you narrower columns, you can then go to the menu bar and select View > Word Wrap so everything remains on the screen.

To switch back to single-column view, press OPTION+CTRL+1.

Comment out a line from anywhere on that line. Often I’m using binding.pry to look into my program, but then I want to stop using binding.pry and just see if my tests pass. Instead of repeatedly typing and deleting “binding.pry,” you can just comment it out by typing COMMAND and the slash key (/). The nice thing is that you don’t even have to be on the beginning of the line. If you type COMMAND + / from anywhere on that line, it will put a pound sign at the beginning of the line and comment out the whole line. To un-comment a line, just press COMMAND + / again.

Replace every instance of some text with different text. Sometimes you’ve named a variable but later decide you want to change it to something else. You can easily change every instance of the variable in your program. Just highlight any instance of the text, and every instance of that text will then have an outline around it:

highlighted_text

Now, press CTRL+COMMAND+G and you can then edit every single instance of that text at once! Everything you type will affect every instance of that text in your program:

edited_text

When you’re done, click anywhere in your program to stop. So cool.

(Just be careful – if your program contains, for example, “item” and “items,” you won’t be able to edit “item” without also editing “items.”)

Move an entire chunk of text up or down. If you want to move some text to a different part of your program – for example, if you want to rearrange the order of your methods – just highlight the lines of text you want to move, then click CTRL-COMMAND and the up-arrow or down-arrow. The entire chunk of text will move up or down a line each time you click.

There are so many other shortcuts in Sublime. It would be hard to memorize them all at once, but the more I use Sublime, the more I’ll probably become familiar with them. You will, too.

Sublime is… sublime.