view Lib/IMPL/Config.pm @ 81:077357224bec

IMPL::Web::Security alpha version IMPL::Security improovements
author Sergey
date Wed, 07 Apr 2010 14:45:34 +0400
parents 2f31ecabe9ea
children 76515373dac0
line wrap: on
line source

package IMPL::Config;
use strict;
use warnings;

use base qw(IMPL::Object::Accessor IMPL::Object::Serializable IMPL::Object::Autofill);

__PACKAGE__->PassThroughArgs;

use IMPL::Class::Member;
use IMPL::Class::PropertyInfo;
use IMPL::Exception;

use IMPL::Serialization;
use IMPL::Serialization::XmlFormatter;



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 == IMPL::Class::Member::MOD_PUBLIC and
    		$_->canGet;
    	},
    	1);    
}

sub spawn {
	goto &LoadXMLFile;
}

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;
}

1;
__END__

=pod

=head1 NAME

C<IMPL::Config> - базовый класс для настраиваемого приложения.

=head1 SYNOPSIS

=begin code

# define application

package MyApp;
use base 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 base 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