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