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