File Coverage

blib/lib/Object/Depot.pm
Criterion Covered Total %
statement 150 169 88.7
branch 48 92 52.1
condition 13 23 56.5
subroutine 37 40 92.5
pod 14 14 100.0
total 262 338 77.5


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