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

NAME

  Class::Builder - auto-generator of class accessors/special methods

SYNOPSIS

Creating Class Members (fields):

  package SystemUser;
  use Class::Builder {
    uid    => { number => undef },
    uname  => { string => 'default user' },
    group  => { arrayref => []},
    ctime  => { number => time },
    disable => { boolean => 0 },
    log_count => { counter => 0},
  };
  1;

  # then in your script:
  package main;
  use SystemUser;

  my $user = new SystemUser ({name => 'chopin'});
  print $user->uname(), "\n"; # print chopin;
  print scalar localtime $user->ctime(), "\n";

  foreach my $group ($user->group){
    &do_something($group);
  }

  my $user = new SystemUser ({uname => 'chopin'});
  print $user->uname(), "\n"; # print chopin;
  print scalar localtime $user->ctime(), "\n";

  sub system_user_loggin{
    if($user->disable){
      die "you account is disabled, contact system administrator.";
    }
    # ... do some thing
    $user->log_count_add;
    print "you are logged into the system. you have loged ",$user->log_count," times.";
  }

  system_user_loggin(); # print $user->log_count as 1.

Special Methods

  package SystemUser;
  use Class::Builder {
    '-methods' => {
      constructor => 'create',
      dumper => 'as_string',
      clone  => 'copy',
    },
    uid    => { number => undef },
    uname  => { string => '' },
  };
  1;

  package main;
  use SystemUser;

  my $user = SystemUser->create({name=>'mozart'});

  my $user_cp = $user->copy;
   # deep copy the structure, not only the pointer

  print $user_cp->as_string() # dump the contents of $user

There also a function `struct' opened for you, let you create modules `on the fly':

  package SystemUser;
  use Class::Builder;

  struct Machine => {
    machine_name => { string => 'default name' },
    location => {string => '' }
  }; # define Machine before use it.

  struct {
    uname => {string => ''},
    main_machine => { Machine => undef },
  }; # implicit class name: 'SystemUser' in this case.


  package main;
  my $machine = new Machine( machine_name => 'veryslow' );
  my $user = new SystemUser( uname => 'mozart', main_machine => $machine );

  print $user->main_machine->machine_name();

DESCRIPTION

Class::Builder is a module helps OOP programmers to create `class's (packages, in terms of perl) in several ways of automation.

If you've used one of Class::MethodMaker, Class::Struct or Class::Accessor, the concept of Class::Builer is not newer to you. In fact, this module can be viewed as a combination of the above modules. I'm trying to include most frequently used functions, while keep the module as lightweight as possible.

Field Methods:

To create a new member field for you class, simply say:

  use Class::Builder {
    <fieldname> => { <fieldtype> => <default_vaule>, [ final => 1 ]},
  };

where <fieldname> is the name you want to set as a member field, it must be legal as a name of perl function.

If you defined the field as `final', you can not use the accessor methods to change the field's value. But you still can change it by directly access data stored in the object (a blessed hashref).

`final' attribute is ignored by `hashref' and `arrayref' field, under current implementation.

The following field types are available now:

  • string

    You can get/set a string field via functions. For example

      $user->name("myname")

    change `$user''s name to `myname'. You can even do:

      $user->name = "otername";

    Then you can access the value by: print $user->name;

  • number

    Provide same functions as string field. (no extra checking for this field under current implementation).

  • boolean

    Boolean field will be set to '1' if you passed a argument has `true' values in perl.

    Additionally, for each boolean field `x', you can:

      $obj->x_rev();    # reverse. 1 to 0, 0 to 1.
      $obj->x_reset();  # reset to it's default values.
  • counter

    Count is a number fields that provides some additional values:

      $obj->x_add();  # x becomes x+1
      $obj->x_add(2); # x becomes x+2
      $obj->x_remove(); # do the reverse of above
      $obj->x_set(19);  # directly set the value to 19
      $obj->x_reset();  # reset to 0 or any value you defined
  • hashref

    There a special methods provides for fields contains reference to hashes and arraies. Those methods are simple wrappers over perl build-in functions, as the following example:

      package SysInfo;
      use Class::Builder {
        passwd => {hashref => {huang => 'passwdinsecret', phantom=>'longpasswd'}},
      };
    
      package main;
      my $sysinfo = new SysInfo;
      $sysinfo->passwd->{newuser} = 'validpasswd';
      do_something() if($sysinfo->passwd_exists('huang'));
      foreach my $user ($sysinfo->passwd_keys){ do_something($user); }
      foreach my $pass ($sysinfo->passwd_values){ checkpasswd($pass); }
      $sysinfo->passwd_delete('huang');
  • arrayref

      package MusicCD;
      use Class::Builder {
        trackList => {
            arrayref => [qw(track1 silent noise)],
          },
      }
    
      package main;
      my $mcd = new MusicCD;
      foreach my $tract ($mcd->trackList){ ... }
      my $arrayref = $mcd->trackList;
      # use this to get the count
      my $count = $mcd->trackList_count;
      $mcd->trackList_push('newTrack');
      $mcd->trackList_pop(); # newTrack
      $mcd->trackList_unshift('firstTrack');
      $mcd->trackList_shift(); # firstTract
      $mcd->trackList_splice(1, 1);
  • other object

    Any other field name will be interpretered as external class names. Sign a default value is little different than other fields:

      # if you passes a single scalar, it will be treated as
      # the constructor of the value:
      use Class::Builder {
        filehandler => {IO::File => 'new'},
      }; # create a new IO::File object
    
      # if you passes a array reference, it will be treated as
      # ['constructor, arg1, arg2, ...]
      use Class::Builder {
        filehandler => {IO::File => ['new', 'filename', 'r']},
      }; # this time, open filename for you too.
    
      # if a reference of other type passed, it will be treated
      # as the object itself:
      my $fh = new IO::File('filename', 'w') or die "$!";
      use Class::Builder {
        filehandler => {IO::File => $fh},
      }; # just use $fh

    Note at the last example, we do not require ref $fh to be IO::File, so, use a object drived from some subclass of IO::File is allowed.

Two special functions applies for all member fields:

  • clear('field1', 'field2', ...)

    will set member field 'field1', 'field2', ... to undef.

  • get('field1', 'field2', ...)

    return a list contains values of member fields 'field1', 'field2', ....

Note that currently all types are implemented as a key of a perl hash, so you can assign any scalar value to any field without causing errors. Typically, number field and string field have no difference it all, it is introduced only for future development (such as dynamic linkage with databases). I.e, you can sign a perl string to a number field, but we do not recommend you do that.

On-the-Fly Classes

You can use function struct() in three styles:

  • struct(\%args)

    where \%args takes the same form as you used in

      use Class::Builder \%args;

    Class::Builder will take the current package as the class name.

  • struct(Classname1 = \%args1, Classname2=> \%args2, ...)>

    This form of struct creates classes `on the fly', so, you do not need to define classname1, classname2 munually. You can thinks the code:

      struct(Classname1 => \%args1, Classname2=> \%args2)

    as a shortcut as:

      package Classname1;
      use Class::Builder \%args1;
      1;
    
      package Classname2;
      use Class::Builder \%args2;
      1;
  • struct([classname = \%args, classname2=> \%args])>

    Almost as same as the above case. Passes arguments as a reference of a array is (theorically) little faster than passes them as a legacy array. Use this form if you concerns with perfomance issues.

    IMPORTANT: struct({classname = \%args, classname2=> \%args})> do not work as you expected. (Class::Builder interpreted the hashref as arguments for current class, as the first style mentioned above).

    Note that

      package SomeClass;
      use Class::Builder { ... some defination ... };
      struct { ... some defination ... };

    is not recommended, any field defined in struct could not get a chance to setup it's default values.

Special Methods

Class::Builder also has a ability to create special methods:

  • constructor

    By default, Class::Builder will create a function new() as the constructor. you can change the name of constructor to others such like create() by defining:

      use Class::Builder {
        '-methods' => { constructor => 'create' },
      };

    or even you can define more than one constructor, each has same functions:

      use Class::Builder {
        '-methods' => { constructor => ['create', 'new'] },
      };

    you can pass a hash reference to initialize member fields:

      package SomeClass;
      use Class::Builder {
        '-methods' => { constructor => 'create' },
        somefield => { string => '' },
      };
      1;
      package main;
      my $sc = new SomeClass ({somefield => 'newvalues'});

    Pass initial values as a list is also acceptable:

      my $sc = new SomeClass (somefield => 'newvalues');

    UNTIL you defined any initializers as below.

  • initializer

    You can define one or more initializers, each will by called by constructor (new() , by the default) in orders as you defined:

      package SomeBody;
      use Class::Builder {
        '-methods' => {initializer => ['init1', 'init2']},
      }
    
      sub init1{
          ...
        };
    
      sub init2{
          ...
        };

    Then, init1() and init2() will get called whenever you create a new instance of SomeBody:

      package SystemUser;
        use Class::Builder {
          '-methods' => {
              initializer => ['init1', 'init2'],
            },
          name => {string => 'bethoven'},
        };
    
      sub init1{shift->{name} = 'chopin';}
      sub init2{shift->{name} = 'Walsh';}
    
      1;
    
      package main;
      my $user = new SystemUser( {name => 'mozart'} );
    
      print $user->name(); # print Walsh

    You can pass arguments as a array (not a hash reference) to constructor, and all of the initializers will get them also.

INHERITAGE

To create a subclass of Class::Builder, at lease you must define two functions:

  • struct();

    use the following code:

      sub struct{
        __PACKAGE__->_struct(@_);
      }
  • setup();

    Do something before Class::Builder takes actions.

  • known_types();

    If you want to provide some new field types, rewrite this function to avoid fatal errors.

BUGS

  This package is not yet well tested.

AUTHOR

  Wei, Huang < huang@toki.waseda.jp >

SEE ALSO

5 POD Errors

The following errors were encountered while parsing the POD:

Around line 813:

'=item' outside of any '=over'

Around line 859:

You forgot a '=back' before '=head2'

Around line 863:

'=item' outside of any '=over'

Around line 937:

You forgot a '=back' before '=head1'

Around line 960:

You forgot a '=back' before '=head1'