|
NAMESPOPS::Manual::CodeGeneration - How SPOPS builds classesSYNOPSISThis part of the SPOPS manual describes how SPOPS generates the class code and how you can customize the process.DESCRIPTIONSo with configuration, we would create a number of slots into which classes could install behaviors. The slots are:
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. SLOTSWe 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 BehaviorsSlot 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 ListingHere 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 GENERATORThe 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 DESCRIPTIONBehaviors 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:
COPYRIGHTCopyright (c) 2001-2004 Chris Winters. All rights reserved.See SPOPS::Manual for license. AUTHORSChris Winters <chris@cwinters.com>
Visit the GSP FreeBSD Man Page Interface. |