File Coverage

blib/lib/Tie/MooseObject.pm
Criterion Covered Total %
statement 48 48 100.0
branch n/a
condition n/a
subroutine 28 28 100.0
pod n/a
total 76 76 100.0


line stmt bran cond sub pod time code
1 1     1   826 use strict;
  1         2  
  1         54  
2             package Tie::MooseObject;
3             BEGIN {
4 1     1   19 $Tie::MooseObject::VERSION = '0.0001';
5             } # for Pod::Weaver
6             # ABSTRACT: a tied hash interface to Moose object attributes
7              
8              
9 1     1   6 use MooseX::Declare 0.33;
  1         36  
  1         22  
10              
11 1     1   11426 class Tie::MooseObject {
  1     1   2  
  1     1   35  
  1     1   10283  
  1         1  
  1         9  
  1         6183  
  1         2  
  1         10  
  1         120  
12 1     1   850 use MooseX::Has::Sugar 0.0405;
  1         681  
  1         7  
13 1     1   105 use MooseX::Types::Moose 0.12 qw( Bool Str HashRef CodeRef Object );
  1         23  
  1         12  
14 1     1   4995 use Moose::Util::TypeConstraints 1.03 qw( enum );
  1         24  
  1         7  
15 1     1   400 use List::Util 1.23 qw( first );
  1         21  
  1         59  
16 1     1   4 use Carp qw( croak );
  1         2  
  1         223  
17              
18              
19             has '_reader' => ( isa => HashRef[CodeRef], ro, lazy_build );
20             has '_writer' => ( isa => HashRef[CodeRef], ro, lazy_build );
21             has '_predicate' => ( isa => HashRef[CodeRef], ro, lazy_build );
22              
23              
24             has 'is' => ( isa => enum( [ qw(ro rw) ] ), default => 'ro', rw );
25              
26              
27             has 'write_loop' => ( isa => Bool, default => 0, rw );
28              
29              
30             has 'object' => ( isa => Object, ro, required );
31              
32              
33 1     1   280779 method BUILD {
34             $self->TIEHASH;
35             }
36              
37 1     1   98807 method TIEHASH(ClassName|Object $self: @args ) {
38             $self = $self->new( @args )
39             unless ref $self;
40             return $self;
41             }
42              
43 1     1   28384 method _build__reader() {
44             return $self->_build_rw( 'read' );
45             }
46              
47 1     1   28337 method _build__writer() {
48             return $self->_build_rw( 'write' );
49             }
50              
51 1     1   29324 method _build__predicate() {
52             my $object = $self->object;
53             my $meta = Class::MOP::Class->initialize( ref $object );
54             my ( %predicate );
55             for ( $meta->get_method_list ) {
56             my $method = $meta->get_method( $_ );
57             next unless $method->can( 'associated_attribute' );
58             my $attr = $method->associated_attribute;
59             my ( $predicate ) = ref( $attr->predicate ) ? %{ $attr->predicate } : $attr->predicate;
60             if ( $predicate and $method->name eq $predicate ) {
61             $predicate{ $predicate } = $method->body;
62             }
63             }
64             return \%predicate;
65             }
66              
67 1     1   55428 method _build_rw( Str $type ) {
68             my ( $has, $get ) = $type eq 'read'
69             ? qw( has_read_method get_read_method )
70             : qw( has_write_method get_write_method );
71             my $meta = Class::MOP::Class->initialize( ref $self->object );
72             my %return;
73             for ( $meta->get_method_list ) {
74             my $method = $meta->get_method( $_ );
75             next unless $method->can( 'associated_attribute' );
76             my $attr = $method->associated_attribute;
77             if ( $attr->$has() && $method->name eq $attr->$get() ) {
78             $return{ $method->name } = $method->body;
79             }
80             }
81             return \%return;
82             }
83              
84              
85 1     1   104162 method STORE( Str $key, Any $value ) {
86             croak "Attempt to modify a readonly Moose tied hash"
87             if $self->is eq 'ro';
88              
89             if ( exists $self->_writer->{$key} ) {
90             $self->_writer->{$key}->( $self->object, $value );
91             return $value;
92             }
93             else {
94             croak "Invalid attempt to call write method $key on $self->object for Moose tied hash";
95             }
96             }
97              
98              
99 1     1   59134 method FETCH( Str $key ) {
100             if ( exists $self->_reader->{$key} ) {
101             return $self->_reader->{$key}->( $self->object );
102             }
103             croak "Invalid attempt to call read method $key on $self->object for Moose tied hash";
104             }
105              
106              
107 1     1   70361 method FIRSTKEY {
108             my $h = $self->_get_loop_hashref;
109             my $a = scalar keys %{ $h };
110             if ( wantarray ) {
111             my ( $k, $v ) = each %{ $h };
112             return ( $k, $v->( $self->object ) );
113             }
114             # else scalar or void context
115             return each %{ $h };
116             }
117              
118 1     1   52942 method NEXTKEY {
119             my $h = $self->_get_loop_hashref;
120             if ( wantarray ) {
121             my ( $k, $v ) = each %{ $h };
122             return ( $k, $v->( $self->object ) );
123             }
124             # else scalar or void context
125             return each %{ $h };
126             }
127              
128              
129 1     1   55111 method SCALAR {
130             return scalar( keys( %{ $self->_get_loop_hashref } ) );
131             }
132              
133              
134 1     1   66166 method EXISTS( Str $key ) {
135             return $self->_predicate->{$key}->( $self->object ) if exists $self->_predicate->{$key};
136             return exists $self->_reader->{$key} if exists $self->_reader->{$key};
137             return exists $self->_writer->{$key} if $self->is eq 'rw';
138             return;
139             }
140              
141              
142 1     1   60466 method DELETE( Str $key ) {
143             croak "$self->DELETE not implemented";
144             }
145              
146             # override this method if you have some default for clearing the method hash values...
147 1     1   50507 method CLEAR {
148             croak "$self->CLEAR not implemented";
149             }
150              
151 1     1   49024 method _get_loop_hashref {
152             return $self->write_loop
153             ? $self->_writer
154             : $self->_reader;
155             }
156 1     1   3601 }
157              
158             1;
159              
160              
161              
162             __END__
163             =pod
164              
165             =head1 NAME
166              
167             Tie::MooseObject - a tied hash interface to Moose object attributes
168              
169             =head1 VERSION
170              
171             version 0.0001
172              
173             =head1 SYNOPSIS
174              
175             package Point;
176              
177             has 'x' => (
178             is => 'rw',
179             isa => 'Int',
180             predicate => 'has_x',
181             reader => 'get_x',
182             writer => 'set_x'
183             );
184             has 'y' => ( isa => 'Int', is => 'rw' );
185              
186             my $p = new Point( x => 1, y => 20 );
187             my %point;
188             tie %point, 'Tie::MooseObject', { object => $p };
189              
190             $point{set_x} = 4;
191             $point{y} = 20;
192             print $p->get_x, "\n",
193             $p->y, "\n";
194             use Data::Dumper;
195             print Dumper( \%point );
196              
197             =head1 DESCRIPTION
198              
199             This module is BETA software. It seems to work so far, but it is not well
200             tested. B< USE AT YOUR OWN RISK >.
201              
202             B<NOTE>: This documentation assumes you already have knowledge of L<Moose> and Moose
203             attributes.
204              
205             Tie::MooseObject allows you to tie a hash to a Moose object. The tied hash
206             uses the object's attributes accessor methods as keys. The C<reader> accessor
207             method is the key for fetching from the tied hash, the C<writer> method is the
208             key for assigning.
209              
210             This module does not support C<handles>. C<handles> is used to delegate methods
211             to the object stored in the attribute. There is no way to know if the delegation
212             is for an attribute accessor or to perform some task. In the future this may
213             be supported through explicit options.
214              
215             =head1 ATTRIBUTES
216              
217             =head2 C<is>
218              
219             Expects a string of either C<ro> or C<rw>, If set to C<ro>, Tie::MooseObject
220             will not allow access to the C<writer> attribute methods. This means that
221             C<STORE> will fatal.
222              
223             =head2 C<write_loop>
224              
225             This tells Tie::MooseObject to use the C<writer> method names as the keys when
226             you call C<each()> or C<keys()>
227              
228             =head2 C<object>
229              
230             The object to C<tie()> to. Required.
231              
232             =head1 METHODS
233              
234             =head2 C<TIEHASH>
235              
236             When using C<tie()>, you should pass in a hash or hash reference of
237             arguments as the last argument. These arguments are the same style
238             as a standard Moose constructor. See L</ATTRIBUTES> for a list of
239             possible and required arguments.
240              
241             =head2 C<STORE>
242              
243             Assignment to a key in the hash will call the C<writer> method by the same name
244             as the key. If you attempt to call this method on a read-only hash,
245             Tie::MooseObject will throw an error. Also, If you attempt to add a new value
246             to the tied hash a error will be thrown.
247              
248             =head2 C<FETCH>
249              
250             When fetching a value from the tied hash, the key should be the name of the
251             C<reader> attribute method. If you pass in a key which does not exist, an error
252             will be thrown.
253              
254             =head2 C<FIRSTKEY>
255              
256             =head2 C<NEXTKEY>
257              
258             When looping, by default, the key will be the name of attributes C<reader>
259             method. If you specify C<write_loop> when constructing the tied hash, the key
260             will be the C<writer> method instead.
261              
262             =head2 C<SCALAR>
263              
264             In scalar context, by default, the number of C<reader> attribute methods are
265             returned. If you specified C<write_loop> when C<tie()>ing the hash, the number
266             of C<writer> attribute methods will be returned.
267              
268             =head2 C<EXISTS>
269              
270             If the key is the name of the attributes C<predicate> method, the value
271             returned by a call to this method is returned. If the key is the name of a
272             C<reader> method, true is returned. If the hash is C<rw> and the key is the
273             names of a C<writer> method, this returns true.
274              
275             =head2 C<DELETE>
276              
277             =head2 C<CLEAR>
278              
279             These method are not implemented so do not attempt to call them.
280              
281             =head1 AUTHOR
282              
283             Scott A. Beck <scottbeck@gmail.com>
284              
285             =head1 COPYRIGHT AND LICENSE
286              
287             This software is copyright (c) 2010 by Scott A. Beck <scottbeck@gmail.com>.
288              
289             This is free software; you can redistribute it and/or modify it under
290             the same terms as the Perl 5 programming language system itself.
291              
292             =cut
293