Off the Rails with Phrase Ninja

Posted by Adam Weissman on April 5, 2020

Phrase Ninja is my third portfolio project within the Flatiron Software Engineering School. The title of this blog post alludes to the approach I ended up with for my third project: “Off The Rails.” Now, it didn’t exactly start out that way… but before I get to the part where my Code Train got derailed… there’s a few things I want to share.

Firstly, my background prior to coding is that I was an aspiring screenwriter/novelist, and although I had a manager for screen and television, it wasn’t enough to build a career and provide for my family, my family being the prime motivation as to why I began this journey. As for “code” in particular, one of the things that attracted me most is that the level of subjectivity (compared to the arts) is really toned down; if something works, IT WORKS! Granted there are levels elegance or eloquence etc when it comes to code, but all that seems secondary to code that works, and perhaps even “tertiary” if you consider that there is some overall Goal to be achieved. In other words, we don’t write code just for the sake of writing code. I often think about an anecdote about Elon Musk from the Ashlee Vance biography about him. Musk had (before merging companies with Peter Thiel to form PayPal) written hundreds if not thousands of lines of “terrible code” (that worked) but was a nightmare for subsequent coders to refactor as the business grew; still though, that terrible code got the job done and made it possible for Elon to pursue subsquent adventures. I’m also reminded of a similar anecdote in the Jeff Bezos book “The Everything Store”, where the author details how Amazon servers were crashing as the company grew because the initial code was not written efficiently. I’m also reminded of stories about Twitter or AirBNB refactoring and even rewriting their backend code because what they started with did not scale. I’ve often also heard a quote/mantra around Flatiron attributed to Avi Flombaum (I hope I get it right and am not butchering the intention or meaning), but it is this: “Get it working, then get it right.” So, all of that was floating around my mind when I first began the project.

Secondly, I find myself most captivated by new technologies and in particular the applications of Augmented Reality. It’s not my only tech passion, but I’ve quietly been reaching out to people on LinkedIn to find out about the skills I need to be an entry level developer on an augmented reality product. To my dismay, I discovered that Rails/Ruby is not frequently helpful for an Augmented Reality developer’s toolbox. On the other hand, I have been heartily recommended JavaScript, Node and React – Three.js in particular… I’ve also been pointed to C# for gaming components, and C++ for Computer Vision if I wanted to go really deep. Last but not least is Python. Now, I don’t want to reinvent the course into something it’s not, but even as I worked on my Sinatra project, I began to have some inkling that my first job and every job thereafter thereafter would probably not be jobs where I needed to showcase idiomatic Rails-skills or even eloquent Ruby… so much as (to quote fellow student Stephen B) to create something that scratches the complexity itch. In other words, I wanted to get something working that would be an immense stretch goal for me, moreso than I was concerned about checking off boxes that could be described as stylistic convention. While I’m in awe of DHH and other Rails developers, I had less interest in mastering the specific tools of Ruby than trying to solve a problem I didn’t think I had the skillset to solve.

Thirdly, when it comes to learning, I’m unable to learn anything if I don’t have the greater context. Since high school, I’ve discovered the learning strategy that works best for me is to skim everything the first time around, basically just to get a sense of what is possible in some given domain – whether that domain is movies, animation, drawing, speaking a foreign language, or computer programming. I want to know “the best” of what’s possible, and “the worst”, much of what’s “in the middle”, “the standard conventions” and also how to break them. To make it specific to Rails, I went through the curriculum as fast as I could, using solutions as tutorials when I had no idea what was going on and taking lots of handwritten notes so that I could try to keep as much of this in mind as possible. The faster I go, the more there is a “unity in time” of what I’m learning that helps organize the complex features into the building blocks of a more integrated whole. As I do this, I also try to use what I’m learning as jumping points to be creative, in other words: how might Such and Such Piece of Code be used in a compelling and novel way. I looked at walkthroughs of Rails projects to see what was common, uncommon, exceptional, and average. I thought about what might be possible given what I was learning and what I had seen.

Enter Phrase Ninja. By the time I finished my first iteration through the course material, I had a rather detailed idea of what I wanted to build, and where to look back throughout the course to find what I needed to ‘build the thing’ that was consuming me. Phrase Ninja was born out of the idea that Foreign Language Learning typically focuses on common vocabulary, but does very little to account for the situations that a language learner is most excited about. I wanted to build something that was essentially a wrapper for Google Translate that would allow a user to catalog phrases and make them into flashcards. Wrongfully, I thought it would be easy. Or at least relatively doable with what I had seen.

It wasn’t long before I realized that what I was trying to do was somewhat “off the rails.” To go into detail, after designing a 20 page pitch deck originally called “One Step” that focused more on The Whys of the program, I realized by the end of the rails module that One Step was not something I could build. (NOTE: I started planning “One Step” almost as soon as I finished my Sinatra Project, and it became “the creative frame” for which all other design ideas were siloed.) I also designed an overly complex keynote presentation for Phrase Ninja that originally would not be specific for Japanese. I got some brief feedback from Dwayne on the presentation and Ayana in an awesome Open Rails Study Session, that I might want to dial back my project into a more manageable MVP. I unfortunately did not take the feedback to heart and figured I would aim for the original version before refactoring (or pivoting) to a more simple approach. So, when I was setting up my omniauth for Google, I also setup the Google Translate API (thinking I would kill two birds with one stone). This was a bit of a headache because instead of focusing deeply on omniauth, I was trying to do two tutorials for two separate functions at the same time. I ended up having to give my credit card to google in order to get Google Translate API to work. Also, though I had looked over the documentation of the API, I took it for granted that Google Translate would give you a phonetic translation (the same as it does on the Google Translate home page).

It took me about a week to get login with google and the translate API working, hours upon nightmarish hours of trying to get it to work and then get the Translate API to do something that it actually couldn’t do. After my first week, I thought I was going to abandon the project until a fellow student, Sunny, felt equally captivated by the problem that was presented: the problem of “How does Google Translate provide phonetic pronunciation?” Seeing that it was possible made it hard to give up even when I probably should have. Thanks to Sunny’s insight, we found a Python library (the equivalent of a Ruby gem) that would give you phonetic translation. We weren’t sure if it was a hack or whatever, but seeing that it worked we wondered if it was possible to run the Python inside of Ruby. Having tried various ruby gems for morphological text parsing and lots or workaround, running Python inside of Ruby (while a last resort) seemed comparatively simple once we saw it could be done. NOTE: I have a file titled oldcode in github where the reader of this blog can see some earlier attempts that were ultimately commented out. The only reason I didn’t delete it from my project is I wanted to remember Those Casualties of Time and Effort to hack out a comparable solution to what Google does so effortlessly. Long story short, hacking this solution felt like such a victory that I became so invested in what I was building that I was unable to fully take stock of other functionality that would be impossible for me to accomplish with my current skillset. Things like mass assignment, and how to fake JavaScript functionality (creating the illusion of updating in place) with rails only. To clarify the illusion of JS: if you make two pages look incredibly similar, a button that redirects to the the second page will make it seem as if you’re still on the same page (so long as the user doesn’t notice the URL changing).

Once translation was working, I was ready for my next big goal. Creating a text parser that would enable a user to creatively enter as many sentences as they like, and have the program parse out translated phrases, as if by magic. I got it working, but at the expense of exploring validations in depth. Having already touched on the validations requirement with the login page, I felt that I fulfilled the requirement and continued to be consumed by the problem before my eyes. Getting the parser to work made me realize there was assocations I did not need, and so I had to go back and manually delete them.

Another bad thing I had done, a rule I had broken was that I used scaffolding without tests to generate the basic outline of the program. Although I knew to “avoid scaffolding”, the reason why to avoid it seemed primarily because a student would just use the boilerplate forms it created and not get their hands dirty learning anything beyond basic customization. I figured it would be fine for me since I suspected what I wanted to build was so far off from whatever the scaffold generated that I’d at the very least get to see how scaffolding worked. I say this was a bad thing because it generated a lot of code that I didn’t use, and even worse: it set stuff up for models and migrations that quickly became obstacles when I ran into problems and had do redo my associations halfway through the project. There was an entire day I spent renaming code MANUALLY because I was too afraid to do global replace. Basically, I had used scaffolding with a model title PhraseScore. It wasn’t until I had to assign PhraseScores halfway through the project that I realized Rails was expecting a join table while I was attempting to give it a unique “PhraseScore” object. I eventually renamed everything Scores, but had to be careful that I wasn’t derailing any existing code paths.

Ultimately scaffolding turned out to be a really shaky foundation (so let this be a cautionary tale that it does not actually make things easier unless you really are treating it as a template). After having to manually edit everything I was mentally and emotionally exhausted from making sure my associations met the Flatiron requirement and achieved the functionality I wanted. What the associations finally ended up as were/are:

User has_many Situations User has_many Scores

A Situation has_many Phrases A Situation has_many Scores through Phrases

A Phrase belongs_to a Situation A Phrase belongs_to a Score

A Score has_many Phrases A Score has_many Situations through Phrases

The project requirement for ‘the join object being editable’ was somewhat easy since the phrase was the central point of the project. The score model was somewhat problematic from the beginning because I sorta forced functionality on it to meet the requirement that I might’ve designed differently otherwise. Ultimately, it worked out nicely because The Scores (while annoying to have them generated at the same time as the user, in order to avoid dependency issues when creating Phrases) turned out to be a nice reference point when I got deeper. Essentially, the scores are treated almost as constants, where the user accesses their functionality in the context of using them as a filter for flash cards.

Anyways, the scores controller ALSO turned into a mess because I was attempting to do mass assignment. I was able to get the mass assignment/filtering working fine for situations, but because I couldn’t figure out how to have a flash card study “session” (from sessions) keep relevant data stored, I had to create a workaround when I ended up filtering data for the scoring cycle. The methods that I used to get me into the cycle were not the same once I was in the cycle since the filters were already in place. I couldn’t figure out the Rails Way to get it done, and I did not have the patience to learn Rails magic for something that was even ill-defined in my own mind. To understand what was happening, I peeled back all the Rails magic substituting direct html forms into the code. Because it was html, I was unable to get the authenticity tokens. I tried a workaround using GET requests instead of POST or PATCH (thanks to Daniel Dawson for letting me know this was possible although he did recommend against it). So, getting it working with GET was a step in the right direction. I refactored the HTML to include an authenticity token, but Rails was still giving me trouble. OR, perhaps I should say I was still giving Rails trouble… long story short, it wouldn’t accept the params I was passing, some error that lead me to get lost in the woods of Mass Assignment. The workaround was to force compliance using params.permit!, something I have yet to refactor.

But, getting to a point where I could see my app working, where I could actually use it, being mentally and emotionally exhausted to top it all off, brings me here. To the point where I want to see if I can pass the assessment with what I have, and if I can’t, know more simply what I must change in order to meet the minumum.

A final reflection, while I have regrets about not having gone about the project in a more “Railsy” way, I also realize that had I been more conscientious and tried to build what I wanted “correctly”… I quite possibly would’ve really derailed my studies with a project that would’ve taken me months instead of weeks. I’m grateful to have “hacked” solutions with limited resources and community support, especially in the context of what I hope to do in the future – I feel that I’ve learned how to tackle problems that are way beyond me – but at the same time, I wonder if I would’ve been better off building a best practice template that showcased “attention to detail” as opposed to “creative problem solving.”

In the future, when I demo this for companies, I hope that what I created can be seen in the light that it was created: an attempt to tackle the unknown, and though I limped to finish line, humbled but not humiliated, I DID make it to the otherside, and hopefully, when I finally get a secure version up on Heroku the user will only be inspired by the project’s novelty and never have a clue of what went into it.

VIDEO WALKTHROUGH

https://www.youtube.com/watch?v=nw8IlUhbXTs