Blurb Engineering Blog

How we work.

By Andrew Hao ( @andrewhao )

A quick glimpse into developer life at Blurb:

I get into the office around 9AM and check email (and make my daily coffee – can't miss it). Great. I usually spend some time cleaning up correspondence between some product owners and our tech leads discussing new ideas around new product X or feature Y. A quick scan of New Relic and Airbrake reveal that the site's humming along well.

I log into Slack, our central communication hub, and quickly get a dump of the conversations by our teammates in the morning and last night. Surprisingly, something as simple as a central chatroom has helped break down some communication walls in our teams. Furthermore, having a continual log of team conversations have helped our remote workers keep abreast of continual updates.

Agile @ Blurb

I log into JIRA and scan the board to check our team status. Looks like we still have a lot of in-progress stories. I'm currently not assigned anything.

At our daily standup, Chris, a senior engineer on my team, is working on story Z. I decide I'm going to ask Chris how he's doing and see if I can help.

Chris tells me that he needs some help writing a few tests for his feature, so I pull up a chair and we jump right into it – he implements the feature, I write the tests. Time passes. We finish our work and we push our branch up to the Github server for some code review.

Code reviews

Code review on our team is done via the pull request system – we ping our teammates on Slack and ask for a code review. The comments begin flowing in, and we respond.

Simulataneously, we're looking at Code Climate to give us feedback on how our code fared against its static analysis tools. Hmm, we seem to have bumped code complexity a bit in this class, so let's sit down and refactor some more. We push a change and we ask folks in our chatroom to help give comments once more.

Pull requests

(Lunch rolls around. We grab some coworkers to stretch our legs and go to lunch at Senor Sisig, which is probably the best food truck in the world.)

Our team works on a 2-"+1" code review system. When we make code changes, we need at least two other people on the team to approve our code. It sounded a little daunting at first, but it's been a good way to make sure that the right conversations are happening around the code.

Robots make life better.

Our hubot sits in the chatroom and tries to be a little helpful. Here, hubot chimes in and lets us know that two people have given their approvals.

Slack chat #dayinthelife

Continous integration @ Blurb

We merge the pull request and wait for Jenkins to run the unit test build. We keep a big ol' build light on a bunch of build monitors in our work area. If you break the build, the big red light will radiate waves of Shame upon you until you fix the build.

Build light: green is good.

Once the build passes, it automatically deploys the app to an internal staging environment – the whole process happens within 15 minutes.

Sheree, a test engineer on our team, has already run ahead of us and written some RSpec integration tests prior to our implementation. Chris has already sat down with Sheree prior to beginning the story and defined some of the stories and use cases, BDD-style. We work together to get the integration tests running and working.

All in a day's work.

A big reason I enjoy working here is because our daily development workflow encourages teamwork and collaboration, and you can get feedback on your changes very quickly. And we're making it better! Stay tuned as we continue to write more about our engineering practices.

Andrew is an Engineering Manager on the Web Team. He has long ago acknowledged the superiority of Senor Sisig burritos and looks forward to them all week.

Tagged: engineering, day in the life

Tips for Making I18n-js Play Nicely with Ember and Ember Validations

By Chris Zhang

Ember's handlebars helpers don't like having too many arguments (it allows you to have one argument and an options hash). It also doesn't like nested calls, such as having one helper that takes the result of another helper as an argument.

A while ago, we added a helper method for i18n-js that allows us to translate things in the view layer. In order to make the helper method handle string interpolation with nested translations (for example: translating "If you want to learn more, click #{here}", and then here is itself a translated link to somewhere), we make the translate helper recognize a special suffix. We called it Key, so any translation key that ends in Key represents a nested translation (such as hereKey for the earlier example).

Then, we translate the key before passing it in for string interpolation as part of the original translation.

The final method looks like this:

Ember Starter Kit

We also had some trouble using the message field in ember-validations, as it overwrites i18n and appeared to be setting the message value before we set our i18n-js locale, causing all our error messages to be stuck in english.

We fixed it by changing the message from trying to translate the string to just returning the translation key and changing the view accordingly to handle the translation there instead.

Ember Starter Kit

Tagged: ember.js, i18n-js, ember-validations

Ember Data Sideloading for Objects with Nonstandard Primary Keys

By Chris Zhang

This builds off the sideloading ApplicationSerializer introduced in this article and adapts it to handle JSON objects that don't have "id" as their primary key. Several instances where this might come up are when handling currencies or countries.

Let's start with data that looks like:

Sample Data

If we import this using the previously defined sideloading serializer, we end up with four currencies, each with a generated id, which can quickly start to clutter our store when we start handling dozens or hundreds of prices in multiple different currencies.

In order to fix this, we need to look at the sideloadItem method.

Original

primaryKey in this case is always set to "id", because this in our case refers to the ApplicationSerializer rather than the serializer for item. Ember Data always needs an id for its records, so what we can do instead of letting each currency generate a new one is set the id here based on some other primary key.

At this point, type is the class of the item (App.Currency) and item is the payload of the json, which should look something like this (note that it hasn't been run through the serializer for its class yet):

{  iso_id: "USD", symbol: "$", delimiter: "," }
                

So what we can do is shove an id into the item before pushing it into the sideload array, by checking to see if the class has a nonstandard primary key that we want to store as the id of the item.

new

And to set the rawPrimaryKey (raw because we want the key the json gives us, because we haven't yet normalized it to the class's actual ) as a class attribute, we need to reopen the it, as so:

App.Currency.reopenClass({   rawPrimaryKey: "iso_id" })
                

Now, loading the data gives us four prices and two currencies, with the added bonus that comparing the currencies of two prices with the same currency should now work as expected without needing a custom compare method.

The final file looks like:

Final

Tagged: ember.js, ember-data, sideloading

Sorting Ember ArrayController

By Estella Madison ( @chicagoing )

Last April, I presented at the Ember NYC meetup to discuss different approaches for sorting an ArrayController. This is a short synopsis, you can learn more in the meetup video and slides.

To sort an Ember ArrayController you may have used the included SortableMixin sortProperties and sortAscending properties. These are great and work really well for sorting on one property or sorting on multiple properties all in the same direction. But that's a huge caveat, SortableMixin does not allow you to sort by multiple properties in different directions.

For example, suppose you have a list of books you want to display, with the most recent at the top. Using the SortableMixin, this is done by setting sortProperties and sortAscending, and then referencing the arrangedContent property in our template or view:

Ember Starter Kit

While sortProperties lets us specify more than one property to order by, you'll notice sortAscending is a boolean value and gets applied to all properties. But what if we wanted to sort by the publish date first in descending order, then by title in ascending order? We're unable to sort in different directions without applying a custom sort function (which can get cumbersome).

Welcome Ember.computed.sort

The Computed Property Macros have several methods for creating common types of computed properties. Ember.computed.sort allows us to:

  • Sort properties by different criteria
  • Use a custom comparison function when needed
  • Sort by computed properties

Ember.computed.sort has two parameters:

  1. dependentKey: a reference to the content we're sorting
  2. sortDefinition: either a string referencing the property key containing the sort definition or a custom sort function

In the example below, sortDefinition is the property key propertiesToSortBy, which is an array of property keys and the corresponding sort direction. By default, properties are sorted in ascending order, but by appending ":desc" to the key we can change the sort direction. This allows to specify a different sort direction for each property:

Ember Starter Kit

Your template or view will need to reference sortedBooks to reflect the sorted content. See the complete JS Bin example.

Don't forget to check out the slides below for links to more examples.

Ember.js NYC Meetup Presentation

Tagged: ember.js