File Coverage

blib/lib/Lvalue.pm
Criterion Covered Total %
statement 17 51 33.3
branch 0 24 0.0
condition 0 3 0.0
subroutine 6 11 54.5
pod n/a
total 23 89 25.8


line stmt bran cond sub pod time code
1             package Lvalue;
2 2     2   41856 use warnings;
  2         5  
  2         64  
3 2     2   13 use strict;
  2         3  
  2         58  
4 2     2   134 use Carp;
  2         16  
  2         234  
5 2     2   13 use Scalar::Util qw/reftype/;
  2         4  
  2         796  
6             require overload;
7             require Exporter;
8             our @ISA = 'Exporter';
9             our %EXPORT_TAGS = (all => [
10             our @EXPORT_OK = qw/wrap lvalue unwrap rvalue/
11             ]);
12             our $VERSION = '0.21';
13            
14             =head1 NAME
15            
16             Lvalue - add lvalue getters and setters to existing objects
17            
18             =head1 VERSION
19            
20             version 0.21
21            
22             =head1 SYNOPSIS
23            
24             Lvalue takes an object produced by some other package and wraps it with lvalue
25             functionality implemented with the object's original getter and setter routines.
26             Lvalue assumes its object uses the relatively standard getter / setter idiom
27             where any arguments is a setter, and no arguments is a getter.
28            
29             By wrapping an existing object's getters and setters, Lvalue gives you the
30             syntactic niceties of lvalues, without the inherent encapsulation violations of
31             the :lvalue subroutine attribute.
32            
33             my $obj = NormalObject->new();
34            
35             $obj->value(5);
36            
37             print $obj->value(); # prints 5
38            
39             use Lvalue;
40            
41             Lvalue->wrap( $obj );
42            
43             $obj->value = 10;
44            
45             print $obj->value; # prints 10
46            
47             $_ += 2 for $obj->value;
48            
49             print $obj->value; # prints 12
50            
51             =head1 EXPORT
52            
53             this module does not export anything by default but can export the functions
54             below (which can all also be called as methods of Lvalue)
55            
56             use Lvalue qw/lvalue/; # or 'wrap', also 'unwrap'/'rvalue'
57            
58             lvalue my $obj = SomePackage->new;
59            
60             $obj->value = 5;
61            
62             Lvalue->unwrap( $obj );
63            
64             $obj->value = 6; # dies
65            
66             =head1 FUNCTIONS
67            
68             =over 4
69            
70             =cut
71            
72             sub overload {
73 0     0     my ($object, $proxy) = @_;
74 0           my $pkg = ref $object;
75             my $overloader = sub {
76 0     0     my $op = shift;
77             sub {
78 0 0         if (my $sub = overload::Method($pkg, $op)) {
79 0           @_ = ($object, @_[1, 2]);
80 0           goto &$sub;
81             }
82 0           Carp::croak "no overload method '$op' in $pkg";
83             }
84 0           };
  0            
85 2     2   15 no strict 'refs';
  2         5  
  2         954  
86 0           my $fallback = ${$pkg.'::()'};
  0            
87            
88 0           my $overload = join ', ' =>
89             defined $fallback ? 'fallback => $fallback' : (),
90             map "'$_' => \$overloader->('$_')" =>
91 0 0         grep s/^\((?=..)// => keys %{$pkg.'::'};
92            
93 0 0         eval qq {package $proxy;
94             our \@ISA = 'Lvalue::Loader';
95             use overload $overload;
96             } or Carp::carp "Lvalue: overloading not preserved for $pkg, "
97             . "bug reports or patches welcome.\n $@";
98             }
99            
100             =item C
101            
102             =item C
103            
104             wrap an object with lvalue getters / setters
105            
106             my $obj = Lvalue->wrap( SomePackage->new );
107            
108             or in a constructor:
109            
110             sub new {
111             my $class = shift;
112             my $self = {@_};
113             Lvalue->wrap( bless $self => $class );
114             }
115            
116             in void context, an in-place modification is done:
117            
118             my $obj = SomePackage->new;
119            
120             Lvalue->wrap( $obj );
121            
122             $obj->value = 5;
123            
124             the alias C< lvalue > is provided for C< wrap > which when you export it as a
125             function, can lead to some nice code:
126            
127             use NormalObject;
128             use Lvalue 'lvalue';
129            
130             lvalue my $obj = NormalObject->new;
131            
132             $obj->value = 5;
133            
134             =cut
135             {my $num = 0;
136             sub wrap {
137 0     0     my ($object, $proxy) = ($_[$#_], 'Lvalue::Loader');
138            
139 0 0         if (overload::Overloaded $object) {
140 0           overload $object
141             => $proxy = 'Lvalue::Loader::_' . $num++
142             }
143 0           bless my $wrapped = \$object => $proxy;
144             defined wantarray
145 0 0         ? $wrapped
146             : $_[$#_] = $wrapped
147             }}
148            
149            
150             =item C
151            
152             =item C
153            
154             returns the original object
155            
156             =cut
157            
158             sub unwrap {
159 0     0     my $wrapped = $_[$#_];
160            
161 0 0         croak "unwrap only takes objects wrapped by this module"
162             unless (ref $wrapped) =~ /^Lvalue::Loader (?: ::_\d )? $/x;
163            
164             defined wantarray
165 0 0         ? $$wrapped
166             : $_[$#_] = $$wrapped
167             }
168            
169             BEGIN {
170 2     2   7 *lvalue = \&wrap;
171 2         2410 *rvalue = \&unwrap;
172             }
173            
174             my $no = sub {
175             local $Carp::CarpLevel = 1;
176             Carp::croak "no method '$_[1]' on '$_[0]'"
177             };
178            
179             {package
180             Lvalue::Loader;
181             sub AUTOLOAD :lvalue {
182 0 0   0     die unless (my $method) = our $AUTOLOAD =~ /([^:]+)$/;
183 0           my $object = ${+shift};
  0            
184            
185 0 0         if ($method eq 'DESTROY') {
186 0 0         $object->DESTROY if $object->can('DESTROY');
187             return
188 0           }
189 0 0 0       if (@_ or not defined wantarray) {
190 0           unshift @_, $object;
191 0 0         goto &{$object->can($method)
  0            
192             or $object->$no($method)}
193             }
194 0           tie my $tied => 'Lvalue::Tied', [$object, $method];
195 0           $tied
196             }
197            
198             for my $method qw(can isa DOES VERSION) {
199             no strict 'refs';
200             *$method = sub {
201             my $object = ${+shift};
202             unshift @_, $object;
203             goto &{$object->can($method)
204             or $object->$no($method)}
205             }
206             }
207             }
208            
209             {package
210             Lvalue::Tied;
211             use Carp;
212             sub TIESCALAR {bless pop}
213             sub STORE {
214             my ($object, $method) = @{$_[0]};
215             splice @_, 0, 1, $object;
216            
217             goto &{$object->can($method)
218             or $object->$no($method)}
219             }
220             BEGIN {*FETCH = \&STORE}
221             }
222            
223             =back
224            
225             =head1 AUTHOR
226            
227             Eric Strom, C<< >>
228            
229             =head1 BUGS
230            
231             special care is taken to ensure that overloaded objects still work properly.
232             if you encounter an error please let me know.
233            
234             Please report any bugs or feature requests to C, or through
235             the web interface at L. I will be notified, and then you'll
236             automatically be notified of progress on your bug as I make changes.
237            
238             =head1 COPYRIGHT & LICENSE
239            
240             Copyright 2010 Eric Strom.
241            
242             This program is free software; you can redistribute it and/or modify it
243             under the terms of either: the GNU General Public License as published
244             by the Free Software Foundation; or the Artistic License.
245            
246             See http://dev.perl.org/licenses/ for more information.
247            
248             =cut
249            
250             __PACKAGE__ if 'first require'