Getting Started As A Developer

Through my work with PyLadies Boston, I have been asked quite a few times on how to get started with development. I'm going to try to write it all down here.

So you want to become a software developer?

Awesome! It's a pretty fun (albeit sometimes frustrating) gig and the pay is pretty decent too. Just be patient... it's not super easy and sometimes it'll get difficult. It's worth it though, so stick with it.

Step 1: Pick a language

Don't spend too long on this step! I would recommend either Python or Ruby as good beginner languages. The syntax is relatively similar to English, so it's not too hard to read code from early on. Also, these are two languages that are widely used at actual companies! Ruby is a fan favorite of startups and Python has a huge following in the scientific/academic communities. If you want to further progress into web development, I would recommend Ruby because, in my opinion, I think the documentation and tutorials available for Rails are much better (and in some cases easier to understand) than the docs/tutorials for Django.

Either way: don't think too hard about it. You just need to pick one. Once you learn one, you can always, much more easily, learn another.

Step 2: Pick a method

There are a load of resources out there. One I recommend is Zed Shaw's Learn Code the Hard Way (for Ruby, Python, SQL, and C). There's also How To Think Like A Computer Scientist (for Python), along with plenty of others. If you prefer a book, I can recommend both Dietel's How To Program (Java) and Pine's Learn To Program (Ruby, also a web tutorial!). The world of programming books/tutorials is  your oyster! Just pick a learning style that you like and stick with it. If videos are your thing, Codeschool has excellent video tutorials.

What I do not recommend: while Codecademy can be good for trying to decide what language to use, I do not recommend it for learning. Codecademy is software (what you will be building) and software has bugs. What you don't want to be spending time on is trying to figure out if the bug is yours or Codecademy's. If you think that sounds crazy, I have had Python code that I've run locally with no errors that gets a random error on Codecademy. Plus, one of the most difficult parts is installation and setup. You miss that with Codecademy. If this is your tool of choice, you have been warned.

Step 3: Give it some time

Try to dedicate some amount of time every day. 10 minutes when you first get in to work? 30 minutes when you get home? Doesn't matter. The more time you can dedicate, the faster you will progress, but the important thing is to make it a habit so you stick with it. Most of these resources have forums that you can utilize if you run into problems. If they don't, then you can also use StackOverflow. If you google for your error message, you will probably get a result on StackOverflow. Check it out and see if you can fix your bug. Once you get past the basics, give yourself a challenge by trying some exercism.io problems. They have problems for almost all languages and your submissions will actually get code reviewed!

Step 4: Level up!

You have a solid foundation! Time to take it to next level! And by that I mean web development. Is that the only route you can go? Nope! But I'm a web developer, so that's what I actually have experience on. Also, I have the most experience in Python and Ruby, so those are the languages that I'll have the most links for. If anyone has some next level topics for non-web developers, put it in the comments! Or link to your own post. Depending on what you started with, here are some resources:

Ruby:

  • Michael Hartl's Rails Tutorial - This is the best Rails tutorial out there. I'd almost argue that it's the best web dev tutorial out of any language.
  • CodeSchool's Rails For Zombies - If you prefer videos, Rails For Zombies is corny, but pretty great. And the first course is free!
  • Sinatra - a microframework for Ruby. If you really want to dig in and try to learn how things work, using a microframework that doesn't enable all the bells and whistles by default is awesome. 

Python:

  • Tracy Osborn's Hello Web App - Awesome book series made to teach non-programmers web development through Django
  • Getting Started With Django - Short video series. Starts you after the official Django tutorial
  • Django Book - The official Django tutorial. I'm hoping it's been updated since I tried to go through it because it was a bit buggy then.
  • Flask - a microframework for Python. Also see this tutorial
  • Lynn Root's NewCoder.io - Not web dev, but definitely a level up. Lynn has written tutorials on APIs, web scraping, data visualization, GUIs, and networks. These are great if one of these topics is of interest to you.
  • Daniel and Audrey Roy Greenfield's Two Scoops of Django - this is not really a beginner book. More an "after your first app" book. But this is one of the best programming books I have ever read, so I absolutely had to add it to this list.

Java:

  • Play Framework - As far as I can tell, this is the most popular web framework for Java. Their own documentation contains a solid amount of good tutorials to get you up and running fast.

Step 5: Build something!

This is absolutely the hardest step. Why? Because it requires you to actually be a little imaginative and think of something that you want to create. To start, you can create a website (either a personal site or a landing page for your project) on Github Pages. It's free and super easy to get started! As far as picking a project, there are shortcuts if your brain is a bit fried and you can't think of anything. There are lists of coding projects that you can pick from. You can also contribute to open source. Whatever you choose, the important thing is to keep working at it. Even senior developers are still constantly improving their skills, so you will constantly be learning at all stages of your career.

A Quick Post About Resumes

PyLadies Boston recently had a mock interview night and with that I offered to review resumes. I got a few takers, did some reviews, and now I have some thoughts.

  1. If you are randomly switching fonts, please have a good reason for it. It is distracting (in a bad way) if you go from a serif to a sans serif for seemingly no reason.
  2. If you are writing your job duties as a bullet-point list, please make sure each point is connected to itself. I can't seem to think of a great way to say that, but let me give an example. If one of your points is: Improved test coverage by 10%, organized tech talks, and implemented a code quality standard - then those should really be elaborated upon if possibly and definitely split into three separate points.
  3. Make sure your resume reflects the skills of the job you are looking for. That doesn't mean that, if you have been a research scientist that now wants to be a full-time programmer, you have to ignore your past history. However, you do have to highlight different things. For example, how did you analyze a set of data? Did you use Python? What libraries did you use?
  4. Along the same lines: If you are applying for your first programming job and don't have any related experience, you really need a projects section that lists the programming projects you have worked on and any open source you have done, along with descriptions. You know you can do the job, but if you don't put proof that you can code, the internal recruiter/HR person is going to throw your resume out.
  5. For skills section: if you are including it, please make sure they are relevant! If you are applying for programming jobs, you do not need to include photoshop. Also, definitely do not include the Office Suite... familiarity with Office or similar software is assumed if you know how to use a computer (which is also assumed if you are applying for programming jobs).
  6. White space is your friend! Definitely don't jam everything together. Separating out sections, careful use of bold fonts and color, and horizontal lines can really help draw the reader's attention to wherever you want it to go.

Sorry some of those were a bit of a ramble, but these are all things I have seen recently on resumes. A resume is often the first look that many people have into your professional life, so you want it to represent you in the best way possible. If you have any questions, feel free to comment!

Calculating Age... in Java 8

I'm doing a bit more Java now that I'm taking a Java class. With that is coming a lot of "oh that should be easy... wait, there's not a really simple way to accomplish this???". First example of this: determining someone's age.

import java.time.LocalDate;

public class Age {
  private int birthYear, birthMonth, birthDay, age;

  public Age(int birthYear, int birthMonth, int birthDay){
    this.birthYear = birthYear;
    this.birthMonth = birthMonth;
    this.birthDay = birthDay;
    this.age = getAge(birthYear, birthMonth, birthDay);
  }

  public int getAge(int birthYear, int birthMonth, int birthDay){
    LocalDate fullBirthday = LocalDate.of(birthYear, birthMonth, birthDay);
    LocalDate now = LocalDate.now();
    long daysSinceBirth = now.toEpochDay() - fullBirthday.toEpochDay();
    return (int) (daysSinceBirth/365);
  }
}

LocalDate is new to Java 8. Previously it was part of the Joda-Time API, but the Java folks seem to have added the bulk of the functionality directly into Java. Sweet! What does this allow us to do? LocalDate creates an object that represents a date and has quite a verbose API. Since we're calculating someone's age, we are going to need an object that represents their birthday and an object that represents today (in this case fullBirthday and now). If we convert these both to Epoch Days, which is generally just the number of days from 1970-01-01, we can just compare the number of days and divide by 365 to get the age. Not too hard... but did take a second to come up with it... I was a bit surprised that it seemed like I couldn't actually subtract dates. Ruby has spoiled me...

Update: In the comments below, Ted Vinke alerted me to another, even easier way to calculated it with the Period. This solution still uses LocalDate, but takes less math on our part. Thanks for the improvement, Ted!

import java.time.Period;
import java.time.LocalDate;

public class Age {
  public int getAge(int birthYear, int birthMonth, int birthDay){
    LocalDate fullBirthday = LocalDate.of(birthYear, birthMonth, birthDay);
    LocalDate now = LocalDate.now();
    int period = Period.between(fullBirthday, now).getYears();
    return period;
  }
}

Polymorphic Routes

I just started classes (working toward the CS certificate at BU Met) and my new big project at work is porting over a ton of code from Rails 2 to Rails 4, so I’m sure I’m about to have tons to write about. For today, here’s something I somehow just found out about: polymorphic routes in Rails.

What are polymorphic routes? Let’s say you want to have a partial that is used for quite a few different models. Every model you have has a show page for individual instances of that model and each show page has an edit link. So instead of creating a new page for each, the view you have reads in a generic @object and then you can use polymorphic routes to generate the path for the edit link! In this example, I’ll have the @object represent an instance of the Article class. Like so:

edit_polymorphic_path(@object)

results in:

edit_article_path(@object)

I’m pretty surprised I haven’t seen this yet, but now I’m glad that I have! This is pretty cool :D

Oh My Zsh!

The terminal is fun. And it should also look fun (and be functional). I’ve been using Robby Russell’s Oh My Zsh for years now, but it still surprises me when people haven’t heard of it.

I’m not going to go into a ton of detail as to why you should use it. Here are a few great posts that already do that.

Nope, I’m mostly going to talk about how I have been adding a series of increasingly ridiculous emojis to my prompt. Why? Because if I am having a bad day, dammit if a 💩 doesn’t make me smile a bit. Right now I have a rainbow if I have a clean git repo, but I’m going to add a unicorn as soon as I can update my work computer.

There are so many awesome, fun themes to use depending on your workflow. I was using a slightly modified bullet-train:

But then today, I decided it was taking up too much space and went back to an old standby: crunch. I modified it just a bit to change the icons (💩) and also to just display the current folder instead of the whole path. Though I’m sure the whole path can be useful to most people, my directory paths can sometimes get long and ridiculous, so generally a folder name does in a pinch. If I need to confirm, there’s always pwd.

Try zsh. It has some other sweet features that are all useful, but my favorite thing is the themes and how much easier they make my day to day.

#ReplaceCodeSchoolsWithCommunity2016

My friend Pamela had some thoughts on code schools that she shared on Twitter today and I just have to echo them in every venue that I have. Code schools have long made me feel uncomfortable. It really started when a local code school started amping up representation at RailsBridge (free & community driven), obviously with an aim to drive RailsBridge grads to their boot camp. Considering the other TAs were there from companies that were interested in hiring, I thought it really sleazy that they were just using it to try to recruit more students. Add this in to women generally needing more validation before they feel comfortable calling themselves developers and that makes me feel even more uncomfortable.

I try to do my part by running PyLadies Boston and Pamela runs Rails Girls Atlanta. Both of these groups exist to provide a community for women to learn to code, with no charge. I’ve actually had multiple women from my group start applying to jobs as developers and I know the same goes with Rails Girls. Community can make a big different and we need to let women know that you don’t have to shell out $$$ to become a developer. The community will help!

PS - I have met so many wonderful and smart grads of code schools. This is not a knock on you! It’s a knock on this f’ed up industry.

A Bash Script For When You Need To Duplicate Host Keys Across Servers

Why would you need to do this? Let’s say you have three production nodes: server1.test.com, server2.test.com, server3.test.com. In general, your production application (at prod.test.com) point to server1.test.com. But OH NO you totally messed something up on that node and you need to easily failover to server2.test.com. Easiest way? If they all have matching server host keys, you can just point prod.test.com to server2’s ip and you won’t get any errors. Otherwise, you’ll get loads. I also added a noop (no operation) mode to this script so it could be run as ./move_keys.sh -n and only print out the lines that it would run.

This script is written to be run on the server you want to copy the keys from as your user, which means you have to have ssh access into each server. For each server in the list that is not the host, it backs up the /etc/ssh directory, rsyncs the keys over, restarts ssh, then removes the relevant lines for that server in the known_hosts file.

#!/bin/bash
# use noop mode by passing -n
NOOP=false
# parse the options
while getopts 'n' opt ; do
  case $opt in
    n) NOOP=true ;;
  esac
done

SSHC='/usr/bin/ssh'
HOST=`hostname -f`

# Params: $1, host to log into and copy ssh dir into backup dir
copyit() {
  if [ $NOOP = true ]
  then
    echo "$SSHC -A $1 'sudo cp -r /etc/ssh /etc/ssh_bak_$(date +%Y%m%d%H%M)'"
  else
    $SSHC -A "$1" "sudo cp -r /etc/ssh /etc/ssh_bak_$(date +%Y%m%d%H%M)"
  fi
}

# Params: $1: src path, $2: dest path
moveit () {
  if [ $NOOP = true ]
  then
    echo "sudo -E rsync -avz -e 'ssh -o StrictHostKeyChecking=no' --rsync-path='sudo rsync' $1 $2"
  else
    sudo -E rsync -avz -e 'ssh -o StrictHostKeyChecking=no' --rsync-path='sudo rsync' $1 $2
  fi
}

# Params: $1, host to log into
restartit() {
  if [ $NOOP = true ]
  then
    echo "$SSHC -A $1 'sudo service ssh restart'"
  else
    $SSHC -A "$1" "sudo service ssh restart"
  fi
}

# Parmas: $1: host to deploy keys to
removeknown() {
  if [ $NOOP = true ]
  then
    echo "sudo ssh-keygen -f '/root/.ssh/known_hosts' -R $1"
    echo ""
  else
    sudo ssh-keygen -f "/root/.ssh/known_hosts" -R $1
  fi
}

# Params: $1: host to deploy keys to
movekeys () {
  copyit "$1"
  moveit "/etc/ssh/ssh_host_key" "$USER@$1:/etc/ssh/"
  moveit "/etc/ssh/ssh_host_key.pub" "$USER@$1:/etc/ssh/"
  restartit  "$USER@$1"
  removeknown "$1"
}


set -e

declare -a Servers
Servers[0]='server1.test.com'
Servers[1]='server2.test.com'
Servers[2]='server3.test.com'

Servers_minus_host=( ${Servers[@]/$HOST*/} )

for server in "${Servers_minus_host[@]}"
do
  movekeys $server
done

Some Thoughts On Soylent

jenlovestheweb:

I’ve gotten a variety of reactions when I’ve told people that I drink Soylent. Usually it’s gentle mocking or ‘BUT WHAT DOES IT TASTE LIKE???’. Both of these reactions make sense… on some level, I think I’m a bit crazy for drinking it. On the other hand, it saves me so much time. I currently drink Soylent for only about 8-10 meals per week, generally breakfast and lunch during the work week. Pre-Soylent, I spent hours on Sunday prepping my meals for the week or was constantly eating leftovers; either way, I was eating the same thing almost every day. Or even worse, I went out to lunch, spent $10, and ended up getting something that wasn’t very healthy for me. Soylent costs about $1.92/500kcal meal - plus one point for cheap! It’s also pretty well balanced nutritionally, so I know I’m getting a decent amount of nutrients per meal. The other benefit is it’s bland taste… it’s hard to describe, but I just don’t even think about it. Since I don’t think about it, I don’t get sick of it. Overall, I like it and I’ll probably keep ordering it for the foreseeable future.

Relevant to all of you programmers out there considering switching to the nerd smoothie.

Sanitize Text in Perl

One of the projects I’m working on is in Perl. Having never touched Perl before, this is a bit of a new adventure! Today I had to sanitize a field being passed by a url. This took me a few google searches before I found the right answer, so I’m going to write down the solution really quick here so I don’t forget it.

Here’s the code:

# assuming $message is your user-inputed data
use HTML::Entities;
my $encoded_message = HTML::Entities::encode($message);

Then you can print your $encoded_message out in html and you will not be vulnerable to XSS attacks.

I literally just looked at Perl for the first time today, so any tips are welcome!

When Bad Things Happen to Good Jobs

No job is perfect. This post is going to cover some types of those imperfections (from personal experience) and what you can do to fix it (or at least make your job a bit better). One piece of advice applicable across the board: if something seems off, document it. Get a weird IM? Save it. Even if something is good now, you’ve got to protect yourself in case the situation gets worse.

1. Retaliation

How to spot it

Retaliation is not acceptable at most companies. However, most people are aware of that, so the way they retaliate can be more nefarious and more difficult to prove. This is one situation in which it is definitely critical to document everything. The two things to look out for are: 1) did a triggering event happen? Example: did someone get fired and could you be blamed in any way? 2) has one of your coworkers’ behavior towards you changed dramatically? Example: did you formerly have a good relationship and now they are criticizing you and complaining about you?

How to fix it

If this person is not your boss, I would first talk to your boss. Unless you have direct proof, it doesn’t serve you to be acussatory, but you can at least clear your name. You can possibly say that you think the retaliator seems to have an issue with you and ask for advice on how to handle it, or ask if you should change teams or departments.

If this person is your boss, go directly to HR. Again, unless you have direct proof, I would stay away from harsh accusations. Say something like “I’m not sure what happened, but ever since [triggering event], Joe has treated me differently and I’m not sure it’s healthy for me to continue working under him.” Ask for advice, see if you can transfer to a different manager, and possibly start updating your resume. Do not be afraid to talk to HR because that is not a fireable offense. However, if it doesn’t go very well, it’s probably not the type of company you want to work for.

2. Boss Is Not a Good Manager

How to spot it

There are so many versions of this. My favorite is the super micro-manager. Or the “never there and you don’t know what to do” manager. Or the “for some reason wants to make sure you don’t grow professionally” manager. All are bad, just on different levels. Depending on how you work, the micro-manager or never-there manager might not be too bad. Or not.

How to fix it

When you have issues like this with your boss, I would go to HR directly, especially for the second two. If you aren’t getting support (because they are never there or are denying you the opportunity for advancement), that has a huge impact on your career and your productivity at the company. Most HR professionals will listen and try to find a solution. This happened to me and I was moved to a different boss within a few weeks. For the micro-manager, I would actually first try to talk to your boss directly, if they are approachable. If you frame it as you wanting the opportunity for more independence and “larger” projects, the conversation could go quite well. No one likes to be told that they are doing something wrong, so make sure it is about what you can do, not what they are doing wrong.

3. Boss Dislikes You

How to spot it

This one can be sorta hard to tell. I think that you can know, but it’s definitely hard to prove and your boss would probably never admit it. If they admit it, then I would go to HR. Otherwise…

How to fix it

If your work environment is good otherwise, I would stay and keep doing good work. Your boss may not be a good reference, but if you keep submitting excellent work, your coworkers will be. I had an experience like this (and my suspicions were confirmed after I left). I still had great work experience to add to my resume, great coworkers to add to my network, and just a man who I know I don’t want to work for again.

If your boss’ dislike of you is really affecting your work environment, I would polish up that resume and leave as soon as possible. If you have proof, take it to HR.

4. Coworkers Are Inaccessible/No Help

How to spot it

This can be a fairly common problem when you are a junior developer or a new employee. It can be especially common if a lot of people work from home and turn their IM off. This can be especially hard if you need to get a particular piece of information and the person everyone points you to is in their “dev cave”.

How to fix it

If you are in an office full of people like this, get out now. Teamwork is an important part of growing as an engineer and obviously you are in an environment that doesn’t value it. Gross.

However, this is unlikely. Most of the time it will be a small handful of people. Search out the people who are available and willing to help you and reach out to them. If you have a decent boss, request a one-on-one and discuss the situation with them. Most bosses want their team to communicate and will not be pleased if they find that quite a few of their engineers are radio silent most days.

5. Total Disrespect

How to spot it

This is luckily an uncommon problem and also can be really easy to spot. However, in my experience, it was the problem that I felt most like I could fix and yet was totally unfixable. If you are constantly getting second-guessed, but nothing you have said has been false or even inaccurate, that is a form of disrespect. If your superiors are saying negative things about you to other employees of the company, that is a form of disrespect.

Those are a bit more outright, but disrespect can also be less blatent and take the form of microagressions. For example, if your coworkers, especially your higher ups, are smiling and nodding at you, then completely ignoring your suggestions and doing the opposite of what they said they’d do (what you recommended), that can be a microagression and a form of disrespect. This can be hard to spot because it usually doesn’t happen all at once, but if you are stripped of the ability to do your job effectively, that is also a form of disrespect.

How to fix it

You probably can’t. Definitely document everything. In a situation like this, you can’t be sure what will happen. Save all your emails, save all your IMs, and, if possible (and with permission of the people in the meeting), record conversations. If this is actually happening with a company large enough to have HR, definitely go to HR. However, that is fairly unlikely. You can try to have conversations with the people or person who are/is being disrespectful, but you definitely have to be ready to quit. I’ve had one job on this level. I kept bringing things up and even coming up with solutions in meetings that were agreed upon, would think things would improve, and they never did. In the end, that was the only job I walked out on. As a human being, you should not be treated like that, but, if you are a programmer, the job market is good enough right now that you absolutely do not have to be treated like that.

Job Search Retrospective

Wow what a month. I’ve been interviewing for a little over a month now. Two weeks ago, I quit my job (post on that mess coming up in a a few more months) and somehow got a job offer the same day. This has definitely been my shortest job search (time when I submitted my first application to first offer), so I wanted to share what I think has been different.

Finding Potential Employers - Recruiters & Networking

So first things first… I may run a fairly large meetup group, but I’m actually pretty bad at networking. If you are a networker and can go to a ton of meetups and talk to people, that’s the best way, hands down. First interview I got was because I went up to someone who I knew was hiring at a conference and talked to them.

If you aren’t much of a networker (like myself), working with recruiters is the way to go. If you are in one of the areas they serve, I have had really good luck with Talener, their Boston office in particular. They have put me in front of some great companies and I’ve had a ton of in person interviews as a result. 

Many people say that you don’t get anywhere from submitting your resume just from the company’s website. NOT THE CASE! I got my last job like that and, during this search, I got a great interview by doing that.

Now that the interviews are lined up…

Interviews!

The biggest change is obviously experience. Time definitely makes each round of interviews a bit easier! There were a few other things that made it a bit easier though. 

The first was inspired by Julie Pagano’s own job search retrospective. I’ve never been very good at asking a bunch of questions, but I looked at the questions she and Julia Evans asked and took a bunch of my favorites. I had a notebook and wrote out all the questions and all the answers. This helped immensely because, not only did I find out more about the companies and how they worked, but it also helped turn the tables a bit to have me interviewing them. 

The second was that I finally actually thought of a good answer for ‘Tell us about an interesting/challenging problem that you have solved and how you did it’. I was asked variations of that question at almost every interview and this is the first year I felt I had something to talk about. I was just talking to a junior dev about this question and, really, anyone who has solved any problem with code should have an answer to this question. If you have a project, you must have created it to solve a problem. If you had a bug in your project and fixed it, there’s your problem. Sure, it’s not the most interesting, but you will get better and better answers as you gain more experience, and employers recognize that.

And the one thing you cannot forget in tech interviews, WHITEBOARDING. Three years into this and whiteboarding is still nerve-wracking and I still feel like I’m awful at it. One thing I have learned: how the interviewers interact with you while whiteboarding tells you a lot how they would interact with you when actually problem solving on that job. If someone is making you feel stupid while whiteboarding, then you probably don’t want to work with that person. On the flip side, If you are having a bit of trouble and the person interviewing you is actually helpful, then that is the type of person that you want to work with.

Overview

Interviewing is tough. No matter what level you’re at, you’ll get people who will tell you don’t have enough experience for the job. Don’t sweat it. It just means it’s not a good fit… you’ll find a good fit somewhere else :D

Chosen & Multiselect

I’ve been doing a bit of work jazzing up our multiple select boxes. These plugins are going to be familiar to most people, but if you haven’t looked into both of these, you should. I had previously been using jQuery multiselect in my current app but have recently switched to Chosen to make use of it’s search functionality. Here are the two that I’m going to highlight:

1) Chosen

Chosen gives you the ability to do a type-ahead search. Instead of scrolling down through a long list, I can start typing and see what options match. Particularly helpful in dealing with food… if I don’t like almonds, it’s nice to see that almonds, almond milk, and almond flour could all potentially be ingredients.


2) jQuery multiselect

jQuery multiselect does require the scroll down list. You can use a separate plugin for search capabilities, but, by default, it doesn’t include it. However, if you have a shorter list, this is a very nice way to let the user select and deselect items.

No posts :(

And why? Because I’ve been pretty stressed and haven’t been doing my best work lately. I took a few days off to spend a few days in the mountains in Vermont. Relaxing, but the stress came back as soon as I returned. I promise I will post as soon as I actually have something to say.

One the upside, I have been exercising my stress away, going to Crossfit most mornings and then running after that about three days out of the week.

Rails Forms - Basic Errors

This is going to be short because I’ve been crazy busy, but nothing really worth blogging about. Two things about forms in Rails:

1) You can’t nest them within a table. Let’s say you have this bit of code in your ERB:

<tr>
  <%= form_for TestClass.new do |f| %>
    <td>
      <%= f.text_field :test_field %>
    </td>
    <td>
      <%= f.submit 'Create' %>
    </td> 
  <% end %>
</tr>

What this results in is this:

<tr>
  <form action='/test_class'></form>
  <td>
    <input name='test_class[test_field]' type='text'>
  </td>
  <td>
    <input value='Create' type='submit'>
  </td>
</tr>

The form ends up closing before it actually ends, which means that it won’t actually submit properly. Bizarrely enough, sometimes it works… but it won’t work consistently. Lesson: don’t nest forms in tables. Haven’t tried it, but there’s a possibility that with HTML5, if you define the form outside of the <table&gth; element, then it will work.

2) You cannot instantiate a form in one div and then end it in another. This may seem obvious, but I see this a lot, especially with juniors. Example:

<div>
  <%= form_for TestClass.new do |f| %>
    <%= f.text_field :test_field %>
</div>
<div>
  <%= f.text_field :num_ponies %>
  <%= f.submit 'Create' %>
<% end %>
</div>

Results in this:

<div>
  <form action='/test_class'></form>
  <input name='test_class[test_field]' type='text'>
</div>
<div>
  <input name='test_class[num_ponies]' type='text'>
  <input value='Create' type='submit'>
</div>

No bueno. Never nest anything across multiple elements. HTML does not appreciate it.

Promo Codes in Rails

When I got tasked with adding promo codes in our Rails apps, the first thing that surprised me was how few posts there were on how to do it. So here I am to fill that gap.

First! We have to create a promo codes model:
class CreatePromoCodes< ActiveRecord::Migration
  def change
    create_table :promo_codes do |t|
      t.string :code
      t.decimal :amount
      t.text :purpose

      t.timestamps
    end
  end
end

And the model:
class PromoCode < ActiveRecord::Base
  # ...
  has_and_belongs_to_many :users
  # ...
end

Then we should add some fields to a user to reference promo codes. For our case, we want to know what promo codes they have used in the past, how much credit they have, and what user referred them.
class AddFieldsToUser < ActiveRecord::Migration
  def change
    add_column :users, :credits, :decimal, default: 0.0
    add_column :users, :referring_user_id, :integer
  end
end

And the updates to the user model:
class User < ActiveRecord::Base
  # ...
  belongs_to :referring_user, class_name: "User", foreign_key: "referring_user_id"
  has_and_belongs_to_many :promo_codes
  # ...
end

Plus one final migration to allow the has_and_belong_to_many to work:
class CreatePromoCodeUser < ActiveRecord::Migration
  def change
    create_table :promo_codes_users, id: false do |t|
      t.belongs_to :user, index: true
      t.belongs_to :promo_code, index: true
      t.timestamps
    end
  end
end

We also have a PromoCodesController. This isn't really doing anything unique, we just need it so we can allow admins to create promo codes.
class PromoCodesController < AdminController
  before_action :set_promo_code, only: [:update, :destroy]

  respond_to :html, only: [:index]
  respond_to :json, except: [:index]

  def index
    @promo_codes = PromoCode.all
    respond_with(@promo_codes)
  end

  def create
    @promo_code = PromoCode.new promo_code_params
    @promo_code.save
  end

  def update
    @promo_code.update(promo_code_params)
  end

  def destroy
    @promo_code.destroy
    render nothing: true, status: 204
  end

  private
    def set_promo_code
      @promo_code = PromoCode.find(params[:id])
    end

    def promo_code_params
      params.require(:promo_code).permit(:code, :amount, :active, :purpose)
    end
end

We then added a route that was specific to adding a promo code (within the user controller). We post to this route whenever a user adds a promo code:
def add_promo_code
  user = User.find(params[:user_id])
  # We decided the easiest way to do referring users
  # was to have codes that looked like this: MYCODE-265,
  # with 265 being the id of the referring user.
  # Not necessarily teh best solution for everyone!
    promo_code, referring_user = params[:promo_code].split('-')
    code = PromoCode.find_by_code(promo_code)
    if code
      if user.promo_codes.include?(promo_code)
        message = 'This promo code has already been used.'
      else
        user.promo_codes << code
        user.credits += code.amount
        if referring_user && user.referring_user_id.nil? && User.find_by_id(referring_user).present?
          user.referring_user_id = referring_user
        end
        if user.save
          message = 'Promo code successfully used!'
        else
          message = 'Error using promo code. Please try again or contact customer service.'
        end
      end
    else
      message = 'Not a valid promo code.'
    end
  respond_to do |format|
    format.js {  flash[:notice] = message }
  end
end

Then, whenever we are getting ready to charge a user, we add code like this:
if @user.credits > 0.0
  if @user.credits >= @price
    @meal_plan.price = 0.0
    @user.credits = @user.credits - @price
  else
    @meal_plan.price -= @user.credits
    @user.credits = 0.0
  end
end

What do referring users get? For us, they get a credit when the person they referred makes their first purchase:
def charge_user(user)
  if user.first_charge? && user.referring_user
    user.referring_user.credits += PromoCode.find_by_code(@user.promo_codes.first).amount
    user.referring_user.save
  end
end

There are a few other smaller things, but that was the bulk. If anyone has any feedback or something they would do differently, I'd love to see it in the comments!