Another issue with dependencies in Air version

2 views
Skip to first unread message

Sean Clark Hess

unread,
Mar 4, 2009, 2:46:09 PM3/4/09
to restfulx-framework
Hi Dima / Everybody, 

I don't know if this is a bug, or a feature, or what, but in our app, we're storing a reference to an object (a Person in this case), which we then update as we go. For example, we might add a photo to a person (A person has many photos). 

The problem is if we create a new photo, set it's person to the main person, and create it, the main person's photos variable is never updated. 

For an example, I've reproduced it in my zombie example. Am I just misunderstanding how this works? I don't want to bind to Rx.models.index(Photos, {metadata:{person_id:myPerson.id}}) because that's bad design (in my opinion). I would like to be able to bind to myPerson.photos and get the updates as they are made. 




Any help? Thanks!

~sean

Sean Clark Hess

unread,
Mar 4, 2009, 2:48:09 PM3/4/09
to restfulx-framework
Just to clarify, in my zombies example, you can see when findAllPeople is called, the person I pulled from Rx.models.index has some zombies, but the person I saved earlier when I created him doesn't. 

Dima Berastau

unread,
Mar 4, 2009, 5:23:22 PM3/4/09
to restfulx-...@googlegroups.com
Hi Sean,

I suspect you are using it wrong. 

Try:

private function addAPerson():void {
        var bob:Person = new Person();
        bob.name = "Killer Bob!";

// this.bob = bob;
   
        bob.create({onSuccess: onPersonCreate});
          
      var ugly:Zombie = new Zombie();
        ugly.type = "ugly";
        ugly.person = bob;
        
    ugly.create();
        
      Alert.show("Added");
      }

private function onPersonCreate(result:Person):void {
this.bob = result;
}

When you do <model>.create whatever instance you just did create on is not by default connected to the cache, it's just a 
standalone reference in memory. What happens is that when you do create, this object's properties are serialized to the DB, then the "raw" object (no type, no relationships, etc) is unmarshalled at which point we check "is it in the cache? 
if not, create a new instance, add it to the cache and hook up all the relationships."

The point is just because you did:

this.bob = bob

doesn't mean anything, you have to do this *after* your object has been created and unmarshalled. You can get at the reference that is actually *connected* to the cache via the callback argument, or you can add a CacheUpdateEvent handler and deal
with CREATE, UPDATE, etc events there.

Hope this helps,
Dima

Dima Berastau

unread,
Mar 4, 2009, 5:28:40 PM3/4/09
to restfulx-...@googlegroups.com
Hi Sean,

Do you still have the recursive index error you were having before? I didn't see any tickets created for this. Let me know if it's still a problem.

cheers,
Dima

Dima Berastau

unread,
Mar 4, 2009, 8:21:59 PM3/4/09
to restfulx-...@googlegroups.com
I think this actually deserves a slightly more detailed explanation. I've made a note to make a blog post about this later.

Given something like this:

private function doSomeStuff():void {
  var bob:Person = new Person;
  bob.name = "Killer Bob!";

  bob.create();

  var ugly:Zombi = new Zombie();
  ugly.person = bob;

  ugly.create();
}

What's wrong with this? 

Well, in a synchronous universe nothing. Your bob reference can be meaningfully updated with a new ID you get from the database or elsewhere behind the scenes, when all is done with bob this method will move on to creating the ugly zombie, ugly.person = bob will resolve to a fully functional bob object which has an id, etc. Everybody is happy.

In an asynchronous universe, this won't work. You did bob.create(), bob will not be created and it will not have an ID until some unspecified time t. This has no effect on the execution of the rest of this method, which will proceed immediately. The ugly will get created immediately after you did bob.create with no regard for asynchronous nature of that call. As you'd expect, bob database instance was not yet created, it doesn't have an ID, etc. Fail.

While it's possible to deal with AIR SQLite in both synchronous and asynchronous mode, in RestfulX all CRUD operations are asynchronous independently of what service provider you use. This is done so that if you switch to some other service provider which is explicitly asynchronous (anything other than SQLite), you don't have to run around rewriting your code.

How you can you write the above to make sure that these operations perform as expected. Do the following:

private function doSomeStuff():void {
  var bob:Person = new Person;

  bob.name = "Killer Bob!";
  bob.create({onSuccess: onPersonCreate});
}

private function onPersonCreate(result:Person):void {
  var ugly:Zombie = new Zombie();
  ugly.person = result;
  this.bob = result; // or whatever

  ugly.create();
}

In this case, you explicitly wait for the successful creation of bob, then you proceed to create an object which directly references bob ([BelongsTo]). Everybody is happy.

As far as you are concerned, until onSuccess fired or CacheUpdateEvent fired, there's no bob.

Hope this helps,
Dima

Sean Clark Hess

unread,
Mar 4, 2009, 9:01:48 PM3/4/09
to restfulx-...@googlegroups.com
Aha... That totally makes sense, thanks. 

However this brings up another issue that has been bothering me since the beginning. Your onSuccess handlers do not always fire. 

It has seemed in the past (i'll have to play with this case) that if the objects are cached/loaded/whatever, all calls are synchronous, and (the big issue) onSuccess and onFailure never fire. This is a big deal, as I don't want to program in if statements to see if a statement returns immediately or calls a callback every time I call a function. 

I think if you make onSuccess handlers fire EVERY time, regardless of whether or not they've been cached, etc, it would fix the problem. 

A good example of this is index. If you index something, the callbacks consistently only fire the first time. Every time after, they return an array collection right away and they don't fire. 

Thanks!
~sean

Dima Berastau

unread,
Mar 4, 2009, 11:49:31 PM3/4/09
to restfulx-...@googlegroups.com
Hi Sean,

onSuccess callback will always fire for create/update/delete without exception. It won't always fire for index or show because unlike create/update/delete index and show are bindable. Meaning you can do this

<mx:List dataProvider="{Rx.models.index(Zombie, onSuccessFunction)}"/> and you'd expect this to work. Bindings trigger more than once because of the component life-cycle. This is handled in the index and show methods by setting the indexed and shown flags appropriately, to only actually hit the service provider when necessary. onSuccessFunction will fire only when the service provider has been actually invoked. If onSuccess fired every time index was invoked, it would fire multiple times if you happen to place it inside a data-binding expression, which I think is even worse than firing only occasionally.

If you want to know for sure if the onSuccess will fire or not as a result of Rx.models.index, you can always check Rx.models.indexed(Zombie) before calling it, it returns a boolean.

If you want the binding to fire all the time, there's an equivalent of Rx.models.index, which is non-bindable and will hit the service provider every time, called Rx.models.reload. It takes all the same arguments as Rx.models.index and does the same thing, except it's non-bindable and will always go to the service provider (and by implication will always fire onSuccess callback).

Hope this makes sense,
Dima

Sean Clark Hess

unread,
Mar 5, 2009, 9:28:52 AM3/5/09
to restfulx-...@googlegroups.com
It's all coming together. Thanks Dima. If create and update always fire, that's enough for me. 

Thanks again for all your great work!
~sean
Reply all
Reply to author
Forward
0 new messages