File Coverage

blib/lib/Cfn.pm
Criterion Covered Total %
statement 150 193 77.7
branch 39 68 57.3
condition 10 12 83.3
subroutine 36 48 75.0
pod 0 29 0.0
total 235 350 67.1


line stmt bran cond sub pod time code
1 20     20   1193287 use Moose::Util::TypeConstraints;
  20         736618  
  20         183  
2              
3             subtype 'Cfn::Resource::DeletionPolicy',
4             as 'Str',
5             where { $_ eq 'Delete' or $_ eq 'Retain' or $_ eq 'Snapshot' },
6             message { "$_ is an invalid DeletionPolicy" };
7              
8             subtype 'Cfn::Value::ArrayOfPrimitives',
9             as 'Cfn::Value::Array',
10             where { @{ $_[0]->Value } == grep { $_->isa('Cfn::Value::Primitive') } @{ $_[0]->Value } },
11             message { 'This type only supports Primitives' };
12              
13             sub coerce_array {
14             Cfn::Value::Array->new(Value => [
15 96     96   73214 map { Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($_) } @$_
  174         63301  
16             ])
17             }
18              
19             sub coerce_hash {
20 148     148   144222 my $arg = $_;
21 148         549 my @keys = keys %$arg;
22 148         310 my $first_key = $keys[0];
23 148 100 100     1269 if (@keys == 1 and (substr($first_key,0,4) eq 'Fn::' or $first_key eq 'Ref' or $first_key eq 'Condition')){
      66        
24 131 100       502 if ($first_key eq 'Fn::GetAtt') {
    100          
    100          
25 15         155 Cfn::Value::Function::GetAtt->new(Function => $first_key, Value => $arg->{ $first_key });
26             } elsif ($keys[0] eq 'Ref'){
27 77         488 Cfn::Value::Function::Ref->new( Function => $first_key, Value => $arg->{ $first_key });
28             } elsif ($keys[0] eq 'Condition'){
29 2         18 Cfn::Value::Function::Condition->new( Function => $first_key, Value => $arg->{ $first_key });
30             } else {
31 37         206 Cfn::Value::Function->new(Function => $first_key, Value => $arg->{ $first_key });
32             }
33             } else {
34             Cfn::Value::Hash->new(Value => {
35 17         46 map { $_ => Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($arg->{$_}) } @keys
  36         13993  
36             });
37             }
38             }
39              
40             coerce 'Cfn::Value',
41             from 'Int|Str', via { Cfn::Value::Primitive->new( Value => $_ ) },
42             from 'HashRef', via (\&coerce_hash),
43             from 'ArrayRef', via (\&coerce_array);
44              
45             coerce 'Cfn::Value::Array',
46             from 'HashRef', via (\&coerce_hash),
47             from 'ArrayRef', via (\&coerce_array);
48              
49             coerce 'Cfn::Value::ArrayOfPrimitives',
50             from 'HashRef', via (\&coerce_hash),
51             from 'ArrayRef', via (\&coerce_array);
52              
53              
54             coerce 'Cfn::Value::Hash',
55             from 'HashRef', via (\&coerce_hash);
56              
57              
58             subtype 'Cfn::MappingHash',
59             as 'HashRef[Cfn::Mapping]';
60              
61             my $cfn_mapping_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Mapping');
62             coerce 'Cfn::MappingHash',
63             from 'HashRef', via {
64             my $original = $_;
65             return { map { ($_ => $cfn_mapping_constraint->coerce($original->{ $_ }) ) } keys %$original };
66             };
67              
68             coerce 'Cfn::Mapping',
69             from 'HashRef', via {
70             return Cfn::Mapping->new(%$_);
71             };
72              
73             subtype 'Cfn::OutputHash',
74             as 'HashRef[Cfn::Output]';
75              
76             my $cfn_output_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Output');
77             coerce 'Cfn::OutputHash',
78             from 'HashRef', via {
79             my $original = $_;
80             return { map { ($_ => $cfn_output_constraint->coerce($original->{ $_ }) ) } keys %$original };
81             };
82              
83             coerce 'Cfn::Output',
84             from 'HashRef', via {
85             return Cfn::Output->new(%$_);
86             };
87              
88             subtype 'Cfn::ConditionHash',
89             as 'HashRef[Cfn::Value]';
90              
91             my $cfn_value_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value');
92             coerce 'Cfn::ConditionHash',
93             from 'HashRef', via {
94             my $original = $_;
95             return { map { ($_ => $cfn_value_constraint->coerce($original->{ $_ }) ) } keys %$original };
96             };
97              
98             subtype 'Cfn::ParameterHash',
99             as 'HashRef[Cfn::Parameter]';
100              
101             my $cfn_parameter_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Parameter');
102             coerce 'Cfn::ParameterHash',
103             from 'HashRef', via {
104             my $original = $_;
105             return { map { ($_ => $cfn_parameter_constraint->coerce($original->{ $_ }) ) } keys %$original };
106             };
107              
108             coerce 'Cfn::Parameter',
109             from 'HashRef', via {
110             return Cfn::Parameter->new(%$_);
111             };
112              
113             subtype 'Cfn::ResourceHash',
114             as 'HashRef[Cfn::Resource]';
115              
116             my $cfn_resource_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Resource');
117             coerce 'Cfn::ResourceHash',
118             from 'HashRef', via {
119             my $original = $_;
120             return { map { ($_ => $cfn_resource_constraint->coerce($original->{ $_ }) ) } keys %$original };
121             };
122              
123             coerce 'Cfn::Resource',
124             from 'HashRef', via {
125             my $type = delete $_->{Type};
126             die "Can't coerce HashRef into a Cfn::Resource if it doesn't have a Type key" if (not defined $type);
127             $type = "AWS::CloudFormation::CustomResource" if ($type =~ m/^Custom\:\:/);
128              
129             Cfn->load_resource_module($type);
130             # Properties is needed, although there are no properties
131             $_->{Properties} = {} if (not exists $_->{Properties});
132             return "Cfn::Resource::$type"->new(
133             %$_
134             );
135             };
136              
137             subtype 'Cfn::MetadataHash',
138             as 'HashRef[Cfn::Value]';
139              
140             coerce 'Cfn::MetadataHash',
141             from 'HashRef', via {
142             my $original = $_;
143             return { map { ($_ => $cfn_value_constraint->coerce($original->{ $_ }) ) } keys %$original };
144             };
145              
146             package Cfn::Value {
147 20     20   60146 use Moose;
  20         1039200  
  20         128  
148             has Value => (isa => 'Cfn::Value', is => 'rw', required => 1, coerce => 1);
149              
150 4     4 0 73 sub as_hashref { shift->Value->as_hashref(@_) }
151             }
152              
153             package Cfn::Value::Function {
154 20     20   122590 use Moose;
  20         51  
  20         87  
155             extends 'Cfn::Value';
156             has 'Function' => (isa => 'Str', is => 'rw', required => 1);
157             # inherits Value property as a Cfn::Value
158              
159             sub as_hashref {
160 11     11 0 26 my $self = shift;
161 11         278 my $key = $self->Function;
162 11         258 return { $key => $self->Value->as_hashref(@_) }
163             }
164             }
165              
166             package Cfn::Value::Function::Condition {
167 20     20   115555 use Moose;
  20         45  
  20         86  
168             extends 'Cfn::Value::Function';
169             #has '+Value' => (isa => 'Cfn::Value::Primitive', coerce => 1);
170              
171             sub Condition {
172 0     0 0 0 shift->Value->Value;
173             }
174             }
175              
176             package Cfn::Value::Function::Ref {
177 20     20   115504 use Moose;
  20         48  
  20         86  
178             extends 'Cfn::Value::Function';
179             #has '+Value' => (isa => 'Cfn::Value::Primitive', coerce => 1);
180              
181             sub LogicalId {
182 42     42 0 1036 shift->Value->Value;
183             }
184             }
185              
186             package Cfn::Value::Function::GetAtt {
187 20     20   115188 use Moose;
  20         46  
  20         87  
188             extends 'Cfn::Value::Function';
189             has '+Value' => (isa => 'Cfn::Value::ArrayOfPrimitives', coerce => 1);
190              
191             sub LogicalId {
192 8     8 0 112 my $self = shift;
193 8         202 $self->Value->Value->[0]->Value;
194             }
195              
196             sub Property {
197 2     2 0 5 my $self = shift;
198 2         40 $self->Value->Value->[1]->Value;
199             }
200             }
201              
202              
203             package Cfn::Value::Array {
204 20     20   117583 use Moose;
  20         46  
  20         93  
205             extends 'Cfn::Value';
206             has '+Value' => (
207             isa => 'ArrayRef[Cfn::Value]',
208             traits => ['Array'],
209             handles => {
210             'Count' => 'count',
211             }
212             );
213              
214             sub as_hashref {
215 12     12 0 25 my $self = shift;
216 12         31 my @args = @_;
217 12         23 return [ map { $_->as_hashref(@args) } @{ $self->Value } ]
  23         89  
  12         256  
218             };
219             }
220              
221             package Cfn::Value::Hash {
222 20     20   114999 use Moose;
  20         86  
  20         130  
223             extends 'Cfn::Value';
224             has '+Value' => (isa => 'HashRef[Cfn::Value]');
225             override as_hashref => sub {
226             my $self = shift;
227             my @args = @_;
228             return { map { $_ => $self->Value->{$_}->as_hashref(@args) } keys %{ $self->Value } };
229             };
230             }
231              
232             package Cfn::Value::Primitive {
233 20     20   115924 use Moose;
  20         45  
  20         86  
234             extends 'Cfn::Value';
235             has '+Value' => (isa => 'Int|Str');
236             override as_hashref => sub {
237             my $self = shift;
238             return $self->Value;
239             }
240             }
241              
242             package Cfn::Resource {
243 20     20   114194 use Moose;
  20         44  
  20         84  
244             # CCfnX::Dependencies is not production ready
245             with 'CCfnX::Dependencies';
246             has Type => (isa => 'Str', is => 'rw', required => 1, default => sub {
247             my $type = shift->meta->name;
248             $type =~ s/^Cfn\:\:Resource\:\://;
249             return $type;
250             });
251             has Properties => (isa => 'Cfn::Resource::Properties', is => 'rw', required => 1);
252             has DeletionPolicy => (isa => 'Cfn::Resource::DeletionPolicy', is => 'rw');
253             has DependsOn => (isa => 'ArrayRef[Str]|Str', is => 'rw');
254             has Condition => (isa => 'Str', is => 'rw');
255              
256             sub DependsOnList {
257 32     32 0 58 my $self = shift;
258 32 100       620 return () if (not defined $self->DependsOn);
259 10 100       188 return @{ $self->DependsOn } if (ref($self->DependsOn) eq 'ARRAY');
  9         167  
260 1         20 return $self->DependsOn;
261             }
262              
263             has Metadata => (isa => 'Cfn::Value::Hash', is => 'rw', coerce => 1);
264             #TODO: validate this http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatepolicy.html
265             has UpdatePolicy => (isa => 'HashRef', is => 'rw');
266              
267             sub as_hashref {
268 15     15 0 37 my $self = shift;
269 15         37 my @args = @_;
270             return {
271 16         396 (map { $_ => $self->$_->as_hashref(@args) }
272 30         724 grep { defined $self->$_ } qw/Properties Metadata/),
273 16         294 (map { $_ => $self->$_ }
274 15         46 grep { defined $self->$_ } qw/Type DeletionPolicy DependsOn UpdatePolicy Condition/),
  75         1562  
275             }
276             }
277             }
278              
279             package Cfn::Resource::Properties {
280 20     20   119193 use Moose;
  20         48  
  20         89  
281             sub as_hashref {
282 15     15 0 43 my $self = shift;
283 15         41 my @args = @_;
284              
285             #return {
286 15         35 my $ret = {};
287             #map { (defined $self->$_) ? ($_ => $self->$_->as_hashref(@args)) : () } $self->meta->get_all_attributes
288 15         61 foreach my $att ($self->meta->get_all_attributes) {
289 91         1285 my $el = $att->name;
290 91 100       2549 if (defined $self->$el) {
291 26         639 my @ret = $self->$el->as_hashref(@args);
292 26 50       78 if (@ret == 1) {
293 26         82 $ret->{ $el } = $ret[0];
294             } else {
295 0         0 die "A property returned an odd number of values";
296             }
297             }
298             }
299 15         58 return $ret;
300             #}
301             }
302              
303             sub resolve_references_to_logicalid_with {
304 59     59 0 332 my ($self, $logical_id, $object) = @_;
305 59         162 foreach my $att ($self->meta->get_attribute_list) {
306 180 100       11885 next if (not defined $self->$att);
307              
308 63 100 100     1234 if ($self->$att->isa('Cfn::Value::Function::Ref') and $self->$att->LogicalId eq $logical_id) {
    100 66        
    100          
    100          
    50          
    0          
309 8         128 my $func = $self->$att;
310             #$self->$att('TBD'); #$object->$objects_ref_prop
311             #warn "Resolved TBD $logical_id";
312 8         58 my @attrs = $object->meta->get_all_attributes;
313 8         533 my @ref = grep { $_->does('CCfnX::Meta::Attribute::Trait::RefValue') } @attrs;
  32         2000  
314 8 50       1406 if (not @ref) { die $object . " has no RefValue trait. Cannot resolve Ref" }
  0         0  
315             else {
316 8         24 my $property = $ref[0]->name;
317 8         149 my $value = $object->$property;
318 8         165 $self->$att($value);
319             }
320             } elsif ($self->$att->isa('Cfn::Value::Function::GetAtt') and $self->$att->LogicalId eq $logical_id) {
321 2         34 my $func = $self->$att;
322 2         14 my $property = $func->Property;
323 2         39 $self->$att($object->$property);
324 2         1379 warn "Resolved $logical_id $property";
325             } elsif ($self->$att->isa('Cfn::Value::Array')) {
326 12         584 map { resolve_references_to_logicalid_with($_, $logical_id, $object) } @{ $self->$att->Value };
  12         30  
  12         190  
327             } elsif ($self->$att->isa('Cfn::Value::Function')) {
328 2         121 resolve_references_to_logicalid_with($self->$att, $logical_id, $object);
329             } elsif ($self->$att->isa('Cfn::Value::Primitive')) {
330             # End case. Primitives do nothing
331             # This case is important to be here, as it filters out any Primitives for
332             # the next if
333             } elsif ($self->$att->isa('Cfn::Value')) {
334 0         0 resolve_references_to_logicalid_with($self->$att, $logical_id, $object);
335             } else {
336 0         0 die "Don't know how to resolve $att on " . $self->$att;
337             }
338             }
339             }
340             }
341              
342             package Cfn::Output {
343 20     20   121640 use Moose;
  20         50  
  20         92  
344             has Value => (isa => 'Cfn::Value', is => 'rw', required => 1, coerce => 1);
345             has Condition => (isa => 'Str', is => 'rw');
346             sub as_hashref {
347 6     6 0 12 my $self = shift;
348 6 100       121 return { Value => $self->Value->as_hashref,
349             (defined $self->Condition) ? (Condition => $self->Condition) : ()
350             }
351             }
352             }
353              
354             enum 'Cfn::Parameter::Type', [
355             'String',
356             'Number',
357             'List<Number>',
358             'CommaDelimitedList',
359             'AWS::EC2::AvailabilityZone::Name',
360             'List<AWS::EC2::AvailabilityZone::Name>',
361             'AWS::EC2::Instance::Id',
362             'List<AWS::EC2::Instance::Id>',
363             'AWS::EC2::Image::Id',
364             'List<AWS::EC2::Image::Id>',
365             'AWS::EC2::KeyPair::KeyName',
366             'AWS::EC2::SecurityGroup::GroupName',
367             'List<AWS::EC2::SecurityGroup::GroupName>',
368             'AWS::EC2::SecurityGroup::Id',
369             'List<AWS::EC2::SecurityGroup::Id>',
370             'AWS::EC2::Subnet::Id',
371             'List<AWS::EC2::Subnet::Id>',
372             'AWS::EC2::Volume::Id',
373             'List<AWS::EC2::Volume::Id>',
374             'AWS::EC2::VPC::Id',
375             'List<AWS::EC2::VPC::Id>',
376             'AWS::Route53::HostedZone::Id',
377             'List<AWS::Route53::HostedZone::Id>',
378             ];
379              
380             package Cfn::Parameter {
381 20     20   116086 use Moose;
  20         42  
  20         82  
382             has Type => (isa => 'Cfn::Parameter::Type', is => 'ro', required => 1);
383             has Default => (isa => 'Str', is => 'rw');
384             has NoEcho => (isa => 'Str', is => 'rw');
385             has AllowedValues => ( isa => 'ArrayRef[Str]', is => 'rw');
386             has AllowedPattern => ( isa => 'Str', is => 'rw');
387             has MaxLength => ( isa => 'Str', is => 'rw');
388             has MinLength => ( isa => 'Str', is => 'rw');
389             has MaxValue => ( isa => 'Str', is => 'rw');
390             has MinValue => ( isa => 'Str', is => 'rw');
391             has Description => ( isa => 'Str', is => 'rw');
392             has ConstraintDescription => ( isa => 'Str', is => 'rw');
393              
394             sub as_hashref {
395 0     0 0 0 my $self = shift;
396             return {
397 0 0       0 map { (defined $self->$_) ? ($_ => $self->$_) : () }
  0         0  
398             qw/Type Default NoEcho AllowedValues AllowedPattern MaxLength
399             MinLength MaxValue MinValue Description ConstraintDescription/,
400             }
401             }
402             }
403              
404             package Cfn::Mapping {
405 20     20   116379 use Moose;
  20         49  
  20         89  
406             has Map => (isa => 'HashRef', is => 'ro');
407              
408             sub as_hashref {
409 0     0 0 0 my $self = shift;
410 0         0 return $self->Map;
411             }
412             }
413              
414             package Cfn {
415 20     20   115658 use Moose;
  20         46  
  20         87  
416 20     20   114145 use Moose::Util;
  20         45  
  20         167  
417             has AWSTemplateFormatVersion => (isa => 'Str', is => 'ro', default => '2010-09-09' );
418             has Description => (isa => 'Str', is => 'rw', required => 1, default => '' );
419             has Transform => (isa => 'Str', is => 'rw');
420             has Parameters => (
421             is => 'rw',
422             isa => 'Cfn::ParameterHash',
423             coerce => 1,
424             default => sub { {} },
425             traits => [ 'Hash' ],
426             handles => {
427             ParameterCount => 'count',
428             },
429             );
430             has Mappings => (
431             is => 'rw',
432             isa => 'Cfn::MappingHash',
433             coerce => 1,
434             traits => [ 'Hash' ],
435             handles => {
436             Mapping => 'get',
437             MappingCount => 'count',
438             },
439             default => sub { {} }
440             );
441             has Conditions => (
442             is => 'rw',
443             isa => 'Cfn::ConditionHash',
444             traits => [ 'Hash' ],
445             coerce => 1,
446             handles => {
447             Condition => 'get',
448             ConditionList => 'keys',
449             ConditionCount => 'count',
450             },
451             default => sub { {} }
452             );
453             has Resources => (
454             is => 'rw',
455             isa => 'Cfn::ResourceHash',
456             coerce => 1,
457             traits => [ 'Hash' ],
458             handles => {
459             Resource => 'get',
460             ResourceList => 'keys',
461             ResourceCount => 'count',
462             },
463             default => sub { {} }
464             );
465             has Outputs => (
466             is => 'rw',
467             isa => 'Cfn::OutputHash',
468             coerce => 1,
469             traits => [ 'Hash' ],
470             handles => {
471             Output => 'get',
472             OutputCount => 'count',
473             },
474             default => sub { {} },
475             );
476             has Metadata => (
477             is => 'rw',
478             isa => 'Cfn::MetadataHash',
479             coerce => 1,
480             traits => [ 'Hash' ],
481             handles => {
482             MetadataItem => 'get',
483             MetadataList => 'keys',
484             MetadataCount => 'count',
485             },
486             default => sub { {} },
487             );
488 20     20   7362 use Module::Runtime qw//;
  20         46  
  20         20412  
489             sub load_resource_module {
490 79     79 0 213 my (undef, $type) = @_;
491 79         262 my $cfn_resource_class = "Cfn::Resource::$type";
492 79         342 my $retval = Module::Runtime::require_module($cfn_resource_class);
493 79 50       2075 die "Couldn't load $cfn_resource_class" if (not $retval);
494 79         183 return $cfn_resource_class;
495             }
496              
497             #method addParameter (Str $name, Cfn::Parameter|Str $type, %rest) {
498             sub addParameter {
499 0     0 0 0 my ($self, $name, $type, %rest) = @_;
500 0 0       0 die "A parameter named $name already exists" if ($self->Parameters->{ $name });
501 0 0       0 if (ref $type) {
502 0         0 return $self->Parameters->{ $name } = $type;
503             } else {
504 0         0 return $self->Parameters->{ $name } = Cfn::Parameter->new(Type => $type, %rest);
505             }
506             }
507              
508             #method addMapping (Str $name, $mapping) {
509             sub addMapping {
510 0     0 0 0 my ($self, $name, $mapping) = @_;
511 0 0       0 die "A mapping named $name already exists" if ($self->Mappings->{ $name });
512 0 0       0 if (ref $mapping eq 'HASH') {
513 0         0 return $self->Mappings->{ $name } = Cfn::Mapping->new(Map => $mapping);
514             } else {
515 0         0 return $self->Mappings->{ $name } = $mapping;
516             }
517             }
518              
519             #method addOutput (Str $name, $output, @rest) {
520             sub addOutput {
521 8     8 0 31 my ($self, $name, $output, @rest) = @_;
522 8 50       150 die "An output named $name already exists" if ($self->Outputs->{ $name });
523 8         61 return $self->Outputs->{ $name } = Cfn::Output->new( Value => $output, @rest );
524             }
525              
526             sub addCondition {
527 1     1 0 4 my ($self, $name, $value) = @_;
528 1 50       22 die "A condition named $name already exists" if ($self->Conditions->{ $name });
529 1         5 return $self->Conditions->{ $name } = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($value);
530             }
531              
532             sub addResource {
533 79     79 0 124526 my ($self, $name, $type, @rest) = @_;
534 79 50       1788 die "A resource named $name already exists" if ($self->Resources->{ $name });
535 79 100       293 if (not ref $type){
536 6         27 return $self->Resources->{ $name } = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Resource')->coerce({
537             Type => $type,
538             Properties => { @rest }
539             });
540             } else {
541 73         1459 return $self->Resources->{ $name } = $type;
542             }
543             }
544              
545             #method addMetadata (Str $name, %args) {
546             sub addMetadata {
547 1     1 0 26 my ($self, $name, %args) = @_;
548 1 50       31 die "A resource named $name must already exist" if (not defined $self->Resources->{ $name });
549 1         20 $self->Resources->{ $name }->Metadata({ %args });
550             }
551             #method addResourceMetadata (Str $name, %args) {
552             sub addResourceMetadata {
553 0     0 0 0 my ($self, $name, %args) = @_;
554 0 0       0 die "A resource named $name must already exist" if (not defined $self->Resources->{ $name });
555 0         0 $self->Resources->{ $name }->Metadata({ %args });
556             }
557             #method addDependsOn (Str $name, @args) {
558             sub addDependsOn {
559 0     0 0 0 my ($self, $name, @args) = @_;
560 0 0       0 die "A resource named $name must already exist" if (not defined $self->Resources->{ $name });
561 0         0 $self->Resources->{ $name }->DependsOn( [ @args ] );
562             }
563             #method addDeletionPolicy (Str $name, Str $policy) {
564             sub addDeletionPolicy {
565 0     0 0 0 my ($self, $name, $policy) = @_;
566 0 0       0 die "A resource named $name must already exist" if (not defined $self->Resources->{ $name });
567 0         0 $self->Resources->{ $name }->DeletionPolicy( $policy );
568             }
569             #method addUpdatePolicy (Str $name, Str $policy) {
570             sub addUpdatePolicy {
571 0     0 0 0 my ($self, $name, $policy) = @_;
572 0 0       0 die "A resource named $name must already exist" if (not defined $self->Resources->{ $name });
573 0         0 $self->Resources->{ $name }->UpdatePolicy( $policy );
574             }
575              
576             sub from_hashref {
577 0     0 0 0 my ($class, $hashref) = @_;
578 0         0 return $class->new(%$hashref);
579             }
580              
581             sub as_hashref {
582 10     10 0 68 my $self = shift;
583             return {
584             AWSTemplateFormatVersion => $self->AWSTemplateFormatVersion,
585             Description => $self->Description,
586             (defined $self->Transform) ? (Transform => $self->Transform) : (),
587 15         447 Resources => { map { ($_ => $self->Resource($_)->as_hashref($self)) } $self->ResourceList },
588 10         264 (keys %{$self->Mappings} > 0) ? ( Mappings => { map { ($_ => $self->Mappings->{ $_ }->as_hashref) } keys %{ $self->Mappings } } ) : (),
  0         0  
  0         0  
589 0         0 Parameters => { map { ($_ => $self->Parameters->{ $_ }->as_hashref) } keys %{ $self->Parameters } },
  10         254  
590 6         112 Outputs => { map { ($_ => $self->Outputs->{ $_ }->as_hashref($self)) } keys %{ $self->Outputs } },
  10         226  
591 1         27 Conditions => { map { ($_ => $self->Condition($_)->as_hashref($self)) } $self->ConditionList },
592 10 100       295 Metadata => { map { ($_ => $self->Metadata->{ $_ }->as_hashref($self)) } $self->MetadataList },
  4 50       73  
593             }
594             }
595              
596             has json => (is => 'ro', lazy => 1, default => sub {
597             require JSON;
598             return JSON->new->pretty->canonical
599             });
600              
601             sub as_json {
602 0     0 0   my $self = shift;
603 0           my $href = $self->as_hashref;
604 0           return $self->json->encode($href);
605             }
606              
607             sub from_json {
608 0     0 0   my ($class, $json) = @_;
609              
610 0           require JSON;
611 0           return $class->from_hashref(JSON::from_json($json));
612             }
613              
614             }
615              
616             1;