The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
=head1 NAME

Froody::QuickStart - the froody Quick Start tutorial

=head1 DESCRIPTION

At the core of Froody is the concept of Froody Methods, methods that you
can call remotely over the web.

For example, we have a hypothetical method called "examples.myapi.greet" that
can return us greetings.  We need to pass it one parameter, called "who", that
is the name of the person we're greeting. These two things are passed as CGI
parameters to a froody endpoint (a cgi script, a mod_perl server, whatever)
sitting at "http://myserver.com/fe"

  bash$ lwp-request 'http://myserver.com/fe?method=examples.myapi.greet&who=Mark'
  <?xml version="1.0" encoding="utf-8"?>
  <rsp stat="ok">
    <greeting>Hello Mark!</greeting>
  </rsp>

We get a chunk of XML back that is the result of calling the function.

=head1 Hello World Server Example

The easiest way to show you how Froody works is to expand the above example and
demonstrate how we might implement that on the server.

=head2 Writing The API

Firstly we have to define a class that builds an API - a way of describing what
methods can be called on our endpoint, along with what they expect to be passed
and what they're going to return.  This is normally done by subclassing
Froody::API::XML and implementing C<xml> to return a block of XML that describes 
the method(s):

  package MyAPI;
  use base qw(Froody::API::XML);
  1;
  
  sub xml {
  return <<"XML";
  <spec>
   <methods>
     <!-- each method that we can call via the API needs one of these -->
     <method name="examples.myapi.greet">
       
       <!-- the parameters we pass to the method -->
       <arguments>
         <argument name="who" type="text" optional="0" />
       </arguments>
       
       <!-- an example response, what the returned XML will look like -->
       <response>
         <greeting>Hello World!</greeting>
       </response>
       
     </method>
   </methods>
  </spec>
  XML
  }

Note that I've omitted the optional documentation sections that should normally
be included (the C<< <errors> >> and C<< <description> >> sections.)

Though this seems like a lot of stuff to have to write upfront, it saves us a
lot of work in the Perl code we're going to write later since it allows Froody
to verify parameters for you and convert your return values to XML
automatically.

Information on exactly what should be put in an XML spec can be found
by reading up on L<Froody::API::XML>.

=head2 Writing The Implementation

Having declared our API we now need to write a class that implements it - i.e.
write the code that actually does the work of calculating the greeting.

This is done like so:

  package SayHello;
  use base qw(Froody::Implementation);
  
  # say what we implement.
  sub implements { MyAPI => "examples.myapi.*" }
  
  sub greet
  {
    my $self = shift;
    my $args = shift;
    return "Hello $args->{who}!";
  }

Note that we don't have to do any checks to see if we've got the required
parameter C<< $args->{who} >> and returning an error instead, or wrapping our
arguments in the right sort of XML, or even worrying about encoding the output
or escaping anything that is going to cause our XML.  This is all done for us
by Froody.

More infomation on how we marry the implementations to the APIs can be
found by reading the docs for L<Froody::Implementation>.  The way the
data structure your methods returns is converted to XML is documented in
L<Froody::Invoker::Implementation>

=head2 Testing the Code From The Command Line

When you install the Froody distribution, you get the froody client command
line tool installed .  This can be used to test the method call

  bash$ froody -MSayHello examples.api.greet who "Mark"
  <?xml version="1.0" encoding="utf-8"?>
  <rsp stat="ok">
    <greeting>Hello Mark!</greeting>
  </rsp>

The C<-M> option tells C<froody> to load a module.  If a Froody::Implementation
has been loaded into memory then the client will automatically use that to make
local froody calls against.

=head2 Setting up the Webserver

Local implementations are all very well and good, but the point is to make
Froody methods avalible across the web.  To do this we need to set up our
endpoint so that it knows about the Froody Methods it's supposed to serve.

For performance reasons this should really be run from a mod_perl handler,
which can be configured with a section in your httpd.conf like so:

  PerlModule SayHello;
  PerlModule Froody::Server;
  
  <Location /fe>
    SetHandler perl
    PerlHandler Froody::Server->handler
  </Location>

This is all documented in L<Froody::Server>.  If you need a little more control
over your server setup (i.e. you need multiple Froody endpoints for any one
Apache) then you should read L<Froody::Server>.

For testing or an infrequently called script, we could run our endpoint as
a CGI script:

  #!/usr/bin/perl
  use warnings;
  use strict;
  use Froody::Server;
  Froody::Server->dispatch();

Either way, we can now make calls to our server:

  bash$ lwp-request 'http://myserver.com/fe?method=example.myapi.greet&who=Mark'
  <?xml version="1.0" encoding="utf-8"?>
  <rsp stat="ok">
    <greeting>Hello Mark!</greeting>
  </rsp>

=head2 Using Froody as a Client

Froody is useful as a client as well as a server:

  # create a client and then tell it where to get its methods from
  my $client = Froody::Dispatch->new();
  $client->add_endpoint("http://myserver.com/fe");
  
  # call the method on the remote server
  my $response = $client->dispatch( 
    method => "example.myapi.greet", 
    params => { who => "Mark" },
  );

  # print out the same XML as above
  print $response->render;
  
There are several different response objects, and the C<as_*> methods
can be used to turn them into one another.  For example:

  use Froody::Response::PerlDS;
  my $perl_ds = $response->as_perlds;

  use Data::Dumper;
  print Dumper $perl_ds->content;

Prints:

  $VAR1 = {
    name  => "greeting"
    value => "Hello Mark!"
  };

Mostly you want to use the Froody::Response::Terse response that returns the
data in the same format as you passed out of the original Perl method back
on the server:

  use Froody::Response::Terse;
  my $terse = $response->as_terse;

  use Data::Dumper;
  print $terse->content;  # prints "Hello Mark!"

We've added a shortcut for this, so all you need to do is:

  my $response = $client->call('example.myapi.greet', who => 'Mark');

which is equivalient to:

  my $rsp = $client->dispatch( 
    method => "example.myapi.greet", 
    params => { who => "Mark" },
  );
  my $response = $rsp->as_terse->content;


The terse response makes use of Reflection. The 'add_endpoint()' method calls
the server to ask it what methods it provides and what the responses look like.
It then uses these specifications (like the one we declared at the top of this
document) to do the conversion back to the expected response.

=head1 A More Complicated Example

Of course, there would be little point returning XML if all we were going to do was
return simple strings.  We can return much more complicated data structures:

  package Corelist::API;
  use base qw(Froody::API::XML);
  1;
  
  sub xml {
    return <<'XML';
    <spec>
     <methods>
      <method name="froody.demo.core">
      <description>When modules were distributed with core Perl</description>
      <arguments>
        <argument name="modules" type="csv" optional="0" />
      </arguments>
      <response>
        <core_list module_core_list_version="1.23">
           <module name="Foo::Bar" first_in="5.7">
             <distribution perl_version="5.7" module_version="1.2"/>
             <distribution perl_version="5.8" module_version="1.2"/>         
           </module>
           <module name="Foo::Baz" first_in="5.7">
             <distribution perl_version="5.7" module_version="1.2"/>
             <distribution perl_version="5.8" module_version="1.2"/>         
           </module>
           <serveradmin>
             <name>Mark Fowler</name>
             <email>mark@twoshortplanks.com</email>
           </serveradmin>
        </core_list>
      </response>
      </method>
     </methods>
    </spec>
  XML
  }

We now have elements that are contained within elements, attributes, and things
that appear multiple times.  I'll explain why we had to do that later.

First note that we've defined the argument type as C<csv>.  This means that it
expects the data passed to it via the CGI parameter to be a comma seperated
list. So if we call this method like so

  http://server.com/fe?method=examples.perl.core&modules=Foo::Bar,Foo::Baz

The arguments (the second parameter) that's passed to the method we define will
be:

   {
     modules => [ "Foo::Bar", "Foo::Baz" ]
   }

If there's only one value in our URL:

  http://server.com/fe?method=exaples.perl.core&modules=Foo::Bar

We'll still get a one item array:

   {
     modules => [ "Foo::Bar" ]
   }

The data structure we want to return is not nearly as complicated as the
XML we've defined above.

  { 
    module_core_list_version => "1.31",
    module => [
      {
        name => "Foo::Bar",
        first_in => "5.7",
        distrbution => [
          { perl_version => "5.7", module_version => "1.2" },
          { perl_version => "5.8", module_version => "1.2" },          
        ],
      },
      {
        name => "Foo::Baz",
        first_in => "5.7",
        distrbution => [
          { perl_version => "5.7", module_version => "1.2" },
          { perl_version => "5.8", module_version => "1.2" },
        ],
      },
    ],
    server_admin => { 
       name => "Mark Fowler",
       email => 'mark@twoshortplanks.com'
    }
  }

Note how we haven't specified which hash keys above are attributes and which
contain data to be put in elements - in fact you can't tell what's going to
turn into an attribute and what's going to turn into a child element by looking
at the data structure alone.  Froody works out what the right thing to do is by
comparing the hash keys with the attributes and element names defined in the
example response.

Froody also works out if something can occur multiple times by examining the
example response. If you want to return a list of similar items, for example,
you have to specify it multiple (at least 2) times.

Here's the implementation that can actually create these data structures for us
(you can find a fully working demo of this example in the C<examples> directory
in the Froody distribution).

  package Corelist::Implementation;
  use base qw(Froody::Implementation);
  use strict;
  use warnings;
  1;
  
  sub implements { 'Corelist::API' => "froody.demo.core" }
  
  use Module::CoreList;
  sub core
  {
     my $self = shift;  # not used
     my $args = shift;
  
     # insert the static server stuff
     my $ds = { 
       server_admin => { 
         name => "Mark Fowler",
         email => 'mark@twoshortplanks.com',
       },
       module_core_list_version => Module::CoreList->VERSION,
       module => [],
     };
         
     # for each of the modules
     foreach my $module (@{ $args->{modules} })
     {
       my $module_hash = { name => $module, distribution => [] };
          
       # what was the first one?
       if (my $first_in = Module::CoreList->first_release($module))
         { $module_hash->{first_in} = $first_in }
     
       # build the distributions list
       foreach my $dist (sort { $a <=> $b } keys %Module::CoreList::version)
       {
         if (exists $Module::CoreList::version{ $dist }{ $module }) {
           my $version = $Module::CoreList::version{ $dist }{ $module };
           $version = "undef" unless defined $version;
           push @{ $module_hash->{distribution} }, {
             perl_version => $dist,
             module_version => $version,
           };
         }
       }
  
       push @{ $ds->{module} }, $module_hash;
     }
  
     return $ds;
  }


And now we can test the code with the Froody client:

  bash$ froody -MGetCore examples.perl.core modules Tie::File
  <?xml version="1.0" encoding="utf-8"?>
  <rsp stat="ok">
    <core_list module_core_list_version="2.02">
      <module first_in="5.007003" name="Tie::File">
        <distribution perl_version="5.007003" module_version="0.17"/>
        <distribution perl_version="5.008" module_version="0.93"/>
        <distribution perl_version="5.008001" module_version="0.97"/>
        <distribution perl_version="5.008002" module_version="0.97"/>
        <distribution perl_version="5.008003" module_version="0.97"/>
        <distribution perl_version="5.008004" module_version="0.97"/>
        <distribution perl_version="5.008005" module_version="0.97"/>
        <distribution perl_version="5.008006" module_version="0.97"/>
        <distribution perl_version="5.008007" module_version="0.97"/>
        <distribution perl_version="5.009" module_version="0.97"/>
        <distribution perl_version="5.009001" module_version="0.97"/>
        <distribution perl_version="5.009002" module_version="0.97"/>
      </module>
    </core_list>
  </rsp>

=head1 BUGS

None known.

Please report any bugs you find via the CPAN RT system.
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Froody>

=head1 AUTHOR

Copyright Fotango 2005.  All rights reserved.

Please see the main L<Froody> 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<Froody>

=cut