package Workflow::Action; # $Id: Action.pm 302 2007-07-03 14:54:51Z jonasbn $ # Note: we may implement a separate event mechanism so that actions # can trigger other code (use 'Class::Observable'? read observations # from database?) use strict; use base qw( Workflow::Base ); use Log::Log4perl qw( get_logger ); use Workflow::Action::InputField; use Workflow::Validator::HasRequiredField; use Workflow::Factory qw( FACTORY ); $Workflow::Action::VERSION = '1.09'; my @FIELDS = qw( name class description ); __PACKAGE__->mk_accessors( @FIELDS ); #################### # INPUT FIELDS sub add_fields { my ( $self, @fields ) = @_; push @{ $self->{_fields} }, @fields; } sub required_fields { my ( $self ) = @_; return grep { $_->requirement() eq 'required' } @{ $self->{_fields} }; } sub optional_fields { my ( $self ) = @_; return grep { $_->requirement() eq 'optional' } @{ $self->{_fields} }; } sub fields { my ( $self ) = @_; return @{ $self->{_fields} }; } #################### # VALIDATION sub add_validators { my ( $self, @validator_info ) = @_; my @validators = (); foreach my $conf ( @validator_info ) { my $validator = FACTORY->get_validator( $conf->{name} ); my @args = $self->normalize_array( $conf->{arg} ); push @validators, { validator => $validator, args => \@args }; } push @{ $self->{_validators} }, @validators; } sub get_validators { my ( $self ) = @_; return @{ $self->{_validators} }; } sub validate { my ( $self, $wf ) = @_; my @validators = $self->get_validators; return unless ( scalar @validators ); my $context = $wf->context; foreach my $validator_info ( @validators ) { my $validator = $validator_info->{validator}; my $args = $validator_info->{args}; my @runtime_args = ( $wf ); foreach my $arg ( @{ $args } ) { if ( $arg =~ /^\$(.*)$/ ) { push @runtime_args, $context->param( $1 ); } else { push @runtime_args, $arg; } } $validator->validate( @runtime_args ); } } # Subclasses override... sub execute { my ( $self, $wf ) = @_; die "Class ", ref( $self ), " must implement 'execute()'\n"; } ######################################## # PRIVATE sub init { my ( $self, $wf, $params ) = @_; # So we don't destroy the original... my %copy_params = %{ $params }; $self->class( $copy_params{class} ); $self->name( $copy_params{name} ); $self->description( $copy_params{description} ); ## init normal fields my @fields = $self->normalize_array( $copy_params{field} ); foreach my $field_info ( @fields ) { $self->add_fields( Workflow::Action::InputField->new( $field_info ) ); } ## establish validator for fields with is_required="yes" @fields = $self->required_fields(); my $validator = Workflow::Validator::HasRequiredField->new ( { name => 'HasRequiredField for is_required fields', class => 'Workflow::Validator::HasRequiredField' }); my @args = (); foreach my $field ( @fields ) { next if (not $field); ## empty @fields array push @args, $field->name(); } push @{ $self->{_validators} }, { validator => $validator, args => \@args }; ## init normal validators my @validator_info = $self->normalize_array( $copy_params{validator} ); $self->add_validators( @validator_info ); delete @copy_params{ qw( class name description field validator ) }; # everything else is just a passthru param while ( my ( $key, $value ) = each %copy_params ) { $self->param( $key, $value ); } } 1; __END__ =head1 NAME Workflow::Action - Base class for Workflow actions =head1 SYNOPSIS # Configure the Action... $username $email # Define the action package MyApp::Action::CreateUser; use base qw( Workflow::Action ); use Workflow::Exception qw( workflow_error ); sub execute { my ( $self, $wf ) = @_; my $context = $wf->context; # Since 'username' and 'email' have already been validated we # don't need to check them for uniqueness, well-formedness, etc. my $user = eval { User->create({ username => $context->param( 'username' ), email => $context->param( 'email' ) }) }; # Wrap all errors returned... if ( $@ ) { workflow_error "Cannot create new user with name '", $context->param( 'username' ), "': $@"; } # Set the created user in the context for the application and/or # other actions (observers) to use $context->param( user => $user ); # return the username since it might be used elsewhere... return $user->username; } =head1 DESCRIPTION This is the base class for all Workflow Actions. You do not have to use it as such but it is strongly recommended. =head1 OBJECT METHODS =head2 Public Methods =head3 add_field( @fields ) Add one or more Ls to the action. =head3 required_fields() Return a list of L objects that are required. =head3 optional_fields() Return a list of L objects that are optional. =head3 fields() Return a list of all L objects associated with this action. =head3 add_validators( @validator_config ) Given the 'validator' configuration declarations in the action configuration, ask the L for the L object associated with each name and store that along with the arguments to be used, runtime and otherwise. =head3 get_validators() Get a list of all the validator hashrefs, each with two keys: 'validator' and 'args'. The 'validator' key contains the appropriate L object, while 'args' contains an arrayref of arguments to pass to the validator, some of which may need to be evaluated at runtime. =head3 validate( $workflow ) Run through all validators for this action. If any fail they will throw a L, the validation subclass. =head3 execute( $workflow ) Subclasses B implement -- this will perform the actual work. It's not required that you return anything, but if the action may be used in a L object that has multiple resulting states you should return a simple scalar for a return value. =head3 add_fields Method to add fields to the workflow. The method takes an array of fields. =head2 Private Methods =head3 init( $workflow, \%params ) init is called in conjuction with the overall workflow initialization. It sets up the necessary validators based on the on configured actions, input fields and required fields. =head1 SEE ALSO L L =head1 COPYRIGHT Copyright (c) 2003-2004 Chris Winters. All rights reserved. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 AUTHORS Chris Winters Echris@cwinters.comE