]> git.vpit.fr Git - perl/modules/Scope-Context.git/blob - lib/Scope/Context.pm
Bump copyright year
[perl/modules/Scope-Context.git] / lib / Scope / Context.pm
1 package Scope::Context;
2
3 use 5.006;
4
5 use strict;
6 use warnings;
7
8 use Carp         ();
9 use Scalar::Util ();
10
11 use Scope::Upper 0.18 ();
12
13 =head1 NAME
14
15 Scope::Context - Object-oriented interface for inspecting or acting upon upper scope frames.
16
17 =head1 VERSION
18
19 Version 0.01
20
21 =cut
22
23 our $VERSION = '0.01';
24
25 =head1 SYNOPSIS
26
27     use Scope::Context;
28
29     for (1 .. 5) {
30      sub {
31       eval {
32        # Create Scope::Context objects for different upper frames.
33        my ($block, $sub, $eval, $loop);
34        {
35         $block = Scope::Context->new;
36         $sub   = $block->sub;    # = $block->up
37         $eval  = $block->eval;   # = $block->up(2)
38         $loop  = $eval->up;      # = $block->up(3)
39        }
40
41        eval {
42         # This will throw an exception, since $block has expired.
43         $block->localize('$x' => 1);
44        };
45
46        # This prints "hello" when the eval block above ends.
47        $eval->reap(sub { print "hello\n" });
48
49        # Ignore $SIG{__DIE__} just for the loop body.
50        $loop->localize_delete('%SIG', '__DIE__');
51
52        # Execute the callback as if it ran in place of the sub.
53        my @values = $sub->uplevel(sub {
54         return @_, 2;
55        }, 1);
56
57        # Immediately return (1, 2, 3) from the sub, bypassing the eval.
58        $sub->unwind(@values, 3);
59
60        # Not reached.
61       }
62
63       # Not reached.
64      }->();
65
66      # unwind() returns here. "hello\n" was printed, and now
67      # $SIG{__DIE__} is undefined.
68     }
69
70 =head1 DESCRIPTION
71
72 This class provides an object-oriented interface to L<Scope::Upper>'s functionalities.
73 A L<Scope::Context> object represents a currently active dynamic scope (or context), and encapsulates the corresponding L<Scope::Upper>-compatible context identifier.
74 All of L<Scope::Upper>'s functions are then made available as methods.
75 This gives you a prettier and safer interface when you are not reaching for extreme performance, but rest assured that the overhead of this module is minimal anyway.
76
77 The L<Scope::Context> methods actually do more than their subroutine counterparts from L<Scope::Upper> : before each call, the target context will be checked to ensure it is still active (which means that it is still present in the current call stack), and an exception will be thrown if you attempt to act on a context that has already expired.
78 This means that :
79
80     my $cxt;
81     {
82      $cxt = Scope::Context->new;
83     }
84     $cxt->reap(sub { print "hello\n });
85
86 will croak when L</reap> is called.
87
88 =head1 METHODS
89
90 =head2 C<new>
91
92     my $cxt = Scope::Context->new;
93     my $cxt = Scope::Context->new($scope_upper_cxt);
94
95 Creates a new immutable L<Scope::Context> object from the L<Scope::Upper>-comptabile context C<$context>.
96 If omitted, C<$context> defaults to the current context.
97
98 =cut
99
100 sub new {
101  my ($self, $cxt) = @_;
102
103  my $class = Scalar::Util::blessed($self);
104  unless (defined $class) {
105   $class = defined $self ? $self : __PACKAGE__;
106  }
107
108  $cxt = Scope::Upper::UP() unless defined $cxt;
109
110  bless {
111   cxt => $cxt,
112   uid => Scope::Upper::uid($cxt),
113  }, $class;
114 }
115
116 =head2 C<here>
117
118 A synonym for L</new>.
119
120 =cut
121
122 BEGIN {
123  *here = \&new;
124 }
125
126 sub _croak {
127  shift;
128  require Carp;
129  Carp::croak(@_);
130 }
131
132 =head2 C<cxt>
133
134     my $scope_upper_cxt = $cxt->cxt;
135
136 Read-only accessor to the L<Scope::Upper> context corresponding to the topic L<Scope::Context> object.
137
138 =head2 C<uid>
139
140     my $uid = $cxt->uid;
141
142 Read-only accessor to the L<Scope::Upper> UID of the topic L<Scope::Context> object.
143
144 =cut
145
146 BEGIN {
147  local $@;
148  eval "sub $_ { \$_[0]->{$_} }; 1" or die $@ for qw<cxt uid>;
149 }
150
151 =pod
152
153 This class also overloads the C<==> operator, which will return true if and only if its two operands are L<Scope::Context> objects that have the same UID.
154
155 =cut
156
157 use overload (
158  '==' => sub {
159   my ($left, $right) = @_;
160
161   unless (Scalar::Util::blessed($right) and $right->isa(__PACKAGE__)) {
162    $left->_croak('Cannot compare a Scope::Context object with something else');
163   }
164
165   $left->uid eq $right->uid;
166  },
167  fallback => 1,
168 );
169
170 =head2 C<is_valid>
171
172     my $is_valid = $cxt->is_valid;
173
174 Returns true if and only if the topic context is still valid (that is, it designates a scope that is higher than the topic context in the call stack).
175
176 =cut
177
178 sub is_valid { Scope::Upper::validate_uid($_[0]->uid) }
179
180 =head2 C<assert_valid>
181
182     $cxt->assert_valid;
183
184 Throws an exception if the topic context has expired and is no longer valid.
185 Returns true otherwise.
186
187 =cut
188
189 sub assert_valid {
190  my $self = shift;
191
192  $self->_croak('Context has expired') unless $self->is_valid;
193
194  1;
195 }
196
197 =head2 C<want>
198
199     my $want = $cxt->want;
200
201 Returns the Perl context (in the sense of C<wantarray> : C<undef> for void context, C<''> for scalar context, and true for list context) in which is executed the scope corresponding to the topic L<Scope::Context> object.
202
203 =cut
204
205 sub want {
206  my $self = shift;
207
208  $self->assert_valid;
209
210  Scope::Upper::want_at($self->cxt);
211 }
212
213 =head2 C<up>
214
215     my $up_cxt = $cxt->up;
216     my $up_cxt = $cxt->up($frames);
217     my $up_cxt = Scope::Context->up;
218
219 Returns a new L<Scope::Context> object pointing to the C<$frames>-th upper scope above the topic context.
220
221 This method can also be invoked as a class method, in which case it is equivalent to calling L</up> on a L<Scope::Context> object for the current context.
222
223 If omitted, C<$frames> defaults to C<1>.
224
225     sub {
226      {
227       {
228        my $up = Scope::Context->new->up(2); # = Scope::Context->up(2)
229        # $up points two contextes above this one, which is the sub.
230       }
231      }
232     }
233
234 =cut
235
236 sub up {
237  my ($self, $frames) = @_;
238
239  my $cxt;
240  if (Scalar::Util::blessed($self)) {
241   $self->assert_valid;
242   $cxt = $self->cxt;
243  } else {
244   $cxt = Scope::Upper::UP(Scope::Upper::SUB());
245  }
246
247  $frames = 1 unless defined $frames;
248
249  $cxt = Scope::Upper::UP($cxt) for 1 .. $frames;
250
251  $self->new($cxt);
252 }
253
254 =head2 C<sub>
255
256     my $sub_cxt = $cxt->sub;
257     my $sub_cxt = $cxt->sub($frames);
258     my $sub_cxt = Scope::Context->sub;
259
260 Returns a new L<Scope::Context> object pointing to the C<$frames>-th subroutine scope above the topic context.
261
262 This method can also be invoked as a class method, in which case it is equivalent to calling L</sub> on a L<Scope::Context> object for the current context.
263
264 If omitted, C<$frames> defaults to C<0>, which results in the closest sub enclosing the topic context.
265
266     outer();
267
268     sub outer {
269      inner();
270     }
271
272     sub inner {
273      my $sub = Scope::Context->new->sub(1); # = Scope::Context->sub(1)
274      # $sub points to the context for the outer() sub.
275     }
276
277 =cut
278
279 sub sub {
280  my ($self, $frames) = @_;
281
282  my $cxt;
283  if (Scalar::Util::blessed($self)) {
284   $self->assert_valid;
285   $cxt = $self->cxt;
286  } else {
287   $cxt = Scope::Upper::UP(Scope::Upper::SUB());
288  }
289
290  $frames = 0 unless defined $frames;
291
292  $cxt = Scope::Upper::SUB($cxt);
293  $cxt = Scope::Upper::SUB(Scope::Upper::UP($cxt)) for 1 .. $frames;
294
295  $self->new($cxt);
296 }
297
298 =head2 C<eval>
299
300     my $eval_cxt = $cxt->eval;
301     my $eval_cxt = $cxt->eval($frames);
302     my $eval_cxt = Scope::Context->eval;
303
304 Returns a new L<Scope::Context> object pointing to the C<$frames>-th C<eval> scope above the topic context.
305
306 This method can also be invoked as a class method, in which case it is equivalent to calling L</eval> on a L<Scope::Context> object for the current context.
307
308 If omitted, C<$frames> defaults to C<0>, which results in the closest eval enclosing the topic context.
309
310     eval {
311      sub {
312       my $eval = Scope::Context->new->eval; # = Scope::Context->eval
313       # $eval points to the eval context.
314      }->()
315     }
316
317 =cut
318
319 sub eval {
320  my ($self, $frames) = @_;
321
322  my $cxt;
323  if (Scalar::Util::blessed($self)) {
324   $self->assert_valid;
325   $cxt = $self->cxt;
326  } else {
327   $cxt = Scope::Upper::UP(Scope::Upper::SUB());
328  }
329
330  $frames = 0 unless defined $frames;
331
332  $cxt = Scope::Upper::EVAL($cxt);
333  $cxt = Scope::Upper::EVAL(Scope::Upper::UP($cxt)) for 1 .. $frames;
334
335  $self->new($cxt);
336 }
337
338 =head2 C<reap>
339
340     $cxt->reap($code);
341
342 Execute C<$code> when the topic context ends.
343
344 See L<Scope::Upper/reap> for details.
345
346 =cut
347
348 sub reap {
349  my ($self, $code) = @_;
350
351  $self->assert_valid;
352
353  &Scope::Upper::reap($code, $self->cxt);
354 }
355
356 =head2 C<localize>
357
358     $cxt->localize($what, $value);
359
360 Localize the variable described by C<$what> to the value C<$value> when the control flow returns to the scope pointed by the topic context.
361
362 See L<Scope::Upper/localize> for details.
363
364 =cut
365
366 sub localize {
367  my ($self, $what, $value) = @_;
368
369  $self->assert_valid;
370
371  Scope::Upper::localize($what, $value, $self->cxt);
372 }
373
374 =head2 C<localize_elem>
375
376     $cxt->localize_elem($what, $key, $value);
377
378 Localize the element C<$key> of the variable C<$what> to the value C<$value> when the control flow returns to the scope pointed by the topic context.
379
380 See L<Scope::Upper/localize_elem> for details.
381
382 =cut
383
384 sub localize_elem {
385  my ($self, $what, $key, $value) = @_;
386
387  $self->assert_valid;
388
389  Scope::Upper::localize_elem($what, $key, $value, $self->cxt);
390 }
391
392 =head2 C<localize_delete>
393
394     $cxt->localize_delete($what, $key);
395
396 Delete the element C<$key> from the variable C<$what> when the control flow returns to the scope pointed by the topic context.
397
398 See L<Scope::Upper/localize_delete> for details.
399
400 =cut
401
402 sub localize_delete {
403  my ($self, $what, $key) = @_;
404
405  $self->assert_valid;
406
407  Scope::Upper::localize_delete($what, $key, $self->cxt);
408 }
409
410 =head2 C<unwind>
411
412     $cxt->unwind(@values);
413
414 Immediately returns the scalars listed in C<@values> from the closest subroutine enclosing the topic context.
415
416 See L<Scope::Upper/unwind> for details.
417
418 =cut
419
420 sub unwind {
421  my $self = shift;
422
423  $self->assert_valid;
424
425  Scope::Upper::unwind(@_ => $self->cxt);
426 }
427
428 =head2 C<uplevel>
429
430     my @ret = $cxt->uplevel($code, @args);
431
432 Executes the code reference C<$code> with arguments C<@args> in the same setting as the closest subroutine enclosing the topic context, then returns to the current scope the values returned by C<$code>.
433
434 See L<Scope::Upper/uplevel> for details.
435
436 =cut
437
438 sub uplevel {
439  my $self = shift;
440  my $code = shift;
441
442  $self->assert_valid;
443
444  &Scope::Upper::uplevel($code => @_ => $self->cxt);
445 }
446
447 =head1 DEPENDENCIES
448
449 L<Carp> (core module since perl 5), L<Scalar::Util> (since 5.7.3).
450
451 L<Scope::Upper> 0.18.
452
453 =head1 SEE ALSO
454
455 L<Scope::Upper>.
456
457 L<Continuation::Escape>.
458
459 =head1 AUTHOR
460
461 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
462
463 You can contact me by mail or on C<irc.perl.org> (vincent).
464
465 =head1 BUGS
466
467 Please report any bugs or feature requests to C<bug-scope-context at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Scope-Context>.
468 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
469
470 =head1 SUPPORT
471
472 You can find documentation for this module with the perldoc command.
473
474     perldoc Scope::Context
475
476 =head1 COPYRIGHT & LICENSE
477
478 Copyright 2011,2012 Vincent Pit, all rights reserved.
479
480 This program is free software; you can redistribute it and/or modify it
481 under the same terms as Perl itself.
482
483 =cut
484
485 1; # End of Scope::Context