package OpenInteract2::ActionResolver; # $Id: ActionResolver.pm,v 1.3 2005/03/02 15:30:38 lachoy Exp $ use strict; use base qw( Class::Factory ); use Log::Log4perl qw( get_logger ); use OpenInteract2::Constants qw( LOG_ACTION ); use OpenInteract2::Exception qw( oi_error ); use OpenInteract2::URL; use OpenInteract2::Util; my ( $log ); $OpenInteract2::ActionResolver::VERSION = sprintf("%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/); sub resolver_sort { return $a->get_order <=> $b->get_order; } sub get_all_resolvers { my ( $class ) = @_; my @all_resolver_types = $class->get_registered_types(); my @resolvers = (); foreach my $type ( @all_resolver_types ) { push @resolvers, $class->new( $type ); } return sort resolver_sort @resolvers; } sub get_action { my ( $class, $request, @other ) = @_; $log ||= get_logger( LOG_ACTION ); my ( $url ); my @resolvers = (); if ( scalar @other ) { unless ( ref $other[0] ) { $url = OpenInteract2::URL->parse_absolute_to_relative( shift @other ); } @resolvers = @other; } unless ( scalar @resolvers ) { @resolvers = $class->get_all_resolvers; } $url ||= $request->url_relative; $log->info( "Sending URL '$url' to ", scalar( @resolvers ), " ", "action resolvers" ); my ( $action ); foreach my $r ( @resolvers ) { $action = eval { $r->resolve( $request, $url ) }; if ( $@ ) { $log->warn( "Resolver ", ref( $r ), " threw an ", "exception ($@); continuing with others..." ); } last if ( $action ); } return $action; } ######################################## # OBJECT METHODS sub assign_additional_params_from_url { my ( $self, $request, @params ) = @_; if ( scalar @params ) { $log ||= get_logger( LOG_ACTION ); $log->info( "Assigning additional URL parameters: ", join( ', ', @params ) ); $request->param_url_additional( @params ); } } ######################################## # SUBCLASSES sub resolve { _must_implement( 'resolve', @_ ) } sub get_order { return 5 } sub _must_implement { my ( $method, $item ) = @_; my $class = ref( $item ) || $item; oi_error "Class '$class' must implement method '$method'"; } OpenInteract2::Util->find_factory_subclasses( 'OpenInteract2::ActionResolver', @INC ); 1; __END__ =head1 NAME OpenInteract2::ActionResolver - Small classes and chain of responsibility to resolve URLs to action objects =head1 SYNOPSIS # Get all the available resolver objects my @resolvers = OpenInteract2::ActionResolver->get_all_resolvers(); # Send OI2::Request object from which we get the URL using the # default resolvers... my $action = OpenInteract2::ActionResolver->get_action( $request ); # ...or specify the URL and resolvers yourself my $action = OpenInteract2::ActionResolver->get_action( $request, $url, @resolvers ); =head1 DESCRIPTION An action resolver takes a URL and tries to create an L from it. If the resolver cannot do so it does nothing and the next one is called. Resolvers are found at runtime as long as they're under the 'OpenInteract2::ActionResolver' namespace. You can also add them manually using normal L directives: OpenInteract2::ActionResolver->register_factory_class( myresolver => 'MyApplication::Resolver::FooResolver' ); =head1 CLASS METHODS C Returns a list of resolver objects -- order is important! C Match up C<$url> to the corresponding action. If not given we ask C<$request> for its C property, and if C<@resolvers> aren't given we use the result from our C class method. Each of the L objects in C<@resolvers> will get called and asked if it can resolve C<$url>. This will either return an L object or throw an exception. C Creates a new resolver -- no parameters are passed in besides the C<$type> that each resolver uses to register itself. =head1 OBJECT METHODS B - subclass may implement Return a number between 1 and 10 indicating when the resolver should be run. If you do not implement this the default is '5', which will probably be fine for most implementations. B - subclass must implement Tries to find data in C<$url> to create an action. The C<$url> will B contain any deployment context information. (Note that C<$url> may have come from C<$request> or may have been specified by the original caller, so don't go peeking around in C<$request> for it unless you know what you're doing.) If that particular resolver does not know what to do with the URL it should return nothing to indicate that the next resolver down the line should get executed. If you're thinking of implementing an this class to create a side-effect (like looking for a 'my_language' request parameter and using that for the language assigned), don't. There's a better way. Just create an observer in the C namespace and we'll pick it up automatically from L. The observation you're looking for is 'action assigned'. So to do the above you'd create: package OpenInteract2::Observer::Controller::Language; use strict; use OpenInteract2::Context qw( CTX ); sub update { my ( $class, $controller, $observation, $action ) = @_; return unless ( $observation eq 'action assigned' ); my $request = CTX->request; my $lang = $request->param( 'my_language' ); if ( $lang ) { $request->find_languages( $lang, $request->language ); } } =head1 SEE ALSO L L =head1 COPYRIGHT Copyright (c) 2005 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