Lessons in Development: my first phase in review
After having gone through the first half of the curriculum, I don’t doubt the fact that I’ve grown as a developer. I’ve faced a lot of tough challenges and had my share of losses, but came out stronger each time. Here’s some of the things that I’ve learned.
Avoid excessive normalization
So I’ve always had this tendency to be very granular with my ActiveRecord models. The way I see it, if it’s something that can ‘owned’ by another and can have its own unique set of attributes, it needs its own model.
This is a part of a sketch I made for planning out my Danebook’s models. The idea was to have the user choose only among a pre-loaded set of address components which they would select using a collection tag. Each component would belong to an Address model, which would belong to a polymorphic :addressable identifier. This identifier would serve as a generic pointer that would allow me to add parent models as needed.
This setup quickly proved to be very difficult to work with.
First, I would’ve had to actually populate the tables for these ‘address components’. Never mind the fact that I would have had to scour the web for a suitable open dataset; having an entire world’s worth of addresses would bloat my database and slow it down considerably.
So much for scale.
Second, it wouldn’t be enough to just have data for these models. Each one would have to relate to the other, since a City is nested within a State, which in turn is nested within a Country. This means more code than is necessary for such a simple problem, and therefore more assumptions that need to be made when iterating on the project. In other words, it’s a brittle solution.
Don’t normalize unless absolutely necessary, especially when the problem is a very simple one to begin with.
Still, I didn’t want the user to simply input random data as an address. I wanted my app to only accept legitimate and verified physical addresses.
Delegate when it’s an option
My solution was to utilize the Google Geocoder API.
Using a plain old ruby object(PORO), I wrote some helper methods to query the Geocoder API. Basically, I’d grab the address information from the parameters in the POST request, prepare it for use in the PORO, then query the API for a matching location. A response of ‘ZERO_RESULTS’ means that the user has to input another set of address information in order to complete his profile. It’s a far more elegant solution because I’m delegating a task to a more qualified source. And who can be more qualified than Google in verifying addresses?
Lesson learned: delegate tasks when the option is available.
You need to deploy.
You have a deadline. Your team won’t wait for you. You need to deploy, and you need to deploy yesterday (yes, I mean yesterday).
Things can go wrong, sometimes through some fault of your own. That doesn’t change the fact that your team needs something from you. And that something is your feature.
So find a solution. Think hard, and think fast. Try this, or maybe that. You can’t let your team down.
You need to deploy.
It was non-negotiable.
We queried the Zillow API for neighborhoods around the user’s location. The user would then be greeted with a search results page, with a map of the area to his left and bootstrap cards for each neighborhood on his right.
But we needed that map, we needed it to have markers, and we we needed the labels to pop up whenever he clicked on the corresponding card.
It was uncharted waters for me, but I pressed on.
I tried interpolating my coords hash, the same way I would interpolate ruby logic in the rest of the template. It seemed to work, but I serialized it to JSON to be sure.
Once I knew that it worked for the user’s location, I applied this process to the neighborhoods.
This is where things get tricky. I needed to add a marker for each of the neighborhoods from the search results. I also needed a marker for the user.
A quick google search allowed me to recall my Code Academy days. And this is what I came up with:
I concluded this for loop by tacking on a listener to the current iteration’s marker and pushing that marker to an array.
So the markers were working, the on-click events were firing, and all was well. The project was completed and presented during our final SCRUM meeting before the break.
But I felt like the maps needed one last little bit of polish: events that would fire other events.
Some research showed me that it was actually possible to grab an array of nodes in the DOM tree by class name. Who knew?
I had more time on my hands, so I didn’t mind using a function that utilized a callback, such as find(). Seemed simple enough: use the same logic from earlier code and iterate through the cards, finding each one’s corresponding marker, and tacking on a listener.
And it worked!
… except for the fact that each card fired the event for the same marker.
Basically, just nest the iteration’s function in an Immediately Invoked Function Expression (IIFE) and pass the value you want it to have a copy of, as an argument to that IIFE.
Final lesson before the break: take some risks. Sometimes, all it takes is a leap of faith.