[Jifty-commit] r3484 - in apps/CASPlus/trunk: lib lib/CASPlus lib/CASPlus/Action lib/CASPlus/Model t

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Thu Jun 14 12:51:33 EDT 2007


Author: sterling
Date: Thu Jun 14 12:51:04 2007
New Revision: 3484

Added:
   apps/CASPlus/trunk/lib/CASPlus/Model/ProfileRelationship.pm
   apps/CASPlus/trunk/t/10-model-ProfileRelationship.t
Modified:
   apps/CASPlus/trunk/   (props changed)
   apps/CASPlus/trunk/lib/CASPlus.pm
   apps/CASPlus/trunk/lib/CASPlus/Action/Login.pm
   apps/CASPlus/trunk/lib/CASPlus/Model/Profile.pm
   apps/CASPlus/trunk/lib/CASPlus/Model/ProfileProperty.pm
   apps/CASPlus/trunk/lib/CASPlus/Util.pm

Log:
 r7621 at dynpc145:  andrew | 2007-06-14 11:50:37 -0500
  * Fixed some small typos and other bugs.
  * Fixed the CASPlus POD as I now know of a CAS server implementation in ruby.
  * Added ProfileRelationship and associated tests and also extracted some common
    code from ProfileRelationship/Profile/ProfileProperty into Util.


Modified: apps/CASPlus/trunk/lib/CASPlus.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus.pm	(original)
+++ apps/CASPlus/trunk/lib/CASPlus.pm	Thu Jun 14 12:51:04 2007
@@ -11,7 +11,7 @@
 
 =head1 DESCRIPTION
 
-CAS is a single sign-on protocol for web applications developed at Yale and the reference implementation server is provided by JA-SIG. This is the only CAS server implementation that I am aware of.
+CAS is a single sign-on protocol for web applications developed at Yale and the reference implementation server is provided by JA-SIG.
 
 =head2 SINGLE SIGN-ON
 

Modified: apps/CASPlus/trunk/lib/CASPlus/Action/Login.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Action/Login.pm	(original)
+++ apps/CASPlus/trunk/lib/CASPlus/Action/Login.pm	Thu Jun 14 12:51:04 2007
@@ -36,7 +36,7 @@
 
 use Jifty::Param::Schema;
 use Jifty::Action schema {
-    param 'lt';
+    param 'lt' =>
         is mandatory,
         render as 'Hidden';
     param 'username' =>

Modified: apps/CASPlus/trunk/lib/CASPlus/Model/Profile.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Model/Profile.pm	(original)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/Profile.pm	Thu Jun 14 12:51:04 2007
@@ -72,7 +72,6 @@
     column model_class =>
         refers_to Jifty::Model::ModelClass,
         label is 'Model class',
-        is mandatory,
         is distinct,
         is immutable;
 
@@ -100,13 +99,21 @@
     my $args = shift;
 
     # Create a ModelName out of "Model name"
-    my $name = $args->{name};
-    $name =~ s/ ^ (\d) /D$1/gx;
-    $name =~ s/ (?: (?<= \W ) | (?<= ^ ) ) (\w) /\U$1/gx;
-    $name =~ s/ \W //gx;
+    my $name = CASPlus::Util->convert_to_camel_case($args->{name});
 
-    # Create a model class
+    # Check to make sure no such table already exists:
+    my $orig_name = $name;
+    my $num = 1;
     my $model_class = Jifty::Model::ModelClass->new;
+    $model_class->load_by_cols( name => $name );
+    while ($model_class->id) {
+        $name = $orig_name . $num++;
+        $self->log("Trying to use $name for a model class.");
+
+        $model_class->load_by_cols( name => $name );
+    }
+
+    # Create a model class
     $model_class->create(
         name          => $name,
         description   => $args->{description},

Modified: apps/CASPlus/trunk/lib/CASPlus/Model/ProfileProperty.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Model/ProfileProperty.pm	(original)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/ProfileProperty.pm	Thu Jun 14 12:51:04 2007
@@ -79,8 +79,7 @@
     my $args = shift;
 
     # Create a column_name out of "Column Name"
-    my $name = lc $args->{name};
-    $name =~ s/ \W /_/gx;
+    my $name = CASPlus::Util->convert_to_identifier($args->{name});
 
     # Create a model class column
     # TODO Column creation is very simple; make it more flexible

Added: apps/CASPlus/trunk/lib/CASPlus/Model/ProfileRelationship.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/ProfileRelationship.pm	Thu Jun 14 12:51:04 2007
@@ -0,0 +1,310 @@
+use strict;
+use warnings;
+
+package CASPlus::Model::ProfileRelationship;
+use Jifty::DBI::Schema;
+
+use constant CLASS_UUID => 'F88DC88A-FFBC-11DB-A0E9-5947F1458521';
+
+=head1 NAME
+
+CASPlus::Model::ProfileRelationship - Specify relationships between profiles
+
+=head1 DESCRIPTION
+
+One profile object may have other objects that it is related to. For example, one profile might represent employees while another represents departments. An employee might belong to exactly one department. Another profile might represent project task forces, which a person might belong to many, but the task force might be overseen by a particular department. These relationships are specified using profile relationships.
+
+=head1 SCHEMA
+
+=head2 name
+
+This is the label you wish to give the relationship. This will be used to name the link table if this is a many-to-many relationship. Otherwise, it is just for your reference.
+
+=head2 parent_name
+
+This is the label you wish to give the parent in the relationship. The column added to the child table will use this value to create the name.
+
+=head2 child_name
+
+This is the label you wish to give the child in the relationship. The column added to the parent table will use this value to create the name.
+
+=head2 relation_parent
+
+This is the profile being used as the parent in the relationship.
+
+=head2 relation_child
+
+This is the profile being used as the child in the relationship.
+
+=head2 many_parents
+
+If this is true, a child may have zero or more parents. If this is false, a child may have zero or one parent.
+
+=head2 many_children
+
+If this is true, a parent may have zero or more children. If this is false, a parent may have zero or one child.
+
+=head2 dependent_relationship
+
+If this is true, any child profile instance objects linked in this relationship will be deleted when the parent object is deleted. Otherwise, the children will be orphaned if the parent is deleted.
+
+XXX TODO FIXME As of this writing, this dependency is not yet implemented.
+
+=head2 roles_propagate_to_children
+
+If this is set to true, any roles held by the parent object will also be passed to the children during role calculation.
+
+=head2 parent_column
+
+Calculated column. Do not modify.
+
+This column is setup by the class itself during creation. A new column will be created in the parent table according to the given L</child_name> and L</relation_parent>. This will be set to the L<Jifty::Model::ModelClassColumn> object created.
+
+=head2 child_column
+
+Calculated column. Do not modify.
+
+This column is setup by the class itself during creation. A new column will be created in the parent table according to the given L</parent_name> and L</relation_child>. This will be set to the L<Jifty::Model::ModelClassColumn> object created.
+
+=head2 link_table
+
+Calculated column. Do not modify.
+
+This column is setup by the class itself during creation. If both L</many_parents> and L</many_children> are set to true, then this is a many-to-many relationship requiring an intermediate linking table. The table will be created and this column will contain a reference to this L<Jifty::Model::ModelClass> object.
+
+If this is not a many-to-many relationship, this will be C<undef>.
+
+=head2 link_table_parent
+
+Calculated column. Do not modify.
+
+This column is setup by the class itself during creation. If this is a many-to-many relationship, this will be set to the column of L</link_table> that is used to link to the parent profile instance object. This will be a L<Jifty::Model::ModelClassColumn> object.
+
+If this is not a many-to-many relationship, this will be C<undef>.
+
+=head2 link_table_child
+
+Calculated column. Do not modify.
+
+This column is setup by the class itself during creation. If this is a many-to-many relationship, this will be set to the column of L</link_table> that is used to link to the child profile instance ofject. This will be a L<Jifty::Model::ModelClassColumn> object.
+
+If this is not a many-to-many relationship, this will be C<undef>.
+
+=cut
+
+use CASPlus::Record schema {
+    column name =>
+        type is 'text',
+        label is 'Relationship name',
+        is mandatory;
+
+    column parent_name =>
+        type is 'text',
+        label is 'Name of Parent',
+        hints is 'Name of the column pointing to the parent in the child table.',
+        is mandtory;
+
+    column child_name =>
+        type is 'text',
+        label is 'Name of Child',
+        hints is 'Name of the column pointing to the child in the parent table.',
+        is mandatory;
+
+    column relation_parent =>
+        refers_to CASPlus::Model::Profile,
+        label is 'Parent',
+        is immutable,
+        is mandatory;
+
+    column relation_child =>
+        refers_to CASPlus::Model::Profile,
+        label is 'Child',
+        is immutable,
+        is mandatory;
+
+    column many_parents =>
+        type is 'boolean',
+        label is 'Many parents?',
+        is mandatory,
+        is immutable,
+        default is 0;
+
+    column many_children =>
+        type is 'boolean',
+        label is 'Many children?',
+        is mandatory,
+        is immutable,
+        default is 0;
+
+#    column count_parents =>
+#        type is 'text',
+#        label is 'From cardinality',
+#        valid_values are qw/ 0..1 1 0..* 1..* /,
+#        is mandatory,
+#        default is '0..*';
+#
+#    column count_children =>
+#        type is 'text',
+#        label is 'To cardinality',
+#        valid_values are qw/ 0..1 1 0..* 1..* /,
+#        is mandatory,
+#        default is '1';
+
+    column dependent_relationship =>
+        type is 'boolean',
+        label is 'Dependent?',
+        hints is 'A dependent relationship deletes the children when the parents are deleted.',
+        is mandatory,
+        default is 0;
+
+    column roles_propagate_to_children =>
+        type is 'boolean',
+        label is 'Roles propagate?',
+        hints is 'If checked, roles held by parent profiles are passed to the children.',
+        is mandatory,
+        default is 0;
+    
+    column parent_column =>
+        refers_to Jifty::Model::ModelClassColumn,
+        label is 'Parent column',
+        is immutable,
+        is distinct;
+
+    column child_column =>
+        refers_to Jifty::Model::ModelClassColumn,
+        label is 'Child column',
+        is immutable,
+        is distinct;
+
+    column link_table =>
+        refers_to Jifty::Model::ModelClass,
+        label is 'Link table',
+        is immutable,
+        is distinct;
+
+    column link_table_parent =>
+        refers_to Jifty::Model::ModelClassColumn,
+        label is 'Parent link',
+        is immutable,
+        is distinct;
+
+    column link_table_child =>
+        refers_to Jifty::Model::ModelClassColumn,
+        label is 'Child link',
+        is immutable,
+        is distinct;
+};
+
+=head1 METHODS
+
+=head2 before_create
+
+This is called during object creation to create the various link columns and link tables required to implement the relationship.
+
+=cut
+
+sub before_create {
+    my $self = shift;
+    my $args = shift;
+
+    my $child_name  = CASPlus::Util->convert_to_identifier($args->{child_name});
+    my $parent_name = CASPlus::Util->convert_to_identifier($args->{parent_name});
+
+    if ($args->{many_parents} and $args->{many_children}) {
+        my $name = CASPlus::Util->convert_to_camel_case($args->{name});
+
+        my $link_table = Jifty::Model::ModelClass->new;
+        $link_table->create(
+            name          => $name,
+            super_classes => 'CASPlus::ProfileBase',
+        );
+
+        my $link_table_parent = Jifty::Model::ModelClassColumn->new;
+        $link_table_parent->create(
+            model_class     => $link_table,
+            name            => 'parent',
+            refers_to_class => $args->{relation_parent}->model_class->name,
+        );
+
+        my $link_table_child = Jifty::Model::ModelClassColumn->new;
+        $link_table_child->create(
+            model_class     => $link_table,
+            name            => 'child',
+            refers_to_class => $args->{relation_child}->model_class->name,
+        );
+
+        my $parent_column = Jifty::Model::ModelClassColumn->new;
+        $parent_column->create(
+            model_class     => $args->{relation_parent}->model_class,
+            name            => $child_name,
+            refers_to_class => $link_table->name . 'Collection',
+            refers_to_by    => 'parent',
+        );
+
+        my $child_column = Jifty::Model::ModelClassColumn->new;
+        $child_column->create(
+            model_class     => $args->{relation_child}->model_class,
+            name            => $parent_name,
+            refers_to_class => $link_table->name . 'Collection',
+            refers_to_by    => 'child',
+        );
+
+        $args->{link_table}        = $link_table;
+        $args->{link_table_parent} = $link_table_parent;
+        $args->{link_table_child}  = $link_table_child;
+        $args->{parent_column}     = $parent_column;
+        $args->{child_column}      = $child_column;
+    }
+    else {
+        my $child_column = Jifty::Model::ModelClassColumn->new;
+        if ($args->{many_parents}) {
+            $child_column->create(
+                model_class     => $args->{relation_child}->model_class,
+                name            => $parent_name,
+                refers_to_class => $args->{relation_parent}->model_class->name . 'Collection',
+                refers_to_by    => $child_name,
+            );
+        }
+        else {
+            $child_column->create(
+                model_class     => $args->{relation_child}->model_class,
+                name            => $parent_name,
+                refers_to_class => $args->{relation_parent}->model_class->name,
+            );
+        }
+
+        my $parent_column = Jifty::Model::ModelClassColumn->new;
+        if ($args->{many_children}) {
+            $parent_column->create(
+                model_class     => $args->{relation_parent}->model_class,
+                name            => $child_name,
+                refers_to_class => $args->{relation_child}->model_class->name . 'Collection',
+                refers_to_by    => $parent_name,
+            );
+        }
+        else {
+            $parent_column->create(
+                model_class     => $args->{relation_parent}->model_class,
+                name            => $child_name,
+                refers_to_class => $args->{relation_child}->model_class->name,
+            );
+        }
+
+        $args->{parent_column} = $parent_column;
+        $args->{child_column}  = $child_column;
+    }
+
+    return 1;
+}
+
+=head1 AUTHOR
+
+Andrew Sterling Hanenkamp, C<< <hanenkamp at cpan.org> >>
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright 2007 Boomer Consulting, Inc. This program is free software and may be modified or distributed under the same terms as Perl itself.
+
+=cut
+
+1;

Modified: apps/CASPlus/trunk/lib/CASPlus/Util.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Util.pm	(original)
+++ apps/CASPlus/trunk/lib/CASPlus/Util.pm	Thu Jun 14 12:51:04 2007
@@ -21,6 +21,12 @@
   my $login_ticket   = CASPlus::Util->generate_ticket('LT');
   my $grant_cookie   = CASPlus::Util->generate_ticket('TGC');
 
+  # Convert user given name to a CamelCaseName
+  my $camel_case = CASPlus::Util->convert_to_camel_case('some-funky NAme');
+
+  # Convert user given name to a identifier_name
+  my $identifier = CASPlus::Util->convert_to_identifier('some-funky NAme');
+
 =head1 DESCRIPTION
 
 Currently, this houses the ticket generator, but any general-purpose function needed by CAS+ may go here in the future.
@@ -152,6 +158,44 @@
     }
 }
 
+=head2 convert_to_camel_case
+
+  my $camel_case = CASPlus::Util->convert_to_camel_case($str);
+
+This method removes all non-alphanumeric characters form the string and converts the first letter after each word boundary into a capital leter. The result is that a string is converted to CamelCase, which is used for internal table names.
+
+=cut
+
+sub convert_to_camel_case {
+    my ($class, $name) = @_;
+
+    $name =~ s/ ^ (\d) /D$1/gx;
+    $name =~ s/ (?: (?<= \W ) | (?<= ^ ) ) (\w) /\U$1/gx;
+    $name =~ s/ \W //gx;
+
+    return $name;
+}
+
+=head2 convert_to_identifier
+
+  my $identifier = CASPlus::Util->convert_to_identifier($str);
+
+This method replaces all non-alphanumeric characters from the string to underscores, eliminates underscores from the beginning and end, reduces strings of underscores to a single underscore, and converts all letters to lower case. The resulting identifier_name string is ideal for internal column names.
+
+=cut
+
+sub convert_to_identifier {
+    my ($class, $name) = @_;
+
+    $name = lc $name;
+    $name =~ s/ \W /_/gx;
+    $name =~ s/^ _+  //x;
+    $name =~ s/  _+ $//x;
+    $name =~ s/  _+  /_/gx;
+
+    return $name;
+}
+
 =head1 SEE ALSO
 
 L<http://www.ja-sig.org/products/cas/overview/protocol/index.html|CAS Protocol, Section 3, CAS Entities>

Added: apps/CASPlus/trunk/t/10-model-ProfileRelationship.t
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/t/10-model-ProfileRelationship.t	Thu Jun 14 12:51:04 2007
@@ -0,0 +1,84 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+=head1 DESCRIPTION
+
+A basic test harness for the ProfileRelationship model.
+
+=cut
+
+use Jifty::Test tests => 13;
+$Carp::Verbose = 1;
+
+# Make sure we can load the model
+use_ok('CASPlus::Model::ProfileRelationship');
+
+# Grab a system user
+my $system_user = CASPlus::CurrentUser->superuser;
+ok($system_user, "Found a system user");
+
+# Create a test profile
+my $parent_profile = CASPlus::Model::Profile->new(current_user => $system_user);
+$parent_profile->create(
+    name         => 'Department',
+    profile_type => 'role',
+);
+ok($parent_profile->id, 'created a parent profile for testing');
+
+# Create a test profile
+my $child_profile = CASPlus::Model::Profile->new(current_user => $system_user);
+$child_profile->create(
+    name         => 'Employee',
+    profile_type => 'user',
+);
+ok($parent_profile->id, 'created a child profile for testing');
+
+# Try testing a create
+my $o = CASPlus::Model::ProfileRelationship->new(current_user => $system_user);
+my ($id) = $o->create(
+    name                        => "Employee's Department",
+    parent_name                 => 'Department',
+    child_name                  => 'Employees',
+    relation_parent             => $parent_profile,
+    relation_child              => $child_profile,
+    many_parents                => 0,
+    many_children               => 1,
+    roles_propagate_to_children => 1,
+);
+ok($id, "ProfileRelationship create returned success");
+ok($o->id, "New ProfileRelationship has valid id set");
+is($o->id, $id, "Create returned the right id");
+
+# And another
+$o->create(
+    name                        => 'Unit Director',
+    parent_name                 => 'Department Managed',
+    child_name                  => 'Manager',
+    relation_parent             => $parent_profile,
+    relation_child              => $child_profile,
+    many_parents                => 0,
+    many_children               => 0,
+    roles_propagate_to_children => 1,
+);
+ok($o->id, "ProfileRelationship create returned another value");
+isnt($o->id, $id, "And it is different from the previous one");
+
+# Searches in general
+my $collection =  CASPlus::Model::ProfileRelationshipCollection->new(current_user => $system_user);
+$collection->unlimit;
+is($collection->count, 2, "Finds two records");
+
+# Searches in specific
+$collection->limit(column => 'id', value => $o->id);
+is($collection->count, 1, "Finds one record with specific id");
+
+# Delete one of them
+$o->delete;
+$collection->redo_search;
+is($collection->count, 0, "Deleted row is gone");
+
+# And the other one is still there
+$collection->unlimit;
+is($collection->count, 1, "Still one left");
+


More information about the Jifty-commit mailing list