Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add afterInfinityModel #77

Closed
wants to merge 1 commit into from
Closed

Conversation

kxcrl
Copy link
Contributor

@kxcrl kxcrl commented Aug 18, 2015

In the application that I'm working on, there are many cases where get(this, 'store').find(modelName, params) is not the end of the promise chain before we hand the fetched models to the route. This PR adds that functionality.

I tried to follow the conventions that I could see for declaring private parameters, using the options object, and deleting keys after creating variables for them. That said, it does seem a little odd to have an _afterModelPromise private property, and you might have better suggestions for how things should be named.

@kxcrl
Copy link
Contributor Author

kxcrl commented Aug 18, 2015

Sorry about opening a PR with test failures. Before I go in and fix them up, I wanted to make sure that this was actually an option you were interested in and an approach that makes sense. If it is, I'm happy to go in and update all of the testing. If the approach doesn't make sense, I'm also happy to try something else.

@hhff
Copy link
Collaborator

hhff commented Aug 19, 2015

innnnnneresting @kxcrl ! thanks for this. Let me mull it over a couple days while we work on supporting ED 1.13x

@kxcrl
Copy link
Contributor Author

kxcrl commented Aug 19, 2015

@hhff Cool. = )

Sounds good. Again, just let me know if you'd like to see any of it changed or rewritten.

@mike-north
Copy link
Collaborator

👍 to this idea. Thanks for the PR!

@hhff
Copy link
Collaborator

hhff commented Sep 7, 2015

I think I'd like the ergonomics to feel more like the regular afterModel() method on the route. Something like

model() {
  return this.infinityModel('product',  { perPage: 12, startingPage: 1 });
},
afterInfinityModel(infinityModelPromise) {
  return infinityModelPromise.then(collection => {
    let preloadPromises = [];

    collection.forEach(item => {
      preloadPromises.push(item.get('somethingAsync'));
    });

    return Ember.RSVP.all(preloadPromises);
  });
}

default would look like:

afterInfinityModel(infinityModelPromise) {
  return infinityModelPromise;
}

I'm a little worried that this may obscure the semantics of how the model hook works though - ie: where should a "catch" live?

@kxcrl / @mike-north / @kellyselden / @mkorfmann thoughts?

@kxcrl
Copy link
Contributor Author

kxcrl commented Sep 9, 2015

@hhff I think that sounds great. I'm not sure I fully follow your concern, though - can you expound on it a bit more? Is it just about the extra overhead in making sure that any promise passed into the chain has a way to fail gracefully?

@hhff
Copy link
Collaborator

hhff commented Sep 10, 2015

@kxcrl I just mean that exposes a couple of places where developers can handle failed promises, instead of just one obvious place. I don't think it's a big concern tho - also, now you can catch failures in the infinityLoad action promise style - which is a new feature.

Do you want to have a pass at implementing it in this style brother?

@kxcrl
Copy link
Contributor Author

kxcrl commented Sep 10, 2015

@hhff Ah, gotcha. Yep, will do!

@kxcrl
Copy link
Contributor Author

kxcrl commented Sep 18, 2015

@hhff Alright, I finally had some time to sit down and implement this. Maybe I did something wrong, but your version was very easy to get working. Let me know what you think and I'll get it wrapped in some tests.

@@ -174,7 +174,8 @@ export default Ember.Mixin.create({
}

var params = Ember.merge(requestPayloadBase, options);
let promise = this.store[this._storeFindMethod](modelName, params);

let promise = this.afterInfinityModel(this.store[this._storeFindMethod](modelName, params));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think afterInfinityModel should be called on the resolved outcome of the promise - that way you know the object passed in is data, rather than a thennable.

This is closer to how the regular afterModel works

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, there was another implementation of this that I tried out and didn't commit:

let promise = this.store[this._storeFindMethod](modelName, params).then(afterInfinityModel);

Something like that?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yuuuuuuuuuuuuup

@hhff
Copy link
Collaborator

hhff commented Sep 19, 2015

👍 awesome work - added a comment in!

@kxcrl
Copy link
Contributor Author

kxcrl commented Sep 22, 2015

Aaaaand maybe that'll do it? I updated the afterInfinityModel method and went ahead and added a test for it.

@kxcrl
Copy link
Contributor Author

kxcrl commented Oct 1, 2015

@hhff Just checking in real quick. Anything else you'd like to see on this one?

return this.infinityModel('item');
},
afterInfinityModel(items) {
return items.setEach('author', 'F. Scott Fitzgerald');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we also do a test that uses a second promise as the return?

something like:

afterInfinityModel(items) {
  return new Promise((resolve) => {
    resolve(items.setEach('author', 'F. Scott Fitzgerald'));
  });
}

@hhff
Copy link
Collaborator

hhff commented Oct 2, 2015

thanks so much @kxcrl !

Could u add a test for a returning promise (comment above)

  • A Readme Note about this functionality
  • Merge master & squash

and then we're good!! You're the man thankyou!

@kxcrl kxcrl changed the title Add afterModelPromise option Add afterInfinityModel Oct 6, 2015
@kxcrl
Copy link
Contributor Author

kxcrl commented Oct 6, 2015

@hhff Awesome! Thanks for all the feedback and walking me through everything you were looking for. Let me know if there's anything else that needs changing.

@hhff
Copy link
Collaborator

hhff commented Oct 7, 2015

Amazing...! I'm very excited for this. Any idea why beta is failing on travis?

@kxcrl
Copy link
Contributor Author

kxcrl commented Oct 7, 2015

@hhff Great question. I read through the 2.2.0-beta.1 changelog and couldn't find anything that would seem to cause this. I can reproduce the failure locally with ember try ember-beta test, but unfortunately it's still there when I do this on the current master branch without my changes, too.

If you checkout df5e340b64334db9d2d95caae37d3d7e3b3ad279, the error goes away. As soon as you add in c7ccf4be414af3d0b632e219e98be84a115bcd0d, the error appears, so it looks like it got introduced with something that was changed in #72.

I tried a few things with the failing test itself, but I ran out of time before I could get anywhere.

@hhff
Copy link
Collaborator

hhff commented Oct 8, 2015

Thanks @kxcrl - do you have some time to play around with those changes made in #72 and figure out what is causing it? Nothing in that PR seems fishy...

@kxcrl
Copy link
Contributor Author

kxcrl commented Oct 8, 2015

@hhff Hmmm, possibly, but unfortunately not until later next week at the earliest. I'm also wondering if it might just be a bug in the latest ember beta. Is this something you want fixed before you merge any pulls?

@hhff
Copy link
Collaborator

hhff commented Oct 8, 2015

Yeah Ideally! No rush / no stress :)

@hhff hhff mentioned this pull request Oct 13, 2015
@return {Ember.RSVP.Promise}
*/
afterInfinityModel(infinityModelPromiseResult) {
return infinityModelPromiseResult;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this gets used as a chained promise, it requires the end-user code to make sure to call _super and also return the correct result needed downstream by updateInfinityModel. It would be better if it was an optional hook like so:

_afterInfinityModel(infinityModelPromiseResult) {
  if (typeof this.afterInfinityModel === 'function') {
    this.afterInfinityModel(infinityModelPromiseResult);
  }
  return infinityModelPromiseResult;
},

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too sure if a call to _super is needed. You're right about it expecting afterInfinityModel to return the objects, which I don't like too much either.

What's your take on this? @kxcrl

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provided the user returns something that can get('content') we should be fine yeah?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be ideal IMHO if the user didn't have to remember to do anything at all. They have the option to alter infinityModelPromiseResult in-place as is.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If returning something from the hook is a desired feature, that can be added, but it should be seen as a feature-add not a requirement IMO.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

regardless of the ember-infinity implementation (always flowing through the hook vs only flowing through it if defined) the developer must always be sure the return implements get('content') though... right?

The difference is if we always flow through the hook, the developer can call _super (conditionally, perhaps) if they feel like it.

afterInfinityModel(newObjects) {
  if (mercuryInRetrograde) {
    return doWork(newObjects)
  } else {
    this._super.apply(...arguments);
  }
}

I'd imagine always flowing through a function is also sliiiiiiiiightly quicker for the machine

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahhh i actually understand what you're saying now @davidgoli sorry

you're saying "do the afterInfinityModel work" in tandem to the actual return, right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like, it could return syncronously but also trigger an async update, so it wouldn't block the population of the infinityModel yeah?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aah not exactly. There's already an infinityModelUpdated hook that executes async, and it doesn't work for #90 because the user route needs to update itself synchronously, before another request can be made.

If we go with this PR, I'd propose treating afterInfinityModel EXACTLY the same as Ember.Route#afterModel (http://emberjs.com/api/classes/Ember.Route.html#method_afterModel), if the value returned from this hook is a promise, the transition will pause until the transition resolves. Otherwise, non-promise return values are not utilized in any way.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree @davidgoli 👍 we should wait if it's a promise!

@kxcrl
Copy link
Contributor Author

kxcrl commented Oct 30, 2015

@hhff @davidgoli @ManuelArno Sorry for the long delay. I went through and rebased this and added in the changes that you all were requesting to #_afterInfinityModel. I'm not sure if I implemented them the way you were looking for. Give it a look and see what you think. If there's something I'm not understanding about it, I'd be happy to hand this off.

})
.then(function(posts) {
return posts
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this then does nothing and can be removed?

return _this.afterInfinityModel(infinityModelPromiseResult);
};
} else {
return infinityModelPromiseResult;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be wrapped in a function? in either case, the return value of this method should be a function that accepts infinityModelPromiseResult. In fact you might just do:

_afterInfinityModel(_this) {
  return function (infinityModelPromiseResult) {
    if (typeof _this.afterInfinityModel === 'function') {
      return _this.afterInfinityModel(infinityModelPromiseResult);
    } else {
      return infinityModelPromiseResult;
    }
  };
}

I'm not seeing a test failure for this though, but I'm p sure infinityModelPromiseResult isn't defined when the method is called on line 270

@kxcrl
Copy link
Contributor Author

kxcrl commented Oct 30, 2015

Right on both counts, @davidgoli

post.get('author')
})
}
```
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd need a return in the afterInfinityModel function, right? Otherwise infinityModel would end up as null. (here and above)

Also, I don't think this code would actually work. We'd need an RSVP#all I think!

afterInfinityModel: function(posts) {
  return Ember.RSVP.all(posts.map(function(post) {
    post.get('author');
  });
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there also aren't enough closing parens here:

afterInfinityModel: function(posts) {
  return Ember.RSVP.all(posts.map(function(post) {
    return post.get('author');
  }));
}

@hhff
Copy link
Collaborator

hhff commented Nov 12, 2015

@davidgoli / @ManuelArno - are u guys happy with this?

After the changes to the Readme - I'm happy to release this badboi

@ghost
Copy link

ghost commented Nov 12, 2015

👍

2015-11-12 4:03 GMT+01:00 Hugh Francis [email protected]:

@davidgoli https://github.com/davidgoli / @ManuelArno
https://github.com/ManuelArno - are u guys happy with this?

After the changes to the Readme - I'm happy to release this badboi


Reply to this email directly or view it on GitHub
#77 (comment).

Manuel Arno Korfmann
Luetzowstrasse 8
24105 Kiel

manu @ korfmann . info
+49 40 210913788

www.manuel-korfmann.de

@@ -250,7 +267,7 @@ export default Ember.Mixin.create({
const nextPage = this.incrementProperty('currentPage');
const params = this._buildParams(nextPage);

return this.store[this._storeFindMethod](modelName, params);
return this.store[this._storeFindMethod](modelName, params).then(this._afterInfinityModel(this));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please wrap before .then to reduce line length

@davidgoli
Copy link
Collaborator

A few nitpicks but otherwise looking solid!

@ghost
Copy link

ghost commented Nov 12, 2015

great nitpicks @davidgoli ! one can never nitpick enough with software ore one has to nitpick later (and longer) 👍

@hhff
Copy link
Collaborator

hhff commented Nov 13, 2015

Thanks @davidgoli - I just made you a collab to this repo. When you're happy with this PR - please merge it and I'll cut a release!

@davidgoli
Copy link
Collaborator

Thanks @hhff ! @kxcrl Let me know if you're able to address the comments, and we'll merge this.

@kxcrl
Copy link
Contributor Author

kxcrl commented Nov 17, 2015

@davidgoli K. Things got pretty busy with work again, but I'm hoping to have time to fix this up real quick tomorrow.

@davidgoli
Copy link
Collaborator

@kxcrl any updates? would be great to get this out!

@kxcrl
Copy link
Contributor Author

kxcrl commented Nov 20, 2015

@davidgoli I implemented a couple of your changes. I couldn't figure out how to refactor the tests to use the new method with the time I had and Ember.typeof caused problems, so those parts aren't done. You're welcome to take this over if you want and implement those other changes if you'd like to see it finished soon and know right away what it takes.

@davidgoli
Copy link
Collaborator

@kxcrl I don't have push access to your repository, so I'm just going to check it out and create a new branch in the main repo to finish the work. Thanks so much for your help!

@davidgoli davidgoli mentioned this pull request Nov 20, 2015
@hhff
Copy link
Collaborator

hhff commented Nov 21, 2015

Closing this in favor of #105

amazing work @kxcrl & @davidgoli !

@hhff hhff closed this Nov 21, 2015
@kxcrl
Copy link
Contributor Author

kxcrl commented Nov 21, 2015

@davidgoli Awesome. 👍
@hhff Sounds good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants