File Coverage

blib/lib/Evo/Di.pm
Criterion Covered Total %
statement 81 81 100.0
branch 38 50 76.0
condition 3 3 100.0
subroutine 11 11 100.0
pod 3 3 100.0
total 136 148 91.8


line stmt bran cond sub pod time code
1             package Evo::Di;
2 3     3   1313 use Evo -Class, '-Class::Attrs ECA_REQUIRED';
  3         7  
  3         30  
3 3     3   20 use Evo 'Module::Load load; Module::Loaded is_loaded; Carp croak';
  3         6  
  3         11  
4              
5             has di_stash => sub { {} };
6              
7             our @CARP_NOT = ('Evo::Class::Attrs');
8              
9 1     1   3 my sub _croak_cirk (@path) { croak "Circular dependencies detected: " . join(' -> ', @path); }
  1         3  
  1         93  
10 2 50   2   8 my sub _croak ($cur_key, $req_key) {
  2 50       9  
  2         4  
  2         5  
  2         4  
11 2         297 croak qq#Can't load dependency "$cur_key" for class "$req_key"#;
12             }
13              
14             # TODO: copypase, optimaze
15 5 50   5 1 19 sub mortal ($self, $class, %args) {
  5 50       14  
  5         7  
  5         18  
  5         18  
  5         7  
16 5         21 load $class;
17 5         369 my @stack = _di_list_pending($self, $class);
18 4         8 my %in_stack;
19 4         120 while (@stack) {
20 17         31 my $cur = pop @stack;
21 17         34 my @pending = _di_list_pending($self, $cur);
22 17 100       39 if (!@pending) {
23 10         22 $self->{di_stash}{$cur} = $cur->new(_di_args($self, $cur));
24 10         50 next;
25             }
26 7 100       23 _croak_cirk(@stack, $cur) if $in_stack{$cur}++;
27 6         17 push @stack, $cur, @pending;
28             }
29 3         10 $class->new(%args, _di_args($self, $class));
30             }
31              
32 12 50   12 1 776 sub single ($self, $key) {
  12 50       28  
  12         19  
  12         17  
  12         16  
33 12 100       51 return $self->{di_stash}{$key} if exists $self->{di_stash}{$key};
34 3         5 $self->{di_stash}{$key} = $self->mortal($key);
35             }
36              
37 5 50   5 1 32 sub provide ($self, %args) {
  5 50       15  
  5         8  
  5         19  
  5         9  
38 5         13 foreach my $k (keys %args) {
39 6 100       205 croak qq#Already has key "$k"# if exists $self->{di_stash}{$k};
40 5         16 $self->{di_stash}{$k} = $args{$k};
41             }
42             }
43              
44 30 50   30   109 sub _di_list_pending ($self, $req_key) : Private {
  30 50       66  
  30         42  
  30         47  
  30         37  
45 30 100       207 return unless $req_key->can('META');
46 25         40 my @results;
47 25         72 foreach my $slot ($req_key->META->attrs->slots) {
48 45 100       116 next if !(my $k = $slot->{inject});
49              
50 41 100       111 next if exists $self->{di_stash}{$k};
51 26   100     64 my $loaded = is_loaded($k) || eval { load($k); 1 };
52 26 100       2267 do { push @results, $k; next; } if $loaded;
  19         33  
  19         66  
53 7 100       30 _croak($k, $req_key) if $slot->{type} == ECA_REQUIRED;
54             }
55              
56 23         91 @results;
57 3     3   21 }
  3         7  
  3         16  
58              
59             # only existing key/values to ->new, skip missing
60 13 50   13   30 sub _di_args ($self, $key) : Private {
  13 50       28  
  13         19  
  13         19  
  13         17  
61 13 100       53 return unless $key->can('META');
62 11         15 my @opts;
63              
64 11         32 foreach my $slot ($key->META->attrs->slots) {
65 25 100       56 next unless my $k = $slot->{inject};
66 21 100       64 push @opts, $slot->{name}, $self->{di_stash}{$k} if exists $self->{di_stash}{$k};
67             }
68 11 100       124 (@opts, $self->{di_stash}{"$key\@defaults"} ? $self->{di_stash}{"$key\@defaults"}->%* : ());
69 3     3   803 }
  3         6  
  3         10  
70              
71              
72             1;
73              
74             # ABSTRACT: Dependency injection
75              
76             __END__