changing the class of promises returned by UserAgent->get_p / post_p / start_p /...

43 views
Skip to first unread message

Roger Crew

unread,
Oct 1, 2019, 9:21:13 AM10/1/19
to Mojolicious
Mojo::UserAgent::Role::PromiseClass now exists on CPAN to play with.

Promises returned by get_p can now be automatically blessed into whatever class you want by just specifying it once on the UserAgent

$ua = Mojo::UserAgent->new(...)
     ->with_roles('+PromiseClass')
     ->promise_roles('+Feature');  # add promise features to UserAgent
$ua->get_p(...)->feature(sub{...}); # and they show up on every get_p call

as opposed to having to type

$ua->get_p(...)->with_roles('+Feature')->feature(sub{...})

everywhere.

Joel Berger

unread,
Oct 2, 2019, 11:10:16 AM10/2/19
to Mojolicious
Reblessing the returned object into the class of your choosing seems like an incredibly fragile way of doing that. I'd recommend that you could just chain the returned promise with one that you create/return. 

Roger Crew

unread,
Oct 2, 2019, 7:17:44 PM10/2/19
to Mojolicious
Note that what I actually want here is for promise_class() to live directly on UserAgent and for start_p to directly do ->promise_class->new rather than Mojo::Promise->new; this module is essentially a way to try that out.

And, yes, this is providing rope for people to choose a stupid class that doesn't implement promises correctly, which is arguably fragile (and part of why I provide ->promise_roles(); it's not strictly necessary but it makes this kind of mistake less likely, though a badly written role can screw things up, too..), but, if so, then large parts of Mojolicious are similarly fragile (e.g., someone could just as easily do ua->transactor(something that doesn't implement Transactor right) and likewise lose).

... and since I can't/shouldn't edit UserAgent directly, the next best thing is to intercept promise creation as early as possible.  And as far as I can tell this implementation is as close as it gets:  start_p() creates the promise, then calls start() but doesn't actually pass the promise itself (it only shows up in the callback which won't get invoked until after start_p returns).  The reblessing is happening before any possibility of method calls on the object.

Also, this way, if someone wants to customize Transactor::promisify, the additional promise behaviours/methods will be available there.  Your solution of returning a fresh promise out of start_p wouldn't allow that.

(yes, I understand start_p could get entirely rewritten, but I'm using an around() hook which is already asking for trouble on that front; I guess I'm not clear on why reblessing is such a huge sin on top of that...)

Roger Crew

unread,
Oct 26, 2019, 11:02:59 PM10/26/19
to Mojolicious
On Tuesday, October 1, 2019 at 6:21:13 AM UTC-7, I wrote:

> Mojo::UserAgent::Role::PromiseClass now exists on CPAN to play with.

... meanwhile, we now have Mojo::Pg::Role::PromiseClass, which does essentially the same thing for the database methods query_p and friends.

i.e., promises returned by ->db->query_p can now be automatically blessed into whatever class you want by just specifying it once on the Mojo::Pg wrapper.

(this is ever-so-slightly trickier in that there are actually 2 roles under the hood. But if you're not customizing the Database class, you don't have to care.
Reply all
Reply to author
Forward
0 new messages