General Assembly WDI, Week 12

Capstone project!

end of an era

Week 12 was strange for many reasons: it was the final week. It was a short week—we had Friday off because of Veteran’s Day. Tuesday was Election Day, which….

It was also our final project week, which meant I spent most of the week sequestered in one of GA’s shiny and tiny new conference rooms with coffee and Ember and Rails. Using Ember for our final projects was optional, but I jumped on the chance because a) client side routing; b) I wanted the practice; and c) it sounded fun.

Regarding that last point:

Can't decide if this is fun or torture #emberjs @ga.boston #capstoneproject #WDI #LifeAtGA

A post shared by Rebekah Heacock Jones (@rebekahredux) on

I chose to use Rails for the backend (instead of Node/Express) because it’s a fairly natural pairing and because we had roughly a week to build the project, and I’m more comfortable with Rails than Express at this point.

It’s been two months exactly since I presented my project—sorry for anyone waiting with bated breath to read about my final week at GA—so in the interest of finally getting a post up, instead of doing a detailed blow-by-blow of that week, I’m going to cover the highlights. If there’s anything that piques your interest, leave a comment or get in touch via Twitter/etc., and I’m happy to expound!

Project Planning

When I first seriously starting learning JavaScript, beyond copying & pasting random code to hide and show things on web pages when I was a kid, I started playing around with the idea of building a tool that would generate a random pattern for a quilt, made out of squares and triangles of different colors. This was inspired by the desire to build something super visual, by Libs Elliott’s stunning and unbelievably cool work to generate much better/more intricate patterns with Processing, and by my own interest in quilting.

My first attempt at building this resulted in 335 lines of JavaScript in a single file, nearly 200 of which were inside of a single onclick event. Also: when I abandoned it in November 2015, it didn’t work, and my commit messages were shoddy enough/my use of branching nonexistent such that I couldn’t remember when it last worked and what I had done to break it.

I decided to take this opportunity to revisit the idea and expand upon it, adding the ability to register, to save/favorite different patterns, and to create “projects” associated with specific patterns that would let users write up notes on actually making the pattern into a quilt and photos of the process/the finished quilt.

Prework: File Uploads with Paperclip and Rails

I started by working on photo uploads: we had briefly covered how use Multer/Express/AWS S3 to upload photos and store URLs to a database, but I needed to apply this knowledge to a Rails API and to Ember. I knew I would need to re-scope my project idea if I couldn’t get uploads to work, so given the tight project timeline, I decided to focus on this first, give it a day or so, and move on if I hit too many roadblocks.

I set up the API and scaffolded a very basic “pattern” resource—at this point, I was thinking that I would need to store the generated pattern as an image (I’ll come back to this later). Following along with a General Assembly repository on using Paperclip that’s a couple of years old/out of date, and is no longer being taught by GA Boston, I installed Paperclip and ImageMagick to help with uploads through Rails, then wrote a curl script to test whether I could POST image data to the API without yet storing it in S3. I had to write a slightly different script than the ones I had been using to send JSON data through POST requests; this Stack Overflow post on data types helped.

I installed the aws-sdk gem, which Paperclip uses to store files on S3, and added the necessary AWS config variables (s3 region, bucket, access key ID, and secret access key) to my .env file so that I could use them in my config files. From there, I used curl to test the upload process and make sure that files were being successfully stored in S3 and the URL saved in my database.

Success! I wrote up my process as an issue on the GA Paperclip repo for a few other students who were hoping to do the same thing in their projects, then moved on to working with Ember.

This was…frustrating. After spinning up a very basic Ember project using the Ember CLI, I tried multiple different plugins/components/guides, including ember-data-paperclip, ember-cli-form-data. In the end, I just wrote a basic HTML form, set the enctype to multipart/form-data, and used JavaScript to get the form data by the form’s ID instead of trying to use any Ember fanciness (though apparently using event.target instead of the ID is a better approach?). I also wrote a short custom upload service to send the AJAX request in the correct format.

This took two afternoons, mostly working solo/Googling like mad, but also checking in with one of the GA instructors, who was also attempting to crack this challenge. (Not at all #humblebrag: proud to say I solved it first!) This was one of my first real moments going “off script” at GA, and it was frustrating and awesome and confusing and wonderful. It’s one thing to take a lesson with step-by-step instructions and well-documented code and apply it to your own very similar project; it’s another thing entirely to take snippets of tutorials and gem/plugin docs and a rough sense of what you want to do and make it work. I know this problem isn’t a gigantic one—many, many people have successfully solved it, and I’m sure there are much better, cleaner ways of approaching it than the one I ultimately took—but it was cool to find my own way through the fog.

Resources in Ember & Rails

Saturday afternoon was spent on Ember: integrating authentication and generating some basic routes/models/components. In the process, I switched from using a simple join table and has_and_belongs_to_many to connect Users and Patterns to building a Favorites resource and connecting Users and Patterns using has_many :through—this was prompted by a question I raised about how to do a post to this join table without having a separate, named resource; a good list of the arguments in favor of has_many :through is in the response here.

While working to get data transfer happening smoothly between Ember and Rails, I ran into some confusion around serialized data for many-to-many relationships. This was solved by tweaking my models in Rails; Ember likes to receive IDs (instead of full JSON records) for associated data (details on how to do this are in this issue).

Quilt Pattern Generation

By Saturday evening, I was ready to start tackling pattern generation. I decided to use the (aptly, if coincidentally, named) Fabric.js library to help with this because it makes exporting data from an HTML5 canvas in different formats super easy. Fabric is so fun to work with!

I also got the chance to write another, more detailed custom service in Ember to handle the pattern generation. There are a number of ways in which I’d like to continue refining this, including streamlining the code that draws each individual quilt block (a square or one of four different triangles), adding a way to calculate the total fabric yardage needed for a quilt based on the final number of blocks of each size and color, and letting users choose their own color schemes. That said, this was an amazing opportunity to revisit old code I’d written and figure out how to make it better. I cut the size of the file roughly in half, with the longest method around 50 lines (STILL VERY LONG, but definitely not 200!).

In the process, I also realized that I don’t need to upload anything to S3 for patterns—no need to store these as images; I can just store them as SVG strings which are smaller, load faster, and are easier to scale for different screens and views. Hooray! (Also: I feel like I’ve come a long way with SVG.)

Ember (Days 54-56)

One of our instructors warned us that working with Ember would be an emotional rollercoaster, and HOO BOY was that true. I cycled back and forth between “I LOVE THIS” and “I HATE THIS” more times than I can count. Two months later, I think this was largely because I was trying to do a bit more than was reasonable within my time frame, I was basing my project off an Ember repo that was given to us that had auth built in (which was nice) but also a pretty intense pre-set template of nested components/styling choices that I had to figure out how to dismantle to get what I wanted out of the project (which is still not 100% mobile responsive, which irks me but not enough that I’ve gotten around to fixing it), and, to be honest, because it was election week. Wednesday was a rough day.

I don’t have a ton to report here, which I realize makes for a less than brilliant blog post, but I will leave a couple of notes/thoughts here:

An issue I wrote on how to write a component that does the same action in multiple different parts of the app without repeating that action’s code in multiple different Ember routes. The advice I got was to inject the Ember store into the component, which allows the action to happen at the component level instead of passing it up to multiple different routes to happen there. This is unconventional at best (and some might say a terrible idea), but was an interesting way of getting rid of a ton of redundant code.

GA provides guidance on how to deploy our projects to the web using Heroku (for our Rails APIs) and GitHub Pages (for our client side code). The deployment instructions were oftentimes a bit shaky—I suspect this is the product of having so many different cohorts and instructors and of rapidly changing technology. I ended up writing a condensed, shorthand version of the Rails & Ember deployment guides for my own use that addressed some of the errors/missing information in the existing guides, and then shared it with enough friends that I ended up posting it as an issue for everyone in the cohort.

Lastly: huge thanks goes to my mom, who took the time to beta test Quiltr and actually made a quilt based on one of the patterns I generated, which is featured on the app’s home page. This made my presentation at least 10x cooler than it would have been otherwise.

Quiltr

Quiltr

Create patterns. Make quilts. Be inspired.

Live app: https://rhjones.github.io/quiltr/
Live API: https://quiltr.herokuapp.com/
Ember repo: https://github.com/rhjones/quiltr
Rails API repo: https://github.com/rhjones/quiltr-api

Go Bag

Packing lists for frequent travelers.

Our second project for General Assembly was to build an API using Rails and then build a single-page JavaScript application for interacting with it. We had solid technical instructions, but the content was totally up to us—for me, this is always the hardest part. I love making things, but inventing them isn’t my forte.

Luckily, I had a problem in need of solving. See this?

packing list chaos

Those are packing lists. Dozens of them, and that’s only counting trips I’ve taken with my partner in the past couple of years. I’ve also made lists in Simplenote, scribbled them on the backs of envelopes or the margins of research papers, and—worst—forgotten to make lists and ended up in Toronto without a toothbrush.

It’s not a gigantic problem, but it was enough to spark Go Bag, a packing list app for people like me.

Quick links, in case you want to skip the chatter below and go straight to the code:

Go Bag (working app)
Go Bag API (deployed on Heroku)
rebekahheacock/go-bag on GitHub
rebekahheacock/go-bag-api on GitHub

Dev Log

During this project, I experimented with keeping a “dev log”—quick notes on what I did each day. It was a good way to track my progress and make note of anything that stumped me/new things I discovered. I kept it in Simplenote, and kept my running to-do list at the bottom, which made it easy to move tasks up into the log one at a time as I worked on them and gave me a space to offload ideas for the future and things I didn’t want to forget (like “double check that that button still works” or “write documentation”). A++ would do again.

9/23

I started by sketching out a data model in LucidChart:

Go Bag Data Model

9/26

We were given a template for a Rails API that included a users resource and authentication. Starting there, I used Rails’ scaffolding to generate models / migrations / routes / controllers / serializers for my lists, items, and contents, then set up the relationships among everything.

9/28

On 9/26 and 9/27 we learned about automated testing using RSpec. It was the best. I decided to try to backfill in tests, but I got tripped up with authentication and, in the interest of time, decided to move on.

9/29

This was our first official project day. I spent the day:

  • Writing and testing curl scripts for every action I wanted to perform with my API.
  • Protecting lists, items, and contents by having their controller classes inherit from the provided ProtectedController class, which checks to make sure the user is authenticated before providing access to the resources.
  • Using current_user to make sure users can only access their own packing lists.
  • Sketching wireframes.
  • Building out forms and API calls from the front end for authentication, based heavily on my Tic Tac Toe app. I was pretty excited to be able to refactor my log in function(s)— I was previously using two separate log in functions, one to handle “regular” logging in, and one to handle automatic log ins for users who had just signed up. I was able to condense this into a single function, which felt great.
  • Starting to separate HTML into Handlebars templates. For this project, we were given the same client-side template as we were for the Tic Tac Toe games. One of the more confusing/frustrating pieces of that project was figuring out how and when to hide/show different DOM elements, and how to make sure that what was displaying on the page always matched up with what was on the server. My game appends things on the page and then sends data to the server, which works functionally but also a) is a lot of steps, and b) leaves open the potential for the client and the server to get out of sync. Working with Handlebars made the concept of “re-rendering” click for me—I can grab data, feed in into a template, and replace everything currently on the page or in a specific section with the product of that template. Figuring out how to split things up and making sure I had the appropriate elements in the DOM to target for replacement/re-filling was a bit tricky, but once I got the hang of it, I felt like I could work more smoothly and cleanly.

9/30

Kept working with Handlebars.

Signed up for a Heroku account and deployed my API. It took me a while to figure out that my global ~/.gitignore file had been set (as part of GA’s Installfest) to ignore all secrets.yml files, which hold the environment variables for secret keys. (I had even checked ~/.gitignore_global, which is the sample file name GitHub uses when talking about global gitignore files, but hadn’t looked inside of ~/.gitignore. Oops.) This caused issues trying to set secret keys on Heroku, and a friend and I spent a significant chunk of the morning working through this. I ended up forcibly adding the file to my repo, which felt scary and bad (but worked!), before one of the instructors jumped in and explained the ~/.gitignore issue.

After that, I went back to Handlebars and discovered the hard way that you can’t register event handlers to DOM elements that aren’t on the page when it initially loads. Whoops! I went back and fixed that by registering events on elements that *do* exist, then moved on to building functionality that allows authenticated users to create lists and add items to a list.

One of the pieces of flair I really wanted for this app was the ability to type a potential packing list item—say, a toothbrush—into an input field and have an autocompleted list of potential items to choose from pop up. This felt like smoother UX than checking off items from a super long list on a page, and it tied in with my desire to avoid having a different toothbrush in the database for each user.

I decided to use the Devbridge jQuery-autocomplete library for this. I spent the rest of the day getting this to work, with a couple of challenges/steps:

  • I needed to implement search functionality in my API for autocomplete to work.
  • Despite the fact that the library claims to allow you to pass in headers, I saw multiple issues filed on the repo from people who couldn’t successfully pass an auth token through in their API call. I couldn’t get this to work either, so I ended up unprotected the index/show methods for my items controller in order to get autocomplete to work.
  • The library makes the API call for you, but it sends back a JSON string instead of a JavaScript object. It took me more time that I want to admit to realize this. Pro tip: are your property keys surrounded by quotation marks when they’re normally not? You’re probably working with JSON!
  • The library wants your potential autocomplete suggestion data to be formatted in a very specific way, so I needed to write a function to transform the data coming back from my API.

Towards the end of the day, I had a fully functional autocompleting input field. Typing in “to” would offer “toothbrush” and “toothpaste” and “Tootsie rolls” (assuming all three of these existed in the items table already) as possible options. Success! From my perspective, this is the coolest part of my project. It feels polished in a way that not much else I’ve built yet does, and it adds functionality—it helps suggest things to users that they might forget and offers a tiny bit of serendipity (typing in “s” to get to “sweater” will give you a list of options that includes “swimsuit,” which might help you make use of the hotel pool in Boston in February). It also, I think, looks nice.

I was feeling pretty impressed with myself, but that balloon popped almost immediately when I realized I hadn’t thought carefully enough through the UX workflow to know how to build the next piece, where choosing an item from that pool of suggestions lets a user add it to a list, and typing in something that’s not in the pool of suggestions creates a new item and then adds it to the list. I spent a few minutes hashing this out, referencing Avocado (a list-making app for pairs of people) heavily in the process. At the end, I had this:

  • form will have a hidden input element for the list id
  • enter an item, save
  • after saving, display another item input field so the user can add another item
  • focus first on items that exist in db
  • figure out later how to add a new item using same UI (ultimately decided: if item exists already, save a “contents” association between the list and the item. If it doesn’t, first save the item, then save the contents association)

That gave me enough to work on starting on Saturday morning.

10/1

Giant cup of coffee and a rubber duck? Must be project week at @ga.boston

A post shared by Rebekah Heacock Jones (@rebekahredux) on

I got single item addition working, then moved on to letting users add multiple items in succession. This involved redrawing the list after saving the item, which meant going back to my serializers to make sure I was passing sufficient data through in order to be able to access a list’s title and id plus all of its items’ ids and names. I also added some validation to the Contents model so that a list can’t contain more than one of the same item.

After this, I realized that I hadn’t done enough testing after splitting up my HTML into different templates, and I had broken more of my click handlers than I realized, particularly around authentication. Bug fixing time!

Since I was working with authentication anyway, I decided to build user profile functionality, where logging in gets a user’s email and lists and renders the user’s list titles (including links that let the user edit each list individually) on the page. This gave me some code I could reuse when switching to and from different views/states while authenticated, which was an added bonus.

I fixed some more click handlers, played around with the default order of lists and items coming back from Rails, and fixed a bug that was popping up when a user tried to add an item (say, “toothbrush holder”) that starts with the name of another item (“toothbrush”). This turned out to be fairly simply, and involved using an option that comes with the jQuery-autocomplete library that is activated when you “invalidate” input (in other words, when you keep typing after already filling the input with a valid option from the list of suggestions).

10/2

I started Sunday with lots of deleting: items from lists, and lists from a user’s account. I decided not to offer item delete functionality through the client—I didn’t set up my data model in such a way that the items a user adds are associated only with their accounts, and I didn’t want users to delete items that would then be (surprise!) removed from other users’ lists. This was a deliberate decision, as I wanted the things a user adds to their list that I haven’t, as the maker of the app, already thought of to be available to other users. For example: if I seed the items table with “insect repellant” but someone else adds “bug spray” to their list, I want other users to be able to find and add “bug spray” as well. There seemed to me to be enough value in that collectively generated set of items that I didn’t want to hide it from users.

There are cons to this approach that I haven’t quite figured out yet. The first is privacy. If you add “Helga’s Wellbutrin” or “surprise birthday present for Alistair,” those items are available for everyone to see, which is a bad thing. The second relates to usefulness: as Fran, I don’t care about or want my options cluttered with Helga and Alistair’s stuff. When I was presenting Go Bag, I talked a bit about this, and I think my next step is to enable a seeded list of items available to all users, but confine new items to specific users’ accounts. Each user will be able to see the seed list plus the items they’ve added, but not the items anyone else has added. Another step past this would be to let users suggest new items for the seed list, and create an admin account with the power to approve/deny these requests. “Bug spray” would get added, but “lolBUTTS” would be visible only to troll1997@butts.butts.

After building the client-side deletion features, I added a checkbox to mark an item within a list as “packed” and updated the API to send back items in a list ordered first by unpacked vs packed, and then, within those groups, with the most recently updated items first. Given that I’m re-rendering the contents of a list each time they change, this means that clicking the checkbox next to an item to pack it will move it toward the bottom of the list, to the top of the section of packed items. Unchecking it will move it back to the top of the list. Since I’m getting all list items (“contents” in my data model) through the lists serializer, I realized I don’t need dedicated index or show methods for contents, so I removed those from the ContentsController class.

At this point, I decided to get what I had up on GitHub pages and make sure it worked with my deployed API. After I set the CLIENT_ORIGIN on my deployed API to my GitHub pages URL, everything worked as expected.

I moved back to the client side of things and moved error messages out of console.log() statements and into the UI. I also added some lightweight validation on lists, so that a user can’t have two lists with the same title.

At this point, I felt like I had all of the client-side functionality that I absolutely needed in order to submit a working project. This freed me up to start working on design, which I both like (shiny things!) and find trying: inventing a design from scratch while simultaneously writing HTML/CSS feels a bit like building the airplane while it’s already in the air. I could solve this problem by putting together better, full-fledged mock-ups with art and typography, but a) that’s not practical on the timeline we have for GA projects, and b) I’m still mourning my loss of access to the Adobe Creative Suite, and I haven’t yet bothered to acquire/teach myself replacement software.

I spent the next few hours working with Handlebars templates, SCSS, stock photography, and Google fonts. I took a few small breaks to build tiny bits of actual functionality: for example, ensuring that clicking on the app’s logo in the upper left would load the default home page for visitors but the correct user’s profile for authenticated users.

Once I had the home page and authentication forms looking mostly how I wanted them to look, I gave myself a reward and built a list title editing feature (in case your New Hampshire camping trip gets rained out and you end up going to the Catskills instead) and—this was super cool—the ability to clone lists. I find myself duplicating my lists in Google docs all the time so that I can tweak an existing list for a new trip instead of starting with a blank slate. In Rails, I used the deep_cloneable gem to clone a record (list) with its associations (contents/items). I didn’t have to write much code for this, but it felt like absolute magic. A couple of tips:

  • Cloning uses a POST request, which expects data. All the data you need is already in the database (which is why you’re cloning in the first place), so you can send an empty data object along with your request to fix the HTTP/1.1 411 Length Required error.
  • I had set up validation so that lists can’t have the same title. To make sure the cloned record validates, I had to edit the title before saving—I prepended “copy of” so that it would be clear to users what had happened.

At this point, it was Sunday evening, and rather than jumping into building new stuff, I decided to work on documentation for my API. I had Willow’s tweet in my head while I was typing:

I know not everyone gets as excited as I do about rules and guidelines, but to me, this is a thing of beauty.

10/3

On Monday, I went back to styling. Such SCSS. So typing. Cool things I learned:

  • You can use word-wrap: break-word; to break up a really long string of characters (say, an email address like rebekahheacockjones@gmail.com?) so it doesn’t, for example, run off the screen on mobile.
  • It’s frustrating to work with Handlebars templates + CSS + JS. Moving things around in Handlebars will (inevitably, again) break your functionality, and relying on the same classes for styling and functionality is starting to feel more and more precarious. I’m curious about best practices here—I know at least one company that has separate classes for CSS-related things and for JS-related things, which makes your markup a bit longer/clunkier but sounds kind of attractive at this point.

I grumble about this, but I’m pretty happy with the way the UI turned out:

Desktop

Mobile

I spent the rest of the day writing up documentation for the front-end repo (a shorter version of this, plus Agile user stories, wireframes, and a list of dependencies), putting together a seed list of items, and tweaking my API documentation.

10/4

Last project day! I spend the morning cleaning up (taking out console.log() statements; organizing and refactoring to the best of my ability) and adding a few extra touches: a favicon, a “loading” icon that replaces the “Sign Up” and “Log In” button text while the form is processing, and adding smooth internal scrolling when you click the “learn more” link on the home page.

I sent the app to a few friends and family for “beta testing,” and my sister discovered a bug when clicking the home button immediately after adding a new list—I needed to re-fetch the user profile data before re-rendering the profile view. (Thanks, Katie!)

At this point, I had half a day left, so I decided to venture back into RSpec. I started by reading through a bunch of articles on testing Rails APIs with Rspec. Many of them recommended using two additional tools, both from Thoughtbot: Shoulda Matchers and Factory Girl.

I was writing tests for one of my models, and I already had set up / tear down steps written to create and delete instances of that model for testing, so I decided to skip Factory Girl for now and experiment with Shoulda.

Readers, I liked it.

After this, I ran into an issue with authentication inside of a test that I still haven’t figured out (hoping to work one-on-one with an instructor soon, as even after some back-and-forth and trying out multiple methods of handling authentication tokens within the test, I’m still getting an “HTTP Token: Access denied” error). It didn’t put me off of automated testing, though—I’m determined to get this working. The first “real” CS course I ever took was CS50, and our first problem set included tests for the C programs we were writing. Seeing those green smiley faces was SO. COOL. I want my code to do that—to not only work the way I expect it to work, but to self-verify that it works the way I expect it to work, and to do that in a way that communicates clearly to other people who work on the same code what should happen and what, if anything, is broken.

10/5

Presentation day! Once again, the random presentation order put me at the end, which gave me all morning and all of lunch to be nervous.

excited scared gif

I used the same tactic I used last time, of pulling up a bunch of tabs with things I wanted to make sure I talked about. I overdid it a bit because I wasn’t sure how long each thing would take, so I only made it through half of the things on my list before the timer beeped, and I felt like I was rushing things and not actually making the points I wanted to make. Overall, though, it went okay—I got some good questions about my approach to testing (all the curl scripts, plus some general sadness about not working with RSpec as much as I wanted to) and—I was kind of surprised by this!—people seemed really interested in how I implemented the random travel quote that’s displayed at the top of the page when you log in. (It’s an array of strings; I pick a random one and send it to the Handlebars template each time the profile is rendered.)

Two projects down, and two to go!

General Assembly WDI, Week 6

AUTOMATED TESTING IS MY FAVORITE.

Day 25

I wrote a lot of SQL scripts today, mostly focused on joins. A few things I learned:

  • You have to use double quotes around names for things (databases, tables, columns) within SQL, but single quotes around strings. Mnemonic: “[S]ingle quote for [S]trings, [D]ouble quote for things in the [D]atabase.” The command line doesn’t care about quotes, so you don’t need to be as specific in psql commands.
  • A new convention for naming join tables: use semantic names, e.g., “loans” for a join table between books and borrowers or “amounts” for ingredients and recipes (assuming you’re specifying things like “1 cup of flour” in that table). This is a change from how I’ve previously done things, where I’ve used the combined names of the two tables, in alphabetical order, separated by an underscore (“books_borrowers” or “ingredients_recipes”).
  • Objects in a database are not just tables. Objects can also be sequences or indices (and maybe other things I don’t know yet).
  • VARCHAR is part of SQL standard; TEXT is not. But Postgres gives us TEXT, which is “efficient/optimized”—I think this means efficient in terms of being easier to write when you’re coding, but I’m not sure.
  • \i in psql reads a script file into buffer and sends it to database server. Rails migrations also do this: they generate SQL commands based on code in migration.
  • psql offers basic logic capabilities You can use bash loops to batch execute psql scripts (thanks for the correction, Jeff!): for i in scripts/cookbook/*; do psql -f $i sql-crud; done
  • UNION in SQL will joins select statements together.
  • A foreign key reference is a constraint: limits what can happen; disallows certain actions.
  • SQL doesn’t execute in order: either the whole statement is valid & executes, or nothing executes. This lets you define things (like aliases) after you use them. The parser parses the entire statement & figures out the details for you.

Not relevant to SQL necessarily, but cool: typing cal into the command line will give you a monthly calendar.

We also talked about how to implement many-to-many relationships in Rails. Scaffolding or creating migrations will set up *part* of the relationship, but you still have to edit your models to specify has_many or has_many through relationships. You also have to add inverse_of in a join table, telling the join table to be the inverse of itself. This sparked significant confusion in the class, and I’m still not clear on what this is, how it works, and why/where it’s needed.

Day 26

We continued working with data relationships in Rails.

Serving Custom JSON From Your Rails API With ActiveModel::Serializers” made serializers “click” for me, particularly with respect to using data relationships (rather than just listing all attributes out) to leverage serializers for other models. Super cool!

We also talked briefly about protecting resources in our Rails API by having our controllers inherit from the ProtectedController class, rather than the ApplicationController. Not clear whether this is a standard feature in Rails, or something that GA built for us.

Behavior-Driven Development (BDD)

This unit was one of my favorites so far: behavior-driven development using RSpec. The approach we took was:

  1. Write a user story/define a user behavior.
  2. Write a feature test that targets this behavior.
  3. Run the feature test. Watch it fail.
  4. Write a unit test.
  5. Run the unit test. Watch it fail.
  6. Write code to satisfy the unit test.
  7. Run the unit test. Watch it pass.
  8. Go back to steps 3-7 and repeat until your feature test passes.
  9. Commit your code.

Day 27

We kept rolling with BDD today. We talked about four-phase testing: (not all four steps happen for each test)

  1. setup (a lot of this happens in before(:all) and before(:each); also parsing JSON, etc.)
  2. act/exercise (actually execute the code the test is acting upon, e.g., Article.create)
  3. assert (expector should)
  4. teardown (after(:all))

My feelings about TDD/BDD can be described as:

So excited

I understand why we learned about Rails before we learned about RSpec, but I’m sad that I got a head start on my second project and set up all of my resources and THEN learned about automated testing. I’m hoping to be able to use BDD/TDD for another project, but in the meantime, I’m trying to go back and write automated tests for the code I’ve already written. More on this when I write up my project (soon, I hope!).

A few more things about testing and RSpec and Rails:

  • Rspec uses TEST (not DEVELOPMENT) environment (test database, not dev database).
  • Code within feature tests will by nature replicate code within unit tests. Feature tests are “black box” tests; they “don’t exist within Rails.” Unit tests (controllers, models, routing) exist within Rails and have access to things. Feature tests are like curl requests. Feature tests spin up a server—this takes a long time/is expensive. (This was a quick explanation to a question I asked about why we’re replicating so many lines of code between our feature tests and unit tests—why can’t we just call a unit test we’ve already defined from within a feature test? I need to come back to this; I still don’t fully understand the separation/redundancy here.)
  • All hashes that come through Rails are called “hashes with indifferent access” and will work with symbols or strings. JSON.parse returns a Ruby hash, meaning we can’t use symbols to access attributes.
  • Use more specific, less semantic tests in unit tests (and more semantic, “friendly” language in feature tests). Example: .to be_successful in feature test vs .to eq(200) in unit test.

Handlebars

We took a very quick spin through Handlebars, a rendering library (templating engine) for JavaScript. For me, this filled in some of the gaps we left by not using views in Rails/using Rails only as an API. We’re using handlebars-loader to load/process Handlebars files for us.

Days 28-29

Thus began project 2. I’m planning to write this up in a separate post, so that wraps things up for this week!

General Assembly WDI, Week 5

Rails is actually a pretty good distraction from the first presidential debate-slash-impending fall of America.

(Apologies in advance if this is more scattered than usual—trying to distract myself from the most painful parts of the first Clinton/Trump presidential debate by blogging.)

Prework

We had some fairly significant studying to do over the weekend, so I’m splitting it into its own section.

The SOLID Principles

We read about SOLID design principles, starting with “Design Principles and Design Patterns” by Robert C. Martin. Delving into these five principles helped scratch some of the itch I’ve been feeling since handing in my Tic-Tac-Toe project: sometimes jumping in and hacking bits off of a large project and tackling whatever I can tackle in order to break the problem down and get it done is exhilarating. Piecemeal != coherent, though, and it’s nice to know there are established guidelines for thinking about a more orderly approach.

The basic gist: bad design is caused by bad dependencies. It’s rigid (difficult to change), fragile (one change causes multiple breaks), immobile (code can’t be easily reused), and viscous (optimal changes are too hard and take too long; it’s easier and faster to circumvent the system). Good object-oriented design uses “dependency firewalls” to help avoid these problems.

  • Single Responsibility Principle: Classes should only do one thing.
  • Open Closed Principle: “A module should be open for extension but closed for modification.” Write modules that can be extended without having to modify them. Rely on abstraction: don’t hard code in a bunch of if/else or switch statements that lock down specific behaviors within a module.
  • Liskov Substitution Principle: “Subclasses should be substitutable for their base classes.” Subclasses should honor the “contracts” of their base classes—they shouldn’t have stronger preconditions (i.e., they shouldn’t need additional things to be true before they’re called), and they shouldn’t have weaker postconditions (i.e., they shouldn’t guarantee that fewer things are true after they’re called).
  • Interface Segregation Principle: Don’t overload a class with methods that all its clients don’t need. It’s better to have multiple small, specific client interfaces than a single large, general interface. Separate methods into different interfaces, and inherit only those that are necessary into a class.
  • Dependency Inversion Principle: “Depend upon Abstractions. Do not depend upon concretions.” High-level things shouldn’t rely on mid-level things that rely on detailed things. Instead, everything should rely on abstract things, which are less likely to break or be changed.

Sandi Metz’s rules

Sandi Metz is a developer and author who specializes in object-oriented programming. Her four rules for developers are focused on keeping things clean and modular in order to help adhere to the SOLID principles. The rules are:

  1. Class shouldn’t be >100 lines
  2. Method shouldn’t be > 5 lines
  3. Methods shouldn’t take >4 parameters (each hash option is 1 parameter)
  4. Controllers can instantiate only one object. Views can only know about one instance variable, and views should only send messages to that object.

Day 20: Code Retreat

Monday gave us a chance to put some of these principles into practice: after a quick discussion on modules (the basics: classes in Ruby can only inherit from one other class. Using modules (mixins) lets us pull in different sets of behaviors/methods and make them available to the class), we spent the rest of the day on a single problem: Conway’s Game of Life. The twist: every 15-45 minutes, we had to wipe the slate clean and start over with a different partner and a different set of constraints. These were the rules:

  • “caveman coder”: We were only allowed a dry erase market and a section of whiteboard.
  • “navigator-driver”: one person types, the other person directs
  • “silent”: can only communicate through comments in the code
  • “flat files”: write methods so things aren’t overly nested
  • “sandi’s rules”: no classes > 100 lines, no methods > 5 lines, etc.
  • “many to one”: one driver, everyone else in the class is navigating
  • “git happens”: we were forced to run git reset --hard HEAD at frequent, random intervals
  • “hot potato”: in a team of four, switch who’s working on the code every 5 minutes

This was both incredibly fun and incredibly frustrating. My takeaways: I like to start with constraints: the board size, the edge case cells. After watching a screencast on using TDD to approach Conway’s Game of Life and talking to others in class, I realized that some people prefer to start with smallest unconstrained piece, ignore the constraints as long as possible, and build from there. This is a totally foreign way of thinking for me, and I’m still struggling to wrap my head around it—even a week later, as I try to process it, my brain is shrieking “BUT…. BUT…. BUT!!!!” Being exposed to this way of thinking and having to pair program with people who tackle problems this way was good for me, though, and I’m hoping to bring a bit more of this kind of thinking into my work, if only as a lightweight experiment at first, and see how it affects my code.

Day 21

Ruby on Rails logo

I’m SO EXCITED to start learning Rails. I’ve used (and am TA-ing for a class on) Laravel, and every time I tell anyone that, I feel like the response is always about how much better Rails is. A few points of comparison so far:

  • The list of ‘uncountables’ in Laravel has long been one of my favorite things (see, e.g., bison, coreopsis, emoji, moose, plankton, pokemon, swine). To be honest, I’m finding the Rails list (in total: equipment information rice money species series fish sheep jeans police) to be sadly lacking.
  • The Rails doctrine is pretty fantastic. I 100% support the idea that “constraints liberate even the most able minds.”
  • Rails gives you the ability to set up example data (used for testing) separately from seed data (necessary for your app to function). This division doesn’t exist by default in Laravel.
  • Serializers let you control what data you pass back (to exclude timestamps or secret/private information, for example). I haven’t used this in Laravel before (it sounds like serializers come into play when you’re building APIs), but the functionality exists.
  • It sounds like I won’t get the chance to compare views in Rails/Laravel—GA only teaches Rails as a way to create APIs (all the front-end work is done as single-page applications).

Before we got too far into Rails, though, we had to learn about PostgreSQL. So far: no real opinions on this vs MySQL, though I am amused by PostgreSQL’s claim to be “the world’s most advanced open source database” compared to MySQL’s “most popular open source database.”

It was good to brush up on my SQL and to play around with psql (a client for working with Postgres), mostly because I can’t wait to start working with data models that I get to design. Front-end is fun, but back-end makes me feel powerful.

Day 22-24

Wednesday kicked off a several-day process of building three different basic APIs: a library API with books and authors, a clinic API with patients and doctors, and a cookbooks API with recipes and ingredients. We learned how to get a Rails app up and running; how to create controllers, models, migrations, and serializers (and how to use scaffold to create all four at once), how to use rails console to interact with our apps through pry, and how to use rails db to interact with the database using psql. A few related notes/tidbits:

  • Sometimes Rails servers don’t close all the way, which prevents you from opening up a new one. To fix: open Activity Monitor and look for spring, ruby, and rails. Kill anything related to those three terms, then try again.
  • Running commands using bundle exec uses the versions you have stored in your gem file instead of global versions.
  • In SQL/Postgres, foreign keys don’t get indices by default. Indices make querying fast. If you’re using an id in a query, write an index. (When you set up references in Rails, indices appear to be automatically created.)
  • SQL Joins: inner joins between tables A and B cover the intersection of data between the two tables. Left joins cover all of table A, plus additional data from table B that meet the join condition. Right joins cover all of table B, plus additional data from table A that meet the join condition. Full joins give you everything.

General Assembly WDI, Week 4

Rubber ducks, Das Sound Machine, and spaceship operators.

Day 15

Monday was project presentation day: we each got five minutes to present our Tic-Tac-Toe games, and two minutes to field questions. I found myself strangely nervous—I was proud of what I had accomplished, but the thought of having to stand up and talk about it, justifying my decisions and explaining my approach, was terrifying.

The order of our presentations was randomized, and I drew one of the last slots, which gave me some time over lunch to open up a couple of different tabs and zoom in on a few things I wanted to talk about in my code—specifically, how I automatically logged users in when they signed up (this Stack Overflow question on passing additional data to AJAX callbacks, which I mentioned in my last weekly write-up, was helpful) and how I checked whether a game had been won or tied. I was proud of the first, and not so proud of the second—I still don’t think a Tic-Tac-Toe game should take 60 lines of code to figure out if it’s over, and near the top of my to-do list is streamlining this.

Taking the time to tee up a browser tab for each of my talking points helped the presentation go smoothly, and at the end, the instructors gave us each a rubber duck, then sent us home early to get some rest. All in all: a good day.

Day 16

Our assignment on Monday night was to walk through a couple of intro to Ruby tutorials and, if we had time, to read Why’s (Poignant) Guide to Ruby. It….

I….

It’s hard to describe.

You should probably read it for yourself.

And then, if it sparks your curiosity as much as it did mine, read Annie Lowrey’s 2009 piece for Slate, “Where’s _why?.”

I read the first half on Monday night and most of the second half on my phone while I was on the train on Tuesday morning, which didn’t in any way do this book justice. I think I need to go back and reread two or three more times at least, preferably on a large screen so I can properly appreciate the sidebars and illustrations.

Jumping into Ruby on Tuesday morning after working with JavaScript for the past year and PHP for several years before that felt like the Barden Bellas felt when they first saw Das Sound Machine.

A few initial notes/impressions (please don’t use these to teach yourself anything about Ruby—this is all Day One knowledge that may or may not be accurate/correct):

  • You don’t need parentheses to call a method without parameters in Ruby. You also don’t need them, even if you’re passing arguments, unless you want to use the results of that call immediately and chain on additional methods.
  • Ruby uses symbols, which, depending on where you read about them, are “lightweight strings,” “a string and a number,” or “the name of the variable, not the value of that variable.” My best shot at describing them: symbols represent a unique spot in memory. They have the same value everywhere in your program. They’re often used in class definitions.
  • Ruby doesn’t have an increment (++) operator. Use += 1 instead.
  • Floats and fixed numbers (integers) are distinct types in Ruby. 7 / 2 will return 3 in Ruby, not 3.5. If you want 3.5, you have to attach .to_f to either the 7 or the 3 before dividing.
  • .to_i truncates, it doesn’t round.
  • Ruby hashes do not allow you to access their keys through a dot notation; you must use square braces.
  • Ruby lets you access values from the end of an array: array[-1] will be the last element in the array.
  • a.equal?(b) will return true if and only if a is the same object in memory as a. Use .eql? to test if the values of a and b are the same.
  • Along the same lines, == is used to test whether values are equal. === is not used to test equality at all, but rather to test for inclusion (is a value in a particular range?). See this Stack Overflow question for details.
  • puts vs p vs print:
    • puts calls .to_s on an object, appends a new line, and prints it.
    • print calls .to_s on an object and prints it without appending a new line.
    • p calls .inspect on an object, appends a new line, and prints it. This can be helpful for debugging because it doesn’t automatically convert everything to a string.
  • Remember my excitement about fat arrow functions in JavaScript? Ruby uses the same symbol (=>) in hash definitions, only it’s called a “hash rocket.”

Day 17

  • More on symbols: Always use symbols as keys when writing hashes. Symbols perform better because Ruby compares them using their object IDs, instead of comparing strings letter by letter.
  • You can search for symbols using Symbolhound. No more frustrating Googling for the Paamayim Nekudotayim!
  • Ruby uses blocks instead of callbacks.
  • In JavaScript, all arguments are optional. JS will try to run the function even if you don’t provide the correct number of arguments. In Ruby, all named arguments are required. In other words: Ruby checks arity (the number of arguments or operands that the function takes); JS does not.
  • The exclamation point after an enumerable method in Ruby means that the method alters the original array.
  • All operators in Ruby are methods. 2 + 2 is the same thing as 2.+(2).

Day 18

We started talking more seriously about enumerables today.

  • Ruby has three basic types of ordered lists: hashes, arrays, and ranges.
  • All of these lists are enumerable, meaning they can take advantage of Ruby’s enumerable methods.
  • In Ruby, hashes have order (in most programming languages, they don’t).
  • In order to use these methods, a class must include the Enumerable module (include Enumerable) and define an each method.
  • Other things—files, for example—are also enumerable. Anything you can iterate over is an enumerable.

We also talked about classes.

  • Ruby is a “classical” language; this means that Ruby uses classes to define and instantiate new objects. JavaScript is a “prototypal” (not prototypical) language; it uses prototypes.
  • Ruby classes have an initialize method that acts like a JS Constructor function. The instance variables set within this method are private by default; “getter” and “setter” methods defined in the class provide access to these variables outside the class definition.
  • Ruby convention is for “getter” and “setter” methods to be named, respectively, propertyName and propertyName=. We can also use attr_reader :varname (getter), attr_writer :varname (setter), and attr_accessor :varname (getter & setter) to create simple versions of these methods.

Day 19

A high-level view of Ruby (made by Artem S.):

Ruby Core Object Model by Artem S.

And a handdrawn version by Jerome Dalbert:

Ruby Core Object Model by Jerome Dalbert

We talked a bit more about enumerables today, and introduced comparables: like enumerables in Ruby, comparables are anything that can be compared, that uses the comparable methods defined in the Comparable module, and that defines a <=> method (aka the “spaceship operator,” apparently so named because it reminded PERL expert Randal L. Schwartz of the spaceship in a Star Trek game).

We also talked briefly about how defining <=> on a comparable class allows you to use the .sort method, which made me wonder what Ruby’s built-in sorting method is. Fun fact: .sort_by in Ruby uses a Schwartzian transform, a sorting algorithm named after the aforementioned Randal L. Schwartz.

A few other notes:

  • Ruby has implicit returns: the last line is always what’s returned.
  • Ruby also has an implicit receiver: you don’t have to use self when you’re calling a getter method on an instance from within a class (though you do have to use it when calling a setter method).
  • super will call the same method defined in the parent or superclass and give you the result.
  • .new is a Class method that calls the Instance method .initialize. .new allocates memory for a new object, creates that object, calls .initialize, and returns the object.
  • self will point to one of three runtime contexts: global context, object context, or class context.
  • In Ruby documentation, # indicates an instance method, while . indicates a class method.
  • def methodname inside a class is how you define an instance method. def self.methodname inside a class is how you define a class method

To sum up

Everyone I know who uses Ruby tells me it’s orders of magnitude better than PHP. I’ve people clap their hands in glee when I told them I’d be learning Ruby as part of GA. I’m hoping to feel some of that excitement soon, but for now: