File Coverage

blib/lib/Object/Depot.pm
Criterion Covered Total %
statement 143 158 90.5
branch 48 92 52.1
condition 13 23 56.5
subroutine 35 37 94.5
pod 13 13 100.0
total 252 323 78.0


line stmt bran cond sub pod time code
1             package Object::Depot;
2             our $VERSION = '0.03';
3 7     7   930752 use strictures 2;
  7         54  
  7         204  
4              
5             =encoding utf8
6              
7             =head1 NAME
8              
9             Object::Depot - Decouple object instantiation from usage.
10              
11             =head1 SYNOPSIS
12              
13             use Object::Depot;
14            
15             my $depot = Object::Depot->new(
16             class => 'CHI',
17             # CHI->new returns a CHI::Driver object.
18             type => InstanceOf[ 'CHI::Driver' ],
19             );
20            
21             $depot->add_key(
22             sessions => {
23             driver => 'Memory',
24             global => 1,
25             },
26             );
27            
28             $depot->store( ip2geo => CHI->new(...) );
29            
30             my $sessions = $depot->fetch('sessions');
31             my $ip2geo = $depot->fetch('ip2geo');
32              
33             =head1 DESCRIPTION
34              
35             Object depots encapsulate object construction so that users of objects
36             do not need to know how to create the objects in order to use them.
37              
38             The primary use case for this library is for storing the connection
39             logic to external services and making these connections globally
40             available to all application logic. See L for
41             turning your depot object into a global singleton.
42              
43             =cut
44              
45 7     7   3536 use Guard qw( guard );
  7         2824  
  7         325  
46 7     7   41 use Carp qw();
  7         12  
  7         94  
47 7     7   2215 use Role::Tiny qw();
  7         17697  
  7         139  
48 7     7   37 use Scalar::Util qw( blessed );
  7         11  
  7         283  
49 7     7   2592 use Sub::Name qw( subname );
  7         2894  
  7         325  
50 7     7   2544 use Types::Common::String qw( NonEmptySimpleStr );
  7         556758  
  7         90  
51 7     7   3196 use Types::Standard qw( Bool CodeRef HashRef Object InstanceOf );
  7         16  
  7         35  
52              
53             sub croak {
54             local $Carp::Internal{'Object::Depot'} = 1;
55             goto &Carp::croak;
56             }
57              
58             sub croakf {
59             my $msg = shift;
60             $msg = sprintf( $msg, @_ );
61             @_ = ( $msg );
62             goto &croak;
63             }
64              
65 7     7   9227 use Moo;
  7         43267  
  7         62  
66 7     7   9607 use namespace::clean;
  7         64418  
  7         39  
67              
68             sub _normalize_args {
69 12     12   25 my ($self, $args) = @_;
70              
71 12 100       32 return {} if !@$args;
72 4 100 66     24 return $args->[0] if @$args==1 and ref($args->[0]) eq 'HASH';
73 1 50       6 return { @$args } unless @$args % 2;
74              
75 0         0 croakf(
76             'Odd number of arguments passed to %s()',
77             scalar( caller ),
78             );
79             }
80              
81             sub _process_key_arg {
82 68     68   1656 my ($self, $args) = @_;
83              
84 68         336 my $caller_sub_name = (caller 1)[3];
85 68 50       198 $caller_sub_name = '__ANON__' if !defined $caller_sub_name;
86 68         296 $caller_sub_name =~ s{^.*::}{};
87              
88 68         104 my $key;
89              
90 68 100 100     311 $key = shift @$args
91             if @$args and !blessed $args->[0];
92              
93 68 100 100     252 if ($self->_has_default_key() and !defined $key) {
94 14         28 $key = $self->default_key();
95             }
96             else {
97 54 100       119 croak "No key was passed to $caller_sub_name()"
98             if !defined $key;
99              
100 46 50       120 if (!NonEmptySimpleStr->check( $key )) {
101 0 0       0 $key = defined($key) ? ["$key"] : 'UNDEF';
102 0         0 croak "Invalid key, $key, passed to $caller_sub_name(): " .
103             NonEmptySimpleStr->message( $key );
104             }
105             }
106              
107 60 100       853 $key = $self->_aliases->{$key} if exists $self->_aliases->{$key};
108              
109 60 50 33     150 if ($self->strict_keys() and !exists $self->_key_args->{$key}) {
110 0 0       0 $key = defined($key) ? qq["$key"] : 'UNDEF';
111 0         0 croak "Undeclared key, $key, passed to $caller_sub_name()"
112             }
113              
114 60         123 return $key;
115             }
116              
117             has _all_objects => (
118             is => 'ro',
119             default => sub{ {} },
120             );
121              
122             sub _objects {
123 40     40   58 my ($self) = @_;
124              
125 40 50       181 return $self->_all_objects() if !$self->per_process();
126              
127 0         0 my $key = $$;
128 0 0       0 $key .= '-' . threads->tid() if $INC{'threads.pm'};
129              
130 0   0     0 return $self->_all_objects->{$key} ||= {};
131             }
132              
133             has _key_args => (
134             is => 'ro',
135             default => sub{ {} },
136             );
137              
138             has _aliases => (
139             is => 'ro',
140             default => sub{ {} },
141             );
142              
143             has _injections => (
144             is => 'ro',
145             default => sub{ {} },
146             );
147              
148             =head1 ARGUMENTS
149              
150             =head2 class
151              
152             class => 'CHI',
153              
154             The class which objects in this depot are expected to be. This
155             argument defaults the L and L arguments.
156              
157             Does not have a default.
158              
159             Leaving this argument unset causes L to fail on keys that were
160             not first populated with L as the L subroutine
161             will just return C.
162              
163             =cut
164              
165             has class => (
166             is => 'ro',
167             isa => NonEmptySimpleStr,
168             predicate => '_has_class',
169             );
170              
171             =head2 constructor
172              
173             constuctor => sub{
174             my ($args) = @_;
175             return __PACKAGE__->depot->class->new( $args );
176             },
177              
178             Set this to a code ref to control how objects get constructed.
179              
180             When declaring a custom constructor be careful not to create memory
181             leaks via circular references.
182              
183             L validates the objects produced by this constructor and will
184             throw an exception if they do not match L.
185              
186             The default code ref is similar to the above example if L is
187             set. If it is not set then the default code ref will return C.
188              
189             =cut
190              
191             has constructor => (
192             is => 'lazy',
193             isa => CodeRef,
194             );
195              
196             my $undef_constructor = sub{ undef };
197              
198             sub _build_constructor {
199 10     10   95 my ($self) = @_;
200              
201 10 50       37 return $undef_constructor if !$self->_has_class();
202              
203 10         31 return _build_class_constructor( $self->class() );
204             }
205              
206             sub _build_class_constructor {
207 10     10   22 my ($class) = @_;
208 10     15   163 return sub{ $class->new( @_ ) };
  15         291  
209             }
210              
211             =head2 type
212              
213             type => InstanceOf[ 'CHI::Driver' ],
214              
215             Set this to a L type to control how objects in the depot
216             are validated when they are stored.
217              
218             Defaults to C L, if set. If the class is not set
219             then this defaults to C (both are from L).
220              
221             =cut
222              
223             has type => (
224             is => 'lazy',
225             isa => InstanceOf[ 'Type::Tiny' ],
226             );
227              
228             sub _build_type {
229 11     11   130 my ($self) = @_;
230 11 100       86 return InstanceOf[ $self->class() ] if $self->_has_class();
231 1         5 return Object;
232             }
233              
234             =head2 injection_type
235              
236             injection_type => Object,
237              
238             By default objects that are injected (see L) are validated
239             against L. Set this to a type that injections validate
240             against if it needs to be different (such as to support mock
241             objects).
242              
243             =cut
244              
245             has injection_type => (
246             is => 'lazy',
247             isa => InstanceOf[ 'Type::Tiny' ],
248             );
249              
250             sub _build_injection_type {
251 3     3   26 my ($self) = @_;
252 3         39 return $self->type();
253             }
254              
255             =head2 per_process
256              
257             per_process => 1,
258              
259             Turn this on to store objects per-process; meaning, if the TID (thread
260             ID) or PID (process ID) change then this depot will act as if no
261             objects have been stored. Generally you will not want to turn this
262             on. On occasion, though, some objects are not thread or forking safe
263             and it is necessary.
264              
265             Defaults off.
266              
267             =cut
268              
269             has per_process => (
270             is => 'ro',
271             isa => Bool,
272             default => 0,
273             );
274              
275             =head2 disable_store
276              
277             disable_store => 1,
278              
279             When on this causes L to silently not store, causing all
280             L calls for non-injected keys to return a new object.
281              
282             Defaults off.
283              
284             =cut
285              
286             has disable_store => (
287             is => 'ro',
288             isa => Bool,
289             default => 0,
290             );
291              
292             =head2 strict_keys
293              
294             strict_keys => 1,
295              
296             Turn this on to require that all keys used must first be declared
297             via L before they can be stored in the depot.
298              
299             Defaults to off, meaning keys may be used without having to
300             pre-declare them.
301              
302             =cut
303              
304             has strict_keys => (
305             is => 'ro',
306             isa => Bool,
307             default => 0,
308             );
309              
310             =head2 default_key
311              
312             default_key => 'generic',
313              
314             If no key is passed to key-accepting methods like L then they
315             will use this default key if available.
316              
317             Defaults to no default key.
318              
319             =cut
320              
321             has default_key => (
322             is => 'ro',
323             isa => NonEmptySimpleStr,
324             predicate => '_has_default_key',
325             );
326              
327             =head2 key_argument
328              
329             key_argument => 'connection_key',
330              
331             When set, this causes L to include an extra argument to be
332             passed to the class during object construction. The argument's key
333             will be whatever you set this to and the value will be the key used to
334             fetch the object.
335              
336             You will still need to write the code in your class to capture the
337             argument, such as:
338              
339             has connection_key => ( is=>'ro' );
340              
341             Defaults to no key argument.
342              
343             =cut
344              
345             has key_argument => (
346             is => 'ro',
347             isa => NonEmptySimpleStr,
348             predicate => '_has_key_argument',
349             );
350              
351             =head2 default_arguments
352              
353             default_arguments => {
354             arg => 'value',
355             ...
356             },
357              
358             When set, these arguments will be included in calls to L.
359              
360             Defaults to an empty hash ref.
361              
362             =cut
363              
364             has default_arguments => (
365             is => 'lazy',
366             isa => HashRef,
367             default => sub{ {} },
368             );
369              
370             =head2 export_name
371              
372             export_name => 'myapp_cache',
373              
374             Set the name of a function that L will
375             export to importers of your depot package.
376              
377             Has no default. If this is not set, then nothing will be exported.
378              
379             =cut
380              
381             has export_name => (
382             is => 'ro',
383             isa => NonEmptySimpleStr,
384             predicate => '_has_export_name',
385             );
386              
387             =head2 always_export
388              
389             always_export => 1,
390              
391             Turning this on causes L to always export
392             the L, rather than only when listed in the import
393             arguments. This is synonymous with the difference between
394             L's C<@EXPORT_OK> and C<@EXPORT>.
395              
396             =cut
397              
398             has always_export => (
399             is => 'ro',
400             isa => Bool,
401             default => 0,
402             );
403              
404             =head1 METHODS
405              
406             =head2 fetch
407              
408             my $object = $depot->fetch( $key );
409              
410             =cut
411              
412             sub fetch {
413 31     31 1 1554 my $self = shift;
414              
415 31         78 my $key = $self->_process_key_arg( \@_ );
416 27 50       55 croak 'Too many arguments passed to fetch()' if @_;
417              
418 27         58 return $self->_fetch( $key );
419             }
420              
421             sub _fetch {
422 27     27   46 my ($self, $key) = @_;
423              
424 27         53 my $object = $self->_injections->{ $key };
425 27   100     102 $object ||= $self->_objects->{$key};
426 27 100       104 return $object if $object;
427              
428 14 100       56 return undef if !$self->_has_class();
429              
430 12         38 $object = $self->_create( $key, {} );
431              
432 12         62 $self->_store( $key, $object );
433              
434 12         58 return $object;
435             }
436              
437             =head2 store
438              
439             $depot->store( $key => $object );
440              
441             =cut
442              
443             sub store {
444 1     1 1 39 my $self = shift;
445              
446 1         4 my $key = $self->_process_key_arg( \@_ );
447 1 50       4 croak 'Too many arguments passed to store()' if @_>1;
448 1 50       4 croak 'Not enough arguments passed to store()' if @_<1;
449              
450 1         1 my $object = shift;
451 1 50       22 croakf(
452             'Invalid object passed to store(): %s',
453             $self->type->get_message( $object ),
454             ) if !$self->type->check( $object );
455              
456             croak qq[Already stored key, "$key", passed to store()]
457 1 50       77 if exists $self->_objects->{$key};
458              
459 1         3 return $self->_store( $key, $object );
460             }
461              
462             sub _store {
463 13     13   41 my ($self, $key, $object) = @_;
464              
465 13 50       43 return if $self->disable_store();
466              
467 13         28 $self->_objects->{$key} = $object;
468              
469 13         40 return;
470             }
471              
472             =head2 remove
473              
474             $depot->remove( $key );
475              
476             =cut
477              
478             sub remove {
479 2     2 1 4 my $self = shift;
480              
481 2         7 my $key = $self->_process_key_arg( \@_ );
482 2 50       5 croak 'Too many arguments passed to remove()' if @_;
483              
484 2         6 return $self->_remove( $key );
485             }
486              
487             sub _remove {
488 2     2   3 my ($self, $key) = @_;
489              
490 2         3 return delete $self->_objects->{$key};
491             }
492              
493             =head2 create
494              
495             my $object = $depot->create( $key, %extra_args );
496              
497             Gathers arguments from L and then calls L
498             on them, returning a new object. Extra arguments may be passed and
499             they will take precedence.
500              
501             =cut
502              
503             sub create {
504 3     3 1 12 my $self = shift;
505              
506 3         7 my $key = $self->_process_key_arg( \@_ );
507              
508 3         7 my $extra_args = $self->_normalize_args( \@_ );
509              
510 3         7 return $self->_create( $key, $extra_args );
511             }
512              
513             sub _create {
514 15     15   31 my ($self, $key, $extra_args) = @_;
515              
516 15         38 my $args = $self->_arguments( $key, $extra_args );
517              
518 15         254 my $object = $self->constructor->( $args );
519              
520 15 0 0     3230 croakf(
    50          
521             'Constructor returned an invalid value, %s, for key %s: %s',
522             defined($object) ? (ref($object) || qq["$object"]) : 'UNDEF',
523             qq["$key"],
524             $self->type->get_message( $object ),
525             ) if !$self->type->check( $object );
526              
527 15         7290 return $object;
528             }
529              
530             =head2 arguments
531              
532             my $args = $depot->arguments( $key, %extra_args );
533              
534             This method returns an arguments hash ref that would be used to
535             instantiate a new L object. You could, for example, use this
536             to produce a base-line set of arguments, then sprinkle in some more,
537             and make yourself a special mock object to be injected.
538              
539             =cut
540              
541             sub arguments {
542 3     3 1 8 my $self = shift;
543              
544 3         8 my $key = $self->_process_key_arg( \@_ );
545              
546 3         6 my $extra_args = $self->_normalize_args( \@_ );
547              
548 3         7 return $self->_arguments( $key, $extra_args );
549             }
550              
551             sub _arguments {
552 18     18   28 my ($self, $key, $extra_args) = @_;
553              
554             my $args = {
555 18         368 %{ $self->default_arguments() },
556 18 100       23 %{ $self->_key_args->{$key} || {} },
  18         355  
557             %$extra_args,
558             };
559              
560 18 100       68 $args->{ $self->key_argument() } = $key
561             if $self->_has_key_argument();
562              
563 18         43 return $args;
564             }
565              
566             =head2 declared_keys
567              
568             my $keys = $depot->declared_keys();
569             foreach my $key (@$keys) { ... }
570              
571             Returns an array ref containing all the keys declared with
572             L.
573              
574             =cut
575              
576             sub declared_keys {
577 0     0 1 0 my $self = shift;
578 0         0 return [ keys %{ $self->_key_args() } ];
  0         0  
579             }
580              
581             =head2 inject
582              
583             $depot->inject( $key, $object );
584              
585             Takes an object of your making and forces L to return the
586             injected object. This is useful for injecting mock objects in tests.
587              
588             The injected object must validate against L.
589              
590             =cut
591              
592             sub inject {
593 3     3 1 5 my $self = shift;
594              
595 3         8 my $key = $self->_process_key_arg( \@_ );
596 3 50       17 croak 'Too many arguments passed to inject()' if @_>1;
597 3 50       7 croak 'Not enough arguments passed to inject()' if @_<1;
598              
599 3         5 my $object = shift;
600 3 50       59 croakf(
601             'Invalid object passed to inject(): %s',
602             $self->injection_type->get_message( $object ),
603             ) if !$self->injection_type->check( $object );
604              
605             croak qq[Already injected key, "$key", passed to inject()]
606 3 50       186 if exists $self->_injections->{$key};
607              
608 3         7 $self->_injections->{$key} = $object;
609              
610 3         6 return;
611             }
612              
613             =head2 inject_with_guard
614              
615             my $guard = $depot->inject_with_guard( $key => $object );
616              
617             This is just like L except it returns a L object
618             which, when it leaves scope and is destroyed, will automatically
619             call L.
620              
621             =cut
622              
623             sub inject_with_guard {
624 1     1 1 1 my $self = shift;
625              
626 1         5 my $key = $self->_process_key_arg( \@_ );
627              
628 1         4 $self->inject( $key, @_ );
629              
630             return guard {
631 1     1   38 return $self->clear_injection( $key );
632 1         16 };
633             }
634              
635             =head2 clear_injection
636              
637             my $object = $depot->clear_injection( $key );
638              
639             Removes and returns the injected object, restoring the original
640             behavior of L.
641              
642             =cut
643              
644             sub clear_injection {
645 3     3 1 7 my $self = shift;
646              
647 3         9 my $key = $self->_process_key_arg( \@_ );
648 3 50       7 croak 'Too many arguments passed to clear_injection()' if @_;
649              
650 3         15 return delete $self->_injections->{$key};
651             }
652              
653             =head2 injection
654              
655             my $object = $depot->injection( $key );
656              
657             Returns the injected object, or C if none has been injected.
658              
659             =cut
660              
661             sub injection {
662 9     9 1 24 my $self = shift;
663              
664 9         27 my $key = $self->_process_key_arg( \@_ );
665 9 50       16 croak 'Too many arguments passed to injection()' if @_;
666              
667 9         41 return $self->_injections->{ $key };
668             }
669              
670             =head2 has_injection
671              
672             if ($depot->has_injection( $key )) { ... }
673              
674             Returns true if an injection is in place for the specified key.
675              
676             =cut
677              
678             sub has_injection {
679 0     0 1 0 my $self = shift;
680              
681 0         0 my $key = $self->_process_key_arg( \@_ );
682 0 0       0 croak 'Too many arguments passed to has_injection()' if @_;
683              
684 0 0       0 return exists($self->_injections->{$key}) ? 1 : 0;
685             }
686              
687             =head2 add_key
688              
689             $depot->add_key( $key, %arguments );
690              
691             Declares a new key and, optionally, the arguments used to construct
692             the L object.
693              
694             Arguments are optional, but if present they will be saved and used
695             by L when calling C (via L) on L.
696              
697             =cut
698              
699             sub add_key {
700 6     6 1 623 my ($self, $key, @args) = @_;
701              
702 6 0       20 croakf(
    50          
703             'Invalid key, %s, passed to add_key(): %s',
704             defined($key) ? qq["$key"] : 'UNDEF',
705             NonEmptySimpleStr->get_message( $key ),
706             ) if !NonEmptySimpleStr->check( $key );
707              
708             croak "Already declared key, \"$key\", passed to add_key()"
709 6 50       112 if exists $self->_key_args->{$key};
710              
711 6         14 $self->_key_args->{$key} = $self->_normalize_args( \@args );
712              
713 6         12 return;
714             }
715              
716             =head2 alias_key
717              
718             $depot->alias_key( $alias_key => $real_key );
719              
720             Adds a key that is an alias to another key.
721              
722             =cut
723              
724             sub alias_key {
725 1     1 1 5 my ($self, $alias, $key) = @_;
726              
727 1 0       3 croakf(
    50          
728             'Invalid alias, %s, passed to alias_key(): %s',
729             defined($alias) ? qq["$alias"] : 'UNDEF',
730             NonEmptySimpleStr->get_message( $alias ),
731             ) if !NonEmptySimpleStr->check( $alias );
732              
733 1 0       14 croakf(
    50          
734             'Invalid key, %s, passed to alias_key(): %s',
735             defined($key) ? qq["$key"] : 'UNDEF',
736             NonEmptySimpleStr->get_message( $key ),
737             ) if !NonEmptySimpleStr->check( $key );
738              
739             croak "Already declared alias, \"$alias\", passed to alias_key()"
740 1 50       16 if exists $self->_aliases->{$alias};
741              
742             croak "Undeclared key, \"$key\", passed to alias_key()"
743 1 50 33     6 if $self->strict_keys() and !exists $self->_key_args->{$key};
744              
745 1         4 $self->_aliases->{$alias} = $key;
746              
747 1         2 return;
748             }
749              
750             1;
751             __END__