Mercurial > pub > Impl
changeset 7:94d47b388442
Улучшены тесты
Исправлены ошибки
Улучшена документация
Работа над схемой DOM
author | Sergey |
---|---|
date | Mon, 24 Aug 2009 01:05:34 +0400 |
parents | e2cd73ccc5bd |
children | fffb153be599 |
files | Lib/IMPL/DOM/FixedNode.pm Lib/IMPL/DOM/Node.pm Lib/IMPL/DOM/Property.pm Lib/IMPL/DOM/Schema/ComplexNode.pm Lib/IMPL/DOM/Schema/Item.pm Lib/IMPL/DOM/Schema/NodeSet.pm Lib/IMPL/DOM/Schema/SimpleNode.pm Lib/IMPL/DOM/Transform/PostToDOM.pm Lib/IMPL/Object/EventSource.pm Lib/IMPL/Transform.pm Lib/IMPL/Tree/Batch.pm Lib/IMPL/Web/TDocument.pm _test/Resources/simple.tt _test/Resources/simple.txt _test/Resources/test.form _test/Test/Web/TDocument.pm _test/Web.t impl.kpf |
diffstat | 18 files changed, 669 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/Lib/IMPL/DOM/FixedNode.pm Fri Aug 14 16:14:13 2009 +0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -package IMPL::DOM::FixedNode; -use strict; -use warnings; - -use base qw(IMPL::DOM::Node); - -sub Validate { - -} - - -1;
--- a/Lib/IMPL/DOM/Node.pm Fri Aug 14 16:14:13 2009 +0400 +++ b/Lib/IMPL/DOM/Node.pm Mon Aug 24 01:05:34 2009 +0400 @@ -61,6 +61,29 @@ } } +sub replaceNodeAt { + my ($this,$index,$node) = @_; + + my $nodeOld = $this->childNodes->[$index]; + + die new IMPL::InvalidOperationException("You can't insert the node to itselft") if $this == $node; + + # unlink node from previous parent + $node->{$parentNode}->removeNode($node) if ($node->{$parentNode}); + + # replace (or set) old node + $this->childNodes->[$index] = $node; + + # save new parent + $node->_setParent( $this ); + + # unlink old node if we have one + $nodeOld->{$parentNode} = undef if $nodeOld; + + # return old node + return $nodeOld; +} + sub removeAt { my ($this,$pos) = @_; @@ -80,6 +103,10 @@ return wantarray ? @result : \@result; } +sub firstChild { + @_ >=2 ? $_[0]->replaceNodeAt(0,$_[1]) : $_[0]->childNodes->[0]; +} + sub _getIsComplex { $_[0]->childNodes->Count ? 1 : 0; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/IMPL/DOM/Property.pm Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,73 @@ +package IMPL::DOM::Property; +use strict; +use warnings; + +use IMPL::Class::Property; +require IMPL::Exception; + +use base qw(Exporter); +our @EXPORT_OK = qw(_dom); + +sub _dom($) { + my ($prop_info) = @_; + $prop_info->Implementor( 'IMPL::DOM::Property' ); + return $prop_info; +} + +sub Make { + my ($self,$propInfo) = @_; + + my ($class,$name,$virt,$access,$mutators) = $propInfo->get qw(Class Name Virtual Access Mutators); + + die new IMPL::InvalidOperationException("DOM properties can be declared only for the DOM objects") unless $class->isa('IMPL::DOM::Node'); + + no strict 'refs'; + die new IMPL::InvalidOperationException("Custom mutators are not allowed","${class}::$name") if ref $mutators; + if (($mutators & prop_all) == prop_all) { + *{"${class}::$name"} = sub { + $_[0]->Property($name,@_[1..$#_]); + }; + $propInfo->canGet(1); + $propInfo->canSet(1); + } elsif( $mutators & prop_get ) { + *{"${class}::$name"} = sub { + die new IMPL::InvalidOperationException("This is a readonly property", "${class}::$name") if @_>1; + $_[0]->Property($name); + }; + $propInfo->canGet(1); + $propInfo->canSet(0); + } elsif( $mutators & prop_set ) { + *{"${class}::$name"} = sub { + die new IMPL::InvalidOperationException("This is a writeonly property", "${class}::$name") if @_<2; + $_[0]->Property($name,@_[1..$#_]); + }; + $propInfo->canGet(0); + $propInfo->canSet(1); + } else { + die new IMPL::InvalidOperationException("Invalid value for the property mutators","${class}::$name",$mutators); + } +} + +1; +__END__ +=pod + +=head1 SYNOPSIS + +package TypedNode; + +use base qw(IMPL::DOM::Node); +use IMPL::DOM::Property qw(_dom); + +BEGIN { + public _dom property Age => prop_all; + public _dom property Address => prop_all; + public property ServiceData => prop_all; +} + +=head1 DESCRIPTION + + , +. + +=cut \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/IMPL/DOM/Schema/ComplexNode.pm Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,79 @@ +package IMPL::DOM::Schema::ComplexNode; +use strict; +use warnings; + +use base qw(IMPL::DOM::Schema::Item); +use IMPL::Class::Property; + +BEGIN { + public property nodeType => prop_all; + public property content => { + get => \&_getContent, + set => \&_setContent + } +} + +__PACKAGE__->PassThroughArgs; + +sub _getContent { + $_[0]->firstChild; +} + +sub _setContent { + $_[0]->firstChild($_[1]); +} + +sub Validate { + my ($this,$node) = @_; + + if (my $type = $this->nodeType) { + my $schemaType = $this->Schema->ResolveType($type); + return $schemaType->Validate($node); + } else { + my @errors; + push @errors, $_->Validate foreach @{$this->childNodes}; + + if (@errors and $this->Message) { + return { + Error => 1, + Message => $this->formatMessage($node), + InnerErrors => \@errors + }; + } else { + return @errors; + } + } +} + +1; + +__END__ + +=pod + +=head1 DESCRIPTION + + . , +. + + .. + , . C<content> + +=head2 PROPERTIES + +=over + +=item C<nodeType> + + , . + C<content> . + +=item C<content> + + , C<IMPL::DOM::Schema::NodeSet> +C<IMPL::DOM::Schema::NodeList>, . + . + +=back + +=cut
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/IMPL/DOM/Schema/Item.pm Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,44 @@ +package IMPL::DOM::Schema::Item; +use strict; +use warnings; + +use base qw(IMPL::DOM::Node); +use IMPL::Class::Property; +use IMPL::DOM::Property qw(_dom); +use IMPL::Class::Property::Direct; + +BEGIN { + public _dom property minOccur => prop_all; + public _dom property maxOccur => prop_all; + public _direct property Schema => prop_get; +} + +__PACKAGE__->PassThroughArgs; + +sub CTOR { + my ($this,%args) = @_; + + $this->minOccur($args{minOcuur}); + $this->maxOccur($args{maxOccur}); + $this->{$Schema} = $args{Schema} or die new IMPL::InvalidArgumentException("A schema should be specified"); +} + +1; + +__END__ +=pod + +=head1 SYNOPSIS + +package Restriction; +use base qw(IMPL::DOM::Schema::Item); + +sub Validate { + my ($this,$node) = @_; +} + +=head1 DESCRIPTION + + . + +=cut
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/IMPL/DOM/Schema/NodeSet.pm Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,71 @@ +package IMPL::DOM::Schema::NodeSet; +use strict; +use warnings; + +use base qw(IMPL::DOM::Schema::Item); +use IMPL::Class::Property; + +BEGIN { + public property UnexpectedMessage => prop_all; + public property MaxMessage => prop_all; + public property MinMessage => prop_all; +} + +sub Validate { + my ($this,$node) = @_; + + my @errors; + + my %nodes = map { + $_->nodeName , + {Schema => $_, Min => $_->minOccur, Max => $_->maxOccur, Seen => 0 } + } @{$this->childNodes}; + + foreach my $child ( @{$node->childNodes} ) { + if (my $info = $nodes{$child->nodeName}) { + $info->{Seen}++; + push @errors,{ + Error => 1, + Source => $this, + Node => $child, + Message => $this->MaxMessage + } if ($info->{Seen} > $info->{Max}); + + push @errors,$info->{Schema}->Validate($child); + } else { + push @errors, { + Error => 1, + Source => $this, + Node => $child, + Message => $this->UnexpectedMessage + } + } + } + + foreach my $info (values %nodes) { + push @errors, { + Error => 1, + Source => $this, + Message => $this->MinMessage + } if $info->{Min} > $info->{Seen}; + } + + return @errors; +} + +1; + +__END__ + +=pod + +=head1 DESCRIPTION + + . . + C<IMPL::DOM::Schema::ComplexNode> C<IMPL::DOM::Schema::SimpleNode>. + + , + , + . + +=cut
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/IMPL/DOM/Schema/SimpleNode.pm Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,21 @@ +package IMPL::DOM::Schema::SimpleNode; +use strict; +use warnings; + +use base qw(IMPL::DOM::Schema::Item); + +__PACKAGE__->PassThroughArgs; + +1; + +__END__ + +=pod + +=head1 DESCRIPTION + + . + . + + +=cut \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/IMPL/DOM/Transform/PostToDOM.pm Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,36 @@ +package IMPL::DOM::Post2DOM; +use strict; +use warnings; + +use IMPL::DOM::Navigator; +use IMPL::Class::Property; + +use base qw(IMPL::Transform); + +BEGIN { + public property Navigator => prop_get | owner_set; +} + +our %CTOR = ( + 'IMPL::Transform' => sub { + return ( + HASH => \&TransfromPostData + ); + } +); + +sub TransformPostData { + my ($this,$data) = @_; + + my $navi = $this->Navigator; + + while (my ($key,$value) = each %$data) { + my $node = $navi->Navigate($key); + $node->nodeValue($value); + } + + return $navi->Document; +} + + +1;
--- a/Lib/IMPL/Object/EventSource.pm Fri Aug 14 16:14:13 2009 +0400 +++ b/Lib/IMPL/Object/EventSource.pm Mon Aug 24 01:05:34 2009 +0400 @@ -51,7 +51,7 @@ no strict 'refs'; *{"${class}::$event"} = sub { - my $class = shift; + shift; if (not @_) { if (not defined wantarray and caller(1) eq $class) { $globalEventTable->Invoke($class); @@ -59,7 +59,7 @@ return $globalEventTable; } } else { - $globalEventTable->Invoke(@_); + $globalEventTable->Invoke($class,@_); } }; } @@ -131,3 +131,63 @@ return delete $this->{$Handlers}{$id}; } 1; + +__END__ +=pod +=head1 SYNOPSIS +package Foo; +use base qw(IMPL::Object IMPL::Object::EventSource); + +# declare events +__PACKAGE__->CreateEvent('OnUpdate'); +__PACKAGE__->CreateStaticEvent('OnNewObject'); + +sub CTOR { + my $this = shift; + // rise static event + $this->OnNewObject(); +} + +sub Update { + my ($this,$val) = @_; + + // rise object event + $this->OnUpdate($val); +} + +package Bar; + +// subscribe static event +Foo->OnNewObject->Subscribe(sub { warn "New $_[0] created" } ); + +sub LookForFoo { + my ($this,$foo) = @_; + + // subscribe object event + $foo->OnUpdate->Subscribe($this,'OnFooUpdate'); +} + +// event handler +sub OnFooUpdate { + my ($this,$sender,$value) = @_; +} + +=head1 DESCRIPTION + . +. + . + , , . + + ( ) +. , + ( ). + , + . + +=head1 METHODS +=level 4 +=back + +=head1 EventTable + +=cut \ No newline at end of file
--- a/Lib/IMPL/Transform.pm Fri Aug 14 16:14:13 2009 +0400 +++ b/Lib/IMPL/Transform.pm Mon Aug 24 01:05:34 2009 +0400 @@ -1,5 +1,5 @@ package IMPL::Transform; -use base qw(IMPL::Object IMPL::Object::Autofill); +use base qw(IMPL::Object); use IMPL::Class::Property; use IMPL::Class::Property::Direct; @@ -10,7 +10,14 @@ protected _direct property Plain => prop_all; } -__PACKAGE__->PassThroughArgs; +sub CTOR { + my ($this,%args) = @_; + + $this->{$Plain} = delete $args{-plain}; + $this->{$Default} = delete $args{-default}; + + $this->{$Templates} = \%args; +} sub Transform { my ($this,$object) = @_; @@ -56,9 +63,19 @@ DocClass => sub { my ($this,$object) = @_; return new DocPreview(Author => $object->Author, Text => $object->Data); + }, + -default => sub { + my ($this,$object) = @_; + return $object; + }, + -plain => sub { + my ($this,$object) = @_; + return $object; } ); +my $result = $t->Transform($obj); + =head1 Summary .
--- a/Lib/IMPL/Tree/Batch.pm Fri Aug 14 16:14:13 2009 +0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -package IMPL::Tree::Batch; -use strict; -use base qw(IMP::Object); \ No newline at end of file
--- a/Lib/IMPL/Web/TDocument.pm Fri Aug 14 16:14:13 2009 +0400 +++ b/Lib/IMPL/Web/TDocument.pm Mon Aug 24 01:05:34 2009 +0400 @@ -2,26 +2,144 @@ use strict; use warnings; -use base qw(IMPL::DOM::Node); +use base qw(IMPL::DOM::Node IMPL::Object::Disposable); use Template::Context; use Template::Provider; use IMPL::Class::Property; use File::Spec; BEGIN { - public property Templates => prop_get | owner_set; - public property Context => prop_get | owner_set; + private property _Provider => prop_all; + private property _Context => prop_all; + public property Template => prop_get | owner_set; } our %CTOR = ( 'IMPL::DOM::Node' => sub { nodeName => 'document' } ); -sub load { - my ($this,$file) = @_; +sub Provider { + my ($this,%args) = @_; + + if (my $provider = $this->_Provider) { + return $provider; + } else { + return $this->_Provider(new Template::Provider( + \%args + )); + } +} + +sub Context { + my ($this) = @_; + + if (my $ctx = $this->_Context) { + return $ctx; + } else { + return $this->_Context ( + new Template::Context( + VARIABLES => { + document => $this + }, + TRIM => 1, + RECURSION => 1, + LOAD_TEMPLATES => [$this->Provider] + ) + ) + } +} + +sub loadFile { + my ($this,$filePath,$encoding) = @_; + + die new IMPL::InvalidArgumentException("A filePath parameter is required") unless $filePath; - $file = File::Spec->rel2abs($file); + $encoding ||= 'utf8'; + + $this->_Context(undef); + $this->_Provider(undef); + + my ($vol,$dir,$fileName) = File::Spec->splitpath($filePath); + + my $inc = File::Spec->catpath($vol,$dir,''); + + $this->Provider( + ENCODING => $encoding, + INTERPOLATE => 1, + PRE_CHOMP => 1, + POST_CHOMP => 1, + INCLUDE_PATH => $inc + ); + $this->Template($this->Context->template($fileName)); +} + +sub Title { + $_[0]->Template->Title; +} + +sub Render { + my ($this) = @_; + + return $this->Template->process($this->Context); +} + +sub Dispose { + my ($this) = @_; + + $this->Template(undef); + $this->_Context(undef); + $this->_Provider(undef); + + $this->SUPER::Dispose(); } 1; +__END__ +=pod + +=head1 SYNOPSIS + +// create new document +my $doc = new IMPL::Web::TDocument; + +// load template +$doc->loadFile('Templates/index.tt'); + +// render file +print $doc->Render(); + +=head1 DESCRIPTION + +, Template::Toolkit. , + . C<IMPL::DOM::Node>, +.. DOM . + + C<document> . + , + C<Dispose> . + +=head1 METHODS + +=level 4 + +=item C<new()> + + + +=item C<$doc->loadFile($fileName,$encoding)> + + C<$fileName>, C<$encoding>. + , utf-8. + +=item C<$doc->Render()> + + . + +=item C<$doc->Dispose()> + + . + +=back + +=cut \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/_test/Resources/simple.tt Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,2 @@ +[% META Title = " 1" %] + $document.Title \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/_test/Resources/simple.txt Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,1 @@ + 1 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/_test/Resources/test.form Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,9 @@ +<form> + <name>Peter</name> + <passport> + <code>sssddd-ffd-ssd</code> + </passport> + <passport> + <code></code> + </passport> +</form> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/_test/Test/Web/TDocument.pm Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,34 @@ +package Test::Web::TDocument; +use strict; +use warnings; +use encoding 'cp1251'; + +use base qw(IMPL::Test::Unit); +use IMPL::Test qw(test failed); +use IMPL::Web::TDocument; +__PACKAGE__->PassThroughArgs; + +test Creation => sub { + my $document = new IMPL::Web::TDocument(); + + failed "Failed to create document" unless $document; +}; + +test SimpleTemplate => sub { + my $document = new IMPL::Web::TDocument(); + + failed "Failed to create document" unless $document; + + $document->loadFile('Resources/simple.tt','cp1251'); + + my $out = $document->Render; + + open my $hFile,'<:encoding(cp1251)',"Resources/simple.txt" or die "Failed to open etalon file: $!"; + local $/; + my $eta = <$hFile>; + + failed "Rendered data doesn't match the etalon data","Expected:\n$eta","Actual:\n$out" if $out ne $eta; +}; + + +1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/_test/Web.t Mon Aug 24 01:05:34 2009 +0400 @@ -0,0 +1,15 @@ +#!/usr/bin/perl -w +use strict; +use lib '../Lib'; +use lib '.'; + +use IMPL::Test::Plan; +use IMPL::Test::TAPListener; + +my $plan = new IMPL::Test::Plan qw( + Test::Web::TDocument +); + +$plan->AddListener(new IMPL::Test::TAPListener); +$plan->Prepare(); +$plan->Run();
--- a/impl.kpf Fri Aug 14 16:14:13 2009 +0400 +++ b/impl.kpf Mon Aug 24 01:05:34 2009 +0400 @@ -3,8 +3,6 @@ <project id="66c7d414-175f-45b6-92fe-dbda51c64843" kpf_version="4" name="impl.kpf"> <file id="c255e877-43b0-4625-a9f2-fbf8e65179c8" idref="66c7d414-175f-45b6-92fe-dbda51c64843/Lib/IMPL/DOM" name="Node.pm" url="Lib/IMPL/DOM/Node.pm"> </file> -<file id="91cab186-0c9b-4ed6-98e8-3de5c132e296" idref="66c7d414-175f-45b6-92fe-dbda51c64843/Lib/IMPL/Object" name="Node.pm" url="Lib/IMPL/DOM/Node.pm"> -</file> <preference-set idref="155f1fd9-8a20-46fe-90d5-8fbe879632d8"> <preference-set id="Invocations"> <preference-set id="default"> @@ -120,6 +118,32 @@ </preference-set> </preference-set> </preference-set> +<preference-set idref="66c7d414-175f-45b6-92fe-dbda51c64843/Lib/IMPL/DOM/Schema/Item.pm"> +<preference-set id="Invocations"> +<preference-set id="default"> + <string id="cookieparams"></string> + <string id="cwd"></string> + <long id="debugger.io-port">9011</long> + <string id="documentRoot"></string> + <string id="executable-params"></string> + <string relative="path" id="filename">Lib/IMPL/DOM/Schema/Item.pm</string> + <string id="getparams"></string> + <string id="language">Perl</string> + <string id="mpostparams"></string> + <string id="params"></string> + <string id="postparams"></string> + <string id="posttype">application/x-www-form-urlencoded</string> + <string id="request-method">GET</string> + <boolean id="show-dialog">1</boolean> + <boolean id="sim-cgi">0</boolean> + <boolean id="use-console">0</boolean> + <string id="userCGIEnvironment"></string> + <string id="userEnvironment"></string> + <string id="warnings">enabled</string> +</preference-set> +</preference-set> + <string id="lastInvocation">default</string> +</preference-set> <preference-set idref="66c7d414-175f-45b6-92fe-dbda51c64843/Lib/IMPL/Object.pm"> <preference-set id="Invocations"> <preference-set id="default"> @@ -146,6 +170,32 @@ </preference-set> <string id="lastInvocation">default</string> </preference-set> +<preference-set idref="66c7d414-175f-45b6-92fe-dbda51c64843/_test/Web.t"> +<preference-set id="Invocations"> +<preference-set id="default"> + <string id="cookieparams"></string> + <string id="cwd"></string> + <long id="debugger.io-port">9011</long> + <string id="documentRoot"></string> + <string id="executable-params"></string> + <string relative="path" id="filename">_test/Web.t</string> + <string id="getparams"></string> + <string id="language">Perl</string> + <string id="mpostparams"></string> + <string id="params"></string> + <string id="postparams"></string> + <string id="posttype">application/x-www-form-urlencoded</string> + <string id="request-method">GET</string> + <boolean id="show-dialog">1</boolean> + <boolean id="sim-cgi">0</boolean> + <boolean id="use-console">0</boolean> + <string id="userCGIEnvironment"></string> + <string id="userEnvironment"></string> + <string id="warnings">enabled</string> +</preference-set> +</preference-set> + <string id="lastInvocation">default</string> +</preference-set> <preference-set idref="66c7d414-175f-45b6-92fe-dbda51c64843/_test/object.t"> <preference-set id="Invocations"> <preference-set id="default">