The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

OP::Class - Root-level "Class" class

VERSION

$Id: //depotit/tools/source/snitchd-0.20/lib/OP/Class.pm#9 $

SYNOPSIS

Class Allocation

  #
  # File: OP/Example.pm
  #

  use OP qw| create true false |;

  create "OP::Example" => {
    #
    # This is an empty class prototype
    #
  };

Class Consumer

  #
  # File: testscript.pl
  #

  use strict;
  use warnings;

  use OP::Example;

  my $exa = OP::Example->new();

  $exa->setName("My First OP Object");

  $exa->save("This is a checkin comment");

  say "Saved object:";

  $exa->print();

DESCRIPTION

OP::Class is the root-level parent class in OP, and also provides the class prototyping function create().

METHODS

Public Class Methods

  • get(OP::Class $class: Str $key)

    Get the named class variable

      my $class = "OP::Example";
    
      my $scalar = $class->get($key);
    
      my @array = $class->get($key);
    
      my %hash = $class->get($key);
  • set(OP::Class $class: Str $key, *@value)

    Set the named class variable to the received value

      my $class = "OP::Example";
    
      $class->set($key, $scalar);
    
      $class->set($key, @array);
    
      $class->set($key, %hash);
  • pretty(OP::Class $class: Str $key)

    Transform camelCase to Not Camel Case

      my $class = "OP::Example";
    
      my $uglyStr = "betterGetThatLookedAt";
    
      my $prettyStr = $class->pretty($uglyStr);
  • members(OP::Class $class:)

    Class introspection method.

    Return an array ref of all messages supported by this class.

    Does not include messages from superclasses.

      my $members = OP::Example->members();
  • membersHash(OP::Class $class:)

    Class introspection method.

    Return a hash ref of all messages supported by this class.

    Does not include messages from superclasses.

      my $membersHash = OP::Example->membersHash();

Private Class Methods

  • init(OP::Class $class:)

    Abstract callback method invoked immediately after a new class is allocated via create().

    Override in subclass with additional logic, if necessary.

  • __checkVarName(OP::Class $class: Str $varName)

    Checks the "safeness" of a class variable name before eval'ing it.

PROTOTYPE COMPONENTS

Class (Package) Name

The name of the class being created is the first argument sent to create().

  use OP qw| create |;

  #
  # The class name will be "OP::Example":
  #
  create "OP::Example" => {

  };

Class Prototype

A class prototype is a hash describing all fundamental characteristics of an object class. It's the second argument sent to create().

  create "OP::Example" => {
    #
    # This is an empty prototype (perfectly valid)
    #
  };

Instance Variables

Instance variables are declared with assertions.

See the SYNOPSIS section in this module, and further examples in the OP::Subtype and OP::Type modules.

Instance Methods

Instance methods are declared as keys in the class prototype. The name of the method is the key, and its value in the prototype is a Perl 5 sub{} or Perl6::Subs method(){} block.

  create "OP::Example" => {
    #
    # Add a public instance method, $self->handleFoo()
    #
    handleFoo => method() {
      printf 'The value of foo is %s', $self->foo();
      print "\n";

      return true;
    }
  }

  my $exa = OP::Example->new();

  $exa->setFoo("Bar");

  $exa->handleFoo();

  #
  # Expected output:
  #
  # The value of foo is Bar
  #

The OP convention for private or protected instance methods is to prefix them with a single underscore.

  create "OP::Example" => {
    #
    # private instance method
    #
    _handleFoo => method() {
      say "The value of foo is $self->{foo}";
    }
  };

Class Variables

Class variables are declared as keys in the class prototype. They should be prepended with double underscores (__). The value in the prototype is the literal value to be used for the class variable.

  use OP qw| create true false |;

  create "OP::Example" => {
    #
    # Override a few class variables
    #
    __useYaml => false,
    __dbiType => OP::DBIType::MySQL
  };

OP class variables are just Perl package variables, scoped in a list using our.

  package OP::Example;

  use OP qw| true false |;

  use base qw| OP::Node |;

  our @__useYaml = false;
  our @__dbiType = OP::DBIType::MySQL;

  true;

Class Methods

Class methods are declared in the same manner as instance methods. The only difference is that the class will be the receiver.

  create "OP::Example" => {
    #
    # Add a public class method
    #
    loadXml => method(OP::Class $class: Str $xml) {
      # Code here, $class and $xml are already set!
    }
  };

The OP convention for private or protected class methods is to prefix them with double underscores.

  create "OP::Example" => {
    #
    # Override a private class method
    #
    __basePath => method(OP::Class $class:) {
      return join('/', '/tmp', $class);
    }
  };

Inheritance

By default, classes created with create() inherit from OP::Node. To override this, include a __BASE__ attribute, specifying the parent class name.

  create "OP::Example" => {
    #
    # Override parent class
    #
    __BASE__ => "Acme::CustomClass"
  };

OPTIONAL EXPORTS

Constants

  • true, false

    Constants provided by OP::Enum::Bool

      use OP qw| true false |;

Functions

  • create(Str $class: Hash $prototype)

      use OP qw| create |;

    Allocate a new OP-derived class.

    Objects instantiated from classes allocated with create() have built-in runtime assertions-- simple but powerful rules in the class prototype which define runtime and schema attributes. See the OP::Type module for more about assertions.

    OP classes are regular old Perl packages. create() is just a wrapper to the package keyword, with some shortcuts thrown in.

    The inline methods for these examples are using method from Perl6::Subs.

      use OP qw| create true false |;
    
      create "OP::Example" => {
        __someClassVar => true,
    
        someInstanceVar => OP::Str->assert(),
    
        anotherInstanceVar => OP::Str->assert(),
    
        publicInstanceMethod => method() {
          # Code here, $self will be set for you.
        },
    
        _privateInstanceMethod => method() {
          # Code here, $self will be set for you.
        },
    
        publicClassMethod => method(OP::Class $class:) {
          # Code here, $class will be set for you
        },
    
        __privateClassMethod => method(OP::Class $class:) {
          # Code here, $class will be set for you
        }
    
        #
        # Yes, you can still make methods with sub { } blocks.
        #
        oldschoolPerlMethod => sub {
          my $self = shift;
    
          warn "$self fears change :-(";
        }
      };

DIAGNOSTICS

XXX TODO Figure out what to put in this section.

CONFIGURATION AND ENVIRONMENT

OP looks for the .oprc configuration file under the location specified by $OP_HOME. See OP::Constants for details.

OP under mod_perl/mod_perl2

OP classes should be precompiled under mod_perl by referencing them in the Apache instance's startup.pl script. OP_HOME must be set in a BEGIN block.

  #
  # File: startup.pl
  #
  BEGIN {
    $ENV{OP_HOME} = '/home/user/op'; # Directory with the .oprc
  }

  #
  # Load any OP-derived packages at startup:
  #
  use MyApp::Component;
  use MyApp::OtherComponent;

  1;

OP under HTML::Mason

OP classes should be preloaded by startup.pl, as in the above example.

If (and only if) you are not using a startup.pl: Mason loads packages outside the context of package main, but OP must currently be bootstrapped from package main, so one must explicitly drop back into main before consuming OP-derived classes. Do this in a do block.

  <%init>

    do {
      package main;

      use MyApp::Component;
    }

  </%init>
  <%perl>

    $m->print( MyApp::Component->sayHello() );

  </%perl>

It is highly recommended that startup.pl be used in production environments, so the initial requests to the webserver are not delayed by the lengthy source filtering and compilation steps.

  #
  # File: httpd.conf
  #
  PerlModule HTML::Mason::ApacheHandler
  PerlRequire /opt/op/bin/startup.pl

  <LocationMatch "/.*\.html$">
    SetHandler perl-script

    PerlHandler HTML::Mason::ApacheHandler
  </LocationMatch>

DEPENDENCIES

OP's dependencies are numerous, and subject to change while the API is ironed out. See the Makefile.PL which accompanied this distribution for a current list of prerequisites.

INCOMPATIBILITIES

Code which parses Perl source, such as Perl::Tidy and PPI, (not to mention Perl::Critic, which uses both of those packages) are easily confused by tokens in the Perl 6 method prototype-- this may result in not-very-tidy perltidy output and possibly malformed PPI DOM trees. This also limits the brutality which the criticism module is capable of dishing out by default. Quite unfortunate.

Ending method blocks with a semicolon character has been found to help PPI do the right thing when parsing OP documents.

BUGS AND LIMITATIONS

OP uses source filters, so the usual source filter warnings apply, not the least of which is that compilation takes longer than usual. OP is best suited for daemonized applications, not short-lived scripts which frequently spawn new Perl interpreters.

OP and the modules it uses tweak Perl in some low level ways. I've seen undef warnings manifest from third-party or builtin Perl packages, when requiring modules that wrap require statements inside of evals, which many Perl modules seem to do. I don't currently understand why this happens, but have worked around this by loading any modules which exhibit this behavior before loading OP.

Test suite is currently incomplete.

SEE ALSO

OP::Type, OP::Subtype

This file is part of OP.