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   1381 use Evo -Class, '-Class::Attrs ECA_REQUIRED';
  3         8  
  3         30  
3 3     3   18 use Evo 'Module::Load load; Module::Loaded is_loaded; Carp croak';
  3         6  
  3         10  
4              
5             has di_stash => sub { {} };
6              
7             our @CARP_NOT = ('Evo::Class::Attrs');
8              
9 1     1   4 my sub _croak_cirk (@path) { croak "Circular dependencies detected: " . join(' -> ', @path); }
  1         3  
  1         94  
10 2 50   2   10 my sub _croak ($cur_key, $req_key) {
  2 50       7  
  2         5  
  2         4  
  2         3  
11 2         377 croak qq#Can't load dependency "$cur_key" for class "$req_key"#;
12             }
13              
14             # TODO: copypase, optimaze
15 5 50   5 1 27 sub mortal ($self, $class, %args) {
  5 50       17  
  5         14  
  5         11  
  5         26  
  5         11  
16 5         25 load $class;
17 5         497 my @stack = _di_list_pending($self, $class);
18 4         10 my %in_stack;
19 4         16 while (@stack) {
20 17         48 my $cur = pop @stack;
21 17         50 my @pending = _di_list_pending($self, $cur);
22 17 100       57 if (!@pending) {
23 10         34 $self->{di_stash}{$cur} = $cur->new(_di_args($self, $cur));
24 10         80 next;
25             }
26 7 100       27 _croak_cirk(@stack, $cur) if $in_stack{$cur}++;
27 6         23 push @stack, $cur, @pending;
28             }
29 3         14 $class->new(%args, _di_args($self, $class));
30             }
31              
32 12 50   12 1 1136 sub single ($self, $key) {
  12 50       39  
  12         24  
  12         26  
  12         23  
33 12 100       79 return $self->{di_stash}{$key} if exists $self->{di_stash}{$key};
34 3         10 $self->{di_stash}{$key} = $self->mortal($key);
35             }
36              
37 5 50   5 1 46 sub provide ($self, %args) {
  5 50       20  
  5         11  
  5         35  
  5         15  
38 5         23 foreach my $k (keys %args) {
39 6 100       244 croak qq#Already has key "$k"# if exists $self->{di_stash}{$k};
40 5         25 $self->{di_stash}{$k} = $args{$k};
41             }
42             }
43              
44 30 50   30   152 sub _di_list_pending ($self, $req_key) : Private {
  30 50       82  
  30         58  
  30         57  
  30         52  
45 30 100       285 return unless $req_key->can('META');
46 25         55 my @results;
47 25         92 foreach my $slot ($req_key->META->attrs->slots) {
48 45 100       170 next if !(my $k = $slot->{inject});
49              
50 41 100       146 next if exists $self->{di_stash}{$k};
51 26   100     83 my $loaded = is_loaded($k) || eval { load($k); 1 };
52 26 100       3168 do { push @results, $k; next; } if $loaded;
  19         44  
  19         144  
53 7 100       42 _croak($k, $req_key) if $slot->{type} == ECA_REQUIRED;
54             }
55              
56 23         138 @results;
57 3     3   20 }
  3         7  
  3         16  
58              
59             # only existing key/values to ->new, skip missing
60 13 50   13   49 sub _di_args ($self, $key) : Private {
  13 50       42  
  13         31  
  13         28  
  13         25  
61 13 100       81 return unless $key->can('META');
62 11         26 my @opts;
63              
64 11         45 foreach my $slot ($key->META->attrs->slots) {
65 25 100       94 next unless my $k = $slot->{inject};
66 21 100       101 push @opts, $slot->{name}, $self->{di_stash}{$k} if exists $self->{di_stash}{$k};
67             }
68 11 100       208 (@opts, $self->{di_stash}{"$key\@defaults"} ? $self->{di_stash}{"$key\@defaults"}->%* : ());
69 3     3   719 }
  3         7  
  3         10  
70              
71              
72             1;
73              
74             # ABSTRACT: Dependency injection
75              
76             __END__