The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Class::HPLOO - Easier way to declare classes on Perl, based in the popular class {...} style and ePod.

DESCRIPTION

This is the implemantation of OO-Classes for HPL. This brings an easy way to create PM classes, but with HPL resources/style.

USAGE

  use Class::HPLOO ;

  class Foo extends Bar , Baz {
  
    use LWP::Simple qw(get) ; ## import the method get() to this package.
  
    attr ( array foo_list , int age , string name , foo ) ## define attributes.

    vars ($GLOBAL_VAR) ; ## same as: use vars qw($GLOBAL_VAR);

    my ($local_var) ;
  
    ## constructor/initializer:
    sub Foo {
      $this->{attr} = $_[0] ;
    }
  
    ## methods with input variables declared:
    sub get_pages ($base , \@pages , \%options) {
      my @htmls ;
      
      if ( $options{proxy} ) { ... }
  
      foreach my $pages_i ( @pages ) {
        my $url = "$base/$pages_i" ;
        my $html = get($url) ;
        push(@htmls , $html) ;
        $this->cache($url , $html) ;
      }
      
      return @htmls ;
    }
    
    ## methos like a normal Perl sub:
    sub cache {
      my ( $url , $html ) = @_ ;
      $this->{CACHE}{$url} = $html ;
    }
    
    sub attributes_example {
      $this->set_foo_list(qw(a b c d e f)) ;
      my @l = $this->get_foo_list ;
      
      $this->set_age(30) ;
      $this->set_name("Joe") ;
      $this->set_foo( time() ) ;
      print "NAME: ". $this->get_name ."\n" ;
      print "AGE: ". $this->get_age ."\n" ;
      print "FOO: ". $this->get_foo ."\n" ;
    }
    
  }
  
  ## Example of use of the class:
  
  package main ;
  
  my $foo = new Foo(123) ;
  $foo->get_pages('http://www.perlmonks.com/', ['/index.pl','/foo'] , {proxy => 'localhost:8080'}) ;

CONTRUCTOR

The "method" new() is automatically declared by Class::HPLOO, then it calls the initializer that is a method with the name of the class, like Java.

  class Foo extends {
    ## initializer:
    sub Foo {
      $this->{attr} = $_[0] ;
    }
  }

** Note that what the initializer returns is ignored! Unless you return a new constructed object or UNDEF. Return UNDEF (a constant of the class) makes the creation of the object return undef.

DESTRUCTOR

Use DESTROY() like a normal Perl package.

Class VERSION

From Class::HPLOO 0.12, you can define the class version in it's declaration:

  class Foo [0.01] extends bar , baz {
   ...
  }

This is just a replacement of the original Perl syntax:

  use vars qw($VERSION) ;
  $VERSION = '0.01' ;

SUBs

The syntax for subs was extend to allow argument definitions:

  Class Foo {
    sub add( $x , $y ) {
      return $x + $y ;
    }
  }

Also you can define HASH and ARRAY arguments:

  Class Foo {
    sub add( \@list , $n , \%hash ) {
      foreach my $list_i ( @list ) {
        $list_i += $n ;
      }
    }
  }

This new syntax for arguments make much more easy to create functions and paste references for HASHes and ARRAYs.

INLINE SUBs

From version 0.18 is possible to define inline functions directly in the class code:

  class Foo {
  
    sub normal_perl_sub {
      print "PERL SUB> @_\n" ;
    }
  
    sub[C] int add( int x , int y ) {
      int res = x + y ;
      return res ;
    }
  
  }

The parser for the INLINE SUBs is defined by:

  sub[\w+].*?{...}
       |   |   |
       |   |   ---> body.
       |   ---> sub/function/method header.
       ---> language

So, basically is possible to use any language that Inline supports. Here's a Java example:

  class Foo {
  
    sub[Java] public void msg( String txt ) {
      System.out.println(txt) ;
    }
  
  }

Enjoy! ;-P

ATTRIBUTES , GLOBAL VARS & LOCAL VARS

You can use 3 types of definitions for class variables:

ATTRIBUTES

The main difference of an attribute of normal Perl variables, is the existence of the methods set and get for each attribute/key. Also an attribute can have a type definition and a handler, soo each value can be automatically formatted before be really set.

For better OO and persistence of objects ATTRIBUTES should be the main choice.

To set an attribute you use:

To define:

  attr( type name , ref type name , array name , hash name , sub id )

To set:

  $this->set_name($val) ;
  ## or:
  $this->{name} = $val ;

To get:

  my $foo = $this->get_name ;
  ## or:
  my $foo = $this->{name} ;

The attr() definition has this syntax:

  REF? ARRAY?|HASH? TYPE? NAME
NAME

The name of the attribute.

An attribute only can be set by set_name() and get by get_name(). It also has a tied key with it's NAME in the HASH reference of the object.

TYPE (optional)

Tells the type of the attribute. If not defined any will be used as default.

Standart types:

any

Accept any type of value.

string | str

A normal string.

boolean | bool

A boolean value. Also accept 'true' and 'false' strings.

integer | int

An integer that accepts only [0-9] digits.

floating | float

A floating point, with the format /\d+\.\d+/. If /\.\d+$/ doesn't exists '.0' will be added in the end.

sub

Define an attribute as a sub call:

  class foo {
    attr( sub id ) ;
  
    sub id() { return 123 ; }
  }
  
  ## call:
  
  $foo->id() ;
  ## or
  print " $foo->{id} \n" ;
Object

This type will accept only object types or a defined object class:

  class Foo {
    attr ( XML::Smart xml , object any_obj , obj any_obj2 , UNIVERSAL any_obj3 ) ;
  }

In the example above the attribute xml will accept only objects from XML::Smart, or objects that inherites from XML::Smart. The attribute any_obj will aceept any type of object. You can see that the types object, obj and UNIVERSAL are all the same.

Personalized types:

To create your own type you can use this syntax:

  attr( &mytypex foo ) ;

Then you need to create the sub that will handle the type format:

  sub mytypex ($value) {
    $value =~ s/x/y/gi ; ## do some formatting.
    return $value ; ## return the value
  }

Note that a type will handle one value/SCALAR per time. Soo, the same type can be used for array attributes or not:

  attr( &mytypex foo , array &mytypex list ) ;

Soo, in the definition above, when list is set with some ARRAY, eache element of the array will be past one by one to the &mytypex sub.

REF (optional)

Tells that the value is a reference. Soo, you need to always set it with a reference:

  attr( ref foo ) ;

  ...
  
  $this->set_foo( \$var ) ;
  ## or
  $this->set_foo( [1 , 2 , 3] ) ;
ARRAY or HASH (optional)

Tells that the value is an array or a hash of some type.

Soo, for this type:

  attr( array int ages ) ;

You can set and get without references:

  $this->set_ages(20 , 25 , 30 , 'invalid' , 40) ;
  
  ...
  
  my @ages = $this->get_ages ;

Note that in this example, all the values of the array will be formated to integer. Soo, the value 'invalid' will be set to undef.

The attribute definition was created to handle object databases and the persistence of objects created with Class::HPLOO. Soo, for object persistence you should use only ATTRIBUTES and GLOBAL VARS.

Note that for object persistence, keys sets in the HASH reference of the object, that aren't defined as attributes, own't be saved. Soo, for the attr() definition below, the key foo won't be persistent:

  attr( str bar , int baz ) ;
  $this->{bar} = 'persistent' ;
  $this->{foo} = 'not persistent' ;

GLOBAL VARS

To set a global variable (static variable of a class), you use this syntax:

  vars ($foo , @bar , %baz) ;

Actually this is the same to write:

  use vars qw($foo @bar %baz) ;

** Note that a global variable is just a normal Perl variable, with a public access in it's package/class.

LOCAL VARS

This are just variables with private access (only accessed by the scope defined with it).

  my ($foo , @bar , %baz) ;

** Note that a local variable is just a normal Perl variable accessed only through it's scope.

Persistence with HDB::Object

From Class::HPLOO/0.16 we can use HDB::Object as a base class for persitence.

Example of class built with it:

  use Class::HPLOO ;
  
  class User extends HDB::Object {
  
    use HDB::Object ;
  
    attr( user , pass , name , int age ) ;
    
    sub User( $user , $pass , $name , $age ) {
      $this->{user} = $user ;
      $this->{pass} = $pass ;
      $this->{name} = $name ;
      $this->{age} = time ;
    }
  
  }

When you create the object it will be automatically stored in the HDB database:

  my $user = new User('joe' , '123' , 'Joe Smith' , 30) ;
  ...
  $user = undef ; ## Destroy and automatically save (insert into table User).
  

To load an already stored object you should use the method load():

  my $user = load User("user eq 'joe'") ;
  $user->{age} = 40 ;
  $user = undef ; ## Destroy and automatically save (update col age).

** Note that you don't need to care about the DB, including the creation of the table! Is everything automatic.

METHODS

All the methods of the classes are declared like a normal sub.

You can declare the input variables to receive the arguments of the method:

  sub methodx ($arg1 , $arg2 , \@listref , \%hasref , @rest) {
    ...
  }
  
  ## Calling:
  
  $foo->methodx(123 , 456 , [0,1,2] , {k1 => 'x'} , 7 , 8 , 9 ) ;

PREDEFINED METHODS

ATTRS

Return the list of attributes in the declaration order.

__CLASS__

A constant that returns the name of the class.

HTML BLOCKS

You can use HTML blocks in the class like in HPL documents:

  class Foo {
  
    sub test {
      print <% html_test>(123) ;
    }
    
    <% html_test($n)
      <hr>
      NUMBER: $n
      <hr>    
    %>
  
  }

SUB CLASSES

From version 0.04+ you can declare sub-classes:

  class foo {
    class subfoo { ... }
  }

You also can handle the base name of a class adding "." in the begin of the class name:

  class foo {
    class .in { ... }
  }

In the example above, the class name .in will be translated as foo::in.

USE_BASE

By default the code generated by Class::HPLOO is stand alone, so you don't need to install Class::HPLOO to run it, but as disadvantage the code to be loaded by a group of classes built with Class::HPLOO will be bigger.

As an option you can use the USE_BASE parameter, that will define the use of the class Class::HPLOO::Base in the @ISA path, so all the common codes for class that are built by Class::HPLOO will be shared, saving memory and load time:

  use Class::HPLOO qw(use_base) ;
  
  class Foo {
  }

That will generate this code:

  { package Foo ;
  
  use strict qw(vars) ; no warnings ;
  
  use vars qw(%CLASS_HPLOO @ISA) ;
  
  @ISA = qw(Class::HPLOO::Base UNIVERSAL) ;
  
  my $CLASS = 'Foo' ; sub __CLASS__ { 'Foo' } ;
  
  use Class::HPLOO::Base ;
    
  }

Without USE_BASE this is the default code to be generated:

  { package Foo ;
  
  use strict qw(vars) ; no warnings ;
  
  use vars qw(%CLASS_HPLOO @ISA) ;
  
  @ISA = qw(UNIVERSAL) ;
  
  my $CLASS = 'Foo' ; sub __CLASS__ { 'Foo' } ;
   
  sub new { 
    if ( !defined &Foo && @ISA > 1 ) {
      foreach my $ISA_i ( @ISA ) {
        return &{"$ISA_i\::new"}(@_) if defined &{"$ISA_i\::new"} ;
      }
    }
    my $class = shift ;
    my $this = bless({} , $class) ;
    no warnings ;
    my $undef = \'' ;
    sub UNDEF {$undef} ;
    my $ret_this = defined &Foo ? $this->Foo(@_) : undef ;
    if ( ref($ret_this) && UNIVERSAL::isa($ret_this,$class) ) { $this = $ret_this }
    elsif ( $ret_this == $undef ) { $this = undef }
    return $this ;
  }
    
  sub SUPER {
    my ($pack , undef , undef , $sub0) = caller(1) ;
    unshift(@_ , $pack) if ( (!ref($_[0]) && $_[0] ne $pack) || (ref($_[0]) && !UNIVERSAL::isa($_[0] , $pack)) ) ;
    my $sub = $sub0 ;
    $sub =~ s/.*?(\w+)$/$1/ ;
    $sub = 'new' if $sub0 =~ /(?:^|::)$sub\::$sub$/ ;
    $sub = "SUPER::$sub" ;
    $_[0]->$sub(@_[1..$#_]) ;
  }
    
  }

DUMP

You can dump the generated code:

  use Class::HPLOO qw(dump nice) ;

** The nice option just try to make a cleaner code.

BUILD

The script "build-hploo.pl" can be used to convert .hploo files to .pm files.

So you can write a Perl Module with Class::HPLOO and release it as a normal .pm file without need Class::HPLOO installed.

If you have ePod (0.03+) installed you can use ePod to write your documentation. For .hploo files the ePod need to be always after __END__.

Note that ePod accepts POD syntax too, so you still can use normal POD for documentation.

SEE ALSO

Perl6::Classes, HPL, HDB::Object, HDB.

AUTHOR

Graciliano M. P. <gmpassos@cpan.org>

I will appreciate any type of feedback (include your opinions and/or suggestions). ;-P

COPYRIGHT

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.