File Coverage

blib/lib/Readonly/Tiny.pm
Criterion Covered Total %
statement 51 52 98.0
branch 25 30 83.3
condition 14 21 66.6
subroutine 10 11 90.9
pod 3 4 75.0
total 103 118 87.2


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