File Coverage

blib/lib/Syntax/Keyword/Dynamically.pm
Criterion Covered Total %
statement 25 26 96.1
branch 3 4 75.0
condition n/a
subroutine 10 10 100.0
pod 0 3 0.0
total 38 43 88.3


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2018-2023 -- leonerd@leonerd.org.uk
5              
6             package Syntax::Keyword::Dynamically 0.14;
7              
8 9     9   2095037 use v5.14;
  9         48  
9 9     9   77 use warnings;
  9         20  
  9         473  
10              
11 9     9   54 use Carp;
  9         15  
  9         4656  
12              
13             require XSLoader;
14             XSLoader::load( __PACKAGE__, our $VERSION );
15              
16             =head1 NAME
17              
18             C - dynamically change the value of a variable
19              
20             =head1 SYNOPSIS
21              
22             =for highlighter language=perl
23              
24             use Syntax::Keyword::Dynamically;
25              
26             my $logger = ...;
27              
28             sub operate
29             {
30             dynamically $logger->level = LOG_DEBUG;
31              
32             do_things();
33             }
34              
35             =head1 DESCRIPTION
36              
37             This module provides a syntax plugin that implements a single keyword,
38             C, which alters the behaviour of a scalar assignment operation.
39             Syntactically and semantically it is similar to the built-in perl keyword
40             C, but is implemented somewhat differently to give two key advantages
41             over regular C:
42              
43             =over 2
44              
45             =item *
46              
47             You can C assign to lvalue functions and accessors.
48              
49             =item *
50              
51             You can C assign to regular lexical variables.
52              
53             =back
54              
55             Semantically, the behaviour can be considered equivalent to
56              
57             {
58             my $old = $VAR;
59             $VAR = "new value";
60              
61             ...
62              
63             $VAR = $old;
64             }
65              
66             Except that the old value will also be restored in the case of exceptions,
67             C, C or similar ways to leave the controlling block
68             scope.
69              
70             =cut
71              
72             =head1 KEYWORDS
73              
74             =head2 dynamically
75              
76             {
77             dynamically LVALUE = EXPR;
78             ...
79             }
80              
81             The C keyword modifies the behaviour of the following expression.
82             which must be a scalar assignment. Before the new value is assigned to the
83             lvalue, its current value is captured and stored internally within the Perl
84             interpreter. When execution leaves the controlling block for whatever reason,
85             as part of block scope cleanup the saved value is restored.
86              
87             The LVALUE may be any kind of expression that allows normal scalar assignment;
88             lexical or package scalar variables, elements of arrays or hashes, or the
89             result of calling an C<:lvalue> function or method.
90              
91             If the LVALUE has any GET magic associated with it (including a C
92             method of a tied scalar) then this will be executed exactly once when the
93             C expression is evaluated.
94              
95             If the LVALUE has any SET magic associated with it (including a C
96             method of a tied scalar) then this will be executed exactly once when the
97             C expression is evaluated, and again a second time when the
98             controlling scope is unwound.
99              
100             When the LVALUE being assigned to is a hash element, e.g. one of the following
101             forms
102              
103             dynamically $hash{key} = EXPR;
104             dynamically $href->{key} = EXPR;
105              
106             the assignment additionally ensures to remove the key if it is newly-added,
107             and restores by adding the key back again if it had been deleted in the
108             meantime.
109              
110             =cut
111              
112             sub import
113             {
114 8     8   11699 my $pkg = shift;
115 8         23 my $caller = caller;
116              
117 8         31 $pkg->import_into( $caller, @_ );
118             }
119              
120             sub unimport
121             {
122 1     1   12 my $pkg = shift;
123 1         3 my $caller = caller;
124              
125 1         5 $pkg->unimport_into( $caller, @_ );
126             }
127              
128 8     8 0 49 sub import_into { shift->apply( sub { $^H{ $_[0] }++ }, @_ ) }
  8     8   43  
129 1     1 0 6 sub unimport_into { shift->apply( sub { delete $^H{ $_[0] } }, @_ ) }
  1     1   6  
130              
131             sub apply
132             {
133 9     9 0 22 my $pkg = shift;
134 9         29 my ( $cb, $caller, @syms ) = @_;
135              
136 9         19 my %syms = map { $_ => 1 } @syms;
  1         5  
137 9         42 $cb->( "Syntax::Keyword::Dynamically/dynamically" );
138              
139 9 100       49 _enable_async_mode() if delete $syms{'-async'};
140              
141 9 50       11100 croak "Unrecognised import symbols @{[ keys %syms ]}" if keys %syms;
  0            
142             }
143              
144             =head1 WITH Future::AsyncAwait
145              
146             As of L version 0.32, cross-module integration tests
147             assert that the C correctly works across an C boundary.
148              
149             use Future::AsyncAwait;
150             use Syntax::Keyword::Dynamically;
151              
152             our $var;
153              
154             async sub trial
155             {
156             dynamically $var = "value";
157              
158             await func();
159              
160             say "Var is still $var";
161             }
162              
163             When context-switching between scopes in which a variable is C
164             modified, the value of the variable will be swapped in and out, possibly
165             multiple times if necessary, to ensure the visible value remains as expected.
166              
167             =head1 AUTHOR
168              
169             Paul Evans
170              
171             =cut
172              
173             0x55AA;