File Coverage

blib/lib/Readonly/Tiny.pm
Criterion Covered Total %
statement 55 56 98.2
branch 25 30 83.3
condition 14 21 66.6
subroutine 11 12 91.6
pod 3 4 75.0
total 108 123 87.8


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   73776 use 5.008;
  4         13  
29 4     4   22 use warnings;
  4         6  
  4         117  
30 4     4   28 use strict;
  4         7  
  4         172  
31              
32             our $VERSION = "3";
33              
34 4     4   21 use Exporter "import";
  4         7  
  4         243  
35             our @EXPORT = qw/readonly/;
36             our @EXPORT_OK = qw/readonly readwrite Readonly/;
37              
38 4     4   19 use Carp qw/croak/;
  4         8  
  4         249  
39 4     4   21 use Scalar::Util qw/reftype refaddr blessed/;
  4         6  
  4         563  
40             #use Data::Dump qw/pp/;
41              
42 4     4   22 use constant RX_MAGIC => (reftype(qr/x/) ne "REGEXP");
  4         5  
  4         3239  
43              
44             if (RX_MAGIC) {
45             require B;
46              
47             *is_regexp = sub {
48             my $o = B::svref_2object($_[0]) or return;
49             blessed($o) eq "B::PVMG" or return;
50              
51             my $m = $o->MAGIC;
52             while ($m) {
53             $m->TYPE eq "r" and return 1;
54             $m = $m->MOREMAGIC;
55             }
56              
57             return;
58             };
59             }
60              
61       0 0   sub debug {
62             #warn sprintf "%s [%x] %s\n", @_;
63             }
64              
65             =head2 readonly
66              
67             my $ro = readonly $ref, \%opts;
68              
69             Make a data structure readonly. C<$ref> must be a reference; the
70             referenced value, and any values referenced recursively, will be made
71             readonly. C<$ref> is returned, but it will not itself be readonly; it is
72             possible to make a variable readonly by passing a reference to it, as in
73             the L.
74              
75             C<%opts> is a hashref of options:
76              
77             =over 4
78              
79             =item peek
80              
81             Normally blessed references will not be looked through. The scalar
82             holding the reference will be made readonly (so a different object
83             cannot be assigned) but the contents of the object itself will be left
84             alone. Supplying C<< peek => 1 >> allows blessed refs to be looked
85             through.
86              
87             =item skip
88              
89             This should be a hashref keyed by refaddr. Any object whose refaddr is
90             in the hash will be skipped.
91              
92             =back
93              
94             Note that making a hash readonly has the same effect as calling
95             L|Hash::Util/lock_hash>; in particular, it
96             causes restricted hashes to be re-restricted to their current set of
97             keys.
98              
99             =head2 readwrite
100              
101             my $rw = readwrite $ref, \%opts;
102              
103             Undo the effects of C. C<%opts> is the same. Note that making
104             a hash readwrite will undo any restrictions put in place using
105             L.
106              
107             B calling this on values you have not made readonly
108             yourself. It will silently ignore attempts to make the core values
109             C, C and C readwrite, but there are
110             many other values the core makes readonly, usually with good reason.
111             Recent versions of perl will not allow you to make readwrite a value the
112             core has set readonly, but you should probably not rely on this.
113              
114             =cut
115              
116             sub _recurse;
117              
118 20     20 1 31172 sub readonly { _recurse 1, @_; $_[0] }
  20         51  
119 10     10 1 1002 sub readwrite { _recurse 0, @_; $_[0] }
  10         25  
120              
121             my %immortal =
122             map +(refaddr $_, 1),
123             \undef, \!1, \!0;
124              
125             sub _recurse {
126 64     64   118 my ($ro, $r, $o) = @_;
127              
128 64 50       216 my $x = refaddr $r
129             or croak "readonly needs a reference";
130              
131 64 50       219 exists $o->{skip}{$x} and return $r;
132 64         162 $o->{skip}{$x} = 1;
133              
134 64 100 66     193 !$ro && $immortal{$x} and return $r;
135 61 100 100     230 blessed $r && !$o->{peek} and return $r;
136              
137 58         188 my $t = reftype $r;
138             #debug $t, $x, pp $r;
139              
140             # It's not clear it's meaningful to SvREADONLY these types. A qr//
141             # is a ref to a REGEXP, so a scalar holding one can be made
142             # readonly; the REGEXP itself would normally be skipped anyway
143             # because it's blessed.
144 58 100 66     543 $t eq "CODE" || $t eq "IO" || $t eq "FORMAT" || $t eq "REGEXP"
      66        
      66        
145             and return $r;
146              
147             # Look for r magic pre-5.12
148 56         61 RX_MAGIC and is_regexp($r) and return $r;
149              
150 56 50       143 unless ($o->{shallow}) {
151 56 100       121 if ($t eq "REF") {
152 3         15 _recurse $ro, $$r, $o;
153             }
154 56 100       123 if ($t eq "ARRAY") {
155 11         64 _recurse $ro, \$_, $o for @$r;
156             }
157 56 100       119 if ($t eq "HASH") {
158 7         20 &Internals::SvREADONLY($r, 0);
159 7         33 _recurse $ro, \$_, $o for values %$r;
160 7         34 Internals::hv_clear_placeholders(%$r);
161             }
162 56 100       130 if ($t eq "GLOB") {
163             *$r{$_} and _recurse $ro, *$r{$_}, $o
164 3   33     23 for qw/SCALAR ARRAY HASH/;
165             }
166             }
167              
168             # bleeding prototypes...
169 56         193 &Internals::SvREADONLY($r, $ro);
170             #debug "READONLY", $r, &Internals::SvREADONLY($r);
171             }
172              
173             =head2 Readonly
174              
175             Readonly my $x, 1;
176             Readonly my @y, 2, 3, 4;
177             Readonly my %z, foo => 5;
178              
179             This is a compatibility shim for L. It is prototyped to take a
180             reference to its first argument, and assigns the rest of the argument
181             list to that argument before making the whole thing readonly.
182              
183             =cut
184              
185             sub Readonly (\[$@%]@) {
186 3     3 1 1928 my $r = shift;
187 3 50       15 my $t = reftype $r
188             or croak "Readonly needs a reference";
189              
190 3 100 66     17 if ($t eq "SCALAR" or $t eq "REF") {
191 1         3 $$r = $_[0];
192             }
193 3 100       7 if ($t eq "ARRAY") {
194 1         3 @$r = @_;
195             }
196 3 100       9 if ($t eq "HASH") {
197 1         3 %$r = @_;
198             }
199 3 50       8 if ($t eq "GLOB") {
200 0         0 *$r = $_[0];
201             }
202              
203 3         6 readonly $r;
204             }
205              
206             1;
207              
208             =head1 EXPORTS
209              
210             C is exported by default. C and C are
211             exported on request.
212              
213             =head1 BUGS
214              
215             Please report bugs to >.
216              
217             =head1 AUTHOR
218              
219             Ben Morrow
220              
221             =head1 COPYRIGHT
222              
223             Copyright 2015 Ben Morrow.
224              
225             Released under the 2-clause BSD licence.
226