Mercurial > pub > Impl
comparison lib/IMPL/Config.pm @ 407:c6e90e02dd17 ref20150831
renamed Lib->lib
author | cin |
---|---|
date | Fri, 04 Sep 2015 19:40:23 +0300 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
406:f23fcb19d3c1 | 407:c6e90e02dd17 |
---|---|
1 package IMPL::Config; | |
2 use strict; | |
3 use warnings; | |
4 use mro; | |
5 | |
6 use Carp qw(carp); | |
7 | |
8 use IMPL::lang qw(is); | |
9 use IMPL::Exception; | |
10 use IMPL::Const qw(:access); | |
11 use IMPL::declare { | |
12 require => { | |
13 PropertyInfo => 'IMPL::Class::PropertyInfo', | |
14 XmlFormatter => 'IMPL::Serialization::XmlFormatter', | |
15 Serializer => '-IMPL::Serializer', | |
16 Activator => '-IMPL::Config::Activator', | |
17 | |
18 Exception => 'IMPL::Exception', | |
19 IOException => '-IMPL::IOException' | |
20 }, | |
21 base => [ | |
22 'IMPL::Object::Accessor' => undef, | |
23 'IMPL::Object::Serializable' => undef, | |
24 'IMPL::Object::Autofill' => '@_' | |
25 ] | |
26 }; | |
27 | |
28 use File::Spec(); | |
29 | |
30 | |
31 our $ConfigBase ||= ''; | |
32 our $AppBase; | |
33 | |
34 sub LoadXMLFile { | |
35 my ($self,$file) = @_; | |
36 | |
37 my $class = ref $self || $self; | |
38 | |
39 my $serializer = Serializer->new( | |
40 formatter => XmlFormatter->new( | |
41 IdentOutput => 1, | |
42 SkipWhitespace => 1 | |
43 ) | |
44 ); | |
45 | |
46 open my $hFile,'<',$file or die IOException->new("Failed to open file",$file,$!); | |
47 | |
48 my $obj; | |
49 eval { | |
50 $obj = $serializer->Deserialize($hFile); | |
51 }; | |
52 | |
53 if ($@) { | |
54 my $e=$@; | |
55 die Exception->new("Can't load the configuration file",$file,$e); | |
56 } | |
57 return $obj; | |
58 } | |
59 | |
60 sub SaveXMLFile { | |
61 my ($this,$file) = @_; | |
62 | |
63 my $serializer = Serializer->new( | |
64 formatter => XmlFormatter->new( | |
65 IdentOutput => 1, | |
66 SkipWhitespace => 1 | |
67 ) | |
68 ); | |
69 | |
70 open my $hFile,'>',$file or die IOException->new("Failed to open file",$file,$!); | |
71 | |
72 $serializer->Serialize($hFile, $this); | |
73 } | |
74 | |
75 sub xml { | |
76 my $this = shift; | |
77 my $serializer = Serializer->new( | |
78 formatter => XmlFormatter->new( | |
79 IdentOutput => 1, | |
80 SkipWhitespace => 1 | |
81 ) | |
82 ); | |
83 my $str = ''; | |
84 open my $hFile,'>',\$str or die IOException->new("Failed to open stream",$!); | |
85 | |
86 $serializer->Serialize($hFile, $this); | |
87 | |
88 undef $hFile; | |
89 | |
90 return $str; | |
91 } | |
92 | |
93 sub save { | |
94 my ($this,$ctx) = @_; | |
95 | |
96 my $val; | |
97 | |
98 $val = $this->rawGet($_) and $ctx->AddVar($_ => $val) foreach map $_->Name, $this->get_meta( | |
99 PropertyInfo, | |
100 sub { | |
101 $_->access == ACCESS_PUBLIC and | |
102 $_->setter; | |
103 }, | |
104 1); | |
105 } | |
106 | |
107 sub spawn { | |
108 my ($this,$file) = @_; | |
109 unless ($file) { | |
110 ($file = ref $this || $this) =~ s/:+/./g; | |
111 $file .= ".xml"; | |
112 } | |
113 return $this->LoadXMLFile( File::Spec->catfile($ConfigBase,$file) ); | |
114 } | |
115 | |
116 sub get { | |
117 my $this = shift; | |
118 | |
119 if (@_ == 1) { | |
120 my $obj = $this->SUPER::get(@_); | |
121 return is($obj,Activator) ? $obj->activate : $obj; | |
122 } else { | |
123 my @objs = $this->SUPER::get(@_); | |
124 return map is($_,Activator) ? $_->activate : $_, @objs ; | |
125 } | |
126 } | |
127 | |
128 sub rawGet { | |
129 my $this = shift; | |
130 return $this->SUPER::get(@_); | |
131 } | |
132 | |
133 sub Exists { | |
134 $_[0]->SUPER::get($_[1]) ? 1 : 0; | |
135 } | |
136 | |
137 sub AppBase { | |
138 carp "obsolete"; | |
139 shift; | |
140 File::Spec->catdir($AppBase,@_); | |
141 } | |
142 | |
143 sub AppDir { | |
144 shift; | |
145 File::Spec->catdir($AppBase,@_); | |
146 } | |
147 | |
148 sub AppFile { | |
149 shift; | |
150 File::Spec->catfile($AppBase,@_); | |
151 } | |
152 | |
153 sub ConfigBase { | |
154 carp "obsolete"; | |
155 shift; | |
156 File::Spec->catdir($ConfigBase,@_); | |
157 } | |
158 | |
159 sub ConfigDir { | |
160 shift; | |
161 File::Spec->catdir($ConfigBase,@_); | |
162 } | |
163 | |
164 sub ConfigFile { | |
165 shift; | |
166 File::Spec->catfile($ConfigBase,@_); | |
167 } | |
168 | |
169 1; | |
170 __END__ | |
171 | |
172 =pod | |
173 | |
174 =head1 NAME | |
175 | |
176 C<IMPL::Config> - базовый класс для настраиваемого приложения. | |
177 | |
178 =head1 SYNOPSIS | |
179 | |
180 =begin code | |
181 | |
182 # define application | |
183 | |
184 package MyApp; | |
185 use parent qw(IMPL::Config); | |
186 | |
187 use IMPL::Class::Property; | |
188 use IMPL::Config::Class; | |
189 | |
190 BEGIN { | |
191 public property SimpleString => prop_all; | |
192 public property DataSource => prop_all; | |
193 } | |
194 | |
195 sub CTOR { | |
196 my $this = shift; | |
197 | |
198 $this->DataSource( | |
199 new IMPL::Config::Activator( | |
200 factory => 'MyDataSource', | |
201 parameters=>{ | |
202 host => 'localhost', | |
203 user => 'dbuser' | |
204 } | |
205 ) | |
206 ) unless $this->Exists('DataSource'); | |
207 } | |
208 | |
209 # using application object | |
210 | |
211 my $app = spawn MyApp('default.xml'); | |
212 | |
213 $app->Run(); | |
214 | |
215 =end code | |
216 | |
217 Ниже приведен пример файла C<default.xml> содержащего настройки приложения | |
218 | |
219 =begin code xml | |
220 | |
221 <app type='MyApp'> | |
222 <SimpleString>The application</SimpleString> | |
223 <DataSource type='IMPL::Config::Activator'> | |
224 <factory>MyDataSourceClass</factory> | |
225 <parameters type='HASH'> | |
226 <host>localhost</host> | |
227 <user>dbuser</user> | |
228 </parameters> | |
229 </DataSource> | |
230 </app> | |
231 | |
232 =end code xml | |
233 | |
234 =head1 DESCRIPTION | |
235 | |
236 C<[Serializable]> | |
237 | |
238 C<[Autofill]> | |
239 | |
240 C<use parent IMPL::Object::Accessor> | |
241 | |
242 Базовый класс для приложений. Использует подход, что приложение | |
243 является объектом, состояние которого предтавляет собой конфигурацию, | |
244 а методы - логику. | |
245 | |
246 Данный класс реализует функционал десериализации (и сериализации) экземпляра | |
247 приложения из XML документа. Для этого используется механизм C<IMPL::Serialization>. | |
248 При этом используются опции C<IMPL::Serialization::XmlFormatter> C<IdentOutput> и | |
249 C<SkipWhitespace> для записи документа в легко читаемом виде. | |
250 | |
251 Поскольку в результате восстановления приложения восстанавливаются все элементы | |
252 из файла конфигурации, то это может потребовать значительных ресурсов для | |
253 создания частей, которые могут никогда не понадобиться. Например, не требуется инициализация | |
254 источника данных для передачи пользователю статических данных, сохраненных на диске. | |
255 | |
256 Для решения этой проблемы используются специальные объекты C<IMPL::Config::Activator>. | |
257 | |
258 Если у приложения описано свойство, в котором хранится C<IMPL::Config::Activator>, то | |
259 при первом обращении к такому свойству, будет создан объект вызовом метода | |
260 C<< IMPL::Config::Activator->activate() >> и возвращен как значение этого свойства. | |
261 Таким образом реализуется прозрачная отложенная активация объектов, что позволяет | |
262 экономить ресурсы. | |
263 | |
264 =head1 MEMBERS | |
265 | |
266 =over | |
267 | |
268 =item C<[static] LoadXMLFile($fileName) > | |
269 | |
270 Создает из XML файла C<$fileName> экземпляр приложения | |
271 | |
272 =item C<SaveXMLFile($fileName)> | |
273 | |
274 Сохраняет приложение в файл C<$fileName> | |
275 | |
276 =item C<[get] xml > | |
277 | |
278 Сохраняет конфигурацию приложения в XML строку. | |
279 | |
280 =item C<[static,operator] spawn($file)> | |
281 | |
282 Синоним для C<LoadXMLFile>, предполагается использование как оператора. | |
283 | |
284 =item C<rawGet($propname,...)> | |
285 | |
286 Метод для получения значений свойств приложения. Данный метод позволяет избежать | |
287 использование активации объектов через C<IMPL::Config::Activator>. | |
288 | |
289 =back | |
290 | |
291 =cut |