source: main/trunk/greenstone2/perllib/cpan/Mojo/Promise.pm@ 32205

Last change on this file since 32205 was 32205, checked in by ak19, 6 years ago

First set of commits to do with implementing the new 'paged_html' output option of PDFPlugin that uses using xpdftools' new pdftohtml. So far tested only on Linux (64 bit), but things work there so I'm optimistically committing the changes since they work. 2. Committing the pre-built Linux binaries of XPDFtools for both 32 and 64 bit built by the XPDF group. 2. To use the correct bitness variant of xpdftools, setup.bash now exports the BITNESS env var, consulted by gsConvert.pl. 3. All the perl code changes to do with using xpdf tools' pdftohtml to generate paged_html and feed it in the desired form into GS(3): gsConvert.pl, PDFPlugin.pm and its parent ConvertBinaryPFile.pm have been modified to make it all work. xpdftools' pdftohtml generates a folder containing an html file and a screenshot for each page in a PDF (as well as an index.html linking to each page's html). However, we want a single html file that contains each individual 'page' html's content in a div, and need to do some further HTML style, attribute and structure modifications to massage the xpdftool output to what we want for GS. In order to parse and manipulate the HTML 'DOM' to do this, we're using the Mojo::DOM package that Dr Bainbridge found and which he's compiled up. Mojo::DOM is therefore also committed in this revision. Some further changes and some display fixes are required, but need to check with the others about that.

File size: 8.5 KB
Line 
1package Mojo::Promise;
2use Mojo::Base -base;
3
4use Mojo::IOLoop;
5use Scalar::Util qw(blessed weaken);
6
7has ioloop => sub { Mojo::IOLoop->singleton };
8
9sub all {
10 my ($class, @promises) = @_;
11
12 my $all = $class->new;
13 my $results = [];
14 my $remaining = scalar @promises;
15 for my $i (0 .. $#promises) {
16 $promises[$i]->then(
17 sub {
18 $results->[$i] = [@_];
19 $all->resolve(@$results) if --$remaining <= 0;
20 },
21 sub { $all->reject(@_) }
22 );
23 }
24
25 return @promises ? $all : $all->resolve;
26}
27
28sub catch { shift->then(undef, shift) }
29
30sub finally {
31 my ($self, $finally) = @_;
32
33 my $new = $self->_clone;
34 push @{$self->{resolve}}, sub { _finally($new, $finally, 'resolve', @_) };
35 push @{$self->{reject}}, sub { _finally($new, $finally, 'reject', @_) };
36
37 $self->_defer if $self->{result};
38
39 return $new;
40}
41
42sub race {
43 my ($class, @promises) = @_;
44 my $new = $class->new;
45 $_->then(sub { $new->resolve(@_) }, sub { $new->reject(@_) }) for @promises;
46 return $new;
47}
48
49sub reject { shift->_settle('reject', @_) }
50sub resolve { shift->_settle('resolve', @_) }
51
52sub then {
53 my ($self, $resolve, $reject) = @_;
54
55 my $new = $self->_clone;
56 push @{$self->{resolve}}, sub { _then($new, $resolve, 'resolve', @_) };
57 push @{$self->{reject}}, sub { _then($new, $reject, 'reject', @_) };
58
59 $self->_defer if $self->{result};
60
61 return $new;
62}
63
64sub wait {
65 my $self = shift;
66 return if (my $loop = $self->ioloop)->is_running;
67 $self->finally(sub { $loop->stop });
68 $loop->start;
69}
70
71sub _clone {
72 my $self = shift;
73 my $clone = $self->new;
74 weaken $clone->ioloop($self->ioloop)->{ioloop};
75 return $clone;
76}
77
78sub _defer {
79 my $self = shift;
80
81 return unless my $result = $self->{result};
82 my $cbs = $self->{status} eq 'resolve' ? $self->{resolve} : $self->{reject};
83 @{$self}{qw(resolve reject)} = ([], []);
84
85 $self->ioloop->next_tick(sub { $_->(@$result) for @$cbs });
86}
87
88sub _finally {
89 my ($new, $finally, $method, @result) = @_;
90 my ($res) = eval { $finally->(@result) };
91 return $new->$method(@result)
92 unless $res && blessed $res && $res->can('then');
93 $res->then(sub { $new->$method(@result) }, sub { $new->$method(@result) });
94}
95
96sub _settle {
97 my ($self, $status) = (shift, shift);
98
99 $_[0]->then(sub { $self->resolve(@_); () }, sub { $self->reject(@_); () })
100 and return $self
101 if blessed $_[0] && $_[0]->can('then');
102
103 return $self if $self->{result};
104
105 @{$self}{qw(result status)} = ([@_], $status);
106 $self->_defer;
107 return $self;
108}
109
110sub _then {
111 my ($new, $cb, $method, @result) = @_;
112
113 return $new->$method(@result) unless defined $cb;
114
115 my @res;
116 return $new->reject($@) unless eval { @res = $cb->(@result); 1 };
117 return $new->resolve(@res);
118}
119
1201;
121
122=encoding utf8
123
124=head1 NAME
125
126Mojo::Promise - Promises/A+
127
128=head1 SYNOPSIS
129
130 use Mojo::Promise;
131 use Mojo::UserAgent;
132
133 # Wrap continuation-passing style APIs with promises
134 my $ua = Mojo::UserAgent->new;
135 sub get {
136 my $promise = Mojo::Promise->new;
137 $ua->get(@_ => sub {
138 my ($ua, $tx) = @_;
139 my $err = $tx->error;
140 $promise->resolve($tx) if !$err || $err->{code};
141 $promise->reject($err->{message});
142 });
143 return $promise;
144 }
145
146 # Perform non-blocking operations sequentially
147 get('https://mojolicious.org')->then(sub {
148 my $mojo = shift;
149 say $mojo->res->code;
150 return get('https://metacpan.org');
151 })->then(sub {
152 my $cpan = shift;
153 say $cpan->res->code;
154 })->catch(sub {
155 my $err = shift;
156 warn "Something went wrong: $err";
157 })->wait;
158
159 # Synchronize non-blocking operations (all)
160 my $mojo = get('https://mojolicious.org');
161 my $cpan = get('https://metacpan.org');
162 Mojo::Promise->all($mojo, $cpan)->then(sub {
163 my ($mojo, $cpan) = @_;
164 say $mojo->[0]->res->code;
165 say $cpan->[0]->res->code;
166 })->catch(sub {
167 my $err = shift;
168 warn "Something went wrong: $err";
169 })->wait;
170
171 # Synchronize non-blocking operations (race)
172 my $mojo = get('https://mojolicious.org');
173 my $cpan = get('https://metacpan.org');
174 Mojo::Promise->race($mojo, $cpan)->then(sub {
175 my $tx = shift;
176 say $tx->req->url, ' won!';
177 })->catch(sub {
178 my $err = shift;
179 warn "Something went wrong: $err";
180 })->wait;
181
182=head1 DESCRIPTION
183
184L<Mojo::Promise> is a Perl-ish implementation of
185L<Promises/A+|https://promisesaplus.com>.
186
187=head1 STATES
188
189A promise is an object representing the eventual completion or failure of a
190non-blocking operation. It allows non-blocking functions to return values, like
191blocking functions. But instead of immediately returning the final value, the
192non-blocking function returns a promise to supply the value at some point in the
193future.
194
195A promise can be in one of three states:
196
197=over 2
198
199=item pending
200
201Initial state, neither fulfilled nor rejected.
202
203=item fulfilled
204
205Meaning that the operation completed successfully.
206
207=item rejected
208
209Meaning that the operation failed.
210
211=back
212
213A pending promise can either be fulfilled with a value or rejected with a
214reason. When either happens, the associated handlers queued up by a promise's
215L</"then"> method are called.
216
217=head1 ATTRIBUTES
218
219L<Mojo::Promise> implements the following attributes.
220
221=head2 ioloop
222
223 my $loop = $promise->ioloop;
224 $promise = $promise->ioloop(Mojo::IOLoop->new);
225
226Event loop object to control, defaults to the global L<Mojo::IOLoop> singleton.
227
228=head1 METHODS
229
230L<Mojo::Promise> inherits all methods from L<Mojo::Base> and implements
231the following new ones.
232
233=head2 all
234
235 my $new = Mojo::Promise->all(@promises);
236
237Returns a new L<Mojo::Promise> object that either fulfills when all of the
238passed L<Mojo::Promise> objects have fulfilled or rejects as soon as one of them
239rejects. If the returned promise fulfills, it is fulfilled with the values from
240the fulfilled promises in the same order as the passed promises. This method can
241be useful for aggregating results of multiple promises.
242
243=head2 catch
244
245 my $new = $promise->catch(sub {...});
246
247Appends a rejection handler callback to the promise, and returns a new
248L<Mojo::Promise> object resolving to the return value of the callback if it is
249called, or to its original fulfillment value if the promise is instead
250fulfilled.
251
252 # Longer version
253 my $new = $promise->then(undef, sub {...});
254
255 # Pass along the rejection reason
256 $promise->catch(sub {
257 my @reason = @_;
258 warn "Something went wrong: $reason[0]";
259 return @reason;
260 });
261
262 # Change the rejection reason
263 $promise->catch(sub {
264 my @reason = @_;
265 return "This is bad: $reason[0]";
266 });
267
268=head2 finally
269
270 my $new = $promise->finally(sub {...});
271
272Appends a fulfillment and rejection handler to the promise, and returns a new
273L<Mojo::Promise> object resolving to the original fulfillment value or rejection
274reason.
275
276 # Do something on fulfillment and rejection
277 $promise->finally(sub {
278 my @value_or_reason = @_;
279 say "We are done!";
280 });
281
282=head2 race
283
284 my $new = Mojo::Promise->race(@promises);
285
286Returns a new L<Mojo::Promise> object that fulfills or rejects as soon as one of
287the passed L<Mojo::Promise> objects fulfills or rejects, with the value or
288reason from that promise.
289
290=head2 reject
291
292 $promise = $promise->reject(@reason);
293
294Reject the promise with one or more rejection reasons.
295
296 # Generate rejected promise
297 my $promise = Mojo::Promise->new->reject('Something went wrong: Oops');
298
299=head2 resolve
300
301 $promise = $promise->resolve(@value);
302
303Resolve the promise with one or more fulfillment values.
304
305 # Generate fulfilled promise
306 my $promise = Mojo::Promise->new->resolve('The result is: 24');
307
308=head2 then
309
310 my $new = $promise->then(sub {...});
311 my $new = $promise->then(sub {...}, sub {...});
312 my $new = $promise->then(undef, sub {...});
313
314Appends fulfillment and rejection handlers to the promise, and returns a new
315L<Mojo::Promise> object resolving to the return value of the called handler.
316
317 # Pass along the fulfillment value or rejection reason
318 $promise->then(
319 sub {
320 my @value = @_;
321 say "The result is $value[0]";
322 return @value;
323 },
324 sub {
325 my @reason = @_;
326 warn "Something went wrong: $reason[0]";
327 return @reason;
328 }
329 );
330
331 # Change the fulfillment value or rejection reason
332 $promise->then(
333 sub {
334 my @value = @_;
335 return "This is good: $value[0]";
336 },
337 sub {
338 my @reason = @_;
339 return "This is bad: $reason[0]";
340 }
341 );
342
343=head2 wait
344
345 $promise->wait;
346
347Start L</"ioloop"> and stop it again once the promise has been fulfilled or
348rejected, does nothing when L</"ioloop"> is already running.
349
350=head1 SEE ALSO
351
352L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
353
354=cut
Note: See TracBrowser for help on using the repository browser.