comparison lib/IMPL/Config/YAMLConfig.pm @ 422:b0481c071bea ref20150831

IMPL::Config::Container tests, YAMLConfiguration now works and tested
author cin
date Sun, 20 Aug 2017 00:20:41 +0300
parents 7798345304bc
children
comparison
equal deleted inserted replaced
421:7798345304bc 422:b0481c071bea
1
2 =head1 NAME
3
4 C<IMPL::Condig::YAMLConfig> - YAML configuration parser for the container
5
6 =head1 SYNOPSIS
7
8 =begin code
9
10 use IMPL::require {
11 YAMLConfig => 'IMPL::Config::YAMLConfig',
12 Container => 'IMPL::Config::Container'
13 };
14
15 my $config = YAMLConfig->new('config.yaml');
16 $config->Load('additional.yaml');
17 my $container = Container->new($parent);
18 $config->ConfigureContainer($container);
19
20 =end code
21
22 =head1 DESCRIPTION
23
24 This module load YAML configuration and applies it to the container.
25
26 =head1 MEMBERS
27
28 =cut
29
1 package IMPL::Config::YAMLConfig; 30 package IMPL::Config::YAMLConfig;
2 use strict; 31 use strict;
3 32
33 use IMPL::Debug;
4 use IMPL::lang qw(:base); 34 use IMPL::lang qw(:base);
5 use IMPL::Exception(); 35 use IMPL::Exception();
6 use YAML::XS(); 36 use YAML::XS();
37 use Sub::Util;
38 use File::Spec();
39 use URI::file();
7 40
8 use IMPL::declare { 41 use IMPL::declare {
9 require => { 42 require => {
10 ReferenceDescriptor => 'IMPL::Config::ReferenceDescriptor', 43 ReferenceDescriptor => 'IMPL::Config::ReferenceDescriptor',
11 ServiceDescriptor => 'IMPL::Config::ServiceDescriptor', 44 ServiceDescriptor => 'IMPL::Config::ServiceDescriptor',
12 ValueDescriptor => 'IMPL::Config::ValueDescriptor' 45 ValueDescriptor => 'IMPL::Config::ValueDescriptor',
46 Descriptor => '-IMPL::Config::Descriptor'
13 }, 47 },
14 base => [ 48 base => [
15 'IMPL::Object' => undef 49 'IMPL::Object' => undef
16 ], 50 ],
17 props => [ 51 props => [
18 container => 'ro' 52 _services => 'ro',
53 _stack => 'ro',
54 _visited => 'ro',
55 _current => 'ro'
19 ] 56 ]
20 }; 57 };
21 58
22 sub CTOR { 59 sub CTOR {
60 my ( $this, %args ) = @_;
61 $this->_services( {} );
62 $this->_stack( [] );
63 $this->_visited( {} );
64 $this->Load( $args{load} ) if ( $args{load} );
65 }
66
67 sub Load {
68 my ( $this, $file ) = @_;
69
70 dbg_log("Load $file");
71 my $prev = $this->_current;
72 push @{ $this->_stack }, "$file";
73
74 dbg_log( "base: ", $prev || '' );
75
76 $this->_current( $prev ? URI::file->new($file)->abs($prev) : URI::file->new_abs($file))
77 unless ref $file;
78
79 dbg_log( "translated: ", $this->_current);
80
81 my $config;
82
83 if ( isscalar($file) ) {
84 $this->LoadConfiguration( YAML::XS::Load( ${$file} ) );
85 }
86 else {
87 if ( not ref $file and defined $file ) {
88 if ( not $this->_visited->{$this->_current} ) {
89 $this->_visited->{$this->_current} = 1;
90
91 dbg_log("Loading YAML from file ", $this->_current->file);
92
93 $config = YAML::XS::LoadFile($this->_current->file);
94 } else {
95 dbg_warn("recursive includes: \n\t", join("\n\t", reverse @{$this->_stack}));
96 }
97 }
98 else {
99 $config = YAML::XS::LoadFile($file);
100 }
101 }
102
103 $this->LoadConfiguration($config) if defined $config;
104
105 $this->_current($prev);
106 pop @{ $this->_stack };
107
108 return 1;
109 }
110
111 sub LoadConfiguration {
112 my ( $this, $config ) = @_;
113
114 die IMPL::InvalidArgumentException->new('config')
115 unless ishash($config);
116
117 $this->Include( $config->{include} );
118
119 $this->_services( $this->ParseServices( $config->{services} ) );
120 }
121
122 sub Include {
123 my ( $this, $inc ) = @_;
124 if ( isarray($inc) ) {
125 $this->Include($_) foreach @$inc;
126 }
127 elsif ( defined $inc and not ref $inc ) {
128 dbg_log("include: $inc");
129 $this->Load( $inc );
130 }
131 }
132
133 sub ConfigureContainer {
23 my ( $this, $container ) = @_; 134 my ( $this, $container ) = @_;
24 die IMPL::InvalidArgumentException('container') 135
136 die IMPL::InvalidArgumentException->new($container)
25 unless $container; 137 unless $container;
26 $this->container($container); 138
27 } 139 foreach my $item ( @{ $this->_services } ) {
28
29 sub LoadConfiguration {
30 my ( $this, $file ) = @_;
31
32 $this->Configure(
33 isscalar($file)
34 ? YAML::XS::Load( ${$file} )
35 : YAML::XS::LoadFile($file)
36 );
37 }
38
39 sub Configure {
40 my ( $this, $config ) = @_;
41
42 die IMPL::InvalidArgumentException('config')
43 unless ishash($config);
44
45 my $container = $this->container;
46 foreach my $item ( @{ $this->ParseServices( $config->{services} ) } ) {
47 $container->Register( $item->{role}, $item->{descriptor} ); 140 $container->Register( $item->{role}, $item->{descriptor} );
48 } 141 }
49 142
50 return $container; 143 return $container;
51 } 144 }
66 } 159 }
67 160
68 sub ParseDescriptor { 161 sub ParseDescriptor {
69 my ( $this, $data ) = @_; 162 my ( $this, $data ) = @_;
70 163
71 my %opts = ( onwer => $this->container() ); 164 my %opts;
72 165 if ( ref $data ) {
73 if ( my $type = $data->{'$type'} ) { 166 if ( my $type = $data->{'$type'} ) {
74 $opts{services} = $this->ParseServices( $data->{services} ); 167 $opts{services} = $this->ParseServices( $data->{services} );
75 $opts{type} = $type; 168 $opts{type} = $type;
76 $opts{args} = $this->ParseDescriptor( $data->{params} ) 169 $opts{args} = $this->ParseParams( $data->{params} )
77 if $data->{params}; 170 if $data->{params};
78 $opts{norequire} = $data->{norequire}; 171 $opts{norequire} = $data->{norequire};
79 $opts{activation} = $data->{activation}; 172 $opts{activation} = $data->{activation};
80 173
81 return ServiceDescriptor->new(%opts); 174 return ServiceDescriptor->new(%opts);
82 } 175 }
83 elsif ( my $dep = $data->{'$ref'} ) { 176 elsif ( my $dep = $data->{'$ref'} ) {
84 $opts{services} = $this->ParseServices( $data->{services} ); 177 $opts{services} = $this->ParseServices( $data->{services} );
85 $opts{lazy} = $data->{lazy}; 178 $opts{lazy} = $data->{lazy};
86 $opts{optional} = $data->{optional}; 179 $opts{optional} = $data->{optional};
87 $opts{default} = $this->ParseDescriptor( $data->{default} ) 180 $opts{default} = $this->ParseDescriptor( $data->{default} )
88 if exists $data->{default}; 181 if exists $data->{default};
89 182
90 return ReferenceDesriptor->new( $dep, %opts ); 183 return ReferenceDescriptor->new( $dep, %opts );
91 } 184 }
92 elsif ( my $value = $data->{'$value'} ) { 185 elsif ( my $value = $data->{'$value'} ) {
93 my ( $parsed, $raw ) = $this->ParseValue($value); 186 my ( $parsed, $raw ) = $this->ParseValue($value);
94 $opts{services} = $this->ParseServices( $data->{services} ); 187 $opts{services} = $this->ParseServices( $data->{services} );
95 $opts{raw} = $raw; 188 $opts{raw} = $raw;
96 return ValueDescriptor->new( $parsed, %opts ); 189 return ValueDescriptor->new( $parsed, %opts );
97 } 190 }
98 else { 191
99 my ( $parsed, $raw ) = $this->ParseValue($value); 192 }
100 $opts{raw} = $raw; 193
101 return ValueDescriptor->new( $parsed, %opts ); 194 my ( $parsed, $raw ) = $this->ParseValue($data);
102 } 195 $opts{raw} = $raw;
103 } 196 return is( $parsed, Descriptor )
104 197 ? $parsed
198 : ValueDescriptor->new( $parsed, %opts );
199 }
200
201 sub IsDescriptorSpec {
202 my ( $this, $spec ) = @_;
203 return ( ishash($spec) and grep exists $spec->{$_}, qw($type $ref $value) );
204 }
205
206 sub ParseParams {
207 my ( $this, $params ) = @_;
208
209 if ( isarray($params) ) {
210 return [ map $this->ParseDescriptor($_), @$params ];
211 }
212 elsif ( ishash($params) and not $this->IsDescriptorSpec($params) ) {
213 return {
214 map { $_, $this->ParseDescriptor( $params->{$_} ) }
215 keys %$params
216 };
217 }
218 return $this->ParseDescriptor($params);
219 }
220
221 # parses value and returns a reference to the parsed value, i.e. descriptors
222 # are recognized and instantinated.
223 # returns ($parsed, $raw)
224 # $parsed - the parsed value
225 # $raw - the parsed value doesn't contain descriptors
105 sub ParseValue { 226 sub ParseValue {
106 my ( $this, $value ) = @_; 227 my ( $this, $value ) = @_;
107 228
108 my $raw = 1; 229 my $raw = 1;
109 230
110 if ( ishash($value) ) { 231 if ( ishash($value) ) {
111 return ( $this->ParseDescriptor($value), 0 ) 232 return ( $this->ParseDescriptor($value), 0 )
112 if grep exists $value->{$_}, qw($type $ref $value); 233 if $this->IsDescriptorSpec($value);
113 234
114 my %res; 235 my %res;
115 while ( my ( $k, $v ) = each %$value ) { 236 while ( my ( $k, $v ) = each %$value ) {
116 my ( $parsed, $flag ) = $this->ParseValue($v); 237 my ( $parsed, $flag ) = $this->ParseValue($v);
117 $res{$k} = $parsed; 238 $res{$k} = $parsed;
123 return ( 244 return (
124 [ 245 [
125 map { 246 map {
126 my ( $parsed, $flag ) = $this->ParseValue($_); 247 my ( $parsed, $flag ) = $this->ParseValue($_);
127 $raw &&= $flag; 248 $raw &&= $flag;
128 return $parsed; 249 $parsed;
129 } @$value 250 } @$value
130 ], 251 ],
131 $raw 252 $raw
132 ); 253 );
133 } 254 }
134 else { 255 else {
135 return ($value, 1); 256 return ( $value, 1 );
136 } 257 }
137 } 258 }
138 259
139 1; 260 1;
140
141 __END__
142
143 =pod
144
145 =head1 NAME
146
147 =head1 SYNOPSIS
148
149 =
150
151 =cut