changeset 28:6d33f75c6e1f

ORM in works
author Sergey
date Mon, 19 Oct 2009 04:13:54 +0400
parents b544a772b654
children 37160f7c8edb
files Lib/IMPL/ORM/Entity.pm Lib/IMPL/ORM/Helpers.pm Lib/IMPL/ORM/Object.pm Lib/IMPL/ORM/Schema.pm Lib/IMPL/ORM/Schema/Entity.pm Lib/IMPL/ORM/Schema/Field.pm Lib/IMPL/ORM/Schema/Relation.pm Lib/IMPL/ORM/Schema/Relation/HasMany.pm Lib/IMPL/ORM/Schema/Relation/HasOne.pm Lib/IMPL/ORM/Schema/Relation/Subclass.pm _test/Test/DOM/Schema.pm _test/Test/ORM/Schema.pm impl.kpf
diffstat 13 files changed, 358 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/Lib/IMPL/ORM/Entity.pm	Fri Oct 16 16:37:53 2009 +0400
+++ b/Lib/IMPL/ORM/Entity.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -16,7 +16,13 @@
 sub CTOR {
     my ($this,$class,$schema) = @_;
     
-    
+    $this->{$Class} = $class;
+    (my $name = $class) =~ s/::/_/g;
+    $this->{$Name} = $name;
+    $this->Schema = $schema;
+    $this->{$Values} = {
+        map {$_->{name},{type => $_->{type}, virtual => $_->{virtual}}} @$schema
+    };
 }
 
 sub Store;
@@ -26,19 +32,17 @@
     my ($this,$prop,$value) = @_;
     
     if ( my $container = $this->{$Values}{$prop} ) {
-        if ($container->{type} eq 'SCALAR') {
-            
-        } else {
-            
-        }
-        
+        $container->{oldValue} = $container->{value};
+        $container->{value} = $value;
     } else {
         die new IMPL::InvalidOperationException("Property not found",$this->Name,$prop);
     }
 }
 
 sub Get {
+    my ($this,$prop) = @_;
     
+    return $this->{$Values}{$prop}{value};
 }
 
 1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/ORM/Helpers.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -0,0 +1,24 @@
+package IMPL::ORM::Helpers;
+use strict;
+use warnings;
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(&Map &Box);
+
+sub Map($$) {
+    my ($TKey,$TValue) = @_;
+    
+    $TKey =~ s/:://g;
+    $TValue =~ s/:://g;
+    
+    return "IMPL::ORM::Map::${TKey}${TValue}";
+}
+
+sub Box($) {
+    my ($TValue) = @_;
+    $TValue =~ s/:://g;
+    return "IMPL::ORM::Box::$TValue";
+}
+
+1;
--- a/Lib/IMPL/ORM/Object.pm	Fri Oct 16 16:37:53 2009 +0400
+++ b/Lib/IMPL/ORM/Object.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -7,9 +7,15 @@
 use IMPL::Class::Property::Direct;
 
 require IMPL::ORM::Entity;
+require IMPL::ORM::Schema::Entity;
+require IMPL::ORM::Schema::Field;
+require IMPL::ORM::Schema::Relation::HasMany;
+require IMPL::ORM::Schema::Relation::HasOne;
+require IMPL::ORM::Schema::Relation::Subclass;
 
 BEGIN {
     private _direct property _entities => prop_all;
+    public property objectType => prop_all;
 }
 
 my %schemaCache;
@@ -36,18 +42,19 @@
     return $this->{$_entities}{$class} ? $this->{$_entities}{$class}->Get($prop,$value) : undef;
 }
 
-sub _PropertyImplementor {
-    'IMPL::ORM::Property'
+#sub _PropertyImplementor {
+#    'IMPL::ORM::Property'
+#}
+
+sub entityName {
+    (my $self = ref $_[0] || $_[0]) =~ s/::/_/g;
+    return $self;
 }
 
 sub ormGetSchema {
-    my ($self) = @_;
-    
-    my $class = ref $self || $self;
+    my ($self,$dataSchema) = @_;
     
-    return $schemaCache{$class} if $schemaCache{$class};
-    
-    my %schema;
+    my $schema = IMPL::ORM::Schema::Entity->new($self->entityName);
     
     foreach my $ormProp (
         $self->get_meta(
@@ -55,17 +62,22 @@
             sub {
                 UNIVERSAL::isa($_->Implementor, 'IMPL::ORM::Property' )
             },
-            1
+            0
         )
     ){
-        push @{$schema{$ormProp->Class}},{
-            name => $ormProp->Name,
-            virtual => $ormProp->Virtual,
-            type => $ormProp->Type
-        };
+        if ($ormProp->Mutators & prop_list) {
+            my $type = $dataSchema->resolveType($ormProp->Type) or die new IMPL::InvalidOperationException("Failed to resolve a reference type due building schema for a class", $ormProp->Class, $ormProp->Name);
+            $schema->appendChild( new IMPL::ORM::Schema::Relation::HasMany($ormProp->Name, $type->entityName) );
+        } elsif (my $type = $dataSchema->isValueType($ormProp->Type,'IMPL::ORM::Object')) {
+            $schema->appendChild( new IMPL::ORM::Schema::Field($ormProp->Name,$type) );
+        } elsif (my $entity = $dataSchema->resolveType($ormProp->Type)) {
+            $schema->appendChild( new IMPL::ORM::Schema::Relation::HasOne($ormProp->Name,$entity->entityName));
+        } else {
+            die new IMPL::Exception('Uexpected error due building schema for a class', $ormProp->Class, $ormProp->Name);
+        }
     }
     
-    return ($schemaCache{$class} = \%schema);
+    return $schema;
 }
 
 1;
@@ -79,4 +91,7 @@
 Базовый объект для реляционного отображения,
 содержит в себе реляционные записи представляющие данный объект.
 
+Каждый класс отображается в определенную сущность. Сущности хранят
+состояние объектов в том виде в котором удобно записывать в реляционную базу.
+
 =cut
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/ORM/Schema.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -0,0 +1,64 @@
+package IMPL::ORM::Schema;
+use strict;
+use warnings;
+
+use base qw(IMPL::DOM::Document);
+use IMPL::Class::Property;
+
+BEGIN {
+    public property mapValueTypes => prop_get | owner_set;
+    public property mapReferenceTypes => prop_get | owner_set;
+}
+
+sub CTOR {
+    my ($this ) = @_;
+    $this->mapValueTypes({});
+    $this->mapReferenceTypes({});
+}
+
+# return an entity for the specified typename
+# makes forward declaration if nesessary
+sub resolveType {
+    my ($this,$typeName) = @_;
+    
+    $this = ref $this ? $this : $this->instance;
+    
+    if (my $entity = $this->mapReferenceTypes->{$typeName}) {
+        return $entity;
+    } elsif (UNIVERSAL::isa($typeName,'IMPL::ORM::Object')) {
+        return $this->declareReferenceType($typeName);
+    } else {
+        return undef;
+    }
+}
+
+# returns valuetype name
+sub isValueType {
+    my ($this,$typeName) = @_;
+    
+    $this = ref $this ? $this : $this->instance;
+    
+    return $this->mapValueTypes->{$typeName};
+}
+
+my %instances;
+sub instance {
+    my ($class) = @_;
+    
+    return ($instances{$class} || ($instances{$class} = $class->new));
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 DESCRIPTION
+
+Схема данных, представляет собой DOM документ, элементами которой
+являются сущности.
+
+Каждый узел - это описание сущности.
+
+=cut
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/ORM/Schema/Entity.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -0,0 +1,24 @@
+package IMPL::ORM::Schema::Entity;
+use strict;
+use warnings;
+
+use base qw(IMPL::DOM::Node);
+use IMPL::Class::Property;
+
+BEGIN {
+    public property entityName => prop_get | owner_set;
+}
+
+our %CTOR = (
+    'IMPL::DOM::Node' => sub {
+        nodeName => 'Entity'
+    }
+);
+
+sub CTOR {
+    my ($this,$name) = @_;
+
+    $this->entityName($name);
+}
+
+1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/ORM/Schema/Field.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -0,0 +1,28 @@
+package IMPL::ORM::Schema::Field;
+use strict;
+use warnings;
+
+use base qw(IMPL::DOM::Node);
+use IMPL::Class::Property;
+
+BEGIN {
+    public property fieldName => prop_get | owner_set;
+    public property fieldType => prop_get | owner_set;
+}
+
+our %CTOR = (
+    'IMPL::DOM::Node' => sub { nodeName => 'Field' }
+);
+
+sub CTOR {
+    my ($this,$name,$type) = @_;
+    
+    $this->fieldName($name) or die new IMPL::InvalidArgumentException('A name is required for the field');
+    $this->fieldType($type) or die new IMPL::InvalidArgumentException('A type is required for the field');
+}
+
+sub canHaveChildren {
+    0;
+}
+
+1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/ORM/Schema/Relation.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -0,0 +1,12 @@
+package IMPL::ORM::Schema::Relation;
+use strict;
+use warnings;
+
+use base qw(IMPL::DOM::Node);
+
+our %CTOR =(
+    'IMPL::DOM::Node' => sub { nodeName => $_[0] }
+);
+
+
+1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/ORM/Schema/Relation/HasMany.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -0,0 +1,27 @@
+package IMPL::ORM::Schema::Relation::HasMany;
+use strict;
+use warnings;
+
+use base qw(IMPL::ORM::Schema::Relation);
+use IMPL::Class::Property;
+
+BEGIN {
+    public property target => prop_get | owner_set;
+    public property name => prop_get | owner_set;
+}
+
+our %CTOR = (
+    'IMPL::ORM::Schema::Relation' => sub { 'HasMany' }
+);
+
+sub CTOR {
+    my ($this,$name,$target) = @_;
+    $this->name($name) or die new IMPL::InvalidArgumentException('A name is required for this relation');
+    $this->target($target) or die new IMPL::InvalidArgumentException('A target is required for this relation',$name);
+}
+
+sub canHaveChildren {
+    0;
+}
+
+1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/ORM/Schema/Relation/HasOne.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -0,0 +1,28 @@
+package IMPL::ORM::Schema::Relation::HasOne;
+use strict;
+use warnings;
+
+use base qw(IMPL::ORM::Schema::Relation);
+use IMPL::Class::Property;
+
+BEGIN {
+    public property target => prop_get | owner_set;
+    public property name => prop_get | owner_set;
+}
+
+our %CTOR = (
+    'IMPL::ORM::Schema::Relation' => sub { 'HasOne' }
+);
+
+sub CTOR {
+    my ($this,$name,$target) = @_;
+    $this->name($name) or die new IMPL::InvalidArgumentException('A name is required for this relation');
+    $this->target($target) or die new IMPL::InvalidArgumentException('A target is required for this relation',$name);
+}
+
+sub canHaveChildren {
+    0;
+}
+
+
+1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/ORM/Schema/Relation/Subclass.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -0,0 +1,26 @@
+package IMPL::ORM::Schema::Relation::Subclass;
+use strict;
+use warnings;
+
+use base qw(IMPL::ORM::Schema::Relation);
+use IMPL::Class::Property;
+
+BEGIN {
+    public property base => prop_get | owner_set;
+}
+
+our %CTOR = (
+    'IMPL::DOM::Node' => sub { 'Subclass' }
+);
+
+sub CTOR {
+    my ($this,$base) = @_;
+    
+    $this->base($base) or die new IMPL::InvalidArgumentException('A base is required for this relation');
+}
+
+sub canHaveChildren {
+    0;
+}
+
+1;
--- a/_test/Test/DOM/Schema.pm	Fri Oct 16 16:37:53 2009 +0400
+++ b/_test/Test/DOM/Schema.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -20,7 +20,7 @@
 
 test AutoverifyMetaSchema => sub {
     my $metaSchema = IMPL::DOM::Schema->MetaSchema();
-    
+
     if (my @errors = $metaSchema->Validate($metaSchema)) {
         failed "Self verification failed", map $_ ? $_->Message : 'unknown', @errors;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/_test/Test/ORM/Schema.pm	Mon Oct 19 04:13:54 2009 +0400
@@ -0,0 +1,57 @@
+package Test::ORM::Schema;
+use strict;
+use warnings;
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw();
+
+package Test::ORM::Schema::Data::User;
+use base qw(IMPL::ORM::Object);
+use IMPL::Class::Property;
+
+BEGIN {
+    public property Name => prop_all, { type => 'String' }; # Field
+    public property Id => prop_all, { type => 'String' }; # Field
+    public property Roles => prop_all | prop_list, { type=> 'Test::ORM::Schema::Data::Role'}; # HasMany
+}
+
+package Test::ORM::Schema::Data::Role;
+use base qw(IMPL::ORM::Object);
+use IMPL::Class::Property;
+
+BEGIN {
+    public property Name => prop_all, { type => 'String' }; # Field
+}
+
+package Test::ORM::Schema::Data::Session;
+use base qw(IMPL::ORM::Object);
+use IMPL::Class::Property;
+use IMPL::ORM::Helpers qw(Map);
+
+BEGIN {
+    public property Id => prop_get, { type => 'String' }; # Field
+    public property User => prop_get, { type => 'Test::ORM::Schema::Data::User' }; # HasOne
+    public property Data => prop_all, { type => Map( 'String','String' ) }; # HasOne
+    public property AccessTime => prop_get { type => 'DateTime' }; # Field
+}
+
+package Test::ORM::Schema::Data;
+use base qw(IMPL::ORM::Schema);
+
+__PACKAGE__->usePrefix(__PACKAGE__);
+__PACKAGE__->Classes qw(
+    User
+    Role
+    Session
+);
+
+__PACKAGE__->ValueTypes (
+    'String' => 'IMPL::ORM::Value::String',
+    'DateTime' => 'IMPL::ORM::Value::DateTime',
+    'Integer' => 'IMPL::ORM::Value::Inetger',
+    'Float' => 'IMPL::ORM::Value::Float',
+    'Decimal' => 'IMPL::ORM::Value::Decimal'
+);
+
+1;
--- a/impl.kpf	Fri Oct 16 16:37:53 2009 +0400
+++ b/impl.kpf	Mon Oct 19 04:13:54 2009 +0400
@@ -456,6 +456,32 @@
 </preference-set>
   <string id="lastInvocation">default</string>
 </preference-set>
+<preference-set idref="66c7d414-175f-45b6-92fe-dbda51c64843/_test/Test/ORM/Schema.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">_test/Test/ORM/Schema.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/_test/Web.t">
 <preference-set id="Invocations">
 <preference-set id="default">