NAME
Role::Subsystem - a parameterized role for object subsystems, helpers, and delegates
VERSION
version 0.101342
DESCRIPTION
Role::Subsystem is a parameterized role. It's meant to simplify creating classes that encapsulate specific parts of the business logic related to parent classes. As in the synopsis below, it can be used to write "helpers." The subsystems it creates must have a reference to a parent object, which might be referenced by id or with an actual object reference. Role::Subsystem tries to guarantee that no matter which kind of reference you have, the other kind can be obtained and stored for use.
What??
Okay, imagine you have a big class called Account. An Account is the central point for a lot of behavior, and rather than dump all that logic in one place, you partition it into subsytems. Let's say we want to write a subsystem that handles all of an Account's Services. We might write this:
package Account::ServiceManager;
use Moose;
use Account;
with 'Role::Subsystem' => {
ident => 'acct-service-mgr',
type => 'Account',
what => 'account',
getter => sub { Account->retrieve_by_id( $_[0] ) },
};
sub add_service {
my ($self, @args) = @_;
# ... do some preliminary business logic
$self->account->insert_related_rows(...);
# ... do some cleanup business logic
}
Then you might add to Account.pm:
package Account;
sub service_mgr {
my ($self) = @_;
return Account::ServiceManager->for_account($self);
}
Then, to add a service you can write:
$account->service_mgr->add_service(...);
You could also just grab the service manager object and use it as a handle for performing operations.
If you don't have an Account object, just a reference to its id, you could get the service manager like this:
my $service_mgr = Account::ServiceManager->for_account_id( $account_id );
Why?
Here's an overview of everything this role will do for you, in terms of the Account::ServiceManager example above.
It will create the for_account
and for_account_id
constructors on your subsystem. (The for_account_id
constructor will only be created if a getter
is supplied.)
It will defer retrieval of account
objects if you construct with only a account_id
, so that if you never need the full object, you never waste time getting it.
It will ensure that any account
and account_id
encountered match the type
and id_type
types, respectively. This will prevent a bogus identifier from being accepted, only to die later when it can't be used for lazy retrieval.
If you create a subsystem object by passing in the parent object (the account
), it will take a weak reference to it to prevent cyclical references from interfering with garbage collection. If the reference goes away, or if you did not start with a reference, a strong reference will be constructed to allow the subsystem to function efficiently afterward. (This behavior can be disabled, if you never want to take a weak reference.)
Swappable Subsystem Implementations
You can also have multiple implementations of a single kind of subsystem. For example, you may eventually want to do something like this:
package Account::ServiceManager;
use Moose::Role;
with 'Role::Subsystem' => { ... };
requries 'add_service';
requries 'remove_service';
requries 'service_summary';
...and then...
package Account::ServiceManager::Legacy;
with 'Account::ServiceManager';
sub add_service { ... };
...and...
package Account::ServiceManager::Simple;
with 'Account::ServiceManager';
sub add_service { ... };
...and finally...
package Account;
sub settings_mgr {
my ($self) = @_;
my $mgr_class = $self->schema_version > 1
? 'Account::ServiceManager::Simple'
: 'Account::ServiceManager::Legacy';
return $mgr_class->for_account($self);
}
This requires a bit more work, but lets you replace subsystem implementations as fairly isolated units.
PERL VERSION
This library should run on perls released even a long time ago. It should work on any version of perl released in the last five years.
Although it may work on older versions of perl, no guarantee is made that the minimum required version will not be increased. The version may be increased for any reason, and there is no promise that patches will be accepted to lower the minimum required perl.
PARAMETERS
These parameters can be given when including Role::Subsystem; these are in contrast to the attributes and methods below, which are added to the classe composing this role.
ident
This is a simple name for the role to use when describing itself in messages. It is required.
what
This is the name of the attribute that will hold the parent object, like the account
in the synopsis above.
This attribute is required.
what_id
This is the name of the attribute that will hold the parent object's identifier, like the account_id
in the synopsis above.
If not given, it will be the value of what
with "_id" stuck on the end.
type
This is the type that the what
must be. It may be a stringly Moose type or an MooseX::Types type. (Or anything else, right now, but anything else will probably cause runtime failures or worse.)
This attribute is required.
id_type
This parameter is like type
, but is used to check the what
's id, discussed more below. If not given, it defaults to Defined
.
id_method
This is the name of a method to call on what
to get its id. It defaults to id
.
getter
This (optional) attribute supplied a callback that will produce the parent object from the what_id
.
weak_ref
If true, when a subsytem object is created with a defined parent object (that is, a value for what
), the reference to the object will be weakened. This allows the parent and the subsystem to store references to one another without creating a problematic circular reference.
If the parent object is subsequently garbage collected, a new value for what
will be retreived and stored, and it will not be weakened. To allow this, setting weak_ref
to true requires that getter
be supplied.
weak_ref
is true by default.
ATTRIBUTES
The following attributes are added classes composing Role::Subsystem.
$what
This will refer to the parent object of the subsystem. It will be a value of the type
type defined when parameterizing Role::Subsystem. It may be lazily computed if it was not supplied during creation or if the initial value was weak and subsequently garbage collected.
If the value of what
when parameterizing Role::Subsystem was account
, that will be the name of this attribute, as well as the method used to read it.
$what_id
This method gets the id of the parent object. It will be a defined value of the id_type
provided when parameterizing Role::Subsystem. It may be lazily computed by calling the id_method
on what
as needed.
METHODS
for_$what
my $settings_mgr = Account::ServiceManager->for_account($account);
This is a convenience constructor, returning a subsystem object for the given what
.
for_$what_id
my $settings_mgr = Account::ServiceManager->for_account_id($account_id);
This is a convenience constructor, returning a subsystem object for the given what_id
.
AUTHOR
Ricardo Signes <cpan@semiotic.systems>
CONTRIBUTORS
Matthew Horsfall <wolfsage@gmail.com>
Ricardo Signes <rjbs@semiotic.systems>
COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by Ricardo Signes.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.