File Coverage

blib/lib/Mixin/ExtraFields/Driver/HashGuts.pm
Criterion Covered Total %
statement 45 45 100.0
branch 6 6 100.0
condition 7 11 63.6
subroutine 16 16 100.0
pod 13 13 100.0
total 87 91 95.6


line stmt bran cond sub pod time code
1 3     3   19 use strict;
  3         8  
  3         91  
2 3     3   15 use warnings;
  3         8  
  3         119  
3              
4             package Mixin::ExtraFields::Driver::HashGuts 0.140003;
5 3     3   17 use parent qw(Mixin::ExtraFields::Driver);
  3         7  
  3         21  
6             # ABSTRACT: store extras in a hashy object's guts
7              
8             #pod =head1 SYNOPSIS
9             #pod
10             #pod package Your::HashBased::Class;
11             #pod
12             #pod use Mixin::ExtraFields -fields => { driver => 'HashGuts' };
13             #pod
14             #pod =head1 DESCRIPTION
15             #pod
16             #pod This driver class implements an extremely simple storage mechanism: extras are
17             #pod stored on the object on which the mixed-in methods are called. By default,
18             #pod they are stored under the key returned by the C> method,
19             #pod but this can be changed by providing a C argument to the driver
20             #pod configuration, like so:
21             #pod
22             #pod use Mixin::ExtraFields -fields => {
23             #pod driver => { class => 'HashGuts', hash_key => "\0Something\0Wicked\0" }
24             #pod };
25             #pod
26             #pod =head1 METHODS
27             #pod
28             #pod In addition to the methods required by Mixin::ExtraFields::Driver, the
29             #pod following methods are provided:
30             #pod
31             #pod =head2 hash_key
32             #pod
33             #pod my $key = $driver->hash_key;
34             #pod
35             #pod This method returns the key where the driver will store its extras.
36             #pod
37             #pod =cut
38              
39             sub hash_key {
40 47     47 1 79 my ($self) = @_;
41 47         156 return $self->{hash_key};
42             }
43              
44             #pod =head2 default_hash_key
45             #pod
46             #pod If no C argument is given for the driver, this method is called
47             #pod during driver initialization. It will return a unique string to be used as the
48             #pod hash key.
49             #pod
50             #pod =cut
51              
52             my $i = 0;
53             sub default_hash_key {
54 2     2 1 5 my ($self) = @_;
55 2         23 return "$self" . '@' . $i++;
56             }
57              
58             #pod =head2 storage
59             #pod
60             #pod This method returns the hashref of storage used for extras. Individual objects
61             #pod get weak references to their id within this hashref.
62             #pod
63             #pod =cut
64              
65 42     42 1 155 sub storage { $_[0]->{storage} }
66              
67             #pod =head2 storage_for
68             #pod
69             #pod my $stash = $driver->storage_for($object, $id);
70             #pod
71             #pod This method returns the hashref to use to store extras for the given object and
72             #pod id. This hashref is stored on both the hash-based object (in its C
73             #pod entry) and on the driver (in the entry for C<$id> in its C hash).
74             #pod
75             #pod All objects with the same id should end up with the same hash in their
76             #pod C field. B of these references are weakened, which means two
77             #pod things: first, even if all objects with a given id go out of scope, future
78             #pod objects with that id will retain the original extras; secondly, memory used to
79             #pod store extras is never reclaimed. If this is a problem, use a more
80             #pod sophisticated driver.
81             #pod
82             #pod =cut
83              
84             sub storage_for {
85 42     42 1 80 my ($self, $object, $id) = @_;
86              
87 42   100     82 my $store = $self->storage->{ $id } ||= {};
88              
89 42 100 66     91 unless ($object->{ $self->hash_key }||0 == $store) {
90 5   33     12 $object->{ $self->hash_key } ||= $store;
91             }
92              
93 42         212 return $store
94             }
95              
96             sub from_args {
97 5     5 1 13 my ($class, $arg) = @_;
98              
99 5         16 my $self = bless { storage => {} } => $class;
100              
101 5   66     26 $self->{hash_key} = $arg->{hash_key} || $self->default_hash_key;
102              
103 5         30 return $self;
104             }
105              
106             sub exists_extra {
107 20     20 1 46 my ($self, $object, $id, $name) = @_;
108              
109 20         44 return exists $self->storage_for($object, $id)->{$name};
110             }
111              
112             sub get_extra {
113 8     8 1 22 my ($self, $object, $id, $name) = @_;
114              
115             # avoid autovivifying entries on get.
116 8 100       23 return unless $self->exists_extra($object, $id, $name);
117 4         12 return $self->storage_for($object, $id)->{$name};
118             }
119              
120             sub get_detailed_extra {
121 3     3 1 10 my ($self, $object, $id, $name) = @_;
122              
123             # avoid autovivifying entries on get.
124 3 100       10 return unless $self->exists_extra($object, $id, $name);
125 1         4 return { value => $self->storage_for($object, $id)->{$name} };
126             }
127              
128             sub get_all_detailed_extra {
129 7     7 1 19 my ($self, $object, $id) = @_;
130              
131 7         19 my $stash = $self->storage_for($object, $id);
132 7         33 my @all_detailed = map { $_ => { value => $stash->{$_} } } keys %$stash;
  4         55  
133             }
134              
135             sub get_all_extra {
136 4     4 1 35 my ($self, $object, $id) = @_;
137              
138 4         9 return %{ $self->storage_for($object, $id) };
  4         13  
139             }
140              
141             sub set_extra {
142 4     4 1 15 my ($self, $object, $id, $name, $value) = @_;
143              
144 4         11 $self->storage_for($object, $id)->{$name} = $value;
145             }
146              
147             sub delete_extra {
148 1     1 1 6 my ($self, $object, $id, $name) = @_;
149              
150 1         4 delete $self->storage_for($object, $id)->{$name};
151             }
152              
153             sub delete_all_extra {
154 1     1 1 4 my ($self, $object, $id) = @_;
155 1         3 %{ $self->storage_for($object, $id) } = ();
  1         3  
156             }
157              
158             1;
159              
160             __END__