Name

Class::Plain::Document::Cookbook - Cookbook of Class::Plain

Description

This is the cookbook of the Class::Plain.

Use Class::Plain with Existing Classes

Use Class::Plain with existing classes.

Before:

  package Point;
  
  sub new {
    my $class = shift;
    
    my $self = {@_};
    
    return bless $self, ref $class || $class;
  }
  
  sub x {
    my $self = shift;
    
    if (@_) {
      $self->{x} = shift;
      return $self;
    }
    
    return $self->{x};
  }

  sub y {
    my $self = shift;
    
    if (@_) {
      $self->{y} = shift;
      return $self;
    }
    
    return $self->{y};
  }
  
  my $point = Point->new(x => 1, y => 2);
  $point->x(3);
  $point->y(4);
  
  print $point->x . "\n";
  print $point->y . "\n";

After

Use class, field, method keywords.

  use Class::Plain;
  
  class Point {
    field x;
    field y;
    
    method new : common {
      my $self = {@_};
      
      return bless $self, ref $class || $class;
    }
    
    method x {
      if (@_) {
        $self->{x} = shift;
        return $self;
      }
      
      return $self->{x};
    }

    method y {
      if (@_) {
        $self->{y} = shift;
        return $self;
      }
      
      return $self->{y};
    }
  }

  my $point = Point->new(x => 1, y => 2);
  $point->x(3);
  $point->y(4);
  
  print $point->x . "\n";
  print $point->y . "\n";

Rewite Existing Classes using Class::Plain

If you don't need to take care of the fll backword compatilibity, you can rewrite above code to the following code.

  use Class::Plain;
  
  class Point {
    field x : rw;
    field y : rw;
  }

  my $point = Point->new(x => 1, y => 2);
  $point->x(3);
  $point->y(4);
  
  print $point->x . "\n";
  print $point->y . "\n";

Customizing Object Initialization

A class created by Class::Plain inherits Class::Plain::Base. Class::Plain::Base has the new method.

  use Class::Plain;
  
  # Inherit Class::Plain::Base
  class Point {
    field x;
    field y;
  }

  # (x => 1)
  my $point = Point->new(x => 1);

This can be customize by overriding the new method.

  use Class::Plain;
  
  class Point {
    field x;
    field y;
    
    # Override the new method
    method new : common {
      my $self = $class->SUPER::new(@_);
      
      $self->{x} //= 0;
      $self->{y} //= 0;
      
      return $self;
    }
  }
  
  # (x => 1, y => 0)
  my $point = Point->new(x => 1);

Using Other Data Structures

Array Based Object

An example of the array based object.

  use Class::Plain;
  
  class ArrayBased {
    method new : common {
      return bless [@_], ref $class || $class;
    }
    
    method push {
      my ($value) = @_;
      
      push @$self, $value;
    }
    
    method get {
      my ($index) = @_;
      
      return $self->[$index];
    }
    
    method to_array {
      return [@$self];
    }
  }
  
  my $object = ArrayBased->new(1, 2);

  $object->to_array; # [1, 2]
  
  $object->push(3);
  $object->push(5);
  
  $object->get(0); # 1
  $object->get(1); # 2
  $object->get(2); # 3
  $object->get(3); # 5
  $object->to_array; # [1, 2, 3, 5]

Scalar Based Object

An example of the scalar based object.

  use Class::Plain;
  
  class ScalarBased {
    method new : common {
      
      my $value = shift;
      
      return bless \$value, ref $class || $class;
    }
    
    method to_value {
      return $$self;
    }
  }
  
  my $object = ScalarBased->new(3);
  
  $object->to_value; # 3;

Inheritance

Single Inheritance

An example of single inheritance.

  use Class::Plain;
  
  class Point {
    field x;
    field y;
    
    method new : common {
      my $self = $class->SUPER::new(@_);
      
      $self->{x} //= 0;
      $self->{y} //= 0;
      
      return $self;
    }
    
    method move {
      my ($x, $y) = @_;
      
      $self->{x} += $x;
      $self->{y} += $y;
    }
    
    method describe {
      print "A point at ($self->{x}, $self->{y})\n";
    }
  }
  
  my $point = Point->new(x => 5, y => 10);
  $point->describe;

  class Point3D : isa(Point) {
    field z;
    
    method new : common {
      my $self = $class->SUPER::new(@_);
      
      $self->{z} //= 0;
      
      return $self;
    }
    
    method move {
      my ($x, $y, $z) = @_;
      
      $self->SUPER::move($x, $y);
      $self->{z} += $z;
    }
    
    method describe {
      print "A point at ($self->{x}, $self->{y}, $self->{z})\n";
    }
  }
  
  my $point3d = Point3D->new(x => 5, y => 10, z => 15);
  $point3d->describe;

Multiple Inheritance

An example of multiple inheritance. It is used for modules using multiple inheritance such as DBIx::Class.

  use Class::Plain;
  
  # The multiple inheritance
  class MultiBase1 {
    field b1 : rw;
    
    method ps;
    
    method b1_init {

      push @{$self->ps}, 2;
      $self->{b1} = 3;
    }
  }
  
  class MultiBase2 {
    field b2 : rw;

    method ps;
    
    method b1_init {

      push @{$self->ps}, 7;
      $self->{b1} = 8;
    }
    
    method b2_init {
      
      push @{$self->ps}, 3;
      $self->{b2} = 4;
    }
  }

  class MultiClass : isa(MultiBase1) isa(MultiBase2) {
    field ps : rw;
    
    method new : common {
      my $self = $class->SUPER::new(@_);
      
      $self->{ps} //= [];
      
      $self->init;
      
      return $self;
    }
    
    method init {
      push @{$self->{ps}}, 1;
      
      $self->b1_init;
      $self->b2_init;
    }
    
    method b1_init {
      $self->next::method;
    }
    
    method b2_init {
      $self->next::method;
    }
  }
  
  my $object = MultiClass->new;
  
  $object->b1; # 3
  
  $object->b2; # 4
  
  $object->ps; # [1, 2, 3]

Embeding Class

An example of embeding classes. Embeding class is similar to Corinna Role although the methods are embeded manually in the case of using Class::Plain.

  use Class::Plain;

  class EmbedBase1 {
    field b1 : rw;
    
    method ps;
    
    method init {

      push @{$self->ps}, 2;
      $self->{b1} = 3;
    }
  }
  
  class EmbedBase2 {
    field b2 : rw;

    method ps;
    
    method init {
      
      push @{$self->ps}, 3;
      $self->{b2} = 4;
    }
  }

  class EmbedClass {
    field ps : rw;
    
    method new : common {
      my $self = $class->SUPER::new(@_);
      
      $self->{ps} //= [];
      
      $self->init;
      
      return $self;
    }
    
    method init {
      push @{$self->{ps}}, 1;
      
      $self->EmbedBase1::init;
      $self->EmbedBase2::init;
    }
    
    method b1 { $self->EmbedBase1::b1(@_) }
    
    method b2 { $self->EmbedBase2::b2(@_) }
  }
  
  my $object = EmbedClass->new;
  
  $object->b1; # 3
  $object->b2; # 4
  $object->ps; # [1, 2, 3]

Role

An example of the role. The above example is rewritten by "role" in Class::Plain.

  use Class::Plain;

  role EmbedBase1 {
    field b1 : rw;
    
    method ps;
    
    method init {

      push @{$self->ps}, 2;
      $self->{b1} = 3;
    }
  }
  
  role EmbedBase2 {
    field b2 : rw;

    method ps;
    
    method init {
      
      push @{$self->ps}, 3;
      $self->{b2} = 4;
    }
  }

  class EmbedClass : does(EmbedBase1) does(EmbedBase2) {
    field ps : rw;
    
    method new : common {
      my $self = $class->SUPER::new(@_);
      
      $self->{ps} //= [];
      
      $self->init;
      
      return $self;
    }
    
    method init {
      push @{$self->{ps}}, 1;
      
      $self->EmbedBase1::init;
      $self->EmbedBase2::init;
    }
  }
  
  my $object = EmbedClass->new;
  
  print $object->b1 . "\n"; # 3
  print $object->b2 . "\n"; # 4
  print "@{$object->ps}" . "\n"; # [1, 2, 3]

Use Other OO Module With Class::Plain

Class::Plain can be used with other OO modules.

Moo

Use Moo with Class::Plain.

  use Class::Plain;
  
  class Foo : isa() {
    field x;
    
    use Moo;
    has "x" => (is => 'rw');
    
    method to_string { "String:" . $self->x }
  }

  my $object = Foo->new(x => 1);
  print $object->x . " " . $object->to_string;

Class::Accessor::Fast

Use Class::Accessor::Fast with Class::Plain.

  use Class::Plain;
  
  class Foo {
    field x;
    
    use base 'Class::Accessor::Fast';
    __PACKAGE__->mk_accessors('x');
    method to_string { "String:" . $self->x }
  }
  
  my $object = Foo->new(x => 1);
  print $object->x . " " . $object->to_string;

Class::Accessor

Use Class::Accessor with Class::Plain.

  use Class::Plain;
  
  class Foo {
    field x;
    
    use base 'Class::Accessor::Fast';
    __PACKAGE__->mk_accessors('x');
    method to_string { "String:" . $self->x }
  }
  
  my $object = Foo->new(x => 1);
  print $object->x . " " . $object->to_string;

Mojo::Base

Use Mojo::Base with Class::Plain.

  use Class::Plain;
  
  class Foo : isa() {
    field x;
    
    use Mojo::Base -base;
    has "x";
    
    method to_string { "String:" . $self->x }
  }

  my $object = Foo->new(x => 1);
  print $object->x . " " . $object->to_string;

Weakening Field

Weaken a field.

  use Scalar::Util 'weaken';
  
  use Class::Plain;
  
  class Foo {
    field x;
    
    method weaken_x {
      weaken $self->{x};
    }
  }

Class Variable

A class variable is represented using a package variable or a lexical variable.

  use Class::Plain;
  
  class ClassVariable {
    # Public
    our $FOO;
    
    # Private
    my $BAR;
    
    BEGIN {
      $FOO = 1;
      $BAR = 2;
    }
    
    method FOO : common { $FOO }
    method BAR : common { $BAR }
  }
  
  ClassVariable->FOO: # 1
  ClassVariable->BAR; # 2

Signatures

Use Class::Plain with subroutine signatures from Perl 5.26+.

  use v5.36; # Enable signatures and other features.
  
  use Class::Plain;
  
  class Point {
    field x;
    field y;
    
    method new : common {
      my $self = $class->SUPER::new(@_);
      
      $self->{x} //= 0;
      $self->{y} //= 0;
      
      return $self;
    }
    
    # Subroutine signatures
    method move ($x = 0, $y = 0) {
      
      $self->{x} += $x;
      $self->{y} += $y;
    }
    
    method describe {
      print "A point at ($self->{x}, $self->{y})\n";
    }
  }
  
  my $point = Point->new(x => 5, y => 10);
  $point->describe;