package Froody::Implementation; use strict; use warnings; our $VERSION = "0.01"; use Scalar::Util qw(blessed); use List::MoreUtils qw(any); use Froody::Error; use Froody::Logger; my $logger = Froody::Logger->get_logger("Froody::Implementation"); use UNIVERSAL::require; # NOTE: this is not a subclass of Froody::Base! # people should be able to use their own superclass for new et al sub register_in_repository { my $class = shift; my $repository = shift; # what of this API do we implement? my ($api_class, @method_matches) = $class->implements; return unless $api_class; # Allow for superclasses doing crazy things. @method_matches = map { Froody::Method->match_to_regex( $_ ) } @method_matches; # load the api $api_class->require or Froody::Error->throw("perl.use", "unknown or broken API class: $api_class"); # create an invoker instance my $invoker_class = $class->invoker_class; $invoker_class->require or Froody::Error->throw("perl.use", "unknown or broken Invoker class: $invoker_class"); my $inv = $invoker_class->new() ->delegate_class($class); # process each thing based on it's type foreach my $thingy ($api_class->load()) { # froody method? Set the invoker and register it if (blessed($thingy) && $thingy->isa("Froody::Method")) { my $full_name = $thingy->full_name; next unless any { $full_name =~ $_ } @method_matches; $repository->register_method($thingy); $thingy->invoker($inv); next; } # froody errortype? register the error type if (blessed($thingy) && $thingy->isa("Froody::ErrorType")) { $repository->register_errortype($thingy); next; } # hmm, unknown $logger->info("unknown thingy back from ->load: $thingy"); } return } sub implements { Froody::Error->throw("perl.methodcall.unimplemented", "You must define an 'implements' method in '$_[0]'") } # this is defined here because someday someone might subclass # Froody::Invoker::Implementation and it would be nice to not force # them to rewrite Froody::Implementation sub invoker_class { "Froody::Invoker::Implementation" } 1; __END__ # Module::Build::Kwalitee wants us to declare =item or =head with our method # names to show we've documented them, but it doesn't work well with the # tutorial style of the current pod. Let's just declare they're documented # with the magic strings in the following comments: # # register_in_repository is documented =head1 NAME Froody::Implementation - define Perl methods to implement Froody::Method =head1 SYNOPSIS package MyCompany::PerlMethods::Time; use base qw(Froody::Implementation); # say what api you're implementing, and what subset of those methods # should be handled by perl methods in this class sub implements { "MyCompany::API" => "mycompany.timequery.*" } use DateTime; # this is mycompany.timequery.gettime sub gettime { my $self = shift; my $args = shift; $now = DateTime->now; $now->set_time_zone($args->{time_zone}) if $args->{time_zone}; return $now->datetime; } ... =head1 DESCRIPTION This class is a simple base class that allows you to quickly and simply provide code for Froody to run when it needs to execute a method. =head2 How to write your methods It's fairly straightforward to write methods for Froody, and is best demonstrated with an example. Imagine we've got a Froody::Method that's been defined like so: package PerlService::API; use base qw(Froody::API::XML); 1; sub xml { <<'XML'; XML We are now ready to start writing a class implementing this API: package MyCompany::PerlMethods; use base qw(Froody::Implementation); sub implements { "MyCompany::API" => "mycompany.timequery.datetime" } The methods will be called with two parameters, a L object and a hashref containing the method arguments. The arguments will have already been pre-processed to verify that they are all there and of the right type, for example. Look at L if you want to change the behaviour of this pre-processing (you can use this to implement authentication common for all methods, for example). =head2 Abstract methods =over =item implements() Should return a hash of Namespace => 'method.names.*' mappings specifying in what modules the given methods are implemented. =back =head2 Instance methods =over =item invoker_class() Returns the class of the invoker. Override this if you need to do fancy checking in the invoker (for sessions or similar). =back =head1 BUGS You can't use this to run code for a Froody::Method whose full name ends with ".implements", ".invoker_class" or ".register_in_repository" as those methods are special. Sorry. Please report any bugs you find via the CPAN RT system. L =head1 AUTHOR Copyright Fotango 2005. All rights reserved. Please see the main L documentation for details of who has worked on this project. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L =cut 1;