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