File Coverage

blib/lib/Mojo/Promisify.pm
Criterion Covered Total %
statement 37 37 100.0
branch 7 8 87.5
condition n/a
subroutine 11 11 100.0
pod 3 3 100.0
total 58 59 98.3


line stmt bran cond sub pod time code
1             package Mojo::Promisify;
2 2     2   441637 use Mojo::Base -strict;
  2         22  
  2         14  
3              
4 2     2   282 use Exporter 'import';
  2         5  
  2         54  
5 2     2   893 use Mojo::Promise;
  2         286641  
  2         14  
6 2     2   87 use Mojo::Util 'monkey_patch';
  2         4  
  2         1041  
7              
8             our @EXPORT_OK = qw(promisify promisify_call promisify_patch);
9             our $VERSION = '0.01';
10              
11             sub promisify {
12 4     4 1 1581 my ($obj, $method) = @_;
13              
14             return sub {
15 4     4   11 my @args = @_;
16 4         18 my $p = Mojo::Promise->new;
17              
18             eval {
19             $obj->$method(
20             @args,
21             sub {
22 4         843 my ($obj, $err) = (shift, shift);
23 4 100       19 return $err ? $p->reject($err) : $p->resolve(@_);
24             }
25 4         24 );
26 3         173 1;
27 4 100       140 } or do {
28 1         38 $p->reject($@);
29             };
30              
31 4         77 return $p;
32 4         26 };
33             }
34              
35             sub promisify_call {
36 3     3 1 3728 my ($obj, $method, @args) = @_;
37 3         10 return promisify($obj => $method)->(@args);
38             }
39              
40             sub promisify_patch {
41 1     1 1 830 my ($class, @methods) = @_;
42              
43 1         4 for my $method (@methods) {
44             monkey_patch $class => "${method}_p" => sub {
45 2     2   867 my ($obj, @args) = @_;
        2      
46 2         9 my $p = Mojo::Promise->new;
47              
48             eval {
49             $obj->$method(
50             @args,
51             sub {
52 2     2   411 my ($obj, $err) = (shift, shift);
53 2 50       24 return $err ? $p->reject($err) : $p->resolve(@_);
54             }
55 2         13 );
56 1         33 1;
57 2 100       54 } or do {
58 1         38 $p->reject($@);
59             };
60              
61 2         61 return $p;
62 1         10 };
63             }
64             }
65              
66             1;
67              
68             =encoding utf8
69              
70             =head1 NAME
71              
72             Mojo::Promisify - Convert callback code to promise based code
73              
74             =head1 SYNOPSIS
75              
76             use Mojo::Promisify qw(promisify_call promisify_patch);
77             use Some::NonBlockingClass;
78              
79             # Create an object from a callback based class
80             my $nb_obj = Some::NonBlockingClass->new;
81              
82             # Call a callback based method, but return a Mojo::Promise
83             promisify_call($nb_obj => get_stuff_by_id => 42)->then(sub {
84             my @res = shift;
85             warn @res;
86             })->catch(sub {
87             my $err = shift;
88             die $err;
89             });
90              
91             # Add a method that wraps around the callback based method and return a
92             # Mojo::Promise.
93             promisify_patch "Some::NonBlockingClass" => "get_stuff_by_id";
94              
95             # The added method has the "_p" suffix
96             $nb_obj->get_stuff_by_id_p(42)->then(sub {
97             my @res = shift;
98             warn @res;
99             });
100              
101             =head1 DESCRIPTION
102              
103             L is a utility module that can upgrade your legacy callback
104             based API to a L based API.
105              
106             It might not be the most efficient way to run your code, but it will allow
107             you to easily add methods that will return promises.
108              
109             This method only works with methods that passes on C<$err> as the first argument
110             to the callback, like this:
111              
112             sub get_stuff_by_id {
113             my ($self, $id, $cb) = @_;
114              
115             my $err = "Some error";
116             my $res = undef;
117             Mojo::IOLoop->next_tick(sub { $self->$cb($err, $res) });
118              
119             return $self;
120             }
121              
122             It can however pass on as many arguments as it wants after the C<$err> and all
123             will be passed on to the fulfillment callback in the promise. C<$err> on the
124             other hand will cause the promise to be rejected.
125              
126             Note that this module is currently EXPERIMENTAL, but it will most probably not
127             change much.
128              
129             =head1 FUNCTIONS
130              
131             =head2 promisify
132              
133             $code = promisify($obj => $method);
134             $promise = $code->(@args);
135              
136             Will return a curried function that wraps around a given C<$method> in a
137             C<$class> and returns a promise. C<@args> are the same arguments you would
138             normally give to the C<$method>, but without the callback at the end.
139              
140             It can be useful to use this function instead of L, in case
141             you want to call the same C<$method> on the I object over and over again.
142              
143             =head2 promisify_call
144              
145             $promise = promisify_call($obj => $method, @args);
146              
147             This function basically does:
148              
149             my $promise = promisify($obj => $method)->(@args);
150              
151             =head2 promisify_patch
152              
153             promisify_patch $class, @methods;
154              
155             Used to monkey patch a class with new promise based methods. The methods that
156             are patched in, will have the "_p" suffix added.
157              
158             Note that this function I replace existing methods!
159              
160             =head1 AUTHOR
161              
162             Jan Henning Thorsen
163              
164             =head1 COPYRIGHT AND LICENSE
165              
166             Copyright (C) 2019, Jan Henning Thorsen.
167              
168             This program is free software, you can redistribute it and/or modify it under
169             the terms of the Artistic License version 2.0.
170              
171             =head1 SEE ALSO
172              
173             L
174              
175             =cut