**** BEGIN LOGGING AT Mon Jan 27 03:00:00 2014 Jan 27 13:37:30 hey everyone, does any of you have some experience with enyo 2.3's Models and ModelControllers? Jan 27 13:39:50 I'm getting a stack overflow when fetching the model on my ModelController. I'm pretty sure I'm doing something wrong, but not sure what I should be doing instead. Jan 27 14:40:29 anyone? Jan 27 16:00:38 ruben_vreeken: this channel is a little bit slow, stick around Jan 27 16:04:48 Allright, I'll be sticking around a little. So, whats the usual etiquette here? Should I just write down the specifics of my question? Or wait for someone to confirm that they've got a little time to help me out? Jan 27 16:05:40 go ahead and ask your question, people come and go, and some check logs Jan 27 16:05:48 allright then. Jan 27 16:05:52 if you don't get an answer for a while, you could ask again Jan 27 16:06:08 I'm trying to create a simple a authentication module for an enyo web-app. The thinking behind the module is as follows: Jan 27 16:06:08 - The authentication module should allow users to log in or log out through api calls with a webserver (`api/users/login` and `api/users/logout`. Jan 27 16:06:08 - A user is considered to be `logged in` *if* and *only if* a user object is available through an api call to `api/users/me`. Jan 27 16:06:08 - The authentication module should be the *only* module to manage this data and should contain a `CurrentUserModel` representing the profile of the currently authenticated user. Jan 27 16:06:08 (a while being a day or so) Jan 27 16:06:11 - Other modules should be able to subscribe to state-changes regarding user authentication through `enyo.Signals` such that neither the Authentication module nor the top-level application needs to concern themselves with authentication requirements of specific modules. Jan 27 16:06:18 gotcha :) Jan 27 16:06:20 thanx Jan 27 16:07:06 The way I have implemented this so far, is to make the authentication module a `ModelController`. The model is set to an instance of `CurrentUserModel`. Jan 27 16:07:46 In order to check if the current user has a session, I call `this.model.fetch()` from inside a method on my `ModelController`. Jan 27 16:08:16 The idea is, if the model fetches, the primary id of the current user might change, confirming that a new user has been authenticated. Jan 27 16:08:32 I'm using an observer to observe the user id property on the model to do this. Jan 27 16:09:06 If a user change is observed, a signal will be broadcasted for other modules to know about the change. Jan 27 16:09:36 If there is no active user session, the model fetches, comes up empty, and the id is set to an empty string. All works as intended. Jan 27 16:10:16 However, if a user is already logged in, I end up in an infinite loop. Jan 27 16:10:53 It appears that fetching the CurrentUserModel triggers the `modelChanged`method, which in turn triggers listeners for model changes. Jan 27 16:10:54 are you certain that the infinite loop is because of the MVC structure? Jan 27 16:11:11 for some reason, it appears as if `modelChanged` is considered to be one of these listeners. Jan 27 16:11:53 I'm pretty sure, it has to do with fetching the model. If I comment out everything else, including observables and signals, I still end up with the infinite loop. Jan 27 16:11:53 what is the kind of your CurrentUserModel? Jan 27 16:12:14 CurrentUserModel is an extension of UserModel, which is an extension of enyo.Model. Jan 27 16:12:48 oh, huh, the official API docs don't have this yet Jan 27 16:13:16 Ah yes, I should've mentioned, I'm working with enyo 2.3-rc1. Jan 27 16:13:50 lemme find the docs Jan 27 16:14:03 http://enyojs.com/docs/2.3.0-pre.10/api.html <= here ya go Jan 27 16:14:08 yeah, it's loading now Jan 27 16:16:24 what event are you sending through enyo.Signals? are you sure it's not the same name as what would trigger the modelChanged() function? Jan 27 16:17:21 The modelChanged should be triggered by the model itself, not by an enyo signal. Jan 27 16:17:55 Hold on, i'm preparig a pastebin of the code. Jan 27 16:18:43 so, I should say, I don't actually know anything about the 2.3 stuff, just trying to think through what might be occurring Jan 27 16:19:19 No problem, who knows, an extra set of brains might help right? Jan 27 16:19:26 could be Jan 27 16:20:08 http://pastebin.com/SzM93Qnv Jan 27 16:20:11 here ya go Jan 27 16:20:28 by the way, thanx for thinking along, it much appreciated. Jan 27 16:21:03 and you haven't overloaded modelChanged() right? Jan 27 16:21:11 nope, haven't overloaded it. Jan 27 16:22:00 first I thought it had something to do with the observers, but i've tried commenting the observer definition as well as the userIdChanged callback, and the problem still persists. Jan 27 16:22:08 ok, so, based on what the APIs say, modelChanged "responds to the model property being set on this controller. Overload this method for additional behaviors." Jan 27 16:22:24 indeed. Jan 27 16:22:43 and it looks like in your fetchUser() function, you're resetting this.model conditionally Jan 27 16:22:53 are you certain you're not always hitting that? Jan 27 16:23:27 I'm alway hitting the this.model.fetch call. Jan 27 16:24:01 the this.set(model) call is only hit if the controller hasn't yet received a model or if the model has been destroyed Jan 27 16:24:05 and never the if-statement? Jan 27 16:24:31 the if-statement gets executed the 1st time. when fetchUser is called from within the create method. Jan 27 16:24:36 sure Jan 27 16:24:40 to instantiate the initial model. Jan 27 16:24:53 After that, it doesn't get executed again. At least, not yet so far. Jan 27 16:24:57 sure Jan 27 16:25:11 The idea is that, once the user logged out, the model gets destroyed. Jan 27 16:25:15 so, it makes sense to me that this.model.fetch() would throw the modelChanged() event Jan 27 16:25:39 and what you're saying is that modelChanged() listens for it's own event? Jan 27 16:25:40 upon destruction the modelcontroller will actually remove the model. such that it would be necessary to instantiate a new model if another user wants to log in again. Jan 27 16:25:47 right rigth Jan 27 16:25:49 I see that now Jan 27 16:25:51 The model.fetch does indeed throw the modelChanged event. Jan 27 16:26:06 yes, it listens to its own event Jan 27 16:26:08 strangely. Jan 27 16:26:44 you might consider going and looking into the ModelController source itself, and see why it might be doing that Jan 27 16:26:51 modelChanged, receives two arguments. "previous" and "model". where "model" is the new model. Jan 27 16:27:18 if it receives a new model, it adds a "change" listener, and a "destroy" listener, and calls "sync" Jan 27 16:27:51 sure Jan 27 16:28:22 the changed listener gets triggered, and iterates over observers for each attribute in the revised model Jan 27 16:28:32 one of those observers appears to be the modelChanged method itself. Jan 27 16:28:51 either that or i'm completely misinterpretating things.. Jan 27 16:29:13 the other thought I had is maybe sync is triggering modelChanged()? Jan 27 16:29:28 that's an interesting thought.. Jan 27 16:29:38 i'll look into that Jan 27 16:29:40 does UserModel have an observer on the change event? Jan 27 16:31:54 So, the sync call from inside modelChanged is the one notifying the observers if modelChanged receives a new model. Jan 27 16:32:33 CurrentUserModel has a didFail callback, which explicitly sets all attributes to either their default value or undefined. Jan 27 16:32:47 hmm Jan 27 16:33:10 That's actually old code from when I thought I could initialize a model once, and re-use it. Jan 27 16:33:21 I assumed a Model would contain a record as its attributes. Jan 27 16:33:40 what's the backtrace in the console look like? Jan 27 16:33:46 But, as it turns out, the record and the model instance are actually one and the same. Jan 27 16:34:11 this all sounds like it's gonna be a lot of work to switch to :-/ Jan 27 16:34:17 Thus I decided to destroy the model if the user logs out, and create a new one if a new user logs in. Jan 27 16:35:08 the backtrace says: enyo.kind.getLocal, enyo.kind.get, enyo.ObserverSupport.notifyObservers, enyo.ComputedSupport.notifyObservers, enyo.sync, enyo.modelChanged Jan 27 16:35:49 what's before modelChanged? Jan 27 16:35:52 from there on it repeates "enyo.ObserverSupport.notifyObservers", up to and including, "enyo.sync" Jan 27 16:36:14 from there on it repeates "enyo.ObserverSupport.notifyObservers", up to and including, "enyo.modelChanged"* Jan 27 16:36:32 That's the first part of the backtrace yes. the get calls are before the first modelChanged Jan 27 16:36:47 so are you saying that modelChanged causes sync, which causes modelChanged, which causes sync ? Jan 27 16:36:54 yup Jan 27 16:37:04 have you read the enyo source? Jan 27 16:37:08 yup Jan 27 16:37:45 The problem only occurs if there is already an active user session. Jan 27 16:37:50 On initial login, it works fine. Jan 27 16:38:20 So, i'm thinking, the enyo.kind.getLocal and enyo.kind.get might have something to do with whats going on. Jan 27 16:38:31 activeUser session meaning "the model is actually changing" Jan 27 16:38:37 yes. Jan 27 16:38:54 active user session meaning, if I try to fetch the current user, the server gives me a current user. Jan 27 16:39:27 if there is no active session, aka, no user has been authenticated on the server, the server returns a 404. Jan 27 16:39:33 in userIdChanged, your if statement? Jan 27 16:39:39 are previous and current objects? Jan 27 16:40:00 previous and current are the values of the "id" attribute of the model. Jan 27 16:40:16 so int Jan 27 16:40:28 or something that is actually comparable using == Jan 27 16:41:02 if no user has logged in yet, and the model is instantiated, previous is undefined and current is an empty string. Jan 27 16:41:26 The default value of id is an empty string at the moment. though I could also make that a int 0 Jan 27 16:41:29 but if you've got a user logged in, previous is a string and current is a string Jan 27 16:41:49 you might try strengthening that comparison (!==) Jan 27 16:41:58 make sure there's no weird typing going on Jan 27 16:42:10 if the user then logged in, previous would be an empty string (the previous default value that resulted from instantiating the model) Jan 27 16:42:23 and current, would be int 3 (the id of the user) Jan 27 16:42:39 however, while that part is a bit sloppy for now, I don't think it's where things go wrong Jan 27 16:42:40 I'm just guessing, but in the inner conditional, if you hit the else clause, you start the login process again (by changing the model again) ? Jan 27 16:43:08 because the problem persists if I completely remove both the observers definition ánd the userIdChanged callback. Jan 27 16:43:23 sure Jan 27 16:43:24 ok Jan 27 16:43:27 ignore me then :) Jan 27 16:43:53 no actually, if I hit the else clause, it just broadcasts that there is no user anymore. Jan 27 16:44:24 If some part of the application requires a user in order to access certain functionality, that module would call the requireLogin method. Jan 27 16:44:32 which would have a login form appear in a popup. Jan 27 16:44:40 sure Jan 27 16:44:49 I don't see anything obviously wrong in your pastebin Jan 27 16:45:02 I'm wondering if perhaps there's an inheritance issue somewhere Jan 27 16:45:06 yeah, thats what's puzzeling me. Jan 27 16:45:20 i'll see about deleting that didFail callback. maybe that's causing problems Jan 27 16:45:25 could be Jan 27 16:45:45 One of the things that confuses me at the moment is that the Model instance and the Record are actually the same. Jan 27 16:45:49 the other thing I do, and this is stupid debugging technique, but I put log statements in the call chain, and try to narrow down what's causing the failure Jan 27 16:46:37 you could obstensibly do something similar with breakpoints Jan 27 16:47:10 I'm more used to a backbone-ish scenario where a model is more of a container of data rather than 'the record itself'. So that's messing a bit with my mental model of what's going on I guess. Jan 27 16:47:57 haha oh yeah, console.log, I find that one to be as useful as breakpoints themselves. Jan 27 16:48:20 in enyo, if you use this.log() it'll give you some better context Jan 27 16:48:25 in the backtrace Jan 27 16:48:41 or, not backtrace, but the console Jan 27 16:48:42 ohhh spiffy! I didn't know that. Jan 27 16:49:00 this MVC stuff _is_ backbone under the hood, I believe Jan 27 16:49:00 maybe that'll improve debuggability (is that a word?) Jan 27 16:49:05 sure :) Jan 27 16:49:20 it's backbone inspired, yes. But its no longer backbone as I know it. Jan 27 16:49:22 have you seen all the wiki pages and things about the MVC? Jan 27 16:49:33 I thought it was wrapping backbone Jan 27 16:49:36 I think i've seen all of them. Jan 27 16:49:41 Yeah, that's what they started out with Jan 27 16:49:47 but then they decided to go their own way Jan 27 16:49:50 still backbone-inspired and such Jan 27 16:49:54 but decidedly different. Jan 27 16:50:12 For instance, in backbone, you don't really have a "datastore" component. Jan 27 16:50:22 in enyo, every model is attached to a datastore. Jan 27 16:50:45 huh Jan 27 16:50:48 interesting Jan 27 16:50:55 this datastore exists in the browser and is somewhat queryable. Jan 27 16:51:23 and is local Jan 27 16:51:26 yes? Jan 27 16:51:26 I think they wanted to add local storage and such as an integrated part of the solution, should you desire it. Jan 27 16:51:35 I desire it :) Jan 27 16:52:02 I havn't looked at the source of the stores too much yet, but its potentially interesting. Jan 27 16:52:06 we're on 2.2, and quickly getting to the point of our is starting to look somewhat unmanageable Jan 27 16:52:14 particularly merging Jan 27 16:52:46 As I understand, the current store accepts no duplicates of a record by default, but can be set to allow duplicates Jan 27 16:53:01 so you can cheat a bit with mergin there if needed. Jan 27 16:53:37 I'd really like a "canonical" dataset, that can merge changes in Jan 27 16:53:38 duplicate records do however impact how easily you can retrieve data from the local store Jan 27 16:53:42 right Jan 27 16:54:04 I'm going to have to dive into the whole store thing soon, hopefully before the end of the week. Jan 27 16:54:32 I'm thinking of writing a merge strategy that checks for duplicates and overwrites the existing record with the new one. Jan 27 16:54:40 right Jan 27 16:54:43 a little bit like backbone.relational does Jan 27 16:54:52 if i'm not mistaken. Jan 27 16:54:57 I'm not overly looking forward to switching to 2.3, but it'll get me some useful stuff Jan 27 16:55:14 For me, its the 1st version of enyo i'm touching :) Jan 27 16:55:34 I actually disliked the lack of mvc components in the past Jan 27 16:55:50 like, putting controller code inside views just felt wrong, u know? Jan 27 16:55:55 it's a weird paradigm to get used to Jan 27 16:56:37 yea it is. I notice my colleague prefers sending events around through multiple layers of components Jan 27 16:56:55 whereas I tend to wish for something like event aggregation Jan 27 16:57:11 I like that you can send events, but I find it hard to follow the call chains Jan 27 16:57:22 I despise long call chains haha Jan 27 16:57:30 makes everything feel tangled and such Jan 27 16:57:33 yep Jan 27 16:57:45 I guess signals are sort of like the event aggregation I'd wish for, apart from always being global. Jan 27 16:57:59 sorry I don't have a better answer for you about how to fix your issue. You might try asking in the forums also Jan 27 16:58:32 Yeah, I signed up there. My profile on the forum is under review at the moment. I hope the admin will approve it soon. Jan 27 16:58:41 I'll be using the name "ruben_vreeken" there too Jan 27 16:59:05 sure Jan 27 16:59:15 if you get into checking out 2.3 and have some questions you can probably find me there :) Jan 27 16:59:15 I don't see why they wouldn't approve you Jan 27 16:59:22 exactly :) Jan 27 16:59:35 We'll probably wait until it's an official release Jan 27 16:59:43 allright Jan 27 16:59:48 understandable :) Jan 27 16:59:56 heh Jan 27 17:00:26 for me, its a brand-new app, so we're figure its worth the risk. The RC seems pretty stable so far, unless my current problem turns out to be a bug haha Jan 27 17:00:55 heh :) Jan 27 17:01:22 I'm sure it's fine, but we're on 2.2, and it'll be a fair amount of work to make the switch, and I'd like to not have something change out from underneath me Jan 27 17:03:45 yup, been there, done that. Jan 27 17:04:16 its better to introduce incremental change, especially if the project is already growing a bit complex. Jan 27 17:07:08 right Jan 27 17:08:36 its great fun to get into one of those "refactor all the things!!" moods, but its not exactly a practical choice. **** ENDING LOGGING AT Tue Jan 28 02:59:59 2014