Setting Up HTML Emails in Rails

21 06 2012

A few weeks ago we decided to step up our email game. We had just received the masterclass in designing emails from Justine (@meladorri) and were about to launch our free plan, so the timing seemed right. It’s not that they looked sooo bad in the past, but we wanted a more unified theme that could be used for our many purposes (appearance and design is more of Joe’s area, so I’ll let him write more on that).

We send two types of emails – some that are general reference and informational (think newsletters) that we use email marketing tools like MailChimp and Pardot for, and some that are more personalized or time-sensitive for our customers (think overage alerts) that are sent via Rails.  So the challenge was two-fold – get the emails looking right in an email marketing tool, but also make sure they looked consistent coming out of Rails.

After Joe toiled on a beautiful, table-based layout (you have to use tables for good looking email, it’s terrible) and added it to our email marketing software without much trouble, the next challenge was porting that to the system-based emails.

Long story short – sending out HTML email is pretty tricky. There are some best practices for creating HTML emails (examples: NetTuts, MailChimp, and pretty much anything by Litmus ) but all of them involve painful stuff like inline styling and duplicate code. After much painful research and experimentation, I’ve got a few tips to help set up AND maintain great looking emails in Rails. It relies on a few gems and some basic Rails functionality, but it’s guaranteed to make your email life a lot easier.

Step 1: Set up the mailers
The first thing you need to do is set up Mailers and Views for the email you’d like to send out. There are much better tutorials around this part than I can provide, and they are fairly straightforward to generate in Rails:

rails generate mailer name_of_email_campaign name(s)_of_email


This will create a mailer model in your ‘app/mailers’ directory, and corresponding views (in text.erb) for the emails attached to that model. Since you’ll be using layouts, I recommend editing the text version first (to get the wording right) before setting up the .html.erb version of your email.


Step 2: Set up MailCatcher
Now that you’ve been messing with your text emails, you probably want to see what they would look like sent, right? No need to deploy and then execute actions to generate the emails (dangerous!) – using a gem called MailCatcher and Rails functionality, you can do this painlessly (and fast!).


MailCatcher makes testing your Rails-based emails super easy. Install the gem (‘gem install mailcatcher’), run it (‘mailcatcher -f’), and then open a browser tab to localhost:1080 to start seeing mailer magic! More info on generating an email to show up here to come. You don’t need to add it to a gemfile, unless others in your team will be using it as well. You may need to make some configuration adjustments, so the emails end up in MailCatcher (check out their documentation for what might apply to your setup).

To send yourself an email for testing, fire up your Rails Console. The format for delivering an email through the console is:


In the case of Wistia emails, we deliver them to a specific account, so the function looks like MailerModel.email_name(account).deliver. The email_name corresponds to the function within your mailer model.

Changes made in views will appear if you run ‘deliver’ again, but any changes to the model will require you to re-start the console to take effect.

With MailCatcher, you can see the HTML and text version quickly, and also run analysis to see what devices won’t work with elements of your layout. It’s super useful.


Step 3: Set up the layout
So now it’s time to get your email looking good. If you’re lucky like me, you’ve got one of the best designers around putting together your table-based layout. If not…my apologies, I haven’t found a good way around that step. If you have (found a way to translate div-based layout into tables) please respond in the comments. Otherwise, I’ll give you some time to get your table-based layout right.

Save it in your normal app/views/layouts directory ‘layout_name.html.erb’. In the spots where you want to add content, I recommend using yields (content_for in your view, see Rails Guides for more). We used yields in three spots: for the image at the top, text content in the body, and link (call-to-action) at the bottom of the email.

All set? Great. In the Mailer model, tell the system to use a layout for HTML emails (and leave the text emails as-is:

mail(to: '', subject: 'something important for you!') do |format|
  format.html { render layout: 'layout.html.erb' }

If you haven’t created an html version of your mailer, you should do so now – same_name_as_text_version.html.erb. But instead of having to include the entire layout, you can just include the content you’d like to pass in. Ah, much better.


Step 4: Commence awesome styling
Ok, so you’re setting up great looking email, and testing it in MailCatcher (use their Fractal functionality as well – so cool to know what won’t work and on which device). What could be better? Well, you had to use inline styles to get things set up, and if you’re anything like me, that drives you just about batshit crazy. Luckily, the Roadie gem is here to rescue you!

Roadie makes using external stylesheets possible with emails. Previously, you had to include ALL styles inline – which was a real pain to look at and maintain. Now, you can use sass to keep track of the styles associated with your emails … wow! Add it to your gemfile (‘gem roadie’) and you’re good to go. Ryan Bates does a good screencast on getting set up with roadie (which is where I first heard about it).

I created a new stylesheet (cleverly called ‘mail.sass’) and moved all my inline styles from the table layout to it.  Then, add a link to your stylesheets at the top of the layout (ie. <link rel=”stylesheet” type=”text/css” href=”mail”> ) and elements in your layout and views will be styled through a stylesheet. So much cleaner.

Extra Notes
One hurdle we had to jump was in relation to images in email layouts. We encountered some real weirdness using ‘image_tag’ with just a link to the image. Instead, we ended up building the full URL using http://#{ ::AppConfig.asset_host }/path_to_image’. Might be helpful if you plan to tackle this project.



Interview an MBA: Contrarian Investing in People

4 06 2012

The prevailing thinking seems to be MBAs shouldn’t take part in this recent ‘startup fever’. As the thinking goes, MBAs are money-hungry robots that lust for power and drain culture from the startup environment. If they are given the reins of a startup, the bubble is sure to follow (strange duality, since many are saying the social media bubble has been defined by a company run by a college dropout). I remember reading sometime ago about a ‘rule of thumb’ for valuing startups: add one mill for each programmer, and subtract half that for each MBA. That is to say, the MBA adds negative value. The fact that investors would have a ‘rule of thumb’ that they actually follow or prescribe for investing is another matter for another time (I mean, this is your expertise, right? A rule of thumb??), but I attack the very notion that this could possibly be true.

Instead, I would argue that just like any other degree, the MBA doesn’t define the person underneath – aka ‘garbage in, garbage out’. I find it almost impossible to believe that only greedy, ‘gutless’ (Seth Godin’s words, not mine) people would sign up for MBAs. So then it must be while in school they turn into evil people. Let’s see, I can remember taking Accounting, Venture Capital, and Marketing…which classes taught us greed and overconfidence again?

The current stigma against MBAs is, in my opinion, pretty lazy thinking. Why waste time actually getting to know the person, if you can find a way to write them off? Our brains seek these heuristics all the time, but I find this one to be pretty destructive. Hiring and maintaining culture is hard, really hard. We’re collectively shooting ourselves in the foot if we ignore/deride an entire group of people.

In my opinion, lots of startups could use what MBAs do gain in school. Big picture thinking, business model evaluation, and a tireless work ethic can be invaluable assets. The ability to learn new concepts, to defend oneself in arguments, to evaluate other perspectives before making snap judgements – these are great skills to have in a startup employee. MBA grads just got out of a program working with brilliant and incredibly driven people – sound similar to a startup? You can’t get much done as a ‘shrinking violet’ in either environment, either.

While the case method isn’t exactly a silver bullet for business greatness, it does teach you to identify the right things to do from a long list of things you could be doing. The requirements of the MBA curriculum also forces more prioritization and focus than the typical undergraduate experience. These might not be the skills typically associated with MBAs, but they are incredibly important as a startup employee.

Jason Freedman has a pretty interesting take on MBAs: “Beware of MBAs!” and I don’t think he’s necessarily wrong. Startups are not smaller versions of normal companies, and the way they compete/grow/learn cannot be taught in a classroom. But knowing the ‘frameworks’ isn’t so destructive, it’s about how they are applied. Yep, that actually means MBAs can work in a ‘lean’ environment. Or a fat environment, or whatever becomes popular next.

You see, it’s not the degree that makes an MBA ineffective (or worse, negative) in a startup, or megacorp, or something inbetween. It’s the person underneath. There are folks from my MBA class that I would work with, and there are some I wouldn’t. No different at all from undergrad. Some people seem hung up on the idea that MBAs don’t listen to engineers or treat them like second-rate citizens. This is simply a matter of personal respect, not a degree-defining quality.

I’ve met a bunch of MBAs who work at startups during the last year and a half, and I’m always left impressed by their intelligence, experience, and depth of thought. I work with another MBA everyday, and his work ethic is pretty unstoppable (despite his dress code). I find it (again) impossible to believe together we contribute a negative valuation to the company. We’re both learners and builders, which we were long before we went to school.

Perhaps we’re an exception to the larger MBA rule. Totally possible. But here’s what I recommend: define your job openings, your salary range, and the qualities you want in a coworker. If MBAs apply for the position, interview them. Get to know the person, you may find them to be a great addition to the team (or you might not!) but it’s silly to exclude them because of the degree. Also, good news, right now might be the time to hire them.

Selling vs. Sales Reps

8 05 2012

We were at brunch the other morning, and I got to talking with a friend, who asked me what I was up to.  After listing out a few of my responsibilities, she said, “Oh, so you’re a sales rep.”  This threw me for a bit of a loop – no one had ever referred to my role in that way, and I didn’t consider myself a Sales Rep.  After thinking through it, I countered that while I work in sales, I don’t consider myself a sales rep.

Perhaps I’m splitting hairs, but allow me to expand on this a bit.  A sales rep’s job is to represent the company in a sales conversation.  Their job is to be focused on closing the prospect that has come down the pipe from marketing, and then once they become a customer, push them to an account rep.  

My experience with sales reps has mostly been in conversations where they cannot tell me about how the product works, or what the plan is for the product in the future.  They cannot answer use-case or support questions, their primary focus is on converting me to a customer (and the quicker the better).

In my opinion, a small startup is no place for sales reps. Sales reps (by my definition) requires a non-flat organizational structure: since sales involves much more than the act of conversion (ie. communication strategy, product dev feedback, pricing), having sales reps indicates there is a ‘sales manager’ who handles this work. This might make a lot of sense in some organizations, but not ours.

Every single person at a small company should be empowered to sell. Sell our product, sell our culture, sell our perspective on the world. Note the difference between that and ‘making a sale’.

We have some folks who spend more time in customer/prospect facing roles than others, but their primary job is to identify ‘rough’ and missing parts of the product that need to be solidified to gain traction and scale.  Customers make the end decision to buy, and while a great salesman can move product, a SaaS business has to take more of a long-term approach. If a customer signs up for the product because the salesman convinced them to, what happens when they get their first recurring bill…or their second, third, etc.?  They slowly forget the clever words the salesman used, and instead ask themselves “is this product valuable for myself and my business?”  Anytime the answer is no, the customer is gone – no matter how good the salesman was.

As I’m writing this, Chris (CEO) is having a sales conversation at his desk across from me. He’s walking someone through the product, and talking about how the analytics we provide make business video better and more effective.  He is not in the conversation looking to gain a customer right this moment.  He is testing language, listening to the feedback, and learning what key benefits/descriptors of what we provide lead to an interested lead.

Yup, I work in sales.  But I’m not a sales rep.

All in Good Time

17 02 2012

It seems like the energy around college students and the start-up scene has been really building lately. I’ve been talking to a lot of smart, driven young people and it’s really awesome. They go to Boston start-up events, they are starting their own companies, they are contributing to the community in a positive way. I say more power to them.

The only thing I do worry about is the gung-ho ones forcing out those at the fringe. Not everyone (me included) had any clue what they want to do while they are in college. Some people just want to study, party, and hang out with their friends (not necessarily in that order). And the last thing I want to happen is to alienate that group. Because they are smart too, and after a few years in another industry, learning amazing things, they could come back and start the next big company. For some, the 4 years after college are more formative than the time at college. If we make them feel like less-than now, like the only option is to be all-in or you’re out, we potentially lose their contribution.

For those folks who know what they want, and have passion around it, GO CRUSH IT. Learn everything you can, the world is your oyster, etc etc. Don’t despair if others don’t follow – the very fact that you are different makes you awesome. Be inclusive of those among us who spent college undecided. Maybe they don’t know who funded this company, or who runs that company. But in time, they could be changing the world alongside you. Respect their decision, and don’t give up hope for them 🙂

Syncing with Rsync/Cron in Mac OS

16 02 2012

With that mess complete, I could now SSH into my Mac Mini from the office using a command like the one below (replacing the X’s with your Time Capsule IP address):

I could now complete a successful rsync sync for the first time as well (command is all one line):

rsync -avz --progress /Users/jv/Desktop/forotherbooks/ jvmini@XXX.XX.X.X:/Users/jmini/Desktop/fromotherbooks

Step 2: Public Key Authentication

Hooray! Since I knew I wanted to end up being able to do this automatically, entering the password each time wasn’t going to be an option. We use public key authentication for a few things in the office, so I had an idea of getting that set up, but if you are new to that game, check out this guide. Here’s the run-down of the commands used:

ssh-keygen -t dsa
scp ~/.ssh/ jvmini@XXX.XX.X.X:.ssh/authorized_keys
ssh jvmini

Step 3: Enter the Cron

Now I had sync set up and working, but the automated part was still missing. For that, I needed cron. Cron is a UNIX tool for scheduling jobs – pretty cool if you want to automate stuff and have it run on schedule. I didn’t have any experience with cron at all, so it took me a little while to get a handle on it. Basically, there are two parts: the script and the cron table.

The script I wrote wasn’t too complicated – essentially sync everything up, and the delete all the old files from the local directory.:

#!/bin/bash # This says which interpreter you want to run the script under (in this case bash)

RSYNC=/usr/bin/rsync # this tells the interpreter where to find rsync (run 'which rsync' to be sure)
SSH=ssh # find SSH in the same place as rsync
LPATH=/Users/jeff/Desktop/otherbooks/ # read: "where is the local computer path you wish to sync?"
RPATH=jeffmini:/Users/jeffmini/Desktop/fromotherbooks # read: "And what about the remote path?"

echo "Syncing and it feels so good" # I put this mostly for myself.

$RSYNC -avz --progress $LPATH $RPATH >> /tmp/output.txt # this is the rsync command, and then '>>' tells the interp to put the output into a text file (like a log)
cd $LPATH # CD into the local path
rm * # delete all the files that are left in there after the sync

I saved this under the title and saved it for now in my Home directory.

Next was setting up the cron table. Cron table is a config file where your list of jobs are for executing. This step was a bit tricky, since the normal command

crontab -e

Wasn’t working properly for whatever reason. I blame Apple but that’s ok. I found a workaround, which is to use the editor Nano, rather than VIM, to edit the crontab file. In retrospect, this makes sense, since Apple has been tried deprecating the use of cron in favor of something called launchd. Anyway, here’s the command for opening the crontable in Nano:

EDITOR=nano crontab -e

This opened up the cron table, so I could add a new line to it. Cron tables (or at least my limited understanding of them) have syntax like so:

* * * * * command to be executed

The asterisks in this case stand for [min (0-59), hour (0-23), day (1-31), month (1-12), day of week (0-7, 0 and 7 are Sunday). A great tutorial for Cron table syntax is over at Admins Choice.

Step 4: Testing

Since I was setting up the cron job to run once a week (2am Sundays), I didn’t want to wait that long to make sure it worked. A one-line addition to my cron table file ran the cron script immediately:

*/1 * * * * /Users/jv/

And then running a tail command in the terminal, to track updates to that “output.txt” file:

tail -f output.txt

And a minute later, voila! Syncing was complete! Now I can dump all the stuff I want to access on the Mac Mini (or archive) without clogging the network, or dumping it into the Time Machine abyss. Enjoy, and let me know if you have any questions!

Curbing My Reading Addiction (and keeping the important stuff in the process)

19 01 2012

I have a problem.  I’m addicted to reading.  While it doesn’t do much damage to my health, if left unchecked it drains time away from my other favorite things to do (like exercise and time with loved ones).  Constant access to the internet is like a crack den for someone with a reading addiction.  When I was in grad school, my range on interests expanded while I wrestled with what to do after school.  From finance to tech news, venture capital to marketing, my Google Reader account was constantly stuck at ‘1000+’.  With a bit of free time on my hands every day, I got to the point where I was ‘reading’ 400+ articles per week, and skimming hundreds more.  I was subscribed to dozens of blogs and news sites, and constantly balancing on the edge of being completely overwhelmed by the barrage of content.  The worst part was my lack of retention – I felt almost powerless to actually read and understand — my tiny brain was full to the read line.  I was saturated.

My girlfriend finally staged an intervention.  Well, she removed the Google Reader plugin from my browser, which acted as an intervention.  Without the constant reminder that I was leaving hundreds of articles unread, my addiction to clearing my Reader list was curbed.  While I still devoured lots of articles, I no longer used an RSS reader to aggregate them.

With starting at Wistia, I took another big step – I stopped following blogs altogether.  I deleted all my bookmarks, and instead used my Twitter feed to tell me what to read.  The integration with Instapaper made it easy – if I saw an article in my timeline that looked interesting, I could add it to my Instapaper list with a simple swipe (nevermind the fact that Twitter has since made this much more difficult).

I can’t lie – I still obsess over clearing my Instapaper queue.  But with 1,300+ articles in my to-read list, this isn’t happening anytime soon.  But I was in control of what articles went into my queue, which was much more efficient.  There was always one problem with this setup – I was missing out on the updates of my favorite blogs.  My friend Courtney’s blog?  Never one of the popular articles in my Twitter feed.  I was also always hunting for a article to read with my coffee – just trying to clean out my Instapaper queue left me with only long articles.

That’s when I heard about ifttt (If This Than That).  I tugged on my imaginary beard as I read over the description of the service.  And I knew it would make my reading problem go away…at least partially.  Who knows if sending this ifttt recipe out will work, but here is my favorite:  I created a folder in my old Google Reader account called “must reads”, and now whenever an item gets added to that folder, it gets auto-added to my Instapaper queue.  Ahhh, efficiency.

So, you might be wondering, what’s in my “must reads” folder?  Well, besides some personal blogs, here’s the current list:

For anyone out there with a reading addiction like mine, I highly recommend checking out Instapaper (as if you aren’t already using it) and ifttt – the combo together is what you need to get your life and attention span back.



What is a Startup?

18 12 2011

I was chatting with a friend the other night at our holiday party. She said she’d been hearing more and more young people idolizing startups. It seemed everyone she was talking to was going to start their own company or join a startup. She lamented that many of them had no idea what a startup was, or what their (venture-backed) business would do. This got me thinking…if someone had sat me down a few years ago and told me I’d be working at a “startup”, would I have had any idea what that meant?

When I was working on the draft for this post, I came across a post by the same title (“What is a Startup?” by Daniel Tenner: I recommend checking it out – it’s excellent analysis on defining a “startup”. His definition is really well-thought out and inclusive: