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   269 use strict;
  39         101  
  39         1271  
4 39     39   177 use warnings;
  39         87  
  39         1006  
5 39     39   179 use namespace::autoclean;
  39         86  
  39         273  
6              
7             our $VERSION = '0.006000';
8              
9 39     39   3388 use List::AllUtils qw( sort_by uniq );
  39         70  
  39         2061  
10 39     39   224 use Stepford::Error;
  39         77  
  39         630  
11 39     39   14667 use Stepford::FinalStep;
  39         151  
  39         1909  
12 39     39   21447 use Stepford::Graph ();
  39         137  
  39         1693  
13 39     39   336 use Stepford::Types qw( ArrayOfSteps HashRef Logger Step );
  39         84  
  39         326  
14              
15 39     39   268655 use Moose;
  39         83  
  39         330  
16 39     39   245586 use MooseX::StrictConstructor;
  39         103  
  39         307  
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   509 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   5985 sort_by { $_->step_class }
79 150         4567 map { $self->_create_graph( $_, {} ) } @{ $self->_final_steps }
  151         870  
  150         6562  
80             ],
81             );
82              
83             $self->logger->debug( 'Graph for '
84 147         4402 . ( join q{ - }, @{ $self->_final_steps } ) . ":\n"
  147         3683  
85             . $graph->as_string );
86              
87 147         9050 return $graph;
88             }
89              
90             sub _build_production_map {
91 150     150   373 my $self = shift;
92              
93 150         270 my %map;
94 150         259 for my $class ( @{ $self->_step_classes } ) {
  150         4682  
95 623         11335 for my $attr ( map { $_->name } $class->productions ) {
  699         90417  
96 699 100       1869 next if exists $map{$attr};
97              
98 698         2211 $map{$attr} = $class;
99             }
100             }
101              
102 150         5297 return \%map;
103             }
104              
105             sub _create_graph {
106 573     573   1158 my $self = shift;
107 573         960 my $step_class = shift;
108 573         811 my $parents = shift;
109              
110             Stepford::Error->throw(
111             "The set of dependencies for $step_class is cyclical")
112 573 100       1525 if exists $parents->{$step_class};
113              
114             my $childrens_parents = {
115 572         811 %{$parents},
  572         2011  
116             $step_class => 1,
117             };
118              
119 572 100       21731 if ( my $graph = $self->_get_cached_graph($step_class) ) {
120 6         173 return $graph;
121             }
122              
123 566         14541 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         20842 $self->_cache_graph( $step_class => $graph );
132              
133 562         9940 return $graph;
134             }
135              
136             sub _create_children_graphs {
137 566     566   1086 my $self = shift;
138 566         824 my $step_class = shift;
139 566         809 my $childrens_parents = shift;
140              
141             my @children_steps = uniq sort
142 566         2825 map { $self->_step_for_dependency( $step_class, $_->name ) }
  542         58998  
143             $step_class->dependencies;
144              
145 564         61428 return [ map { $self->_create_graph( $_, $childrens_parents ) }
  422         1644  
146             @children_steps ];
147             }
148              
149             sub _step_for_dependency {
150 542     542   973 my $self = shift;
151 542         982 my $parent_step = shift;
152 542         1041 my $dep = shift;
153              
154             # if a dependency exists in the config, we don't need to build it.
155 542 50       14508 return if exists $self->config->{$dep};
156              
157 542         13674 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       1401 unless $map->{$dep};
163              
164             Stepford::Error->throw(
165             "A dependency ($dep) for $parent_step resolved to the same step.")
166 541 100       1411 if $map->{$dep} eq $parent_step;
167              
168 540         13018 $self->logger->debug(
169             "Dependency $dep for $parent_step is provided by $map->{$dep}");
170              
171 540         18431 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.006000
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 - 2019 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