Programming

Using jQuery Deferred to wait for multiple Backbone models to save

Backbone's Model implementation is great for most things, but one thing I've had a hard time with is waiting for multiple models to save before proceeding. Backbone offers a success callback like this:

model.save
  success: ->
    alert("We did it!")

You could also use the sync callback like this:

model.on 'sync', ->
  alert("We did it!")
model.save()

But what about when you want to wait for multiple models to finish saving, all with their own asynchronous requests?

Don't Nest It. Chain It!

The jQuery Deferred object is a chainable utility object that can register multiple callbacks to relay the success or failure state of an asynchronous operation. Lucky for us, Backbone's model.save() method returns a jqXHR object, which implements the Deferred API. This means that instead of writing this:

model.save
  success: ->
    alert("We did it!")

We can write this:

model.save().done(-> alert("We did it!"))

That's a nice bit of syntactic sugar, but it still doesn't address our original problem: How can we wait for multiple models to save, and then fire the callback to alert the user?

Tell Me When You're All Done

jQuery.when allows us to combine multiple Deferred objects into one aggregate Deferred object, such that we can chain callbacks to be executed only when all the objects have resolved.

For sake of example, let's say we have a collection of 3 Backbone models we'd like to save:

collection = new MyCollection([{name: "Steve"}, {name: "Dave"}, {name: "Tom"}])

Remember that Backbone's model.save() returns a jqXHR object, which acts as a Deferred. So we can run:

xhrs = collection.map (model) -> model.save()

This will create an array xhrs containing the jqXHR objects for each individual save operation. To alert the user when all of them complete, we can use jQuery.when:

jQuery.when(xhrs...).done(-> alert("All of them are saved!"))

Note: The splat (...) syntax above is required to split the xhrs array into separate arguments. This had me stumped---without the splat, jQuery treats the array as a single Deferred object, which obviously doesn't execute the callbacks in the same manner as multiple jqXHR objects.

And Tell Me When One of You Failed

We can also use Deferred's fail() method to alert the user that one or more of the save operations failed:

jQuery.when(xhrs...).
  done(-> alert("We succeeded!")).
  fail(-> alert("We failed."))

Conclusion

The jQuery Deferred API is a powerful way to elegantly wait for the completion of asynchronous operations in your Backbone application. While it's tempting to resort to workarounds like using setTimeout to wait an arbitrary amount of time for operations to complete, using jQuery.when means you don't introduce race conditions into your application.

If you have any questions or if something isn't working as described above, please leave me a comment. I'll try my best to answer as soon as I can.

How to authenticate Instagram in a command line application

Instagram

Instagram uses OAuth to authenticate, meaning it can be kind of a drag to use its API if you don't want to build a web application. Building the simplest interface you can build to achieve your application's goals is one of the best ways to streamline your development process. And the simplest and cheapest interface is often the command line.

But because the OAuth handshake requires a web callback to operate, it can be cumbersome to build this authentication into a command line application. Below, I'll show you how to do it with only a little bit of annoyance.

Create an Instagram API Client

First, you'll want to go ask Instagram nicely for an API Client ID so you can get access to the Instagram API. Go to their developer portal and click 'Manage Clients' to add a new one.

When asked for URL's, feel free to use non-existent domains. I use guilded.dev for mine.

In the Security tab, be sure to uncheck "Disable implicit OAuth". This will allow you to connect to the API without requiring an explicit server-side post, meaning we can hijack the access token from the callback URL:

Uncheck 'Disable implicit OAuth'

Make a Firm Handshake

So what are you to do when you can't redirect your terminal window to Instagram so you can authorize your account? A little bit of copy-pasta. Here's what we're going to do:

  1. Generate an Instagram authorization URL and ask the user to paste it into their browser.
  2. The user will authenticate their Instagram account like usual. They'll be redirected to our dummy Redirect URI.
  3. We'll prompt the user for their newly-issued access token. Because we unchecked "Disable implicit OAuth" in our Instagram client configuration, the access token will be appended to the redirect URI. We'll ask them kindly to copy and paste it into the terminal.
  4. We'll be authenticated to Instagram in the terminal!
require 'instagram'

Instagram.configure do |config|
  config.client_id = "YOUR CLIENT ID"
  config.client_secret = "YOUR CLIENT SECRET"
end

# Generate an Instagram authorization URL
puts "Visit the Instagram OAuth URL below to get started:\n"
puts "" + ::Instagram.authorize_url(
  redirect_uri: "http://guilded.dev/instagram/callback"
  response_type: 'token'
)

# Prompt the user for their newly-issued access token.
puts "Enter the access token at the end of the redirect URL.\nYou'll find it after the '#access_token=' in the URL."
access_token = gets.strip

# Create an Instagram client with the access token.
client = Instagram.client(access_token: access_token)

Now you should have an authenticated Instagram client. Use the Ruby Instagram API as usual:

for media_item in client.user_recent_media
  puts media_item.images.thumbnail.url
end

Store Your Access Token

Of course, requiring entering the access token each time we use our Instagram command line application is going to annoy our user. What if we could store the access token on the first authentication so we could use it for subsequent runs?

For this example, we'll store the access token in a file called .instagram-access-token. Depending on your application, you might want to use an existing YAML configuration file or another method.

require 'instagram'

# Configure the Instagram gem the same way we did above:
Instagram.configure do |config|
  config.client_id = settings.instagram_client_id
  config.client_secret = settings.instagram_client_secret
end

# If there's an access token saved to the file, then read it.
if File.exists?(".instagram-access-token")
  access_token = File.read(".instagram-access-token")
else
  # Otherwise, generate one
  puts "Visit the Instagram OAuth URL below to get started:\n"
  puts "" + ::Instagram.authorize_url(
    redirect_uri: "http://guilded.dev/instagram/callback"
    response_type: 'token'
  )

  # Prompt the user for their newly-issued access token.
  puts "Enter the access token at the end of the redirect URL.\nYou'll find it after the '#access_token=' in the URL."
  access_token = gets.strip

  # And save the token to the file for the next use:
  File.open(".instagram-access-token", 'w') do |file|
    file.write(access_token)
  end
end

# Create an Instagram client with the access token.
client = Instagram.client(access_token: access_token)

As you can see above, we first check to see if there's an access token saved in our .instagram-access-token file. If there is, we skip the handshake process altogether. If not, we initiate the handshake.

Note that for the purposes of simplifying the example, I've left out some error handling for invalid access tokens. You'll want to verify that the access token stored is valid and go through the handshake process again if you cannot connect.

Conclusion

Building a command line application for Instagram is fairly easy, assuming you're able to build the authentication in a way that doesn't confuse your user. If you're just building a tool for personal use, this is a great way to create real value without incurring the burden of building a full-blown web application.

If you have questions or if something is unclear, please leave a comment below and I'll do my best to answer you.

Software is 10% Code

Building software is about programming, right? Day to day, we turn caffeine into code. We spend countless hours reading about new programming languages, techniques, and platforms. We engage in conferences, get into arguments about whose stack performs better, and scour Stack Overflow for the answers to our problems.

But none of that is programming. In fact, all of it—reading blogs, attending conferences, arguing, research—all of these activities are, at their core, interpersonal communication.

A good programmer knows the hottest programming language is English.

(Disclaimer: I speak English natively. Feel free to substitute your native tongue. I have no bias toward English and don't mind pressing '1'.)

Our stakeholders communicate their vision by telling us about it—in English. We capture their vision for development into well-crafted user stories—in English. We write our Stack Overflow questions in English, chat on Slack in English, and report bugs in English. So why do we look to techno-wizardry as solutions to problems whose root is likely poor team communication?

Bad Writing is a Meeting Factory

Being able to articulate a thought in writing means your team gets to take advantage of asynchronous communication. Whereas meetings are synchronous— requiring all parties to be present and engaged for the duration of the communication event—written communication is asynchronous, meaning the recipient can address your request or idea on their own time.

Understanding this distinction can save your team hours each day. If you're about to hold a meeting, ask whether it's because you don't feel confident writing an email to address the topic. Some topics are best discussed in person: "big picture" decisions and human resources concerns are a couple. But most technical decision-making is better left to the great text file in the sky.

Great Writing is Documentation

When ideas are birthed in writing, they're already documented. There's no need for a secretary in an email thread. No one need spend time writing meeting minutes or informing the team of decided action items. Your Slack channels are searchable.

This means that if we spend time to compose our thoughts concisely—if we re-read our message before sending and ensure we've articulated our thought as succinctly as we can muster—we have created a valuable artifact. We have contributed to our team's canon.

Resources for Better Writing

The Elements of Style

Strunk & White's classic prescriptive style guide The Elements of Style comprises "eight elementary rules of usage", ten "elementary principles of composition", "a few matters of form", a list of 49 "words and expressions commonly misused", and a list of 57 "words often misspelled." It's often cited as the standard for learning great writing style. I once kept a copy on my nightstand.

How to Win Friends and Influence People

Dale Carnegie's How to Win Friends and Influence People will help you adopt interpersonal skills to help win people to your way of thinking. Carnegie stresses that showing respect for other people's opinions and trying honestly to see things from the other person's point of view can dramatically change the way others perceive you. I think this is especially relevant to writing software, since there are often different but comparably adequate ways of approaching the same problem. Seeking to understand your team members' differing opinions can help you reach consensus. Adopting a sympathetic and concise writing style can help you do that.

Influence: The Psychology of Persuasion

This is the classic book on the science of persuasion. A word of warning: The tactics in this book can be (and are) used for some horribly manipulative things. But understanding the fundamentals of persuasion, how to coerce others, and how to defend against coercion, can be beneficial in your team diplomacy efforts.

One of the principles Cialdini covers, the contrast principle, can be used to dramatic effect when working with clients. The principle states that if we see two things in sequence that are different from one another, we will tend to see the second one as more different from the first than it actually is. This means that if we know Approach 1 is costly, but offer a more costly Approach 2 beside it, the client will likely accept Approach 1 by contrast. Consider the contrast principle when making proposals. It's likely you'll see an improvement in your team's buy-in to your ideas. Just don't take advantage of it.

Conclusion

When hiring technical talent, the first thing I look for is strong verbal communication skills. Being able to articulate ideas in writing is more valuable than technical skills because humans think in terms of and react to stories. Being able to tell stories that captivate your team and your customers creates consensus. When consensus is reached, the technicalities fall into place.

Writing is critical to your remote project because you don't get much face time. If you sign up for my free email course, I'll send you 12 patterns to make your remote team better. Sign up for free