Mercurial > pub > Impl
view Lib/IMPL/Config.pm @ 393:69a1f1508696
minor security refactoring
author | cin |
---|---|
date | Fri, 14 Feb 2014 16:41:12 +0400 |
parents | 4ddb27ff4a0b |
children | 212cc86e470b |
line wrap: on
line source
package IMPL::Config; use strict; use warnings; use IMPL::Const qw(:access); use IMPL::declare { base => [ 'IMPL::Object::Accessor' => undef, 'IMPL::Object::Serializable' => undef, 'IMPL::Object::Autofill' => '@_' ] }; use File::Spec(); use IMPL::Class::Member; use IMPL::Class::PropertyInfo; use IMPL::Exception; use IMPL::Serialization; use IMPL::Serialization::XmlFormatter; our $ConfigBase ||= ''; our $AppBase; sub LoadXMLFile { my ($self,$file) = @_; my $class = ref $self || $self; my $serializer = new IMPL::Serializer( formatter => new IMPL::Serialization::XmlFormatter( IdentOutput => 1, SkipWhitespace => 1 ) ); open my $hFile,'<',$file or die new IMPL::Exception("Failed to open file",$file,$!); my $obj; eval { $obj = $serializer->Deserialize($hFile); }; if ($@) { my $e=$@; die new IMPL::Exception("Can't load the configuration file",$file,$e); } return $obj; } sub SaveXMLFile { my ($this,$file) = @_; my $serializer = new IMPL::Serializer( formatter => new IMPL::Serialization::XmlFormatter( IdentOutput => 1, SkipWhitespace => 1 ) ); open my $hFile,'>',$file or die new IMPL::Exception("Failed to open file",$file,$!); $serializer->Serialize($hFile, $this); } sub xml { my $this = shift; my $serializer = new IMPL::Serializer( formatter => new IMPL::Serialization::XmlFormatter( IdentOutput => 1, SkipWhitespace => 1 ) ); my $str = ''; open my $hFile,'>',\$str or die new IMPL::Exception("Failed to open stream",$!); $serializer->Serialize($hFile, $this); undef $hFile; return $str; } sub save { my ($this,$ctx) = @_; my $val; $val = $this->rawGet($_) and $ctx->AddVar($_ => $val) foreach map $_->Name, $this->get_meta( 'IMPL::Class::PropertyInfo', sub { $_->access == ACCESS_PUBLIC and $_->setter; }, 1); } sub spawn { my ($this,$file) = @_; unless ($file) { ($file = ref $this || $this) =~ s/:+/./g; $file .= ".xml"; } return $this->LoadXMLFile( File::Spec->catfile($ConfigBase,$file) ); } sub get { my $this = shift; if (@_ == 1) { my $obj = $this->SUPER::get(@_); return UNIVERSAL::isa($obj,'IMPL::Config::Activator') ? $obj->activate : $obj; } else { my @objs = $this->SUPER::get(@_); return map UNIVERSAL::isa($_,'IMPL::Config::Activator') ? $_->activate : $_, @objs ; } } sub rawGet { my $this = shift; return $this->SUPER::get(@_); } sub Exists { $_[0]->SUPER::get($_[1]) ? 1 : 0; } sub AppBase { shift; File::Spec->catdir($AppBase,@_); } sub ConfigBase { shift; File::Spec->catdir($ConfigBase,@_); } 1; __END__ =pod =head1 NAME C<IMPL::Config> - базовый класс для настраиваемого приложения. =head1 SYNOPSIS =begin code # define application package MyApp; use parent qw(IMPL::Config); use IMPL::Class::Property; use IMPL::Config::Class; BEGIN { public property SimpleString => prop_all; public property DataSource => prop_all; } sub CTOR { my $this = shift; $this->DataSource( new IMPL::Config::Activator( factory => 'MyDataSource', parameters=>{ host => 'localhost', user => 'dbuser' } ) ) unless $this->Exists('DataSource'); } # using application object my $app = spawn MyApp('default.xml'); $app->Run(); =end code Ниже приведен пример файла C<default.xml> содержащего настройки приложения =begin code xml <app type='MyApp'> <SimpleString>The application</SimpleString> <DataSource type='IMPL::Config::Activator'> <factory>MyDataSourceClass</factory> <parameters type='HASH'> <host>localhost</host> <user>dbuser</user> </parameters> </DataSource> </app> =end code xml =head1 DESCRIPTION C<[Serializable]> C<[Autofill]> C<use parent IMPL::Object::Accessor> Базовый класс для приложений. Использует подход, что приложение является объектом, состояние которого предтавляет собой конфигурацию, а методы - логику. Данный класс реализует функционал десериализации (и сериализации) экземпляра приложения из XML документа. Для этого используется механизм C<IMPL::Serialization>. При этом используются опции C<IMPL::Serialization::XmlFormatter> C<IdentOutput> и C<SkipWhitespace> для записи документа в легко читаемом виде. Поскольку в результате восстановления приложения восстанавливаются все элементы из файла конфигурации, то это может потребовать значительных ресурсов для создания частей, которые могут никогда не понадобиться. Например, не требуется инициализация источника данных для передачи пользователю статических данных, сохраненных на диске. Для решения этой проблемы используются специальные объекты C<IMPL::Config::Activator>. Если у приложения описано свойство, в котором хранится C<IMPL::Config::Activator>, то при первом обращении к такому свойству, будет создан объект вызовом метода C<< IMPL::Config::Activator->activate() >> и возвращен как значение этого свойства. Таким образом реализуется прозрачная отложенная активация объектов, что позволяет экономить ресурсы. =head1 MEMBERS =over =item C<[static] LoadXMLFile($fileName) > Создает из XML файла C<$fileName> экземпляр приложения =item C<SaveXMLFile($fileName)> Сохраняет приложение в файл C<$fileName> =item C<[get] xml > Сохраняет конфигурацию приложения в XML строку. =item C<[static,operator] spawn($file)> Синоним для C<LoadXMLFile>, предполагается использование как оператора. =item C<rawGet($propname,...)> Метод для получения значений свойств приложения. Данный метод позволяет избежать использование активации объектов через C<IMPL::Config::Activator>. =back =cut