NAME
RPC::ExtDirect - Expose Perl code to JavaScript web applications through Ext.Direct remoting
SYNOPSIS
package Foo::Bar;
use RPC::ExtDirect Action => 'Fubar';
sub foo : ExtDirect(2) {
my ($class, $arg1, $arg2) = @_;
# do something, store results in scalar
my $result = ...;
return $result;
}
sub bar : ExtDirect(params => ['foo', 'bar']) {
my ($class, %arg) = @_;
my $foo = $arg{foo};
my $bar = $arg{bar};
# do something, returning scalar
my $result = eval { ... };
# or throw an exception if something's wrong
die "Houston, we've got a problem: $@\n" if $@;
return $result;
}
sub baz : ExtDirect(formHandler) {
my ($class, %arg) = @_;
my @form_fields = grep { !/^file_uploads$/ } keys %arg;
my @uploaded_files = @{ $arg{file_uploads} };
# do something with form fields and files
my $result = { ... };
return $result;
}
DESCRIPTION
Abstract
This module provides an easy way to map class methods to ExtDirect RPC interface used with ExtJS JavaScript framework.
What is this for?
There are many RPC protocols out there; ExtJS framework provides yet another one called Ext.Direct. In short, Ext.Direct is a way to call server side code from client side without having to mess with HTML, forms and stuff like that. Besides forward asynchronous data stream (client calls server), Ext.Direct also provides mechanism for backward (server to client) asynchronous event propagation.
For more detailed explanation, see http://www.sencha.com/products/extjs/extdirect/.
Terminology
Ext.Direct uses the following terms, followed by their descriptions: Configuration - Description of server side calls exposed to client side. Includes information on Action and Method names, as well as argument number and/or names.
API - JavaScript code that encodes Configuration.
Usually generated by application server code
called by client once upon startup.
Router - Server side component that receives remoting
calls, dispatches requests, collects and
returns call results.
Action - Namespace unit; collection of Methods. The
nearest Perl analog is package, other
languages may call it a Class. Since the
actual calling code is JavaScript, Action
names should conform to JavaScript naming
rules (i.e. no '::', use dots instead).
Method - Subroutine exposed through Ext.Direct API
to be called by client side. Method is
fully qualified by Action and Method names
using dot as delimiter: Action.Method.
Result - Any data returned by Method upon successful or
unsuccessful call completion. This includes
application logic errors. 'Not authenticated'
and alike events should be returned as Results,
not Exceptions.
Exception - A fatal error, or any other unrecoverable event
in application code. Unlike Results, Exceptions
are not considered successful; Ext.Direct
provides mechanism for managing Exceptions.
Exceptions are not used to indicate errors in
application logic flow, only for catastrophic
conditions. Nearest analog is status code 500
for HTTP responses.
Event - An asynchronous notification that can be
generated by server side and passed to
client side, resulting in some reaction.
Events are useful for status updates, progress
indicators and other predictably occuring
conditions and events.
Event Provider - Server side script that gets polled by
client side every N seconds; default N
is 3 but it can be changed in client side
configuration.
Using RPC::ExtDirect
In order to export subroutine to ExtDirect interface, use ExtDirect(n) attribute in sub's declaration. Note that there can be no space between attribute name and opening parentheses. n is mandatory calling convention declaration; it may be one of the following options: - Number of arguments to be passed as ordered list - Names of arguments to be passed as hash - formHandler that will receive hash of fields and uploaded files - pollHandler that does not receive any arguments
Unlike Ext.Direct specification (and reference PHP implementation, too) RPC::ExtDirect does not impose any calling convention on server side code, except bare minimum. There are no "before" and "after" handlers, no object instantiation and no assumptions about the code called. That said, an RPC::ExtDirect Method should conform to the following conventions: - Be a package (Class) method, i.e. be aware that its first argument will be package name. Just ignore it if you don't want it.
- Ordered (numbered) arguments are passed as list in @_, so
$_[1] is the first argument. No more than number of arguments
declared in ExtDirect attribute will be passed to Method; any
extra will be dropped silently. Less actual arguments than
declared will result in Exception returned to client side,
and Method never gets called.
- Named arguments are passed as hash in @_. No arguments other
than declared will be passed to Method; extra arguments will
be dropped silently. If not all arguments are present in
actual call, an Exception will be returned and Method never
gets called.
- Form handlers are passed their arguments as hash in @_.
Standard Ext.Direct form fields are removed from argument
hash; uploaded file(s) will be passed in file_uploads hash
element. It will only be present when there are uploaded
files.
- All Methods are called in scalar context. Returning one
scalar value is OK; returning array- or hashref is OK too.
Do not return blessed objects; it is almost always not
obvious how to serialize them into JSON that is expected by
client side. Just don't do it.
- If an error is encountered while processing request, throw
an exception with die() or croak(). Do not return some
obscure value that client side is supposed to know about.
- Poll handler methods are called in list context and do not
receive any arguments. Return values must be instantiated
Event object(s), see L<RPC::ExtDirect::EventProvider> for
more detail.
Caveats
In order to keep this module as simple as possible, I had to sacrifice the ability to automatically distinguish inherited class methods. In order to declare inherited class methods as Ext.Direct exportable you have to override them in subclass, like that:
package foo;
use RPC::ExtDirect;
sub foo_sub : ExtDirect(1) {
my ($class, $arg) = @_;
# do something
...
}
package bar;
use base 'foo';
sub foo_sub : ExtDirect(1) {
my ($class, $arg) = @_;
# call inherited method
return __PACKAGE__->SUPER::foo_sub($arg);
}
sub bar_sub : ExtDirect(2) {
my ($class, $arg1, $arg2) = @_;
# do something
...
}
On the other hand if you don't like class-based approach, just don't inherit your packages from one another. In any case, declare your Methods explicitly every time and there never will be any doubt about what Method gets called in any given Action.
DEPENDENCIES
RPC::ExtDirect is dependent on the following modules: Attribute::Handlers, JSON.
BUGS AND LIMITATIONS
Perl versions below 5.6 are not supported.
There are no known bugs in this module. Please report problems to author, patches are welcome.
SEE ALSO
Alternative Ext.Direct Perl implementations: CatalystX::ExtJS::Direct by Moritz Onken, https://github.com/scottp/extjs-direct-perl by Scott Penrose.
For Web server gateway implementations, see CGI::ExtDirect and Plack::Middleware::ExtDirect modules based on RPC::ExtDirect engine.
For configurable Ext.Direct API options, see RPC::ExtDirect::API module.
AUTHOR
Alexander Tokarev <tokarev@cpan.org>
LICENSE AND COPYRIGHT
Copyright (c) 2011-2012 by Alexander Tokarev.
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.