File Coverage

blib/lib/Tie/Moose.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1 6     6   119531 use 5.008;
  6         18  
  6         198  
2 6     6   24 use strict;
  6         10  
  6         181  
3 6     6   25 use warnings;
  6         12  
  6         323  
4              
5             package Tie::Moose;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.003';
9              
10 6     6   5560 use Moose;
  0            
  0            
11             use namespace::autoclean;
12             use Carp qw( croak );
13             use Types::Standard -types;
14              
15             with 'MooseX::Traits';
16              
17             has object => (
18             is => 'ro',
19             isa => Object,
20             required => 1,
21             );
22              
23             has attributes => (
24             is => 'ro',
25             isa => ArrayRef[ ArrayRef[ Maybe[Str] ] ],
26             lazy => 1,
27             builder => '_build_attributes',
28             );
29              
30             has attributes_hash => (
31             is => 'ro',
32             isa => HashRef[ ArrayRef[ Maybe[Str] ] ],
33             lazy => 1,
34             builder => '_build_attributes_hash',
35             );
36              
37             has "+_trait_namespace" => (
38             default => __PACKAGE__,
39             );
40              
41             sub _build_attributes
42             {
43             return [
44             map [
45             $_->name, # 0
46             $_->reader || $_->accessor, # 1
47             $_->writer || $_->accessor, # 2
48             $_->predicate, # 3
49             $_->clearer, # 4
50             ], Class::MOP::class_of(shift->object)->get_all_attributes
51             ]
52             }
53              
54             sub _build_attributes_hash
55             {
56             return +{ map {; $_->[0], $_ } @{ shift->attributes } };
57             }
58              
59             sub fallback
60             {
61             my $self = shift;
62             my ($operation, $key) = @_;
63             croak "No attribute '$key' in tied object";
64             }
65              
66             sub TIEHASH
67             {
68             my $class = shift;
69             my ($object, %opts) = @_;
70             $class->new(%opts, object => $object);
71             }
72              
73             sub FETCH
74             {
75             my $self = shift;
76             my ($key) = @_;
77            
78             $self->attributes_hash->{$key}
79             or return $self->fallback(FETCH => $key);
80            
81             my $accessor = $self->attributes_hash->{$key}[1]
82             or croak "No reader for attribute '$key' in tied object";
83             return $self->object->$accessor;
84             }
85              
86             sub STORE
87             {
88             my $self = shift;
89             my ($key, $value) = @_;
90            
91             $self->attributes_hash->{$key}
92             or return $self->fallback(STORE => $key, $value);
93            
94             my $accessor = $self->attributes_hash->{$key}[2]
95             or croak "No writer for attribute '$key' in tied object";
96             return $self->object->$accessor($value);
97             }
98              
99             sub EXISTS
100             {
101             my $self = shift;
102             my ($key) = @_;
103            
104             $self->attributes_hash->{$key}
105             or return $self->fallback(EXISTS => $key);
106            
107             my $accessor = $self->attributes_hash->{$key}[3];
108             $accessor and return $self->object->$accessor;
109            
110             return $self->object->meta->find_attribute_by_name($key)->has_value($self->object);
111             }
112              
113             sub DELETE
114             {
115             my $self = shift;
116             my ($key) = @_;
117            
118             $self->attributes_hash->{$key}
119             or return $self->fallback(DELETE => $key);
120            
121             my $accessor = $self->attributes_hash->{$key}[4]
122             or croak "No clearer for attribute '$key' in tied object";
123             return $self->object->$accessor;
124             }
125              
126             sub CLEAR
127             {
128             my $self = shift;
129            
130             for my $attr (@{$self->attributes})
131             {
132             my $name = $attr->[0];
133             $self->EXISTS($name) and $self->DELETE($name);
134             }
135             }
136              
137             sub FIRSTKEY
138             {
139             my $self = shift;
140             for my $attr (@{$self->attributes})
141             {
142             next unless $self->EXISTS($attr->[0]);
143             return $attr->[0];
144             }
145             return;
146             }
147              
148             sub NEXTKEY
149             {
150             my $self = shift;
151             my ($lastkey) = @_;
152             my $should_return;
153             for my $attr (@{$self->attributes})
154             {
155             if ($attr->[0] eq $lastkey)
156             { $should_return++; }
157             elsif (not $self->EXISTS($attr->[0]))
158             { next; }
159             elsif ($should_return)
160             { return $attr->[0]; }
161             }
162             return;
163             }
164              
165             sub SCALAR
166             {
167             shift->object;
168             }
169              
170             __PACKAGE__->meta->make_immutable;
171             1;
172              
173             __END__
174              
175             =head1 NAME
176              
177             Tie::Moose - tie a hash to a Moose object (yeah, like Tie::MooseObject)
178              
179             =head1 SYNOPSIS
180              
181             use v5.14;
182            
183             package Person {
184             use Moose;
185             has name => (
186             is => "rw",
187             isa => "Str",
188             );
189             has age => (
190             is => "rw",
191             isa => "Num",
192             reader => "get_age",
193             writer => "set_age",
194             );
195             }
196            
197             my $bob = Person->new(name => "Robert");
198            
199             tie my %bob, "Tie::Moose", $bob;
200            
201             $bob{age} = 32; # calls the "set_age" method
202             $bob{age} = "x"; # would croak
203             $bob{xyz} = "x"; # would croak
204              
205             =head1 DESCRIPTION
206              
207             This module is much like L<Tie::MooseObject>. It ties a hash to an instance
208             of a L<Moose>-based class, allowing you to access attributes as hash keys. It
209             uses the accessors provided by Moose, and thus honours read-only attributes,
210             type constraints and coercions, triggers, etc.
211              
212             There are a few key differences with L<Tie::MooseObject>:
213              
214             =over
215              
216             =item *
217              
218             It handles differently named getters/setters more to my liking. Given the
219             example in the SYNOPSIS, with Tie::MooseObject you need to write:
220              
221             $bob{set_age} = 32;
222             say $bob{get_age};
223              
224             Whereas with Tie::Moose, you just use the C<age> hash key for both fetching
225             from and storing to the hash.
226              
227             =item *
228              
229             Implements C<DELETE> from the L<Tie::Hash> interface. Tie::MooseObject does
230             not allow keys to be deleted from its hashes.
231              
232             (C<DELETE> only works on Moose attributes that have a "clearer" method.)
233              
234             =item *
235              
236             Supplied with various traits to influence the behaviour of the tied hash.
237              
238             tie my %bob, "Tie::Moose"->with_traits("ReadOnly"), $bob;
239              
240             (Note that by design, many of the traits supplied with Tie::Moose are
241             mutually exclusive.)
242              
243             =back
244              
245             =head1 BUGS
246              
247             Please report any bugs to
248             L<http://rt.cpan.org/Dist/Display.html?Queue=Tie-Moose>.
249              
250             =head1 SEE ALSO
251              
252             L<Tie::MooseObject>.
253              
254             Traits for Tie::Moose hashes:
255             L<Tie::Moose::ReadOnly>,
256             L<Tie::Moose::Forgiving>,
257             L<Tie::Moose::FallbackHash>,
258             L<Tie::Moose::FallbackSlot>.
259              
260             =head1 AUTHOR
261              
262             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
263              
264             =head1 COPYRIGHT AND LICENCE
265              
266             This software is copyright (c) 2013 by Toby Inkster.
267              
268             This is free software; you can redistribute it and/or modify it under
269             the same terms as the Perl 5 programming language system itself.
270              
271             =head1 DISCLAIMER OF WARRANTIES
272              
273             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
274             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
275             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
276