Mercurial > pub > Impl
diff lib/IMPL/Config.pm @ 407:c6e90e02dd17 ref20150831
renamed Lib->lib
author | cin |
---|---|
date | Fri, 04 Sep 2015 19:40:23 +0300 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/IMPL/Config.pm Fri Sep 04 19:40:23 2015 +0300 @@ -0,0 +1,291 @@ +package IMPL::Config; +use strict; +use warnings; +use mro; + +use Carp qw(carp); + +use IMPL::lang qw(is); +use IMPL::Exception; +use IMPL::Const qw(:access); +use IMPL::declare { + require => { + PropertyInfo => 'IMPL::Class::PropertyInfo', + XmlFormatter => 'IMPL::Serialization::XmlFormatter', + Serializer => '-IMPL::Serializer', + Activator => '-IMPL::Config::Activator', + + Exception => 'IMPL::Exception', + IOException => '-IMPL::IOException' + }, + base => [ + 'IMPL::Object::Accessor' => undef, + 'IMPL::Object::Serializable' => undef, + 'IMPL::Object::Autofill' => '@_' + ] +}; + +use File::Spec(); + + +our $ConfigBase ||= ''; +our $AppBase; + +sub LoadXMLFile { + my ($self,$file) = @_; + + my $class = ref $self || $self; + + my $serializer = Serializer->new( + formatter => XmlFormatter->new( + IdentOutput => 1, + SkipWhitespace => 1 + ) + ); + + open my $hFile,'<',$file or die IOException->new("Failed to open file",$file,$!); + + my $obj; + eval { + $obj = $serializer->Deserialize($hFile); + }; + + if ($@) { + my $e=$@; + die Exception->new("Can't load the configuration file",$file,$e); + } + return $obj; +} + +sub SaveXMLFile { + my ($this,$file) = @_; + + my $serializer = Serializer->new( + formatter => XmlFormatter->new( + IdentOutput => 1, + SkipWhitespace => 1 + ) + ); + + open my $hFile,'>',$file or die IOException->new("Failed to open file",$file,$!); + + $serializer->Serialize($hFile, $this); +} + +sub xml { + my $this = shift; + my $serializer = Serializer->new( + formatter => XmlFormatter->new( + IdentOutput => 1, + SkipWhitespace => 1 + ) + ); + my $str = ''; + open my $hFile,'>',\$str or die IOException->new("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( + 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 is($obj,Activator) ? $obj->activate : $obj; + } else { + my @objs = $this->SUPER::get(@_); + return map is($_,Activator) ? $_->activate : $_, @objs ; + } +} + +sub rawGet { + my $this = shift; + return $this->SUPER::get(@_); +} + +sub Exists { + $_[0]->SUPER::get($_[1]) ? 1 : 0; +} + +sub AppBase { + carp "obsolete"; + shift; + File::Spec->catdir($AppBase,@_); +} + +sub AppDir { + shift; + File::Spec->catdir($AppBase,@_); +} + +sub AppFile { + shift; + File::Spec->catfile($AppBase,@_); +} + +sub ConfigBase { + carp "obsolete"; + shift; + File::Spec->catdir($ConfigBase,@_); +} + +sub ConfigDir { + shift; + File::Spec->catdir($ConfigBase,@_); +} + +sub ConfigFile { + shift; + File::Spec->catfile($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