File Coverage

blib/lib/Stepford/GraphBuilder.pm
Criterion Covered Total %
statement 74 74 100.0
branch 11 12 91.6
condition n/a
subroutine 16 16 100.0
pod n/a
total 101 102 99.0


line stmt bran cond sub pod time code
1             package Stepford::GraphBuilder;
2              
3 39     39   287 use strict;
  39         87  
  39         1452  
4 39     39   253 use warnings;
  39         90  
  39         1031  
5 39     39   221 use namespace::autoclean;
  39         165  
  39         302  
6              
7             our $VERSION = '0.006001';
8              
9 39     39   3841 use List::AllUtils qw( sort_by uniq );
  39         82  
  39         2246  
10 39     39   245 use Stepford::Error;
  39         119  
  39         754  
11 39     39   16225 use Stepford::FinalStep;
  39         192  
  39         1740  
12 39     39   24123 use Stepford::Graph ();
  39         198  
  39         1900  
13 39     39   444 use Stepford::Types qw( ArrayOfSteps HashRef Logger Step );
  39         87  
  39         388  
14              
15 39     39   298767 use Moose;
  39         135  
  39         339  
16 39     39   270215 use MooseX::StrictConstructor;
  39         97  
  39         327  
17              
18             has config => (
19             is => 'ro',
20             isa => HashRef,
21             required => 1,
22             );
23              
24             has _step_classes => (
25             is => 'ro',
26             isa => ArrayOfSteps,
27             init_arg => 'step_classes',
28             required => 1,
29             );
30              
31             has _final_steps => (
32             is => 'ro',
33             isa => ArrayOfSteps,
34             init_arg => 'final_steps',
35             required => 1,
36             );
37              
38             has graph => (
39             is => 'ro',
40             isa => 'Stepford::Graph',
41             lazy => 1,
42             builder => '_build_graph',
43             );
44              
45             has _production_map => (
46             is => 'ro',
47             isa => HashRef [Step],
48             init_arg => undef,
49             lazy => 1,
50             builder => '_build_production_map',
51             );
52              
53             has logger => (
54             is => 'ro',
55             isa => Logger,
56             required => 1,
57             );
58              
59             has _graph_cache => (
60             traits => ['Hash'],
61             is => 'ro',
62             isa => HashRef,
63             default => sub { {} },
64             handles => {
65             _cache_graph => 'set',
66             _get_cached_graph => 'get',
67             },
68             );
69              
70             sub _build_graph {
71 150     150   423 my $self = shift;
72              
73             my $graph = Stepford::Graph->new(
74             config => $self->config,
75             logger => $self->logger,
76             step_class => 'Stepford::FinalStep',
77             children_graphs => [
78 148     148   6214 sort_by { $_->step_class }
79 150         5103 map { $self->_create_graph( $_, {} ) } @{ $self->_final_steps }
  151         895  
  150         5169  
80             ],
81             );
82              
83             $self->logger->debug( 'Graph for '
84 147         4829 . ( join q{ - }, @{ $self->_final_steps } ) . ":\n"
  147         4109  
85             . $graph->as_string );
86              
87 147         10092 return $graph;
88             }
89              
90             sub _build_production_map {
91 150     150   394 my $self = shift;
92              
93 150         312 my %map;
94 150         305 for my $class ( @{ $self->_step_classes } ) {
  150         5079  
95 623         10999 for my $attr ( map { $_->name } $class->productions ) {
  699         122455  
96 699 100       2305 next if exists $map{$attr};
97              
98 698         2313 $map{$attr} = $class;
99             }
100             }
101              
102 150         5740 return \%map;
103             }
104              
105             sub _create_graph {
106 573     573   1229 my $self = shift;
107 573         965 my $step_class = shift;
108 573         890 my $parents = shift;
109              
110             Stepford::Error->throw(
111             "The set of dependencies for $step_class is cyclical")
112 573 100       1511 if exists $parents->{$step_class};
113              
114             my $childrens_parents = {
115 572         764 %{$parents},
  572         2250  
116             $step_class => 1,
117             };
118              
119 572 100       24562 if ( my $graph = $self->_get_cached_graph($step_class) ) {
120 6         188 return $graph;
121             }
122              
123 566         16341 my $graph = Stepford::Graph->new(
124             config => $self->config,
125             logger => $self->logger,
126             step_class => $step_class,
127             children_graphs =>
128             $self->_create_children_graphs( $step_class, $childrens_parents ),
129             );
130              
131 562         22804 $self->_cache_graph( $step_class => $graph );
132              
133 562         11049 return $graph;
134             }
135              
136             sub _create_children_graphs {
137 566     566   1197 my $self = shift;
138 566         932 my $step_class = shift;
139 566         862 my $childrens_parents = shift;
140              
141             my @children_steps = uniq sort
142 566         3210 map { $self->_step_for_dependency( $step_class, $_->name ) }
  542         86162  
143             $step_class->dependencies;
144              
145 564         64442 return [ map { $self->_create_graph( $_, $childrens_parents ) }
  422         1864  
146             @children_steps ];
147             }
148              
149             sub _step_for_dependency {
150 542     542   1487 my $self = shift;
151 542         921 my $parent_step = shift;
152 542         1262 my $dep = shift;
153              
154             # if a dependency exists in the config, we don't need to build it.
155 542 50       16200 return if exists $self->config->{$dep};
156              
157 542         15949 my $map = $self->_production_map;
158              
159             Stepford::Error->throw( "Cannot resolve a dependency for $parent_step."
160             . " There is no step that produces the $dep attribute."
161             . ' Do you have a cyclic dependency?' )
162 542 100       1657 unless $map->{$dep};
163              
164             Stepford::Error->throw(
165             "A dependency ($dep) for $parent_step resolved to the same step.")
166 541 100       1464 if $map->{$dep} eq $parent_step;
167              
168 540         14601 $self->logger->debug(
169             "Dependency $dep for $parent_step is provided by $map->{$dep}");
170              
171 540         20290 return $map->{$dep};
172             }
173              
174             __PACKAGE__->meta->make_immutable;
175              
176             1;
177              
178             # ABSTRACT: Represents a concrete plan for execution by a Stepford::Runner
179              
180             __END__
181              
182             =pod
183              
184             =encoding UTF-8
185              
186             =head1 NAME
187              
188             Stepford::GraphBuilder - Represents a concrete plan for execution by a Stepford::Runner
189              
190             =head1 VERSION
191              
192             version 0.006001
193              
194             =head1 DESCRIPTION
195              
196             This class has no user-facing parts.
197              
198             =for Pod::Coverage next_step_set
199              
200             =head1 SUPPORT
201              
202             Bugs may be submitted through L<https://github.com/maxmind/Stepford/issues>.
203              
204             =head1 AUTHOR
205              
206             Dave Rolsky <drolsky@maxmind.com>
207              
208             =head1 COPYRIGHT AND LICENSE
209              
210             This software is copyright (c) 2014 - 2023 by MaxMind, Inc.
211              
212             This is free software; you can redistribute it and/or modify it under
213             the same terms as the Perl 5 programming language system itself.
214              
215             =cut