File Coverage

blib/lib/Catalyst/Model/Memcached.pm
Criterion Covered Total %
statement 63 72 87.5
branch 10 20 50.0
condition 12 18 66.6
subroutine 18 18 100.0
pod 5 8 62.5
total 108 136 79.4


line stmt bran cond sub pod time code
1             package Catalyst::Model::Memcached;
2              
3 1     1   334381 use strict;
  1         2  
  1         25  
4 1     1   3 use warnings;
  1         1  
  1         23  
5              
6 1     1   2 use Moose;
  1         6  
  1         16  
7              
8 1     1   4536 BEGIN { extends 'Catalyst::Model' };
9 1     1   114914 use Cache::Memcached::Fast;
  1         1488  
  1         33  
10              
11 1     1   416 use version; our $VERSION = qv('0.01');
  1         1502  
  1         5  
12              
13             my $primary_key;
14             my $ttl;
15              
16             sub BUILD {
17 1     1 0 1321 my $self = shift;
18             # Fix namespace
19 1         4 $self->{__ns_chunk} = lc ref $self;
20 1         6 $self->{__ns_chunk} =~ /.+::([^:]+)$/;
21 1         3 $self->{__ns_chunk} = $1 . '.';
22 1   33     14 $self->{__cache} //= Cache::Memcached::Fast->new( $self->{args} );
23 1     1   129 no strict 'refs';
  1         1  
  1         344  
24 1         121 $self->{primary_key} = ${(ref $self) . "::primary_key"};
  1         6  
25 1 50       3 die 'Primary key is a must' unless $self->{primary_key};
26 1 50       2 $self->{ttl} = ${(ref $self) . "::ttl"} if ${(ref $self) . "::ttl"};
  1         3  
  1         4  
27 1         2 return $self;
28             }
29              
30             #######################################################################
31             # Wrapper methods - imitating DBIx schema
32             sub search {
33 2     2 1 487 my ( $self, $hash ) = @_;
34 2 50 66     13 if ( ref $hash ne 'HASH' || ! $hash->{ $self->{primary_key} } ) {
35 2         12 die 'Search needs hash ref with primary_key';
36             }
37 0         0 return $self->{__cache}->get( $self->{__ns_chunk} . $hash->{ $self->{primary_key} } );
38             }
39              
40             sub find {
41 3     3 1 1704 my ( $self, $hash ) = @_;
42 3 100 100     16 if ( ref $hash ne 'HASH' || ! $hash->{ $self->{primary_key} } ) {
43 2         14 die 'Find needs hash ref with primary_key';
44             }
45 1         185 return $self->{__cache}->get( $self->{__ns_chunk} . $hash->{ $self->{primary_key} } );
46             }
47              
48             sub find_or_new {
49 2     2 1 447 my ( $self, $hash ) = @_;
50 2 50 66     12 if ( ref $hash ne 'HASH' || ! $hash->{ $self->{primary_key} } ) {
51 2         10 die 'Find_or_new needs hash ref with primary_key';
52             }
53 0         0 my $res = $self->find( $hash );
54 0 0       0 unless ( $res ) {
55 0         0 $self->create( $hash );
56 0         0 $res = $hash;
57             }
58 0         0 return $res;
59             }
60              
61             sub create {
62 2     2 1 441 my ( $self, $hash ) = @_;
63 2 50 66     10 if ( ref $hash ne 'HASH' || ! $hash->{ $self->{primary_key} } ) {
64 2         11 die 'Create needs hash ref';
65             }
66 0         0 $self->{__cache}->set( $self->{__ns_chunk} . $hash->{ $self->{primary_key} }, $hash, $self->{ttl} );
67 0         0 return $hash;
68             }
69              
70             sub delete {
71 2     2 1 432 my ( $self, $hash ) = @_;
72 2 50 66     12 if ( ref $hash ne 'HASH' || ! $hash->{ $self->{primary_key} } ) {
73 2         10 die 'Delete needs hash ref';
74             }
75 0         0 return $self->{__cache}->delete( $self->{__ns_chunk} . $hash->{ $self->{primary_key} } );
76              
77             }
78              
79             #######################################################################
80             # internals
81             sub set_primary_key {
82 1     1 0 1042 my ( $class, $pk ) = @_;
83 1 50       4 $pk = $pk->[0] if ref $pk eq 'ARRAY';
84 1     1   4 no strict 'refs';
  1         2  
  1         64  
85 1         2 ${$class."::primary_key"} = $pk;
  1         6  
86 1         1 return 1;
87             }
88             sub set_ttl {
89 1     1 0 6 my ( $class, $pk ) = @_;
90 1 50       3 $pk = $pk->[0] if ref $pk eq 'ARRAY';
91 1     1   3 no strict 'refs';
  1         1  
  1         48  
92 1         1 ${$class."::ttl"} = $pk;
  1         3  
93 1         2 return 1;
94             }
95              
96       1     END { } # module clean-up code
97              
98             1;
99              
100             __END__
101              
102             =pod
103              
104             =head1 NAME
105              
106             Catalyst::Model::Memcached - Wrapper for memcached imitating Catalyst models
107              
108             =head1 SYNOPSIS
109              
110             package MyCatalyst::Model::Token;
111              
112             use Moose;
113             use namespace::autoclean;
114              
115             BEGIN { extends 'Catalyst::Model::Memcached' };
116              
117             __PACKAGE__->config( args => { servers => [ '127.0.0.1:11211' ], namespace => 'db' } );
118             # Alternatively, this could be specified through config file
119              
120             __PACKAGE__->set_primary_key( qw/token/ );
121             __PACKAGE__->set_ttl( 300 );
122              
123             sub BUILD {
124             my $self = shift;
125             $self->{__once_initialized_object} = Object->new;
126             return $self;
127             }
128              
129             sub create {
130             my ($self, $hash) = @_;
131             $hash->{token} = $self->{__once_initialized_object}->create_id();
132             return $self->SUPER::create($hash)
133             }
134              
135             1;
136              
137             =head1 DESCRIPTION
138              
139             Simple Model for Catalyst for storing data in memcached
140              
141             =head1 USAGE
142              
143             One subclass of model handle one set of primary_key and ttl params.
144             You can think of it as one table in regular DB.
145              
146             In case you want to use memcached to store different entities through this
147             model, you can configure it like this in config file:
148              
149             Model:
150             Cached:
151             class: MyApp::Store::Cached
152             config:
153             args:
154             servers:
155             - 127.0.0.1:11211
156             namespace: 'db.'
157             ttl: 86400
158              
159             Assuming your model class is named MyApp::Model::Cached, your memcached
160             server is started on localhost on port 11211.
161             With this configuration all classes MyApp::Store::Cached::*
162             will be loaded with same memcached configuration and default ttl of 86400.
163              
164             Primary key could be the same in different classes - to avoid clashes
165             keys that are stored in memcached are constructed like
166             'global_namespace.last_part_of_module_name.primary_key'.
167              
168             =head1 METHODS
169              
170             =over
171              
172             =item create( hashref )
173              
174             $c->model( 'Cached::Token' )->create(
175             { token => 'aaaa', signature => 'abcd' }
176             );
177              
178             Creates record in memcached with key = C<primary_key>,
179             data = C<hashref>, expire = C<ttl>.
180             C<hashref> must contains C<primary_key>.
181              
182             =item search( hashref )
183              
184             $c->model( 'Cached::Token' )->search( { token => 'aaaa' } );
185              
186             Searches data in memcached by C<primary_key> key and returns memcached answer.
187             C<hashref> must contains C<primary_key>.
188              
189             =item find( hashref )
190              
191             The same as search.
192              
193             =item find_or_new( hashref )
194              
195             Calls find, if nothing found - calls create.
196              
197             =item delete( hashref )
198              
199             Delete record with C<primary_key>.
200              
201             =back
202              
203             =head1 AUTHOR
204              
205             Denis Pokataev
206             CPAN ID: CATONE
207             Sponsored by Openstat.com
208             catone@cpan.org
209              
210             =head1 COPYRIGHT
211              
212             This program is free software; you can redistribute
213             it and/or modify it under the same terms as Perl itself.
214              
215             The full text of the license can be found in the
216             LICENSE file included with this module.
217              
218              
219             =head1 SEE ALSO
220              
221             L<Catalyst>, L<Cache::Memcached::Fast>, perl(1).
222              
223             =cut
224              
225