Mercurial > pub > Impl
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 |