Grace Teng

Blog

Le Wagon: Rent-a-Pokémon and Rent-a-Pokémon Redux

Now that Heroku no longer has a free tier, the Rent-a-Pokemon demo is no longer online. You’ll have to review the repo, the write-up and the screenshots.

About Rent-a-Pokemon

At Le Wagon, every student builds an Airbnb clone in five days as part of a team. The Airbnb project is the first time during the Le Wagon bootcamp when we work in groups and collaborate on code. Although it’s universally referred to as the Airbnb project, the web application does not have to involve apartment rental at all, as long as it involves the creation of a two-sided marketplace (e.g. mentors and mentees, party hosts and guests…).

Our group chose to build the Minimum Viable Product of a service that allows users to put up their Pokémon for rent, or to rent other users’ Pokémon.

Naturally, this project built on many of the skills we put into practice in Mister Cocktail:

  • Database design: the database schema gets a bit more complex, with five models and more associations between models:

Schema design for Rent-a-Pokémon

  • Data retrieval using an API: we used the excellent PokeAPI to populate our Pokédex models. For the sake of simplicity, we seeded only five species of Pokémon during development and 50 for our project demo. Don’t worry, I rectified that in Rent-a-Pokémon Redux, with all 151 Generation I Pokémon in the seed.
  • RESTful routing: not only did we have more RESTful routes to implement this time, but we had nested routes for the first time during our bootcamp:
resources :pokemons do
  resources :rentals, only: [:new, :create]
end
resources :rentals, only: [] do
  resources :reviews, only: [:new, :create]
end

There was one key aspect of building Rent-a-Pokémon that was new:

  • JavaScript plugins: We covered the use of JavaScript plugins during our front-end development module prior to the project weeks, but this was the first time we used plugins (specifically flatpickr) in a Rails project (other than the standard jQuery / Popper / Bootstrap JS combo). I was trying to figure out how to create a date picker for the rental page when our instructor sent us this article written by Sonia Montero, a Le Wagon instructor in Bali: Date validation and flatpickr setup for Rails. I write a little bit more about this below, under “A point of learning”.

Rent-a-Pokemon Redux

Post-bootcamp, I forked the Rent-A-Pokémon repository and continued working on it, with the goal of making the code cleaner and more maintainable, the layout more responsive, and fixing niggling bugs and finishing features that we did not get to in time during the week of the project itself.

The changes I made included:

  • Cutting off unhappy paths by preventing invalid user input from being submitted to the server
  • Displaying and giving ratings using stars instead of simply a number
  • Making the website fully responsive on mobile devices
  • Stripping out the CSS and rebuilding it from scratch

The last point is worth some elaboration. My teammates and I had put in a good amount of work to make the site look good, so why did I choose to throw out the CSS and start over? A big reason I opted to do that after reviewing the code was that we did not have a unified code style or approach to our front-end development. Everyone coded in the style that we each felt most comfortable with and fell back on habits that we’d developed before the project. One person would put headers inside the container, while another would put headers outside; some views utilised the Bootstrap grid while others did not; some components received custom styling while others defaulted to the basic Bootstrap class (or worse, were not styled at all).

Normally, in a team environment, code would be carefully reviewed for both function and style before any pull requests were merged to the master branch. Being new to working collaboratively, however, we approved each other’s pull requests as long as the feature was complete and there were no conflicts. Our product developed quickly, but we were accumulating a lot of unnecessary technical debt. When I returned to the project weeks later, I found myself lost and confused about where to start untangling. Stripping out the CSS and rewriting it from scratch allowed me to kill two birds with one stone: I could rebuild the design system, and at the same time make sure that the code was consistent throughout the web application.

Of course, this type of waste (as Scrum’s Jeff Sutherland would term it) would be unacceptable in a professional setting. This is a lesson to be learnt once, in an educational context, and remembered in future workplaces.

A point of learning

After we finished our Airbnb project and presented the demo to the class, I went home and showed my sister the Rent-a-Pokemon website on my phone. “Play around with it,” I said. “Everything should be working fine.”

Within minutes, she returned my phone to me with a big red ActiveController error page: she had managed to submit the rental form with an end date before the start date.

I was baffled. Our Rental model has a custom validation that prevents the rental from being created if the start date is after the end date, and this was the error that she had triggered. But before the dates even get sent to the server, the flatpickr code I’d copied from Sonia’s article was supposed to prevent the user from selecting an end date before the currently selected start date.

Here’s how the date pickers are implemented:

  1. There are two text fields, one for the start date and one for the end date.
  2. The flatpickr date picker for the start date is initialised, with a predefined set of unavailable dates sent over by the Rails controller to the view, embedded as part of a HTML element’s dataset and retrieved by JavaScript to be given to the flatpickr initializer.
  3. The date picker for the end date is initially disabled. (To be more precise: the text input that contains the end date is disabled.)
  4. When the user selects a start date, the text input for the end date is enabled and the flatpickr date picker for the end date is initialised, with a minimum date equal to the start date. This prevents the user from selecting an invalid end date.

However, it does not prevent the user from selecting a valid end date, and then changing the value of the start date to an invalid date after the end date. That was what my sister had done.

The fix is easy: add a validity check whenever the start date changes. If the new start date is after the end date, clear the end date input:

const startDateInput = document.getElementById('rental_start_date');
const endDateInput = document.getElementById('rental_end_date');

const checkValidity = () => {
  const startDate = startDateInput.value;
  const endDate = endDateInput.value;
  if (Date.parse(endDate) - Date.parse(startDate) < 0) {
    endDateInput.value = "";
  }
}

startDateInput.addEventListener("change", (e) => {
  // if start date is after end date, clear existing end date
  checkValidity();
});

There are some obvious lessons to be learnt from this:

  1. Your product must hold up to real-life use. User behaviour is unpredictable, but no user should be able to break your application within minutes of monkeying around. Test thoroughly and often with people who don’t know how your product is “supposed to” work.
  2. Adapting tutorials to your use case is not simply a matter of copy and paste. Sonia’s article is written for Le Wagon students doing exactly what we were doing, building an Airbnb clone. There are doubtless many other Le Wagon students who have implemented a date picker by following her article. I assumed that if I followed the instructions to solve the problem, I would be home free. Had I invested a little bit of my own time playing around with the result, I would probably have caught the bug myself. I can’t blame the fact that the code didn’t catch unexpected user behaviour on the tutorial — I copied it and I put it in the project. The bug is on me.
  3. If you’re afraid to touch copy-pasted code, you might not fully understand it yet. Part of why I didn’t dig deeper into the code to begin with was that I didn’t want to break it. I didn’t quite understand how it worked, I only knew that it did work. When I returned to the project a few weeks later to improve it and fix its issues, I had a few more weeks of JavaScript under my belt, and now the magic incantations of the code made much more sense. I didn’t have the confidence to mess around with the code then, but now I do.

Takeaway

After a group project like this, I gained an appreciation for a lot of the organisational infrastructure that enables different individuals to work on a product as a single team. We had daily standup meetings and development moved at a quick clip because the team communicated freely and frequently. Everyone was always on the same page about what items in the backlog we were going to work on next. On the other hand, we didn’t review code for style or establish UI/UX guidelines, resulting in code that was difficult to maintain and a disorganised user interface.

All the same, I’m proud of what we managed to achieve in five days, and we took many of the lessons we learned from this project into the development of Macrotery, which was a much more complex product to build.

Le Wagon: Mister Cocktail

Now that Heroku no longer has a free tier, the Mister Cocktail demo is no longer online.

About Mister Cocktail

Mister Cocktail comes at the end of week 6 of Le Wagon’s Full-Stack Web Development bootcamp, and it is the first chance for students to synthesise everything we’ve learnt into a single Rails project.

The assignment is simple: build a cocktail recipe manager. Users should be able to perform the following actions:

  • View all cocktails in the catalogue
  • View details of a single cocktail, including ingredients and a picture
  • Add and delete doses from a cocktail recipe
  • Add a new cocktail to the catalogue, including an image of the cocktail

In order to do this, I needed to:

  • Design a database schema: a cocktail can have many ingredients and an ingredient can go into many cocktails, so the relationship between cocktails and ingredients is many to many. To normalise this relationship into two one-to-many relationships, I created an intermediate table for doses: a dose belongs to one cocktail, and contains one ingredient.
  • Retrieve information from an API: the database is seeded using information from TheCocktailDB’s free API. Because TheCocktailDB’s free API has limitations, the information that goes into the database seed is inaccurate: the cocktail names are real, the ingredients are real cocktail ingredients, but the doses are randomised and fictional. Nonetheless, I needed to pull data from the API and store it in the database in order to populate the recipe catalogue.
  • Attach images to Active Record models using Active Storage and Cloudinary: TheCocktailDB’s API includes a link to a photo of each cocktail in their database. I pulled the URL of each photo, uploaded the photo to Cloudinary, and attached it to the corresponding Active Record cocktail instance using Active Storage.
  • Understand and adhere to the Rails model-view-controller (MVC) architecture: the first time I came across the term “model-view-controller” was in CS50x, when I attempted (and failed to complete) the iOS track. Back then, I simply did not understand what the MVC architecture was, and did not have a good way to learn about it until I started the Le Wagon bootcamp. Over time, through repeated exposure to the concept and Le Wagon drilling the MVC design pattern into us, I came to understand the division of work between these different facets of software design.
  • Create RESTful routes in Rails: the routing for Mister Cocktail is pretty simple, since there are only four routes. It’s a good first step into RESTful routing before the routes get more varied and complex during the project weeks (see Rent-a-Pokemon and Macrotery).
  • Implement a web design using HTML, CSS and Bootstrap: this is a bread-and-butter task for front-end and full-stack developers. Many Le Wagon students take the opportunity to go above and beyond the bare necessities of a navbar and a list of cocktails. In my case, I selected 11 cocktail-related images from Unsplash and made a rotating background. I also made the layout (mostly) responsive for mobile devices.
  • Deploy the app to Heroku: a web application is useless if it only lives on localhost. I deployed my app to Heroku, though unfortunately with the end of Heroku’s free tier, the app is no longer online.

Post-bootcamp

After the bootcamp, I returned to my Mister Cocktail project with two main goals:

  1. Paginate the cocktails#index page: my Mister Cocktail seed pulls every cocktail beginning with the letter A, then B, then C, and so on, until the API stops responding or the Heroku dyno terminates the process, which usually happens somewhere between the letters N and T. That’s hundreds of cocktails, and having 371 pictures of cocktails load on the index page is a suboptimal experience. I used the Pagy gem to paginate them, resulting in a faster load time and an improved user experience.
  2. Improve responsiveness: we’re given two days to complete Mister Cocktail, and at the end of those two days my layout was not fully responsive. Cocktail images would extend past the viewport width on mobile devices, and text would align oddly when the viewport shrank below certain breakpoints. I reworked the layout a little to make it display properly on screen sizes as small as an iPhone SE (320 x 568). There’s still a misaligned button that bothers me a bit… but I’ve decided to put this project down and move on to other things.

Takeaway

Mister Cocktail was the first Rails project I did that felt like a real web application. I’ve since built bigger and more complex Rails projects, but I have a great appreciation for the fundamentals that we had to learn in the bootcamp in order to truly understand the plumbing of Ruby on Rails. Mister Cocktail laid the groundwork for what I’ve since been able to accomplish with Rails.

Working Stiff by Dr. Judy Melinek and T.J. Mitchell

I wanted to learn about human anatomy.

I bought a college anatomy textbook, but found it too dense for a biology beginner. I tried watching Crash Course: Anatomy and Physiology, which was a lot more accessible, but was still a firehose of information that I struggled to fully digest. When I found myself staring blankly at the Michigan edX Anatomy course instructor writing “osseous tissue” on the screen, I decided I needed a different approach.

I needed something oriented around a story, I decided — I needed to find a story of anatomy. Maybe something about clandestine dissections of human bodies, or about how each bone and muscle was discovered and christened, or whatever. Just not a college textbook.

I went on Amazon and searched for narrative non-fiction books with a focus on anatomy. That is how I found Working Stiff by Dr. Judy Melinek and her husband T.J. Mitchell. Working Stiff is a memoir about Dr. Melinek’s two years at New York City’s Office of Chief Medical Examiner (OCME), where she learnt how to conduct autopsies and piece together the stories of the dead.

I’ve been fascinated by medical memoirs for years now, especially the “making-of-a-doctor” subgenre. Like any good genre story, there are certain tropes that appear in every making-of-a-doctor story: the bright-eyed new doctor, fresh out of medical school, sees their first patient; the new doctor who doesn’t know how to insert a cannula/suture an incision/other routine procedure and has to be taught by a nurse; the new doctor’s first code, first death, first “I’m sorry, we tried our best”; the crazy shifts that leave the new doctor falling asleep anywhere it is possible to fall asleep (on a gurney, in traffic); spousal troubles from the doctor’s gruelling workload; the shift where the young doctor is alone overnight and a patient comes in in awful shape; the case where the young doctor nearly misses a serious diagnosis; the case where the young doctor saves the day and gets a laconic utterance of praise from their attending physician; the doctor’s last month, last week, last day as a resident, before they morph into a beautiful, complete, board-certified physician or surgeon.

Well, Working Stiff is a very different kind of making-of medical memoir. The story starts with Dr. Melinek as an overworked first-year surgery resident who quits after she has to perform an appendectomy on a patient while she (Dr. Melinek, not the patient) is feverish with the flu. She switches to pathology, loves it, and her chief resident tells her to do her forensic pathology rotation in New York:

“If you really want to learn forensic pathology, do a rotation at the New York OCME,” my chief resident advised. “All kinds of great ways to die there, and the teaching is brilliant.”

That is how Dr. Melinek finds herself as a forensic pathology fellow at the NYC OCME after completing her pathology residency.

It is true that New Yorkers die in unexpected ways, although I don’t know if I would call them “great”. I’m not going to lie, though: some of the stories made me wish I were still living there, always on the edge of adventure. Dr. Melinek tells of the time she was assigned to the “postal bin case”, when the NYPD hauled an postal bin containing a dead body to the morgue for the medical examiners to deal with. (Spoiler: homicide by heroin poisoning — yes, you read that right.) During a fight, a man is pushed into a 300 deg F steam tunnel and boils alive. (Boils dead? Boils dead alive?) A decomposed body is found on its knees with an electrical cord around its neck, and words scrawled in blood in the kitchen and on the bathroom door.

For the forensic pathologist, the focus is finding the “proximate cause of death”: the last mechanism that resulted in each person’s death. Their analysis is medical, not legal or criminal. We think of people as dying in car accidents or falls, but medical examiners think of them as dying of “acute intrathoracic aortic transection” (translation: broken aorta) or a “subdural hematoma” (translation: bleeding between the brain and the skull). I learnt some physiology that I’d never thought about. For example, medical examiners can reconstruct the order in which bullets hit a victim based on how much blood there is around each gunshot wound, because arterial pressure drops and less blood hemorrhages out of later wounds.

This is a making-of medical memoir that, by nature, turns most of the common medical tropes inside out. For doctors, as for most of us, a dead body is an ending, but for forensic pathologists, it is a starting point. Through each dead body, Dr. Melinek is able to reconstruct each person’s last moments, bringing their stories to life. Instead of the nervous meeting with the first patient, Dr. Melinek dissects her first cadaver and fails to figure out how he died; instead of a nearly-missed diagnosis, Dr. Melinek makes a mistake on a death certificate that could have sunk a homicide trial. She has victories, too: a woman with narcotics in her urine dies after a blood transfusion. Hospital doctors write the death off as narcotic-related, but Dr. Melinek’s investigation leads to the unusual (and correct) diagnosis of TRALI (transfusion-related acute lung injury), and gets a laconic utterance of praise from the chief medical examiner.

An essential component of the medical memoir is the origin story: what made the author decide to become a doctor? In this case, the story about quitting the surgery residency is mechanistic, almost like a plot device that leads to our heroine finding her calling in pathology. The real origin story that runs like an undercurrent throughout the book and gives Dr. Melinek’s work purpose is the suicide of her father Menachem Melinek. The senior Melinek’s death, which happened when Judy Melinek was just 13, shapes how she thinks about her profession. Suicide is perhaps the most brutal type of death for those left behind, and Judy Melinek understands more acutely than most our need for answers from those who cannot give it.

“Taceant colloquia. Effugiat risus. Hic locus est ubi mors gaudet succurrere vitae.” I stared at the words… The security guard’s expression softened; it was clear that she had greeted a lot of stunned people walking into that building. She glanced back at the polished silver motto and said, “‘Let conversation cease. Let laughter flee. This is the place where Death delights to help the living.’”

A shadow that necessarily looms large over the story is 9/11. The book contains the level of precise detail one would expect from a forensic pathologist, including the dates of autopsies, and their proximity to September 11, 2001 is impossible to ignore. As I read, I wondered when I would get to the chapter on 9/11, and when it came near the end of the book, I was not disappointed. From the point of view of narrative structure, 9/11 is a natural climax. Nothing tops 9/11 in terms of drama, or in terms of magnifying the importance of the medical examiner’s office.

Even the 9/11 story turns a medical memoir trope on its head: it begins with one of Dr. Melinek’s friends, an oncologist who rushed to her hospital on the morning of September 11, and sat with her fellow doctors waiting for mass casualties to arrive. They didn’t.

In terms of medical complexity, 9/11 did not seem particularly interesting the way it was described in the book. There was no mystery about the cause of death. That’s not to say the 9/11 story is shallow or insignificant – quite the opposite. The 9/11 chapter documents Dr. Melinek’s boots-on-the-ground perspective of a massive logistical effort to solve the mystery of how many people died in the World Trade Centre buildings, to give the bodies and bones names and to give families and friends closure. It’s clear that Dr. Melinek enjoys the intellectual challenge of her work, but it is just as clear that the driving force of her work is emotional in nature. Medical examiners seek out the stories that dead bodies tell, in order to bring peace to the living.

I wanted to learn about human anatomy. Did I get what I wanted? I certainly didn’t get an anatomy textbook. I got, instead, one heck of a story about anatomy and physiology as dynamic disciplines, and a story was what I was looking for. I’ll be cherishing the experience of reading this book for a long time.