File Coverage

blib/lib/Readonly/Tiny.pm
Criterion Covered Total %
statement 58 59 98.3
branch 25 30 83.3
condition 14 21 66.6
subroutine 12 13 92.3
pod 3 4 75.0
total 112 127 88.1


line stmt bran cond sub pod time code
1             package Readonly::Tiny;
2              
3             =head1 NAME
4              
5             Readonly::Tiny - Simple, correct readonly values
6              
7             =head1 SYNOPSIS
8              
9             use Readonly::Tiny;
10              
11             my $x = readonly [1, 2, 3];
12             # $x is not readonly, but the array it points to is.
13            
14             my @y = (4, 5, 6);
15             readonly \@y;
16             # @y is readonly, as well as its contents.
17              
18             =head1 DESCRIPTION
19              
20             Readonly::Tiny provides a simple and correct way of making values
21             readonly. Unlike L it does not cause arrays and hashes to be
22             tied, it just uses the core C flag.
23              
24             =head1 FUNCTIONS
25              
26             =cut
27              
28 4     4   174813 use 5.008;
  4         11  
29 4     4   17 use warnings;
  4         4  
  4         94  
30 4     4   24 use strict;
  4         4  
  4         120  
31              
32             our $VERSION = "4";
33              
34 4     4   14 use Exporter "import";
  4         5  
  4         206  
35             our @EXPORT = qw/readonly/;
36             our @EXPORT_OK = qw/readonly readwrite Readonly/;
37              
38 4     4   15 use Carp qw/croak/;
  4         5  
  4         180  
39 4     4   16 use Scalar::Util qw/reftype refaddr blessed/;
  4         4  
  4         187  
40 4     4   2100 use Hash::Util;
  4         8609  
  4         41  
41             #use Data::Dump qw/pp/;
42              
43 4     4   322 use constant RX_MAGIC => (reftype(qr/x/) ne "REGEXP");
  4         6  
  4         2453  
44              
45             if (RX_MAGIC) {
46             require B;
47              
48             *is_regexp = sub {
49             my $o = B::svref_2object($_[0]) or return;
50             blessed($o) eq "B::PVMG" or return;
51              
52             my $m = $o->MAGIC;
53             while ($m) {
54             $m->TYPE eq "r" and return 1;
55             $m = $m->MOREMAGIC;
56             }
57              
58             return;
59             };
60             }
61              
62       0 0   sub debug {
63             #warn sprintf "%s [%x] %s\n", @_;
64             }
65              
66             =head2 readonly
67              
68             my $ro = readonly $ref, \%opts;
69              
70             Make a data structure readonly. C<$ref> must be a reference; the
71             referenced value, and any values referenced recursively, will be made
72             readonly. C<$ref> is returned, but it will not itself be readonly; it is
73             possible to make a variable readonly by passing a reference to it, as in
74             the L.
75              
76             C<%opts> is a hashref of options:
77              
78             =over 4
79              
80             =item peek
81              
82             Normally blessed references will not be looked through. The scalar
83             holding the reference will be made readonly (so a different object
84             cannot be assigned) but the contents of the object itself will be left
85             alone. Supplying C<< peek => 1 >> allows blessed refs to be looked
86             through.
87              
88             =item skip
89              
90             This should be a hashref keyed by refaddr. Any object whose refaddr is
91             in the hash will be skipped.
92              
93             =back
94              
95             Note that making a hash readonly has the same effect as calling
96             L|Hash::Util/lock_hash>; in particular, it
97             causes restricted hashes to be re-restricted to their current set of
98             keys.
99              
100             =head2 readwrite
101              
102             my $rw = readwrite $ref, \%opts;
103              
104             Undo the effects of C. C<%opts> is the same. Note that making
105             a hash readwrite will undo any restrictions put in place using
106             L.
107              
108             B calling this on values you have not made readonly
109             yourself. It will silently ignore attempts to make the core values
110             C, C and C readwrite, but there are
111             many other values the core makes readonly, usually with good reason.
112             Recent versions of perl will not allow you to make readwrite a value the
113             core has set readonly, but you should probably not rely on this.
114              
115             =cut
116              
117             sub _recurse;
118              
119 20     20 1 25744 sub readonly { _recurse 1, @_; $_[0] }
  20         25  
120 10     10 1 793 sub readwrite { _recurse 0, @_; $_[0] }
  10         12  
121              
122             my %immortal =
123             map +(refaddr $_, 1),
124             \undef, \!1, \!0;
125              
126             sub _recurse {
127 64     64   62 my ($ro, $r, $o) = @_;
128              
129 64 50       133 my $x = refaddr $r
130             or croak "readonly needs a reference";
131              
132 64 50       125 exists $o->{skip}{$x} and return $r;
133 64         76 $o->{skip}{$x} = 1;
134              
135 64 100 66     110 !$ro && $immortal{$x} and return $r;
136 61 100 100     136 blessed $r && !$o->{peek} and return $r;
137              
138 58         77 my $t = reftype $r;
139             #debug $t, $x, pp $r;
140              
141             # It's not clear it's meaningful to SvREADONLY these types. A qr//
142             # is a ref to a REGEXP, so a scalar holding one can be made
143             # readonly; the REGEXP itself would normally be skipped anyway
144             # because it's blessed.
145 58 100 66     340 $t eq "CODE" || $t eq "IO" || $t eq "FORMAT" || $t eq "REGEXP"
      66        
      66        
146             and return $r;
147              
148             # Look for r magic pre-5.12
149 56         42 RX_MAGIC and is_regexp($r) and return $r;
150              
151 56 50       72 unless ($o->{shallow}) {
152 56 100       72 if ($t eq "REF") {
153 3         10 _recurse $ro, $$r, $o;
154             }
155 56 100       64 if ($t eq "ARRAY") {
156 11         31 _recurse $ro, \$_, $o for @$r;
157             }
158 56 100       70 if ($t eq "HASH") {
159 7         35 &Internals::SvREADONLY($r, 0);
160 7         32 _recurse $ro, \$_, $o for values %$r;
161 7         23 Hash::Util::lock_keys(%$r);
162             }
163 56 100       112 if ($t eq "GLOB") {
164             *$r{$_} and _recurse $ro, *$r{$_}, $o
165 3   33     12 for qw/SCALAR ARRAY HASH/;
166             }
167             }
168              
169             # bleeding prototypes...
170 56         97 &Internals::SvREADONLY($r, $ro);
171             #debug "READONLY", $r, &Internals::SvREADONLY($r);
172             }
173              
174             =head2 Readonly
175              
176             Readonly my $x, 1;
177             Readonly my @y, 2, 3, 4;
178             Readonly my %z, foo => 5;
179              
180             This is a compatibility shim for L. It is prototyped to take a
181             reference to its first argument, and assigns the rest of the argument
182             list to that argument before making the whole thing readonly.
183              
184             =cut
185              
186             sub Readonly (\[$@%]@) {
187 3     3 1 2260 my $r = shift;
188 3 50       13 my $t = reftype $r
189             or croak "Readonly needs a reference";
190              
191 3 100 66     16 if ($t eq "SCALAR" or $t eq "REF") {
192 1         2 $$r = $_[0];
193             }
194 3 100       7 if ($t eq "ARRAY") {
195 1         3 @$r = @_;
196             }
197 3 100       5 if ($t eq "HASH") {
198 1         3 %$r = @_;
199             }
200 3 50       6 if ($t eq "GLOB") {
201 0         0 *$r = $_[0];
202             }
203              
204 3         7 readonly $r;
205             }
206              
207             1;
208              
209             =head1 EXPORTS
210              
211             C is exported by default. C and C are
212             exported on request.
213              
214             =head1 SEE ALSO
215              
216             L was the first module to supply readonly values. It was
217             written for Perl 5.6, and as a result the interface and implementation
218             are both rather clunky. With L the performance is improved
219             for scalar varuables, but arrays and hashes still use a tied
220             implementation which is very slow.
221              
222             L is a greatly improved reaoonly module which uses perl's
223             internal C flag instead of ties. The differences between
224             this module and L are:
225              
226             =over 4
227              
228             =item *
229              
230             The C function does not insist on performing an assignment, it
231             just returns a readonly value. This is, IMHO, more useful, since it
232             means a readonly value can be returned from a function. In particular,
233             it is often useful to return a readonly value from a builder method.
234              
235             =item *
236              
237             It does not attempt to clone deep structures. If C is
238             applied to a structure with cross-links it will clone the whole thing,
239             on the principle that parts of the graph may be shared with something
240             else which should not be readonly. This module takes the approach that
241             if you asked for something to be made readonly you meant it, and if it
242             points to something it shouldn't that's your mistake.
243              
244             =back
245              
246             =head1 BUGS
247              
248             Please report bugs to >.
249              
250             =head1 AUTHOR
251              
252             Ben Morrow
253              
254             =head1 COPYRIGHT
255              
256             Copyright 2015 Ben Morrow.
257              
258             Released under the 2-clause BSD licence.
259