Chris Winters > SPOPS-0.87 > SPOPS::Manual::CodeGeneration

Download:
SPOPS-0.87.tar.gz

Annotate this POD

CPAN RT

New  1
Open  0
View/Report Bugs
Source  

NAME ^

SPOPS::Manual::CodeGeneration - How SPOPS builds classes

SYNOPSIS ^

This part of the SPOPS manual describes how SPOPS generates the class code and how you can customize the process.

DESCRIPTION ^

So with configuration, we would create a number of slots into which classes could install behaviors. The slots are:

manipulate_configuration
id_method
read_code
fetch_by
has_a (relationship)
links_to (relationship)
add_rule

They're described in more detail below.

A class in the hierarchy for an object (or in the 'rules_from' list) could install a behavior in none or all of the slots. So for instance, SPOPS::Configure::DBI has been replaced by SPOPS::ClassFactory::DBI, which reads the configuration for SPOPS::DBI-derived objects and installs DBI-specific links_to behaviors.

Multiple behaviors can be installed in each slot, with the idea that order shouldn't matter. Since we do a depth-first inheritance walk we should be ok -- more specific classes will execute their behaviors before the more general ones.

The processing of each slot uses a form of the 'Chain of Responsibility' pattern -- a behavior can decide to perform or not perform any action and continue (OK), to perform an action, to declare the slot finished (DONE), to stop the process entirely (ERROR) or that the behavior has made changes which necessitates refreshing the behavior listing (RESTART).

As a simple example of a behavior, say we wanted to ensure that all of our objects are using a particular SPOPS::DBI subclass:

  1: package My::UseMyDBIClass; # -*-perl-*-
  2: 
  3: use strict;
  4: 
  5: my $USE_CLASS = 'SPOPS::DBI::Pg';
  6: 
  7: sub behavior_factory {
  8:   my ( $class ) = @_;
  9:   return { manipulate_configuration => \&check_spops_subclass };
 10: }
 11: 
 12: sub check_spops_subclass {
 13:     my ( $config ) = @_;
 14:     foreach ( @{ $config->{isa} } ) {
 15:         s/^SPOPS::DBI::.*$/$USE_CLASS/;
 16:     }
 17:     return ( SPOPS::ClassFactory::RESTART, undef );
 18: }

We would just put this method in a common parent to all our objects and install the behavior in the 'manipulate_configuration' slot. When the class is configured the rule would be executed and we would never have to worry about our objects using the wrong DBI class again. (This is common in OpenInteract when you install new packages and forget to run 'oi_manage change_spops_driver'.)

And that's it! The system enables very focused and flexible behaviors. For instance, we could create one links_to behavior for DBI to handle the current configuration style and another to handle the proposed (and more robust) SPOPS::Inheritable configuration style. The first could step through the 'links_to' configuration items and process only those it can, while the second could do the same. And neither has to know about the other.

We could also do wacky stuff, like install a 'read_code' behavior to use LWP to grab a module and checksums off a code repository somewhere. If the checksum and code match up, we can bring the code into the SPOPS class.

SLOTS ^

We use the term 'slots' to refer to the different steps we walk through to create, configure and auto-generate methods for an SPOPS class. Each 'slot' can have multiple behaviors attached to it.

Finding Slot Behaviors

Slot behaviors can come from any of the classes in the @ISA for the generated class, or from any of the classes listed in the 'rules_from' configuration key.

The differences between the 'isa' and the 'rules_from' class lists are:

Slot Listing

Here are the current slots and a description of each. Note that they might change -- in particular, the 'links_to' and 'has_a' slots might be merged into a single 'relationship' slot.

BEHAVIOR GENERATOR ^

The behavior generator is called 'behavior_factory' (the name can be imported in the constant 'FACTORY_METHOD') and it takes a single argument, the name of the class for which the behaviors are being generated. It should return a hashref with the slot names as keys. A value should either be a coderef (for a single behavior) or an arrayref of coderefs (for multiple behaviors).

Here is an example, directly from from SPOPS:

 sub behavior_factory {
     my ( $class ) = @_;
     $log->is_info &&
         $log->info( "Installing SPOPS default behaviors for ($class)" );
     return { manipulate_configuration =>
                    \&SPOPS::ClassFactory::DefaultBehavior::conf_modify_config,
             read_code                =>
                    \&SPOPS::ClassFactory::DefaultBehavior::conf_read_code,
             id_method                =>
                    \&SPOPS::ClassFactory::DefaultBehavior::conf_id_method,
             has_a                    =>
                    \&SPOPS::ClassFactory::DefaultBehavior::conf_relate_hasa,
             fetch_by                 =>
                    \&SPOPS::ClassFactory::DefaultBehavior::conf_relate_fetchby,
             add_rule                 =>
                    \&SPOPS::ClassFactory::DefaultBehavior::conf_add_rules, };
 }

So with this we're installing one behavior each into the slots 'manipulate_configuration', 'read_code', 'id_method', 'has_a', 'fetch_by' and 'add_rule'. Here's an example that installs multiple behaviors in a single slot:

 sub behavior_factory {
     my ( $class ) = @_;
     return { links_to => [ \&simple_linking, \&complex_linking ] };
 }

 sub simple_linking { ... }
 sub complex_linking { ... }

BEHAVIOR DESCRIPTION ^

Behaviors can be simple or complicated, depending on what you need them to do. The simple behavior we showed above does a single, simple task and then exits. This is probably the best strategy for most behavior uses -- focus each one one a single task so you it's easy to follow and debug. The fact that we run the behaviors only once, when the class is being generated, means that you don't have to worry so much about efficiency.

Every behavior returns a two-item list. The first is the status of the behavior, the second is an optional message.

The potential status return values are all constants that can be imported from SPOPS::ClassFactory:

COPYRIGHT ^

Copyright (c) 2001-2004 Chris Winters. All rights reserved.

See SPOPS::Manual for license.

AUTHORS ^

Chris Winters <chris@cwinters.com>

syntax highlighting: