*** Disclaimer — Here be dragons
Now with that out of the way, I would like to talk to you about the confusion you will have when you begin to work with SproutCore in the age of SproutCore 2.0.
I have no doubt that you have been hit with the SproutCore 2.0 Blitz campaign if you are reading this. So lets begin shall we, SproutCore 2.0 is going to be great for the new community and I have no doubts that it will be great for the original community. I mean the folks at strobe are cleaning up a lot of left over code and implement a lot of features we all we attempting to implement in the 1.x line. With that said SproutCore 2.0 still has a ways to go before it will be ready to produce the quality “desktop like” applications that we have been accustomed to creating with the SproutCore 1.x line.
That’s the divide right now and it could be painful in the future if some considerations aren’t taken into account. What are those considerations you may ask. Well for starters we all know that the focus of SproutCore 2.0 is to be light-weight and easier to pick up. This is a fantastic goal because it has the potential to appeal to a wider base of developers, but here’s the catch, Those of us that made a bet on SproutCore did so b/c it kicked all of the other frameworks out there in the nuts right out of the box. Did you say right out of the box? Yep and that’s where that considerations come in to play. With SproutCore 2.0 you get the framework (i.e. KVO, KVC, Bindings and more) but what you don’t get it the “desktop like” feel. Now you could say that I could create this feel my self and most of us re-style the native look and feel anyhow. You would be right, but that was part of the package right?
I am aware of the efforts of Majd Taby on the new “SproutCore UI” but from what I can tell from the efforts thus far have been on “Mobile Style” navigation and layouts. While this is really good stuff, please don’t forget that there was and still is a decent size community that live and breath the “roots” of SproutCore.
This divide should not exist in the future. When I get asked ( and I often do get asked ) the following question, “Which version of SproutCore should I use to build a large enterprise application”, I want to be able to say, “Just use the latest, you’ll get the same experience, with a leaner footprint”. But as it stands today I cannot in earnest give this answer. I have to give the honest answer and tell those folks to use the 1.4.5 - 1.6.x line of SproutCore.
So let’s build a bridge across the divide and blur the lines drawn in the sand. I’m more than willing to help in any way. Let’s not forget SproutCore’s roots and leave all of the blood, sweat and tears behind for no good reason. Let’s make it possible for all of the early adopters to come along on the Journey with Strobe. But keep in mind most early adopters in the enterprise adopted early because the “desktop like” experience was already there and this is important to them, you see to them agile matters and if it’s already there that’s one less thing they have to wast time on.
Have you ever been writing your SproutCore app, things are going just fine and then you realize that you would like to have a bit more control over your async operations (i.e. Record.refresh(), Record.commit(), Record.destroy() ). Well if you’ve had this feeling, you are not alone. I had this feeling and decided to implement a solution that would not have any harmful effects on the underlying frameworks that make up SproutCore. In this article I will layout my proposal to allow this modification to the underlying frameworks, weighing the pros and cons along the way as well as giving a brief example of how I was able to regain control of my async operation. So let’s get started, shall we…
Proposal ( With pros and cons )
When working with SproutCore you will usually have at least one data-source. This data-source will act as your adapter to the remote services that you app will need to interact with, allowing your app to perform CRUD operations on data.
The data-source is also tied closely to SproutCore’s store for example: If you use the sc-gen command to generate the boiler plate code for your first data-source you will see that the callbacks you are instructed to call are used to notify the ‘store’ that your data-source has finished with the XHRRequest. If you dive a bit deeper you will find that one of the purposes of these callbacks is to set the status of record(s).
If you read the previous paragraph and thought to yourself that you could just use an object controller and observe the status of a record, you would be correct. You should be able to rely on this status to make further decisions. So what’s the catch you say? Well the status property of a record or record array is a publicly accessible property and it can be changed by any other piece of code or any activity on said record.
If you are solely relying on the status of records you could find yourself in a pickle. For example: You refresh a record and once the status has been determined to be READY (or one of it’s many children), you create a nested store from this record. In the time between READY and nested store creation the record is refreshed again from outside code thus placing the record into a BUSY state. You then try to modify this record and receive an exception that the Record is BUSY.
You could say that you could run into this same issue by just using callbacks instead of observers, and you would be correct. Although by using callbacks you can add a bit of safety by having explicit checks for record state changes or modifications and defer you execution plan. Now keep in mind I am not saying that going with the grain of SproutCore is bad.
If you do go with the grain of SproutCore you would have set up an object controller that received the the record as it’s content. You would then observe the status of the controller’s content and make decisions based on it’s status, etc…
Are these extra levels of abstraction really needed just for the cases where you need to refresh, save, or destroy a record? These abstractions end up being extra objects that need to be instantiated and and at least one observer that could have the potential to fire at undesired times if not properly removed. When you start thinking about performance you will begin to ask questions about observers and extra objects taking up memory space. This also becomes a concern when dealing with the dreaded IE. Observers fire very often each counting as one or more statements executed, and IE has a limit and starts spewing long running script errors. Now on to using callbacks…
Callbacks can be useful in the cases stated above as well as many others. I am going to start with an example and then move on to some reasons why callbacks are needed as an addition.
Example:
MyRecord.refresh({
successCallback: function(args) {
// do something useful.
},
errorCallback: function(args) {
// do something useful.
});
In the above example you see that you are able to pass a hash into the record’s refresh function with callback functions. These functions are only executed once in the context of the request. Unlike an observer on the record’s status that will be executed for all of the status transitions, ( there are quite a few.. try one with a console.log() ). If you are using state charts such as the built in StateChart Framework included with SproutCore, you could assign an action that causes a state transition as the receivers of the callback notification. What I am describing here is this, I am able to use the callback functions to avoid an unneeded controller and it’s record status observer. In eliminating these two things I have been able to save memory space and statement executions.
I am not proposing that these callbacks become a replacement for the SproutCore way, but that they can compliment the standard path. By allowing callbacks along side the standard path you are also opening the doors to the rest of the javascript community. The community that thrives by being able to control the flow of their application with callbacks. Take NodeJS, jQuery, etc.. as examples. These developers could instantly feel at home with these callbacks because they already deal with them on a day to day basis.
Hopefully you can see the benefits. So I will now move on to the simple and quick way that I was able to implement these call back with out any side effects to SproutCore. Keep in mind this was the quick path, you could just as easily create a data-source delegate that would provide the same functionality. I am going to link to a gist with the implementation details because Tumblr sucks at code formatting.
https://gist.github.com/741655
If you have given the sample implementation a look over you will see that it’s harmless to SproutCore. When implementing your data-source(s) for your application you will be dealing with the response code from your remote services, this is where you fire the callbacks with any data/information you may want to provide. Examples of data to provide would be the record’s storeKey, id, the service response code, and response messages.
Now that you’ve made it this far you deserve a bonus, so here goes. Would you like to have hooks into the save process that you allow you to execute functions like ‘Record.beforeSave()’ and ‘Record.afterSave()’. You can do this with the implementation in the gist. All you have to do to get these hooks is to add a check for them being defined on the record when you add the ability for callback params to the commit functions of the same files and fire them before and after the actual commit. These little gems can prove to be handy. For example, let’s say that you want to ensure the format of the data before you actually send it to the remote service, then on the way back in from the remote service you want to ensure that it adheres to the model contracts on the client side. If the previous appeals to you, you are not alone and this is just one of the many advantages you will reap from allowing this addition as a compliment to the standard SproutCore path through the data-source/data-store combo.
I have written this proposal in hope that I receive feedback from the fantastic dev community surrounding SproutCore as well as folks who are taking a serious look at SproutCore but felt a bit confused by not have this ability.
Yeah, I know that’s not a word but it works for this post…
I have been doing a lot of work lately creating a full stack with SproutCore, NodeJS, and mongo with a bit of WebSockets and redis and… and… well the list goes on but you get the idea right?
Basically the structure of a SproutCore (http://sproutcore.com) project lends it’s self to holding multiple apps. So you create a manger app or framework in my case, start building a tool box (one app per tool). Sprinkle those on top of NodeJS using Express/Connect (http://expressjs.com).. Grab NPM or seedJS and install Express, node-websocket-server, node-redis, and mongoose.
Next thing you know, you have a sweet set of day to day tools all dressed up in a SproutCore suit holding the “Ace” of spades and your day just got a bit brighter… :)
Well at least mine did… Keep watching b/c I’ll be open sourcing this tool box in the coming weeks..
SproutCore Blog: Quilmes Enters Master -
Late last night we merged the quilmes branch back into master. The merge includes all changes from quilmes, along with the fixes from master. This means that we now only have two major branches: master, new home of the quilmes code, heading towards 1.5 and 1-4-stable for the 1.4 code.
1-4-stable…
(Source: sproutcore)