I find the new Model.notFound fallback strategy to be a little awkward; let me explain why.
Suppose I have a Foo model, and I decide to override the .notFound method in order to kickoff some Ajax call to try and fetch records from the server when they don't exist locally. Should I be returning a new Foo instance from my custom .notFound implementation? Isn't it a little awkward that the .notFound method would synchronously return an empty Foo instance that may or may not be "filled in" by an Ajax call at some point in the future? How was this feature intended to be used with Models that are backed by Ajax persistence?
Suppose I don't return anything from my custom .notFound method (except maybe null). I still kickoff the ajax, but I keep the "return null" contract. At that point, why have .find() return the value returned by .notFound()?
Worse still, my .notFound implementation could return something completely different, like a Promise resulting from the ajax request I kicked off. Now every time I call Foo.find() I have to inspect the type of the return value to see if it's a Foo or a Promise.
Wouldn't it be safer to enforce consistency by making Model.find() look something like this?
@find: (id, notFound) ->
record = @irecords[id]?.clone()
notFound?(id) unless record
return record || null
In general, I think Spine could benefit greatly from embracing asynchronous behavior more readily, especially when it comes to saving or retrieving model instances to a storage mechanism. I often find it problematic to write code that works against both the synchronous LocalStorage implementation and the asynchronous Ajax implementation without my code needing knowledge of how the data is persisted.
Somewhat related, I've been maintaining a set of "monkey patches" to Spine in my current project to add functionality that I need but Spine does not have. I think I'm going to start putting together some pull requests to get those "patches" moved upstream into Spine properly. This idea here might be my first.