SproutCore Meetup at Google

October 27th, 2008

The Silicon Valley SproutCore meetup group will be meeting at Google on November 12.  We are co-hosting this meeting with our friends at the Google Technology User’s Group.  Thanks especially to Van Riper who helped set this up.

The topic, of course, will be using SproutCore with Google AppEngine and Chrome.  If you are in the area, RSVP and come on by!

Meetup Page

Caching Computed Properties

October 26th, 2008

David brings up another good point in a comment on my recent post about computed properties.  If you have a property that is computed but only changes occasionally, you can use a hybrid approach with both observers and computed properties to cache this value efficiently.  Here is how the fullName example we used yesterday would be implemented:

Addressbook.Contact = SC.Record.extend({

  // firstName, lastName provided automatically by server
  // computed property returns fullName.  Use cached value if
  // provided or compute.  Save computed value in cache.
  fullName: function() {
    if (!this._fullName) {
      this._fullName = this.getEach('firstName','lastName').compact().join(' ');
   }
   return this._fullName;
  }.property(),

  // clear fullName cache whenever firstName or lastName changes.
  fullNameShouldChange: function() {
    this.propertyWillChange('fullName') ;
    this._fullName = null; // clear
    this.propertyDidChange('fullName');
  }.observes('firstName','lastName')
});

Note how the computed property here is not listed as having any dependent keys.  Instead, we write both an observer and a computed property.  The observer will notify that the fullName property has changed, but avoids the cost of calculating the new fullName property itself.  This still only happens when another object tries to get() the value of the fullName property.

For an example this simple, the added overhead of managing this caching probably does not yield enough benefits, but if computing the fullName property is significantly more expensive, its easy to see how this might be useful.  Let’s see, for example, how you might use this to automatically lazily load some data.  The example below assumes you are writing a twitter client and lazily loads the twitter feed for the current user.  Whenever the user changes, it clears the feed cache so that the next time you ask for the feed it can load:

Twitter.feedController = SC.Object.create({
  // this is the name of the user you want to load
  user: 'okito',

  // computed property lazily loads the twitter feed for the
  // current user.
  feedContent: function() {
    if (!this._feedContent) this.loadFeedContentFor(this.get('user'));
    return this._feedContent;
  }.property(),

  // uses Ajax to load the new feed data, then calls
  // feedContentDidLoad() with the new feed content.
  loadFeedContentFor: function(user) {
    // loading code goes here...
  },

  // observe the user and reset the feed when needed.
  userDidChange: function() {
    this.propertyWillChange('feedContent');
    this._feedContent = null;
    this.propertyDidChange('feedContent');
  }.observes('user'),

  // called when loadFeedContentFor() gets the data.
  feedContentDidLoad: function(feedContent) {
    this.propertyWillChange('feedContent');
    this._feedContent = feedContent ;
    this.propertyDidChange('feedContent') ;
  }
});

Now an indecisive user can change their mind as often as they want about whose feed they want to view and your client won’t actually try to load the feed until you need to actually display the data.  Best of all, the rest of your application never has to understand any of this logic.  It just binds to the user and feedContent properties and trust the controller to manage it accordingly.

One More Thing… (Sorry, couldn’t resist)

You can use the technique described above to cache computed properties in your applications today.  Over the last year, though, this has become such a common design pattern, that we’ve decided to support it natively in SproutCore.  Starting with the RC1 release of SproutCore 1.0, you will be able to add cacheable() to your computed property and SproutCore will cache it for you.  Here is how you would cache the fullName example above:

fullName: function() {
  return this.getEach('firstName','lastName').compact().join(' ');
}.property('firstName', 'lastName').cacheable()

That’s it.  No observers or private variables are required.  SproutCore will automatically cache the return value of this property the first time you get() it.  It will then use the cached property until one of your dependent keys (’firstName’ or ‘lastName’ in this case) changes or until you call propertyDidChange('fullName') on the record.

This new feature will not only help you eliminate glue code but also can make your application quite a bit faster.  Don’t let that stop you from using cached computed properties today with the techniques describe above though.  It can’t really help you clean up your code.  Thanks for the tip, Dave.

Using Computed Properties To Lazily Load Data

October 24th, 2008

Following up on yesterday’s computed properties post, here is a useful technique for lazily loading data using computed properties.  Let’s say your contacts application will show you a list of all the email you have exchanged with a particular contact.  This is a lot of detail that you generally will not want to load from the server but you need to have it available anyway.

How would we construct such a thing?

Well, a simple way to start would be to create a collection view that uses a property (called “messages”) on your Contact as its content:

<%= list_view :bind => { :content => "Addressbook.currentContact.messages" }, :content_value_key => :subject %>

This view helper would show a list view with the messages array.  But, this means that your Contact record has to include an array of all the messages.  We just said we didn’t want to load those messages immediately, but only when the user views a contact’s details.  How do we do this without having to write a lot of glue code?

Turns out computed properties come to the rescue.  Here is some sample code we could write:

Addressbook.Contact = SC.Record.extend({
  // ... other useful code
  messages: function() {
    if (this._messages) return this._messages; // use cache if present

    // otherwise, load from server
    Addressbook.server.loadMessagesForContact(this);
    return [];
  }.property(),

  // this method is called by Addressbook.server.loadMessagesForContact() when
  // the messages have loaded.  The passed value is an array of
  // Addressbook.Message records.
  messagesDidLoad: function(messages) {
    this.propertyWillChange(’messages’) ;
    this._messages = messages;  // save in cache
    this.propertyDidChange(’messages’) ;
  }
});

This code assumes that you also have an object called Addressbook.server with a method loadMessagesForContact() which will, as the name says, load the messages for the named contact and then calls the messagesDidLoad() method on the contact with the array of messages.

There are two really important things to notice about this code:

First, the computed property simply returns a cached value or, if it does not have the cached value, requests the value from the server.  When you first select a contact, your list view will initially display an empty list while it kicks off the request for more data in the back end.

Second, the messagesDidLoad() method, which is called when the values have loaded from the server, simply saves these in the cache and then tells SproutCore that the value of the messages property has changed.  If you are still viewing this contact’s details when this happens, this change notification will trigger the binding to your ListView and cause it to request the value of messages again, now with the loaded data.

Later, if you view other contacts and come back to this one, the messages will still be cached and will display immediately without refreshing the server.  Simple, lazily loaded data and, best of all, no glue code.

What’s This propertyWillChange/properyDidChange Thing?

The two methods called in messagesDidLoad() here might be a new trick for you.  What do these methods do?  In fact, the code you see in messagesDidLoad() is very similar to what happens whenever you call set().  They simply tell SproutCore that the value for the named property has likely changed.  This will in turn cause SproutCore to notify any observers and trigger bindings.

Usually you do not need to call these two methods yourself, but, as you can see here, they have their place.  You can use this technique anytime you have a “virtual” property like this one.   Note that the messages property above does not explicitly list any dependent property keys.  This is because the property depends on the calls to propertyWillChange()/propertyDidChange() in messagesDidLoad() to tell SproutCore when the property has changed instead.

And What If My Data Changes?

Finally, let me show you a common variant on this technique that can make it even more useful.  The above technique is useful if the data you are loading lazily will never change once you have loaded it once.   But what if this is not the case?  Maybe a new message has been received since the last time you viewed a contact.  How can you make sure your data always stays up to date?

It turns out this is quite easy.  Just rewrite your messages computed property like so:

messages: function() {
  // always refresh
  Addressbook.server.loadMessagesForContact(this);

  // then return the current value, if there is one
  return this._messages || [] ;
}.property()

Now every time some other object asks for the value of messages, it will check the server for any changes.  Of course you could rate limit this if you want to implement any other policy to control how often you check for new messages, but the principle is the same.

Computed properties are a powerful way to lazily compute - and load - data in your application.  When used with bindings, they make it easy for you to keep the data you load at one time to a minimum.  Hopefully this little demonstration will makes it a little easier to do that.

About Computed Properties

October 23rd, 2008

A lot of people who are new to SproutCore wonder about computed properties at some point.  What is the difference between a computed property and an observer?  This article aims to answer that question for you.  Let’s look at a common example.

Let’s say you are building a contact application, not unlike the sample you can get off of our github site.  Your Contact record would probably have the following properties.

  • firstName - the first name, comes from the server
  • lastName - the last name, comes from the server
  • fullName - the first and last name combined.  Used for display in the contact list, etc.

Obviously, you plan to get the firstName and lastName from your server, but where will the fullName come from?  After all, that is the property you want to display in your contact list.

The Observer Way

It would be best to automatically generate the fullName from your firstName and lastName.  Whenever either one of those properties changes, the fullName property should change also.  Sounds like a job for an observer function.  Your first take on this might look something like:

Addressbook.Contact = SC.Record.extend({
  // firstName, lastName both added automatically from server or fixture data
  fullName: '', // default value

  rebuildFullName: function() {
    this.setIfChanged('fullName',
      this.getEach('firstName', 'lastName').compact().join(' '));
  }.observes('firstName', 'lastName')
});

Not bad.  Nice and compact.  Now anytime firstName or lastName changes, fullName will be regenerated.  We even use the nice compact() and join() methods so that if either property is null, the name will still look nice.

But, there is a problem here.  Every time you edit firstName or lastName, you now bear the added expense of recalculating fullName.  Not such a big deal with one contact, but if you load thousands of them or even if you just want to run on Internet Explorer, this added cost really adds up.

Wouldn’t it be better if we could somehow calculate the fullName property on demand instead?  This way you could change firstName and lastName anytime you want.  You only bear the cost of recalculating the fullName property when some part of your application asks for it.  This is where computed properties come in.

The Computed Property Way

Let’s rewrite our class with a computed property now:

Addressbook.Contact = SC.Record.extend({
  // firstName, lastName both added automatically from server or fixture data

  fullName: function() {
    return this.getEach('firstName','lastName').compact().join(' ');
  }.property('firstName','lastName')
});

This code looks really similar to the example we had above, but uses some new notation you may not have seen before.  fullName now points to a function, but that function has a special “property” declaration at the end.  This is how you tell SproutCore that this method is a computed property.  Now, whenever you do something like:

contact.get('fullName');

Instead of simply returning the value of the fullName property, SproutCore will execute the function and return its result.  Hence, a computed property.

The two property names passed to the property() function tell SproutCore that the fullName computed property “depends” on both the firstName and lastName properties.  Whenever either of these properties are changed, SproutCore will also notify observers of the fullName property that it has changed also, just like what happens when you call this.set(’fullName’) in the observer example above.

This computed property approach implements basically the same behavior as we get with the observer method above, but now you only bear the cost of computing fullName when some other part of your app actually needs to use it.

Let’s say you load your contact application with 100 contacts.  Although the firstName and lastName properties will be set on all of the contacts, the fullName property will be calculated only for the 10-15 contacts or so that are visible on the screen.  Instant performance gain.

When to use Computed Properties

Computed properties are a nice way to deal with generating complex values at a lower cost, but when should you use them?

The general guideline is that you should use a computed property when you have one property that is somewhat expensive to generate and that will only be needed some of the time.  Usually you will use computed properties on model objects to compute complex properties, like above, and to lazily load data.

Once you get to the controller or view layer, computed properties have less value because a property you generate here is likely to be used all the time anyway.  Nevertheless, computed properties are another useful tool to add to your arsenal that can give your app a major speed boost, brought to you by SproutCore.

Mike Subelsky on SproutCore

October 16th, 2008

Mike Subelsky, one of the people behind SproutCore-based OtherInbox, gave a great talk at B’more on Rails last week and some folks were kind enough to film it for us. It’s a big video file but a good in-depth look at how Mike is using SproutCore. Great work Mike!

Screenshot

View the movie

Thanks to Smarticus for the link!

SproutCore relaxes with CouchDB

October 1st, 2008

Thanks to the hard work of Geoffrey Donaldson, SproutCore has its first major 3rd-party framework: an SC.Server subclass to interact with a CouchDB backend!

There’s a page on the SproutCore wiki with usage information and you can install the framework by doing this (note: this command should be run from the root of your sproutcore directory, the one with sc-config in it):

sc-install geoffreyd-couchdb

Then edit sc-config to load the framework into your clients:

c[:required] = [:sproutcore, :'geoffreyd-couchdb']

And restart sc-server. Head on over to the wiki to find out how to use it, and be sure to give Geoffrey feedback on the mailing list.

If you have patches, please submit them through SproutCore’s ticket system.

SproutCore 0.9.19 — hot fix release

September 30th, 2008

A change in the new project template resulted in new SC clients not loading. This releases fixes that bug, and also includes a fix for SC.Server from Evin Grano.

To get the new release, just do:

sudo gem install sproutcore

SproutCore 0.9.18 - “Black (Wonderful Life)” release

September 30th, 2008

Black (It’s A Wonderful Life), film by Gerard de Thame

Get the Flash Player to see this player.

This release focuses on user contributions, and there were lots. 21 people contributed to this release, making this our biggest community effort yet. Nearly 50 tickets were retired, all of the IE7 tests now run (with one minor exception), and 100% of our tests run on Google Chrome.

Major highlights of this release include:

  • rewritten SC.SplitView Huge thanks to Lawrence Pit for doing this, including support for both directions! Plus: a new sample client, split_view, demonstrating usage.
  • improved HAML and now SASS support Also from Lawrence Pit
  • Merb-compatibility We now work with merb-core 0.9.1 and later.

Build Tools

  • Ticket [52] sc-gen should give warning on incorrect usage (Alexei Svitkine)
  • Ticket [119] Regression: body.html.erb no longer works (Michael Allman)
  • Ticket [125] c[:javascript_libs] included too often (Lawrence Pit)
  • Ticket [43] sc-server has to be restarted after error in code is executed (macinjosh)
  • Ticket [70] id attribute generated twice for views (Alexei Svitkine)
  • Ticket [24] text_area_view missing ’show-hint’ class (mrc)
  • Ticket [117] Sass support (Lawrence Pit)
  • Ticket [132] html tag is missing required attribute xmlns (Alexei Svitkine)
  • Ticket [173] c[:resources_relative] = true should be default in sc-config (Davide Benini)
  • Ticket [131] Proxy doesn’t forward GET params with merb 0.9.4 (Alexei Svitkine)
  • Ticket [179] text_area_view’s generated textarea tag does not validate (Sindre Aarsaethe)
  • Ticket [130] sc-gen language USAGE file wrong and doesn’t not generate files correctly (Trek)
  • Ticket [147] Allow view_helpers (and templates) to call dom_id to generate a guaranteed unique DOM id (Lawrence Pit)
  • Ticket [146] Fix memory leak in build tool (Lawrence Pit)
  • Ticket [144] require(’…’) doesn’t accept .js extension on end of nam (Lawrence Pit)
  • Ticket [143, 145] -s and –source not working on sc-instal (Trek)
  • Ticket [126] Add –haml and –sass options to sc-init and sc-client (Lawrence Pit)
  • Ticket [116] Refactored rendering…. allow yield :foobar (Lawrence Pit)
  • Ticket [148, 151] Compress CSS when build for production (Lawrence Pit)

Framework

  • Ticket [82] picker.js bug (Alexei Svitkine)
  • Ticket [103, 104] selection_support.js problem with SelectFieldView (Bill Klaila)
  • Ticket [127] Fixed radio enabled display state (Gert Hulstein)
  • Ticket [12] CollectionView doesn’t resize groups after a frame change (Christopher Swasey)
  • Ticket [101] SC.Record.Date should handle GMT formatted strings (Joshua Dickens, Erich Ocean)
  • Ticket [102] Missing SC.Collection.removeRecords method (mm)
  • Ticket [99] SC.Record.create() inconsistency (Michael Allman)
  • Ticket [166] toMany relationship is null (Reto Wolf)
  • Ticket [166] SC.CollectionView itemViewForEvent somtimes throws exception on IE8 (Darryl Fuller)
  • Ticket [152] SC.Collection doesn’t update itself properly (Christopher Swasey)
  • Ticket [155] SC.Record notifies SC.Store improperly of changes (Christopher Swasey)
  • Ticket [156] Basic tests for collections (initial) (Christopher Swasey)
  • Ticket [154] problem with record.js; parent-child relationship; and tomcat (Bill Klaila)
  • Ticket [178] SC.CollectionController.hasSelection() does not return anything when there is no content (Maurits Lamers, Erich Ocean)
  • Ticket [123] CollectionView doesn’t properly redraw contents if it isn’t visible at the time of a content change (Christopher Swasey)
  • Ticket [137] Split view can collapse when thickness becomes less than a given value (Lawrence Pit)
  • Ticket [162] IE6 Fix (sujoykroy)
  • Ticket [165] String.fmt ordered arguments are broken (Boris Smus, Erich Ocean)
  • Ticket [163] Reimplementation of Array.invoke breaks Scriptaculous (Boris Smus)
  • Ticket [139] record.js/readAttributes() Bug (Evin Grano)
  • Ticket [161] Records with multiple toMany relationships mixup data (Reto Wolf, Erich Ocean)
  • Ticket [138] Segmented buttons are 1 pixel too tall towards the bottom (Lawrence Pit)
  • Ticket [128] Split view doesn’t honor canCollapse property (Lawrence Pit)
  • Ticket [129] Split view doesn’t honor min_thickness property (Lawrence Pit)

SproutCore rocks TechCrunch50, thanks to OtherInbox!

September 20th, 2008

When Apple shocked the Internet with their stunning, SproutCore-based MobileMe apps, we all knew it was just a matter of time before another company would follow their example and adopt SproutCore. But no one thought it would happen this quickly!

Less than 60 days after the MobileMe launch, OtherInbox debuted at TechCrunch50 with a top-notch SproutCore application written by Mike Subelsky. Here’s a screenshot of their gorgeous user interface:

OtherInbox Screenshot

From the company’s press page:

OtherInbox is the cure for email overload - it provides consumers with a free email account that automatically organizes newsletters, social networking updates, coupons and receipts from online purchases so that its easy to find the most interesting things and ignore the rest. OtherInbox shows the consumer who is really responsible for sending them spam and gives them a powerful new Block button to stop it once and for all. OtherInbox is an Austin-based start-up founded by serial entrepreneur and email-marketing guru Joshua Baer that launched in September 2008 at TechCrunch50.

OtherInbox is currently in private beta and runs on Internet Explorer 7, Safari 3, FireFox 2 and 3, and Chrome. OtherInbox was kind enough to give us 25 private beta invites which you can access here (while they last).

SproutCore 0.9.17 - “Gem Fixes” Release

August 30th, 2008

This release focuses on gem fixes, as the SproutCore build tools are not compatible with Merb 0.9.5. You can install Merb 0.9.4 like this:

sudo gem install merb-core -v 0.9.4

In addition, Erich Ocean is now able to publish gem updates. Expect SproutCore 0.9.18 in the near future with tons of bug fixes and tests contributed by users over the last 3 weeks.

Build Tools

  • Ticket [158] Fix requirements to load correct version of merb (Paul Barry)
  • Ticket [124] Proxy doesn’t forward GET params with merb 0.9.4 (Lawrence Pit)

Framework

  • require() fixes Fix require()’s for server/rest_server.js and server/rails_server.js (Lawrence Pit)