Skip to content
Oct 14 13

Javascript Promises

by cory
I've been doing a lot of building with ember.js lately, and they recently when all in on the promise pattern for asynchronous actions (such as routing from one page to another). Working heavily with promises in ember has gotten me excited again about the pattern. It's really very powerful, and I use it for all async code that I write now.

Earlier this month, I put together a presentation on Promises for the folks at UtahJS. To test my own knowledge I used a jsbin to test out a few different concepts with promises. This morning I cleaned that up into an interactive tool for exploring promises that you can use to view different examples of promise usage and see the output side by side with the code.

Domenic Denicola, whom I would call the current-day godfather of promises, has a great blog post called You're Missing the Point of Promises.
Jul 28 13

Infinite Scrolling with Ember.js

by cory
Let's implement a simple infinite scrolling pattern using Ember.
First, we will start with a basic scaffolding app. Let's load some fake models in the index route. Our `App.IndexRoute` will look like this:
App.IndexRoute = Ember.Route.extend({
  model: function(){
    var items = Em.A([]);
    for (var i = 0; i < 10; i++) {
      items.pushObject({name: ''+i});
    }
    return items;
  }
});

This code implements the `model` hook on the Index Route to return an array of 10 items that look like `[{name:'1'},{name:'2'}...]`. It's not too exciting but it's enough for us to start with. The return value from the model hook will be set as the `content` property on the `App.IndexController`.
We'll make sure our index controller is an array controller, since it will be showing a list of items (those returned from the route's `model` hook). `App.IndexController` looks like:
App.IndexController = Ember.ArrayController.extend({});

We now have enough scaffolding to write up some simple templates to display this information. We'll create an 'application' template with an outlet, and an 'index' template that shows each object in the index controller's content:
<script data-template-name="application" type="text/x-handlebars">
{{outlet}}
</script>

<script data-template-name="index" type="text/x-handlebars">
<h2>Widgets ({{content.length}})</h2>
<ul>
  {{#each widget in controller}}
    <li>Widget: {{widget.name}}</li>
  {{/each}}
</ul>
</script>
If we fire this up in the browser now, we see a header "Widgets (10)" and a list of widgets: Widget: 1, Widget: 2, etc.

Before we add the ability to automatically pull in content when the user scrolls, we'll add in a link the user can click to load more widgets manually. When that is working we'll figure out how to automatically trigger that code when the user scrolls to the bottom.

Next, add a link to the bottom of the 'index' template. Because we want to avoid double-loading new data, we'll add a boolean 'isLoading' property to the `App.IndexController` that we can also use to both show a 'loading...' message in the browser. We'll add that link to the bottom of the 'index' template:
{{#if loadingMore}}
  Loading more...
{{else}}
  <a href='#' {{action 'getMore'}}>Get More...</a>
{{/if}}
So we'll need to add the `getMore` action to the `App.IndexController`. This will pull in new content and push it onto the end of the 'content' array property on the controller. We could add the widget-finding code directly into the `IndexController`, but in a case like this I like to use the `events` hash on the route to load in the new data and just use the controller to direct this action and update its own properties that change what we see in the GUI (like the `isLoading` property). As I said, there's no reason we couldn't also do the loading of new data (widgets) on the controller, but I like adding it to the route because the other data-loading method (the `model` hook) is already there and this way we can keep all the code that loads external data (eventually -- right now we are only creating fake objects, of course) together.

We also will need to add `page` and `perPage` properties to the IndexController so that it can keep track of what new data to load each time. Here are the changes:
/* Add `getMore` action to App.IndexController */

// add properties
page: 1,
perPage: 10,

getMore: function() {
  // don't load new data if we already are
  if (this.get('loadingMore')) return;

  this.set('loadingMore', true);

  // pass this action up the chain to the events hash on the route
  this.get('target').send('getMore');
},

// Also add a method `gotMore` that the route can call back to
// notify the controller that the new data is in and it can stop
// showing its loading indicator
gotMore: function(items, page) {
  this.set('loadingMore', false);
  this.set('page', page);

  this.pushObjects(items);
}

/* Add `getMore` to the events hash on the App.IndexRoute */
// ...
events: {
  getMore: function() {
    var controller = this.get('controller'),
        nextPage = controller.get('page') + 1,
        perPage = controller.get('perPage'),
        items;

    items = this.events.fetchPage(nextPage, perPage);

    // alert the controller to the new data
    controller.gotMore(items, nextPage);
  },
  // load another page's worth of fake widgets
  fetchPage: function(page, perPage) {
    var items = Em.A([]);
    var firstIndex = (page-1) * perPage;
    var lastIndex  = page * perPage;
    for (var i = firstIndex; i < lastIndex; i++) {
      items.pushObject({name:''+i});
    }

    return items;
  }
}
Try it out in this jsbin demo. Clicking the 'get more' link loads another 10 fake widgets each time. Great! Last step is to do this automatically when the user scrolls to the bottom.

How do we determine that the user has scrolled to the bottom? jQuery has some helpful functions for us here. `$(document).height()` gives the height of the entire document, and `$(window).height()` gives the height of the browser viewport. The difference of these two integers is the distance to the top of the viewport, which happens to be exactly what `$(document).scrollTop()` returns. When those quantities are equal, we know the user has scrolled to the bottom. We will add a listener to the window's "scroll" event to determine when the user has scrolled, so that we can check whether to load new content. We'll add `didScroll` and `isScrolledToBottom` methods to `App.IndexView`:
// App.IndexView
// ...

// this is called every time we scroll
didScroll: function(){
  if (this.isScrolledToBottom()) {
    this.get('controller').send('getMore');
  }
},

// we check if we are at the bottom of the page
isScrolledToBottom: function(){
  var distanceToViewportTop = (
    $(document).height() - $(window).height());
  var viewPortTop = $(document).scrollTop();

  if (viewPortTop === 0) {
    // if we are at the top of the page, don't do
    // the infinite scroll thing
    return false;
  }

  return (viewPortTop - distanceToViewportTop === 0);
}

And now we'll use Ember's `didInsertElement` and `willDestroyElement` to add and remove the scroll listener, respectively:
// App.IndexView
didInsertElement: function(){
  // we want to make sure 'this' inside `didScroll` refers
  // to the IndexView, so we use jquery's `proxy` method to bind it
  $(window).on('scroll', $.proxy(this.didScroll, this));
},
willDestroyElement: function(){
  // have to use the same argument to `off` that we did to `on`
  $(window).off('scroll', $.proxy(this.didScroll, this));
}

Note that it's important that the second argument we give to `$(window).off` is the same function we gave to `$(window).on`, otherwise the 'off' call will remove all scroll listeners on the window, which is not what we want. Luckily, jQuery's `proxy` method is smart enough that it will return the same method when we call it with the same function name. See jQuery's documentation for more on `.off()`, though, because in other cases it may be safer to use an explicitly named, bound function instead.

Now we have a fully working, end-to-end infinite scrolling solution! You can see a jsbin demo of it here, where I've added a slight delay to the loading of new data to simulate it coming from an API with some latency.

If you would like to use this code in your own project, I have extracted all the infinite-scrolling-specific bits to a series of mixins that are available on github here LINK, along with instructions for mixing them into your project's route, controller and view.
Jun 27 13

Ember's BufferedProxy and analog to Ruby's method_missing

by cory
Last week I wrote about state machines in ember-data, and I wanted to follow that up this week with a pattern that can be used to separate the editing and saving of ember-data models.

If you recall, the issue we ran into was that ember-data would complain when you tried to change a model while its data was still being synchronized to the data store (it generated a `willSetProperty` error). In this post we'll see how to intercept changes to the model to prevent updating its properties while its data is still in flight. The ember mixin that we'll use to do this was created by Kris Selden and is called `Ember.BufferedProxy`.

Summary

If you want to skip ahead, you can check out the interactive visual demo here. Playing with it should give you a good idea of how the BufferedProxy works in practice. For more context, check out the previous post and previous demo.

Ember's version of method_missing

Ember object have a little-known feature that allows us to override the setting and getting of properties on an object. Truthfully, it's a white lie to say that this is analogous to method_missing, as you'll see. It's similar, but only applies to properties that are unknown to the object. So it's sort of a method_missing but only for getters and setters of unknown properties. For overriding `get`-ing and `set`-ing of a property on an object we can define methods named `unknownProperty` and `setUnknownProperty` to handle these cases, respectively.

The ember documentation for get explains `unknownProperty`:
Gets the value of a property on an object. If the property is computed, the function will be invoked. If the property is not defined but the object implements the unknownProperty method then that will be invoked.

Say we have the following code:
var Dog = Ember.Object.extend({
  name: "Fido",

  unknownProperty: function(key) {
    console.log("Tried to get the unknown property: " +
                key); 
  }
});
var myDog = Dog.create();
alert("my dog's name is: " + myDog.get('name'));
// alerts: "my dog's name is: Fido"
alert("my dog has fleas? " + myDog.get('hasFleas'));
// alerts: "my dog has fleas? undefined"
// console logs: "Tried to get unknown property: hasFleas"

Any property that is not part of the blueprint for the object will result in calling the `unknownProperty` method, as shown. Any property that was part of the blueprint of the object (in this case, the Dog object knows that it has the property `name`) will not result in calling `unknownProperty`. The relevant code in ember is in the `ember-metal` package. You can imagine how intercepting `get`-ing of unknown properties could be useful in order to look them from some other source. This is what we'll do when we use our BufferedProxy.

`setUnknownProperty` works similarly. See the code below:
var Dog = Ember.Object.extend({
  name: "Fido",

  setUnknownProperty: function(key, value) {
    console.log("Tried to set the unknown property: " + 
                key + " to: " + value); 
  }
});
var myDog = Dog.create();
myDog.set('hasFleas', true);
// console logs: 
//   "Tried to set the unknown property: hasFleas to: true"


If we put these two together with a separate proxy object to hold these changes, we end up with something like this:
var Dog = Ember.Object.extend({
  name: "Fido",
  proxyObject: {},

  unknownProperty: function(key) {
    console.log("Got the unknown property: "
                + key);
    var proxyObject = this.get('proxyObject');
    return proxyObject[key];
  },
  setUnknownProperty: function(key, value) {
    console.log("Set the unknown property: " + key + " to: " + value); 
    var proxyObject = this.get('proxyObject');
    proxyObject[key] = value;
  }
});
var myDog = Dog.create();

myDog.get('hasFleas'); // returns undefined
// console logs: "Got the unknown property: hasFleas"

myDog.set('hasFleas', true);
// console logs: "Set the unknown property: hasFleas to: true"

myDog.get('hasFleas'); // returns true
// console logs: "Got the unknown property: hasFleas"


That should make sense. We're storing unknown properties in the `proxyObject` and looking them up there as well.

There's an important caveat here, though: In order for ember's data bindings to properly reflect data changes into the UI, ember needs to know when those properties change. It uses the property change notifications `propertyWillChange` and `propertyDidChange` internally to do this, and when you don't have a `setUnknownProperty` method defined, then ember transparently handles these notifications for you and everything will work as expected. But if you've overridden `setUnknownProperty`, then ember doesn't know when the property values have actually changed, so we need to manage that ourselves. Here's an example of how this could go wrong if we don't set up property notifiers:
var Dog = Ember.Object.extend({
  name: "Fido",
  puppy: null,
  proxyObject: {},

  puppyHasFleasObserver: function() {
    if (this.get('puppy.hasFleas')) {
      console.log("The puppy just got fleas!");
    }
  }.observes('puppy.hasFleas'),

  unknownProperty: function(key) {
    console.log("Got the unknown property: " + key);
    var proxyObject = this.get('proxyObject');
    return proxyObject[key];
  },
  setUnknownProperty: function(key, value) {
    console.log("Set the unknown property: " + key + " to: " + value); 
    var proxyObject = this.get('proxyObject');
    proxyObject[key] = value;
  }
});
var myPuppy = Dog.create({name: "Puppy"});
var myDog = Dog.create({name: "Fido", puppy: myPuppy});
myPuppy.set('hasFleas', true);
// console DOES NOT log:
//   "The puppy just got fleas!"
// Why not?


We expected that the `puppyHasFleasObserver` would be triggered and the console would log that the puppy just got fleas, but it doesn't. Why not? Because ember's property system never triggered the change notifications that it uses for property observation. This is a rather contrived example, but you can see how this would cause some trouble in an actual app, with a GUI that doesn't update properly when values change.

We can fix this by adding the appropriate calls to `propertyWillChange` and `propertyDidChange` like this:
var Dog = Ember.Object.extend({
  name: "Fido",
  puppy: null,
  proxyObject: {},

  puppyHasFleasObserver: function() {
    if (this.get('puppy.hasFleas')) {
      console.log("The puppy just got fleas!");
    }
  }.observes('puppy.hasFleas'),

  unknownProperty: function(key) {
    console.log("Got the unknown property: " + key);
    var proxyObject = this.get('proxyObject');
    return proxyObject[key];
  },
  setUnknownProperty: function(key, value) {
    console.log("Set the unknown property: " + key + " to: " + value); 
    var proxyObject = this.get('proxyObject');
    var originalValue = proxyObject[key];
    
    if (originalValue === value) return;
    
    this.propertyWillChange(key);
    proxyObject[key] = value;
    this.propertyDidChange(key);
  }
});


Now that we've added the property change notifiers, our contrived example should work as expected:
var myPuppy = Dog.create({name: "Puppy"});
var myDog = Dog.create({name: "Fido", puppy: myPuppy});
myPuppy.set('hasFleas', true);
// console logs "The puppy just got fleas!" -- hooray!


(If you'd like to try this yourself, the easiest way to do it is probably to find a web page with ember already running on it and just copy and paste these examples into the console. You can use the previous demo, for example. Just open the console and copy/paste.)

Applying setUnknownProperty and unknownProperty with BufferedProxy

With this newfound knowledge, we can apply a pattern that intercepts all changes to a model, and buffers them until we are ready to synchronize. At that point we will flush the changes to the model and save the model, allowing the user to make further changes while the model is in flight. We'll use Kris Selden's Buffered Proxy. It's worth taking a moment to look through and understand that code.

To use the BufferedProxy we simply `extend` the controller that has the model as its `content` property (in this model, `content` is the blog post whose title is being edited). Our value bindings in the UI are bound to the controller, which under normal circumstances would proxy them directly to the content, but now that we have the BufferedProxy mixed in it will intercept and hold on to requests to change any property that isn't part of its set of properties.

Here is a interactive demo on jsbin showing the BufferedProxy in action. First, try changing the blog post title. Notice that the model's state doesn't change, and its `isDirty` property doesn't change to true. That's because our changes to the title are stored by the BufferedProxy. You can see that the `hasBufferedChanges` property is true. As soon as you hit enter the code will apply any buffered changes and save the model, so the changes get sent to the server as expected. If instead you click the "Apply buffered changes" button, the change you've made to the post's title (that is stored in the Buffer) will be applied, and you'll see the state for the post change and its `isDirty` property change to true, as expected.

Now, if you switch the latency to "high", you can make a change to the title, hit enter to save it, and immediately start making other changes before the synchronization to the backend completes. The changes will be stored in the buffered proxy, which will successfully prevent the `willSetProperty` error we encountered previously when we attempted to apply the changes directly to the model while it was `inFlight`.

If you think it through, there's still a way to cause that `willSetProperty` error, but I'll leave that as an exercise for the reader.
Jun 10 13

The willSetProperty gotcha in Ember-Data: Understanding the state machine

by cory
Ember Data, the ambitious comprehensive persistence library solution for Ember.js, has been growing in popularity, and even though it still has a prominent disclaimer that it is not production-ready, it has recently been included in the models section of the official ember.js guides. This seems like a good time to start familiarizing ourselves with it.

Summary

I'm going to describe a particular gotcha that I experienced while using ember-data, and then explore through the ember-data code and documentation to try to piece together what happened and how to avoid it. Short version: If you attempt to change a DS.Model's attribute while it is inFlight, ember will give an error. There's an interactive visual app on jsbin that you can use to explore this yourself. If people find this useful I will write a follow-up that shows an example usage of Kris Selden's Buffered Proxy as a way to mitigate this issue. Follow me on twitter to get notified when part two is up.

emberjs

DS.State Events, Flags and Transitions


The error that I kept encountering was this one, that would seemingly randomly show up in my javascript console:
Attempted to handle event `willSetProperty` on
<App.Post> while in state
rootState.loaded.updated.inFlight.
Called with {reference: [object Object],
store: <App.Store:ember418>, name: title}

There are two important parts of this error message: willSetProperty, and the current state: rootState.loaded.updated.inFlight. What the error is trying to tell us is that the model's was in the rootState.loaded.updated.inFlight state when something caused ember-data to try to set a property ('title') on the model. We get the error because the model's state machine tried to send the event 'willSetProperty' but the state machine's current state (rootState.loaded.updated.inFlight) does not define that event.

So let's back up bit more and ask: What does it mean for the record to be in the `rootState.loaded.updated.inFlight` state? How did it get that way? To answer this we need to look a bit at the state machine that ember-data uses internally with its models. That code is in the 'system/model/states.js' package of ember-data, and in the comment it gives a good explanation of how the state manager works. Quoth the docs:
A record's state manager explicitly tracks what state a record is in at any given time.
The class is `DS.State`, which is a subclass of Ember's `Ember.State`. At any rate, the important thing you need to know for our purposes about a `DS.State` is that it has three different components that make it up: Events, Flags, and Transitions.

DS.State Events, Flags and Transitions


Flags are boolean values that are meant to describe the model's current state. `isLoaded`, `isSaving`, etc are flags. The ember.js model lifecycle guide lists all the flags.

Events are, again quoting the ember-data comments: "named functions that are invoked when sent to a record. The state manager will first look for a method with the given name on the current state." States can be nested and so when attempting to respond to an event they will look up their class hierarchy at the parent, grandparent, etc. until they find an event with that name. If they don't find one, they raise an error (we'll come back to that!). It's also worth nothing that events are invoked on the record (using the method `send`), which then delegates to its internal state manager. So you do something like `myPost.send('myEvent')`, which calls `myEvent` on the post's state manager.

Ember-Data states can be `transition`-ed between using `transitionTo`. Transitions work similarly to events but they are intended to be invoked when handling an event, never directly. So a typical usage pattern might be to call `myPost.send('gotFoo')`, which would delegate to the state manager, which in turn would do some internal bookkeeping and then, perhaps, transition to a new state called 'hasFoo' using the call `transitionTo('hasFoo')`.

So with that in mind we can break down our error message. Something tried to invoke the event `willSetProperty` on our instance of `App.Post`. The App.Post was in the state `rootState.loaded.updated.inFlight`, and that `inFlight` state does not have an event named `willSetProperty`. The code for the `inFlight` `DS.State` is on github. If you look at the code you can see that the events on the `inFlight` state are: ` materializingData`, `didCommit`, `didChangeData`, `becameInvalid`, and `becameError`. There is no `willSetProperty` event, hence our error.

How does a model get into `inFlight` and why might its properties change therein?


We are getting pretty close to unraveling this error. We now know what it means. The next step is to figure out what caused it. How did our model get into `inFlight` state and why did ember-data try to change its `title` property while it was in that state?

The answer to the first question is pretty simple to answer, and in fact if we look back at the comments above the `inFlight` `DS.State` it will not only tell us how a model gets into this state but warn us that changing the model's properties should not be done during that time. The comments in the code say:
Once a record has been handed off to the adapter to be saved, it is in the 'in flight' state. Changes to the record cannot be made during this window.
So our App.Post was set to `inFlight` when we asked ember-data to save it to the backend, perhaps by calling `model.save()`. That makes sense. The post will be in the `inFlight` state from the time we call `save()` until the time that the backend api responds to ember-data's request, at which time ember-data knows that the changes have been persisted. At that point it will transition to the `saved` state. Internally, ember-data's `inFlight` state transitions by calling `manager.transitionTo('saved');`.

The last part of this puzzle is to find out why the post's `title` property was changed between the time we started saving the post and the backend api responded. There are actually a number of reasons this might happen, depending on the nature of your application. For instance, if you have an `Ember.TextField` whose value is bound to the post's title property, then if you allow the user to type in that field while the post is being saved you'll see this error. Likewise, if you have other async processes going on that could modify the post, then you need to make sure they don't do so when the post is in flight. In my specific case, the post was listening for another user's updates via websockets, and the callback that responded to the websocket message was attempting to update the post's title without ensuring that the post wasn't in the middle of saving.

Demo!


To see visually how the state and state flags change when saving an ember-data model, check out this demo app on jsbin.
Ember-Data state machine example app
Jun 10 13

Conference Strategies and GORUCO 2013 reflections

by cory
This post is a reflection GORUCO 2013, and at the end I give some advice on how to make the most out of a conference.

I have been a volunteer co-organizer of GORUCO (aka Gotham Ruby Conference) for 5 years in a row now. It's been quite a ride, and every year it's a good checkpoint experience. On a personal level, comparing this year to years past helps me to see my own evolution as a Rubyist and a member of this community. At the first GORUCO I attended, 6 years ago, I remember knowing almost no one and feeling that lots of the talks were well over my head. I was a ruby noob and the experience was intimidating. It's encouraging and rewarding to reflect now and see that over the years I've known more people and felt more comfortable every year. I have similar thoughts when looking at the conference as a whole. Every year it has gotten bigger, and, in my opinion, better. That's hard to do.

goruco
Photo from @_eirik.

One of the things I am particularly proud of this year is the scholarship program we put together with RubyCentral. They had experimented at Mountain West Ruby Conf with paying the way for certain rubyists to attend and pairing them with mentors at the conference (their term was "sherpa"). We were able to continue that program at GORUCO this year. RubyCentral put up the money for 10 tickets that we made available to students who would otherwise have had trouble affording the conference. Our ticket price this year was the highest it has been, and the organizers put a lot of work into mitigating that by making it easier for under-represented groups to attend. This scholarship program was part of that effort. You can read more about the program on GORUCO's blog.

A real credit to the NYC Ruby community is the ease with which we were able to find volunteer mentors, who spent the day of the conference (and also by and large met up pre-conference) with their apprentices/mentees to help them get the most out of the conference. I actually had to turn away a number of volunteer mentors because we ended up with too many. It really is great to see how willing people in this community are to give their own time to help newcomers.

I met a few of the scholarship recipients during the day and it reminded me of myself at my first GORUCO 6 years ago. Having a mentor back then would have gone a long way in making the conference a better experience for me, so I'm happy that we were able to do that for the recipients this year.

At one point I spoke with Sandi Metz, a real Ruby luminary (her book, Practical Object-Oriented Design in Ruby, is the number one Ruby book I would recommend to anyone) and also a mentor, about how things were going with her apprentice. We got to talking about general good strategies for conference attendees. Whether you are at your first conference or your fiftieth, what can you do to make the most out of it? Some ideas:

  1. Approach people. At a conference I often feel like I did in middle school. Lots of cliques of people talking all around me and no way to break in. It can be hard to do, but striding forward up to a person or group of people and saying hi and introducing yourself is one of the best things you can do at a conference. A conference is a great opportunity to meet a bunch of people in your community in the flesh. If you didn't take advantage of that you might as well just read a bunch of blog posts or watch the videos online later.
  2. Be approachable. Like I just said, conferences are made up of people. Each of us being there contributes to the conference for everyone else. The better the contribution you can make, the better the conference can be for everyone. Being inclusive goes a long way, and makes point number one above work even better. When someone sidles up to your group, take advantage of a pause in the conversation to bring them into the conversation with a quick introduction. Just turn and introduce yourself, or if you already know them, proactively intro them to the other people nearby and summarize the conversation so far.
  3. Be ready to learn. The common thread in all these bullet points is: Do the things at a conference that you can only do at a conference. Pay attention to the talks. I know that not all talks are interesting to all people. Some talks can be pretty boring. But still, persevere to avoid using your computer. You can write emails and browse Hacker News anytime; you can only be an attendee at GORUCO once a year. I sometimes keep a laptop open so that I can do complementary research on the speaker's subject while they are talking, or take notes. I think these are worthwhile uses of the computer during a talk, but I still find myself getting distracted and I try to avoid it as much as I can. The best speakers are so dynamic that you can't help but stay focused on them, but for those that aren't, try to follow along anyway. You never know when they'll say something that you can really learn from.The wifi will invariably be spotty anyways, and the internet can wait.


Finally, I wanted to say thank you to: the other organizers. They are great to work with and I'm happy that I've been able to be included; the NYC Ruby community: it's a great community to be a part of, and you are the critical component in making GORUCO a success every year.

<3