SproutCore 0.9.15 - “Out with the outlets” Release

A new SproutCore gem is rolling your way this morning and this one has some big milestones in it.  There are some big changes both on the build-tools side and in the framework itself, so I will address both separately.  Before I get to that though, a big “Thank You” to everyone who has contributed to this release.  SproutCore has now taken commits from over 2-dozen people, which is pretty astounding given how new it is.  Something this big is always a huge team effort and your work is greatly appreciated.  

Build Tools

  • No more :outlet.  When using the view helpers in your RHTML, you have in the past needed to add the :outlet => true option to any non-top level helper.  This has caused endless confusion for new developers and was always a tad pointless since computers are good at automatic things like that.  Starting with 0.9.15 you no longer need to add :outlet => true to your view helpers.  SproutCore will figure out whether your views should be outlets or not.  And there was much rejoicing.
  • Experimental HAML support.  HAML is an interesting new templating language that seeks to make it easier to create HTML in less code.  If you hate mixing HTML and the view helpers in your code, HAML might be a good option for you instead.  Trek, who initiated this feature change, wrote a great blog post explaining HAML and how to use it in SproutCore.
  • Debug Directory Support.  If you ever need to add some debug code to your app, you now have a place to put it.  Create a debug directory in your client or framework and place your JS files in there.  They will be included in development mode but excluded when you do a production build.  The SproutCore framework now bundles the unittest.js code this way so you don’t have to load that code in production.

Framework

  • Introducing SC.Enumerable.  Working with collections is always a big challenge in any framework.  SproutCore in particular devotes a large part of its codebase to helping you efficiently download and display large collections of data.  In the future, a lot of these features will depend on being able to treat objects other than simple arrays as collections, so we’re working on adding the API to support this.  SC.Enumerable is one of the first steps in this direction.  A blog post delving into this further will be forthcoming.
  • Reduced Properties.  Along those lines, the framework now includes support for some special new properties that return summary information about a collection of data.  For example, calling [1,2,3].get(’@max’) will return the maximum number in the array.  Best of all, these properties are bindable so you can use them to display automatically update summary info in your UI.
  • New SC.RailsServer & SC.RestServer.  The server classes are getting some love by Lawrence Pit who has given us both a RestServer and a RailsServer.  If you are building with a REST or Rails backend, these can save you some coding time.  See the online docs for more info.

And Much Much More

As usual, there are a ton of extra little fixes and updates included thanks to contributions from you!  Here is the full list of changes:

 

Build Tools

 

  • Fixed bug that would sometimes convert “faked” HTTP methods sent for Rails apps into real HTTP methods, breaking Rails. (Ticket #115) [Lawrence Pit]
  • Experimental support for rendering templates using HAML (Ticket #114) [Trek, Lawrence Pit]
  • During sc-build, by default the build tools will now calculate a digest key to append to the end of URLs instead of simply using a timestamp.  This will ensure that the URLs generated are properly cacheable even when building on different machines. [CAJ]
  • Regex for replacing occurrences of static_url() are no longer greedy. (Closes #87) [nciagra]
  • Added support for the autobuild config option.  If specified, then the named bundle will be skipped when you run sc-build by itself.  It will still be built if you name the bundle explictly (i.e. sc-build mybundle) or if you pass the -r option and the bundle is required by another bundle that is also being built.  
  • Along those lines, the default config was also changes so that the doc tool and the test runner will no longer autobuild.
  • Added support for the build_languages config option.  If specified, then simply runnig sc-build will automatically build all of the languages named here, even if there are no specific locatizations for said language in your project.
  • Added support for the debug directory.  Any JS files in the debug directory will now be loaded in development mode but excluded from production builds. You can use this to include added instrumentation and benchmarks for use in dev mode.
  • Following with the debug enhancement, the SproutCore JavaScript now includes the unittest.js in its own debug directory so that this library will no longer be included in production builds.
  • Bundles are now reloaded on each request to the development server.  This both allows the server to recover from exceptions when building a file and eliminates a memory leak that caused an eventual slow-down.
  • Eliminated need for :outlet option.  And there was much rejoicing.

 

JavaScript

 

  • Added titleize() helper for Strings.  Also imported various other helpers from Prototype.
  • Ajax requests sent by SproutCore’s SC.Server object will now add a custom header “SproutCore-Version: 1.0″ to every request.  You can use this to detect requests coming from SproutCore on the server side. Fixes #85 [trek]
  • SC.Record does a better job of matching null values.  Fixed #96 [Fredrik  Blomqvist]
  • SC.Server now handles created records with no attributes yet.  Fixed #98 [CAJ]
  • Added support for reduced properties.  These properties begin with “@” and will automatically calculate some kind of summary information about the receiving array.  They are also bindable so you can bind to myArray.@average and it will update whenever the average changes. [CAJ]
  • Cleaned up SC.Array support to incorporate new SC.Enumerable functions.  The Enumerable methods from Prototype are no longer supported. [CAJ]
  • Removed Array.asArray().  This helper has been deprecated for 6mo now.  Use SC.$A() or Array.from() instead.  [CAJ]
  • Added SC.$A() helper.  This works like Prototype’s SC.$A() except that it  will work with any enumerable.  It is also namespaced properly. [CAJ]
  • Added “application/json” to the default Accept header for all servers. Required for proper support of json format. [Lawrence Pit]
  • Added experimental support for SC.RailsServer which works with resource oriented Rails applications.  [Lawrence Pit, Simon Harris]
  • [FIX] SplitView now resizes in both directions properly.  Closes #110 [Christoph Angerer]
  • Added support for the SC.Enumerable mixin and SC.Enumerators.  These items provide the basis for our standard enumeration API.  This will replace relying on iterators provided by Prototype eventually.  See docs on those classes for full information. [CAJ]
  • SC.guidFor() now works consistantly with strings, numbers and other primitive objects.  Before it would differentiate between different instances of these objects.  Now it treats them the same if they are the same value. [CAJ]
  • Added SC.hashFor() method which can be used to determine if two objects are equal.  Normally this returns the same value as guidFor() but if you implement the hash() method on your object, it will return that value instead.  This gives you an easy way to indicate that two objects are equal even if they are different instances. [CAJ]
  • Updated SC.isEqual() to respect SC.hashFor().
  • Added support for hasSelection, hasContent, and other useful properties on controllers for activating UI.
  • Added newObject() and insertNewObjectAt() methods to ArrayController.  These methods will create and register new records automatically.
  • SC.Record’s changeCount now increments whenever a member array’s attributes change.
  • Optimized SC.Object.objectForPropertyPath() to use less memory.
  • You can now pass a string path as a :target and it will be converted to an object. (thanks Peter Blazejewicz!)
  • SC.CollectionView will now call destroyObject on the content array instead of removeObject if that method is defined.  This allows you to implement arrays with a more specific handling of the destroyObject method.
  • Added support for checkbox views in ListItemViews.  Since this is a fairly common requirement, checkbox views are now built in.

 

 

New Feature: Property Chaining

I’ve just committed the latest changes to SproutCore to support property chaining.  This is an important change the SC API that could impact your code, though most likely only for the better.

The Old World

set() has always returned the value that you set of an object.  Since it was intended to replace the “=” in JS, this makes sense.  Just like you could do:

foo.bar = foo2.bar = c

You could do:

foo.set('bar', foo2.set('bar', c)) ;

The problem, of course, is that this model of setting values is not terribly useful.  In my informal survey, in fact, it appeared that almost no one actually uses it.

The New World

Enter the new world of chainable commands.  jQuery has gained a large following thanks mostly to its nifty chainability syntax.  Most methods return the object you just called a method on so you can chain commands like this:

$('.foo').show().css('color','red');

And all elements with CSS class ‘foo’ will be made visible and their color set to red.  Nice.  

This is a very useful way of writing JavaScript and we’re all about efficiency so SproutCore is moving towards a general pattern whereby methods that otherwise would return void, will now return this so you can do chaining.  The first place I’m implementing this pattern is in set().  With the latest SproutCore, instead of returning the value, set() now returns the receiver object.  Other property-related methods such as beginPropertyChanges() and endPropertyChanges() also do this.  This makes it easy to write compact code like this:


contact.beginPropertyChanges()
  .set('firstName','Charles')
  .set('lastName', 'Jolley')
  .endPropertyChanges();

Very compact!

What Do You Need To Change

Unless you’ve relied on the return value of set() in the past, you don’t need to do anything!  Just start enjoying the new compact syntax.  You don’t have to use it, but now it’s there for you.  If you have relied on the return value of set(), then you need to modify your code to use a separate get() on the method after you set it first.

The new SproutCore changes are available from git and SVN today and will be in the 1.0 release of the SproutCore gem.

 

SproutCore 0.9.5

I just released SproutCore 0.9.5 today.  Get it by running:

sudo gem update sproutcore

This includes some major new features for both the build tools and the JavaScript framework.  Here is the list:

Build Tools

This release includes some major new functionality to support localization.

  • Now you can load any localized version of your project simply by visiting http://localhost:4020/appname/lang.  For example, to get the french version you might do:  http://localhost:4020/appname/fr.  This functionality was planned from the beginning but a few bugs kept it from functioning properly.  Now it should work for your project.
  • The build tools now support build-time localization.  To localize a string in your RHTML files, just use the ruby code loc(”KeyName”).  This works just like “KeyName”.loc() in the JavaScript world except that the key will be localized when your HTML file is built instead of later once the file is downloaded to the client.  Just like the JavaScript, this helper will pull its localization key from the strings.js file found in your .lproj directories.
  • In support of the build-time localization, the build tools now also support special build-only language keys.  Any loc key beginning with “@@” in your strings.js file will be removed when the strings.js file is built for your web browser.  This way you can add loc strings intended only for use at build time and they will not be sent to the web browser with the rest of the loc strings.
  • Along the lines of localization, the build tools now automatically set the String.preferredLanguage property in your JavaScript to the current active language.  This is different from String.browserLanguage, which returns the language that is auto-detected from the browser.  The JavaScript loc() method has been updated to use the strings from the preferred language instead of the browser language by default.
  • You can use the same URLs that will be used at production time to manually request resources and sc-server will build them for you.  This makes it easy to reference resources without using the static_url() helper.  This approach is not recommended but sometimes in is necessary for various reasons and now it will work.
  • The build tools no longer automatically merge .html files into the index.html file.  They only merge files ending in .rhtml and .html.erb.  This allows you to include static .html files if you need.

JavaScript Framework

The framework now supports a variety of new features related to localization and a delegate for the collection view that can give you a way to provide fine grained control over how the collection view will handle drag and drop, deletion, etc:

 

  • Collection View now supports selectOnMouseDown which can be turned off to provide better drag and drop behavior on SourceList.  SC.SourceListView now turns this off by default.
  • SC.CollectionView now supports selectAll (Ctrl+A)
  • SC.CollectionView now supports the delete key to remove items.  Also added delegate methods to the collection view delegate to give you control over how deletions happen.
  • SC.window can now properly capture the backspace key in Firefox.  To activate this feature you must declare SC.CAPTURE_BACKSPACE_KEY = YES in your core.js file.  Capturing the backspace key will prevent the browser from going to previous page when the user hits backspace, which can lead to data loss. To capture this key, SC.window will directly set the document.onkeypress handler.  
  • SC.GridView now supports dropping ON items.
  • SC.ListView now supports dropping ON items.
  • Removed the try/catch() that was placed around property notifiers.  This is not only faster but it will make it easier to debug these exceptions in Firebug and IE.
  • [FIX] SC.InlineTextFieldView was using the _frame property even though that is used by a parent class.  Changed to _optframe
  • Improved some documentation here and there.
  • [FIX] SC.View will recache its frames when isVisibleInWindow changes.  This will help to ensure we always have the correct dimensions when bringing views on and offscreen. — All unit tests now pass again.
  • Collection View now supports dropping items ON item views as well as between them. 
  • Collection Views now support a delegate object that can be used to control  drag and drop and selection behavior.  See mixins/collection_view_delegate.js  for a complete description of the new methods.
  • SC.ArrayController now supports the useControllersForContent property.  If set to YES, then getting an objectAt() will return a controller for the value instead of the value itself.  This is useful for those times you are using an array controller to manage a set of objects you want to control.  Previously this feature was always used by array controllers and could not be disabled.  This is now off by default.
  • [FIX] SC.ArrayController and SC.ObjectController now will properly observe their own content, even when the content is set on init.

 

 

Other

Miscellaneous improvements to the packaging system and sample apps.

 

 

  • Added mongrel as a required dependency of SproutCore.  If you have something like thin installed this is technically not required, but several people were experiencing trouble installing the gem.
  • Lots of Safari-specific features for Photos just to demo some of its capabilities.  Client-side storage support is also provided but currently does not save changes you make.
  • Initial changes to SampleControls to add a form-view demo.  None of the controls are wired up yet.
  • Improves Photos sample to include support for adding/deleting albums and drag and drop into albums.

 

 

 

 

 

 

 

New Feature: Style Properties

One of the coolest things about the SproutCore property system is the unknownProperty() handler.  This method is called on your object whenever someone tries to get or set a property that is undefined.  Normally, this method does not do much, but you can use it to implement all sorts of exotic behaviors with just a tiny bit of code if you like.  This is how SC.Controller objects work, for examples.

The latest bit of magic made possible by unknownProperty is the style properties just added to SC.View in trunk.  Now you can directly get or set most CSS properties on your view just by adding “style” to the property name.  For example, if you want to set the background color, you could do:

myView.set(’styleBackgroundColor’, ‘red’);

Likewise, if you want to get the left setting, you could do:

myView.get(’styleLeft’);

Certain properties, notably those ending in “Left”, “Right”, “Top”, “Bottom”, “Width” and “Height” will be automatically converted to integers (we assume pixel measurements for now).  Most other values are passed through directly.

These properties can help make your code easier to read today and they will become very useful for the new animation system in the future.

Remarkably, all of these properties were added with about 10-lines of code thanks to unknownProperty(). If you want to see how some of this magic works for yourself, checkout the code in SC.View.

 

New Feature: Inline Text Editing and SC.Editable

Two new features to announce and explain to day, both of them are related.

Edit Here…

Many times you want to display content in a list view and then click to edit it.  Now you can do this quite easily with the inline text editor.  To enable this feature on any label view just set isEditable: YES (or add :editable => true to the view helper).  

To enable this feature on a collection view, just set contentValueIsEditable: YES (or add :content_value_editable => true to the collection view view helper.)  For the collection view to work you must be using either SC.ListItemView or SC.LabelView for your example view type.  If you are using one of the built in classes such as SC.ListView or SC.SourceListView then this is already done for you.

To see an example of inline editing in action, just visit the sample controls demo and try clicking twice on list item in the sidebar for either the Collections or Collections2 tabs.  You can also select an item in one of these views and hit return to activate the editor.

Edit There…

Of course there is a lot more to this feature than just enabling inline editing in a few places.  You can also reuse the inline editor in your own views as well.  To activate inline editing, you just need to do two things:

 

  1. Implement the SC.InlineEditorDelegate protocol.  You can apply this mixin if you want but mostly you should override the four methods described here.
  2. When you want to begin editing, call the SC.InlineTextFieldView.beginEditing() method with any options you want to set.  You should at least provide a frame, which will tell the text field view where to size and position itself, and an exampleElement.  The exampleElement is DOM element that the text field view will use to clone its text styles.  This makes the inline editor automatically appear to similar to the element that it is supposed to be editing.

 

For an example of how to use the inline editor, checkout the source for SC.LabelView and SC.SourceListView.  It is really easy and generally takes only a few minutes to get going.

Edit Everywhere!

Paired with the new inline editor control is another new mixin that is going to be used throughout the framework for version 1.0 called SC.Editable.  This protocol defines a few standard methods that will can be used by other parts of your application to begin and end text editing of a control.  The collection view for example, looks for this protocol when it tries to begin editing the content value of an item view.  This is how you can trigger inline editing of a control in the collection view in a standard way.

This protocol is implemented by SC.LabelView, SC.SourceListItemView, SC.InlineTextFieldView, SC.TextFieldView, and SC.TextareaFieldView and you can implement it for your own views as well.  If you do implement it, then all of the built in cues that SproutCore recognizes to begin text editing will automatically work for your views as well.

As usual, for more info on these items, checkout the sample code using svn co http://sproutcore.googlecode.com/svn/trunk/samples, consult the reference documentation and ask on the Google Group if you have any questions.  Cheers!

 

New Feature: Timers

Speaking of Cocoa, I’ve added a new feature to SproutCore today very much inspired by Cocoa called Timers.

Timers are a better way to execute deferred code than setTimeout() and setInterval(), especially when you need to execute several different methods and you want them to stay in sync, such as during animation.  

The problem with timeouts is that they are both inefficient and non-deterministic.  That is, even if you set a timeout to execute in 100msec, you don’t know when the timeout will actually run.  It may be 100msec, it may be 150msec, it may be 1500msec.  Anything could happen.  To make matters worse, browsers get really slow when they start executing lots of timeouts, meaning the time ranges when your timeouts execute will start to vary even more.

Although we can’t completely overcome the non-deterministic nature of timeouts, Timers at least make it better.  Instead of setting one timeout per Timer, SproutCore sets only one timeout on an object called SC.runLoop.  This object manages all of the timers you have registered in the system.  When its timeout triggers, it will check your timers and fire any of them whose time has come.  Since this is all done in the context of a single timeout handler, timers that are set to expire at the same time will tend to fire together just like you might expect.

In addition to providing a more consistent firing experience, Timers also schedule their time intervals based on when the run loop currently began running.  This means that if you are in an event handler and you create three timers to fire in 100msec, they will all fire at the same time.

Using Timers is extremely simple.  You can easily create them yourself (see the documentation here), or you can use the invokeLater() method defined on both objects an functions.  Here is how you could invoke a method on an object after 100msec:

myObject.invokeLater('updateAnimation', 100) ;

If you just want to invoke a method the next runloop but as soon as possible, you can also omit the time:

myObject.invokeLater('updateAnimation');

Or sometimes you may have a function you’ve defined that you want to execute which is not attached to the object.  You can do that too:

function() {
  // handle animation
}.invokeLater() ;

If you want the function to execute in the context of an object you can do that also:

function() {
  // handle animation
}.invokeLater(myView, 100);

In the past if you wanted your timeout function to execute in the context of a specific object, you had to use bind() which created a nasty closure to do its work.  Thanks to the way timers are implemented, no closures are created during this process unless you pass a bunch of custom parameters (which is not the common case).

If you do want to pass custom parameters though, you can do so:

myObject.invokeLater('updateAnimationTo', 100, 'newState');

That’s it.  Timers are really easy to use and much more performant than setting your own timeouts.
 

Changes to SC.LabelView API

One of the oldest classes in SproutCore is the SC.LabelView.  This class simply displays strings of text (and optionally allows you to edit them with an inline editor).  Since I’m normalizing the SC API right now, this class is one of those that is simply too out of lockstep to maintain 100% compatibility in.  So I’m going to break the API.  I think you will see though that the new changes are well worth it.  Here is what you need to do to update your code:

The Old Way

In the past, to set the value of an SC.LabelView you set the content property.  For example, you might do something like this:


SC.LabelView.create({
  contentBinding: 'Contacts.detailController.fullName'
});

or if you were using the view helper:


<%= label_view :full_name, :bind => { :content => 'Contacts.detailController.fullName' } %>

If you wanted to set the content to a more complex object such as a record and extract a single value from it, you would use the property property to name the property key you wanted to check:


SC.LabelView.create({
   contentBinding: 'Contacts.contactsController.selection',
   property: 'fullName'
});

Or if you were using the view helper:


<%= label_view :full_name,
  :bind => { :content => 'Contacts.contactsController.selection' },
  :property => 'fullName' %>

The New Way

Now SC.LabelView follows the same convention used by all other views that include SC.Control (such as SC.ButtonView, etc.).  If you want to set the value of the label view, use the label property.  The example above becomes:


SC.LabelView.create({
  valueBinding: 'Contacts.detailController.fullName'
});

or with a view helper:


<%= label_view :full_name, :bind => { :value => 'Contacts.detailController.fullName' } %>

If you need to bind to a more complex object such as a record and you want to get a specific property, use the content property and set the name of the property key you want the label view to display using the contentValueKey:


SC.LabelView.create({
  contentBinding: 'Contacts.contactsController.selection',
  contentValueKey: 'fullName'
});

or with the view helper:


<%= label_view :full_name,
  :bind => { :content => 'Contacts.contactsController.selection' },
  :content_value_key => 'fullName' %>

That’s it.  Note that to use the :content_value_key in the helper above, you will need the new build tools.  If you are still using the old build tools, do the following instead:


<%= label_view :full_name,
  :bind => { :content => 'Contacts.contactsController.selection' },
  :properties => { :content_value_key => 'fullName' } %>

This change should basically bring you up to speed with the latest API.  Note that because of this shift, SC.LabelView can now be used as an item view in a collection.  The newly introduced SC.TextCellView has been merged into SC.LabelView and no longer exists as its own class.

 

The Road to SproutCore 1.0

I started work on SproutCore almost two years ago with the goal of making it easy to create speedy, rich, fluid applications on the web.   We’ve had to really push the boundaries of what is possible in the web browser to deliver something suitable but I’m happy to say that we are almost there.  SproutCore will go 1.0 this summer.

What does 1.0 mean?

SproutCore’s goal is to make it easy to build rich, fluid applications on the web.  We’ll go 1.0 when this objective has been achieved.  That means:

 

  1. Stable APIs.  These APIs will drive the framework for a long life ahead.
  2. Models, Views, and Controllers.  A full stack framework for not only managing widgets in your app, but managing the state.
  3. Automated Build Tools.  Generating performance optimized JavaScript and HTML is no easy task.  That’s why we’ve created this set of build tools.  
  4. Documentation, Testing, Tutorials.  Plenty of resources to ease you into development.  Building web apps is a whole new world, and we need lots of materials to explain how to do it.

 

What Has Been Done?

A lot of changes have taken place already with SproutCore that you may not yet know about.  Here are some of the more important ones:

 

  1. Google Code.  We are moving the SproutCore SVN repository to Google Code.  The new project is at http://sproutcore.googlecode.com.  Of course, most people will never need to visit this site since SproutCore is now also distributed through the Ruby Gems system.  The Google Code site will also be the location for all new tickets.  I have migrated over all open tickets to this system already.  Please file new issues there from now on. 
  2. New Website.  sproutcore.com is not going away.  Instead it is becoming the home to a new website geared towards helping developers using SproutCore to build applications.  Where the Google Code site will be your home if you are hacking SproutCore itself, if you are building an app using SproutCore, then sproutcore.com has much more to offer.  This blog is located there, as well as documentation, sample code, demos, and links to the Google Group and other items that you need.
  3. New Build Tools.  The new build tools, now hosted in the Google Code SVN is one of the biggest improvements I’ve made to SproutCore recently.  Not only are they faster and more consistent, but they make it brain dead easy to build fast loading web applications in a flash.  There are entire books written about how to optimize the packaging and loading of resources on your web page, but with these build tools you don’t need any of them because it does it all for you, all the while making it far easier to manage your own code as you work.

 

What’s Left To Do?

SproutCore has been evolving considerably over the last few months but mostly under the covers.  I’ve intentionally held off on some of the more visible changes to avoid disrupting people who are already using the framework.  In the next few months, though, you’re going to see some of the visible changes start to happen.  Here is what I currently have planned (it’s a lot):

 

  1. Full set of widgets.  SproutCore has a smattering of views right now to largely cover most of the common GUI controls you might need to build.  Expect to see a few new widgets come into the fold before launch as well as a major cleanup of the APIs to make them more consistent across the board.  I’m also going to update some of these APIs to encourage more use of controllers and less subclassing of views.
  2. Atom-REST Interface.  Server interaction is a bit like underwear, it seems to be very personal to everyone.  Most people end up writing their own server code, but I still want to provide an approach that makes it easy to interact with a service that requires less code.  The current system is designed to work with a psuedo-REST oriented API that doesn’t exist anywhere in the wild.  The new system will instead work with APIs built using a REST model, with an Atom-based file format or a JSON derivative of Atom.  Unlike the current code, this system will work with existing APIs used by GData.
  3. General API cleanup.  I’m going to scrub the API for SproutCore one more time before launch.  Expect some major and minor changes here to make everything most consistent overall.
  4. IE Support.  During the course of making some of these recent major changes, I’ve allowed IE support to lapse.  This will be resolved for both IE6, IE7, and IE8 by the time we launch.  I am also working towards building some tests that will make it easier to keep this code working.

 

As always, appreciate your feedback on this.  Find me on the Google Group, IRC (#sproutcore, freenode.net) or on this site.  Also, if you are interested in helping to hack at any part of the framework, please get involved!  If there are things missing from my list that you think should be there for 1.0, you can help out there to.  Hacking SproutCore on the Google Code site will tell you what you need to know to get started.