NAME
RPC::ExtDirect::API - Ext.Direct service discovery handler
SYNOPSIS
use RPC::ExtDirect::Config;
use RPC::ExtDirect::API;
my $config = RPC::ExtDirect::Config->new(
namespace => 'MyApp',
router_path => '/router',
poll_path => '/events',
remoting_var => 'MyApp.REMOTING_API',
polling_var => 'MyApp.POLLING_API',
);
my $api = RPC::ExtDirect::API->new_from_hashref(
config => $config,
api_href => {
before => 'MyApp::Util::global_before_hook',
after => 'MyApp::Util::global_after_hook',
'MyApp::Server::Foo' => {
before => 'MyApp::Server::Foo::package_before_hook',
action => 'MyApp.Server.Foo', # JavaScript style with dots
methods => {
foo => {
len => 1,
},
bar => {
params => [qw/ foo bar /],
},
}
}
}
);
DESCRIPTION
With Ext.Direct, the API exposed by the server side is published to the clients via fixed URI, a GET request to which produces a response with a piece of JavaScript code containing the API declaration. This module handles the API service discovery requests.
The Ext.Direct API declaration is in fact a tree-like data structure (an Object in JavaScript parlance) containing description of Actions and Methods available to the client. This data structure is encoded in JavaScript code statement that is regenerated dynamically every time it is requested. No caching is used at this time.
ACTIONS AND METHODS
An Action in Ext.Direct parlance is a collection of Method definitions. The nearest similar Perl thing is a package, other languages may call it a Class. In RPC::ExtDirect, an Action needs a unique name that can be nested; Methods should have unique names within their Actions.
Action names
If the Action name is not specified explicitly, it will be deduced from the package name. If api_full_action_names Config option is truthy, Action name will be similar to the package name with the same level of nesting, having '::'
replaced with dots: 'Foo::Bar::Baz' -> 'Foo.Bar.Baz'
. Starting with Ext JS version 4.2.1, this allows having nested API objects on the client side as well, so you can call the server side methods like normal JavaScript methods:
Foo.Bar.Baz.do_foo(...);
However nested API objects are not supported in Ext JS below 4.2.1 as well as in Sencha Touch 2.x, so the default behavior is to use only the last chunk of the package name: 'Foo::Bar::Baz' -> 'Baz'
.
Method calling conventions
Ext.Direct specification defines four calling convention for methods:
With parameters passed by position (ordered Method)
With parameters passed by name (named Method)
Form Handler. This is a special case of a Method that accepts field values for a submitted HTML form. Form handlers are used to process file uploads.
Poll Handler. This is another special case of a Method. Poll Handlers do not accept any arguments, except Environment objects when requested.
When an Ext.Direct remoting method is called on the client side, the transport layer will perform a check on the actual arguments passed to the method stub, and throw an exception if arguments do not conform to the API declaration.
To declare an ordered method, define the "len" option with the number of parameters accepted; this number may be 0 for methods that do not accept any parameters at all.
To declare a named method, define the "params" option with the names of mandatory parameters. It is worth noting that only existence of parameters is mandatory; their values may as well be undefined. If not all arguments exist, an exception will be thrown. If there are any extra arguments not declared for this method, an exception will be thrown as well, unless strict argument checking is turned off (see below).
Lazy parameter checking
Starting with Ext JS 4.2.2 and RPC::ExtDirect 3.0+, it is possible to perform less strict parameter checking on by-name methods. All parameters explicitly declared for a method will still be treated as mandatory, but no exception will be thrown if undeclared arguments are passed into the method stub; these "extra" arguments will be transmitted to the server side and passed into the actual Method. It is also possible to completely bypass the argument checking by not declaring any mandatory methods for a Method.
As mentioned above, the strict checking is enabled by default; to disable it, set the strict option to falsy value for any given method.
Lazy parameter checking is not supported in Ext JS below 4.2.2, and in Sencha Touch 2.x.
COMPILE VS RUN TIME DEFINITION
There are two ways to define Ext.Direct API with RPC::ExtDirect: statically by using ExtDirect
subroutine attribute, or dynamically by including Actions and Methods in a hashref that is used to configure an API object.
Both of these ways have their advantages and disadvantages. Using the ExtDirect
attribute, it's easier to keep definitions closer to the actual code so that when the code changes, its definition can be remembered to be changed as well. Also this approach is very easy to use and start with; just add the attribute to your code, and you're good to go. Also, the attribute syntax is expressive enough to be self-documenting, so often no other API documentation is needed.
On the other hand, for larger and more centralized projects it may be easier to keep all API definitions in one place rather than spread over dozens of packages. Besides easier maintenance, using dynamic approach allows having more than one active API object at a given time, possibly implementing different APIs tailored for usage patterns of a particular application.
Note that these two methods are not mutually exclusive, but it is not recommended to mix them unless you really know how to deal with ensuing timing issues. You've been warned.
DEFINING METHODS STATICALLY
In order to add a subroutine to the Ext.Direct interface, use an attribute with the sub definition:
sub foo : ExtDirect(...) {}
Note that there can be no space between the ExtDirect
attribute name and the opening parens; also in Perls older than 5.12, the attribute statement cannot span multiple lines, i.e. the whole ExtDirect(...)
construct should fit in one line.
Inside the parentheses, one of the following mutually exclusive option keywords is mandatory:
n
-
The number of ordered arguments this Method accepts. This form is considered obsolete, use "len" keyword instead. If
n
is used, this keyword should always come first in the list:sub foo : ExtDirect(1, ...) {} # right sub bar : ExtDirect(..., 1) {} # wrong sub baz : ExtDirect(0) {} # right
len
-
A more preferred way to define an Ordered Method. This keyword should be followed by the number of the parameters accepted by the Method:
sub foo : ExtDirect(len => 1) {} # right sub bar : ExtDirect(..., len => 1) {} # also right sub baz : ExtDirect(len => 0) {} # right again
params
-
A list of the named parameters this method accepts. Since a Method can be either ordered or named, this and above options are mutually exclusive. This keyword should be followed by an arrayref with the parameter names, possibly empty:
sub foo : ExtDirect(params => ['foo', 'bar', ...], ...) {} sub bar : ExtDirect(params => [], ...) {}
formHandler
-
This option defines the Method as a Form Handler.
pollHandler
-
This option defines the Method as a Poll Handler.
Having more than one calling convention keyword in the Method definition is not supported and will lead to undefined behavior.
Besides the mandatory calling convention keyword, there are optional Method attributes in hash-like key => value
form. Currently supported attributes are:
strict
-
This option, if set to a falsy value with Named parameters, turns on lazy parameter checking. Since the checks are strict by default, setting this option to truthy value will do nothing.
sub foo : ExtDirect(params => ['foo'], strict => !1, ...) {}
before|instead|after
-
A corresponding Hook slot definition for the Method. This keyword should be followed by an argument that defines the actual Hook behavior. See "add_hook" method documentation below.
- other
-
Any other keyword with the corresponding value will be passed through to the Method class constructor and will end up as a Method object property. Note that accessors for such properties will not be created automatically when the stock RPC::ExtDirect::API::Method class is used.
DEFINING METHODS DYNAMICALLY
If you find the static definition method inconvenient or hard to maintain, use dynamic definition instead. You can create a new API object using new_from_hashref constructor, or just init the global API instance from a hashref containing the API definition:
my $api = RPC::ExtDirect->get_api();
$api->init_from_hashref({
'MyApp::Server::Foo' => {
methods => {
ordered_method => {
len => 1,
},
named_method => {
params => [qw/ foo bar /],
strict => !1,
},
form_handler => {
formHandler => 1,
},
poll_handler => {
pollHandler => 1,
},
},
},
});
Keywords and options are the same as with the static method, refer to the section above for details.
GLOBAL API TREE INSTANCE
Under the hood, static API definition operates on a global instance of RPC::ExtDirect::API
, created at the package compilation time and available globally throughout the application.
Versions 1.x and 2.x of RPC::ExtDirect used package global variables to hold this information; version 3.0 is using a global RPC::ExtDirect::API object instead. This object is held in a private variable and can be retrieved by the get_api method:
my $global_api = RPC::ExtDirect->get_api;
This API object holds an instance of RPC::ExtDirect::Config with a set of options used to configure the API object behavior. This Config instance can be retrieved to set options directly:
my $cfg = RPC::ExtDirect->get_api->config;
$cfg->option1('foo');
$cfg->option2('bar');
Since the API object is a normal object, and the config method is a normal accessor, it is possible to replace that Config instance with a new one. However this may result in a loss of statically defined Config options, and is not recommended. Instead, use import package sub to configure the global API when using static API definitions.
The global API instance is used by default to generate the API declaration requested by the client side, and for dispatching remoting calls from the client side. If you prefer more control over the API tree, create the API object explicitly as shown in the SYNOPSIS, and pass it to the gateway object. Refer to the actual gateway documentation for details.
Because attribute parsing happens at package compilation time, it is hard to predict the order in which the methods will be processed. To provide some help with debugging, RPC::ExtDirect will throw an error if you are trying to redefine a Method; usually that means a mistake has been made somewhere.
API CONFIGURATION
RPC::ExtDirect::API provides two ways to configure Ext.Direct API declaration variables to accommodate specific application needs: dynamic via an RPC::ExtDirect::Config instance, and static via import
package subroutine.
An example of the new dynamic configuration is available in the "SYNOPSIS" above. This is the preferred way of configuring the API in large complex applications; it allows keeping the whole API definition in one place instead of distributed among the packages. It is also possible to define more than one API this way, for publishing to different clients.
The static configuration was available since version 1.0 and will be supported going forward. This way it is possible to configure the API variables at compile time:
use RPC::ExtDirect::API namespace => 'myApp',
router_path => '/router',
poll_path => '/events',
remoting_var => 'Ext.app.REMOTING_API',
polling_var => 'Ext.app.POLLING_API',
auto_connect => 0,
no_polling => 0,
before => \&global_before_hook,
after => \&global_after_hook,
;
Under the hood, the above code will set specified options on the Config instance held in the global API object.
API CONFIGURATION OPTIONS
The following configuration options are supported by RPC::ExtDirect::API:
namespace
-
Declares the namespace your Actions will reside in. To call the Methods on client side, you will have to qualify them with namespace:
namespace.Action.Method
, e.g.:myApp.Foo.Bar
router_path
-
URI for Ext.Direct Router calls. For the CGI environment, this should be the name of the CGI script that provides the API declaration; for more sophisticated environments it is an anchor for the specified PATH_INFO.
poll_path
-
URI for Ext.Direct Event Provider calls. Client side will poll this URI periodically, hence the name.
remoting_var
-
By default, Ext.Direct API declaration for remoting (forward) Methods is stored in Ext.app.REMOTING_API variable. If for any reason you would like to change that, do this by setting remoting_var.
Note that in production environment you would probably want to use a compiled version of the JavaScript application that consist of one big JavaScript file. In this case, it is recommended to include API declaration as the first script in your index.html and change the remoting API variable name to something like
EXT_DIRECT_API
. Default variable name depends on Ext.app namespace being available by the time Ext.Direct API declaration is downloaded, which is often not the case. polling_var
-
Ext.Direct does not provide a standard namespace for Event Providers to be published in. For similarity with "remoting_var",
Ext.app.POLLING_API
name is used to declare an Event Provider so that it could be used on the client side without having to hardcode any URIs explicitly.Ext.app.POLLING_API
configuration will only be published to the client side if there is at least one pollHandler Method defined in the Ext.Direct API.Note that the same variable naming caveat applies here as with "remoting_var".
no_polling
-
Explicitly prohibit the API declaration from containing a "polling_var" definition. This will suppress publishing Event Providers even if there are any pollHandler methods in the actual API.
This option can be useful for testing.
auto_connect
-
This option is deprecated and should not be used anymore.
before
-
Global
before
hook. See "HOOKS" in RPC::ExtDirect. instead
-
Global
instead
hook. See "HOOKS" in RPC::ExtDirect. after
-
Global
after
hook. See "HOOKS" in RPC::ExtDirect.
API OBJECT INTERFACE
RPC::ExtDirect::API provides several public methods:
HOOK_TYPES
-
Class/instance method. Returns the list of supported hook types. See "HOOKS" in RPC::ExtDirect for more information.
Accepts no arguments.
new
-
Constructor. Returns a new RPC::ExtDirect::API object with an empty API tree. Accepts named arguments in a hash.
Parameters:
config
-
Optional RPC::ExtDirect::Config instance to be used. If not provided, a new Config instance will be created.
new_from_hashref
-
Constructor. Returns a new RPC::ExtDirect::API object with an API tree initialized from the api_href argument. Accepts named arguments in a hash.
Parameters:
config
-
Optional RPC::ExtDirect::Config instance to be used. If not provided, a new instance will be created.
api_href
-
Mandatory. A hashref containing the API tree. See "DEFINING METHODS DYNAMICALLY" for more information.
init_from_hashref
-
Instance method. Initializes the API tree in the object from the passed hashref with API definitions. This method will be called internally by "new_from_hashref".
Accepts only one ordered argument:
API definition hashref. See "DEFINING METHODS DYNAMICALLY" for more information.
get_remoting_api
-
Instance method. Returns stringified API declaration for the current API tree contained in the object. Accepts named arguments in a hash.
Parameters:
config
-
An optional RPC::ExtDirect::Config instance to be used when generating the API declaration. This is useful for testing, but should not be used in production.
env
-
An Environment object. This object is not used by the stock RPC::ExtDirect::API code directly; instead, it is passed to the Action's remoting_api method, which in turn will pass it to the Method's get_api_definition method that will return the actual Method API definition.
You can subclass RPC::ExtDirect::API::Method to perform some additional actions, e.g. checking users' authentication status before generating the API declaration.
actions
-
Instance method. Returns the list of names for all Actions defined in the API tree.
add_action
-
Instance method. Adds or replaces an Action in the current API tree. Accepts named arguments in a hash.
Parameters:
package
-
Mandatory. Package name for the Action.
action
-
Optional name for the Action. If not provided, a new Action name will be generated, see "Action names" for more detail.
- other
-
The rest of the arguments is passed directly to the Action constructor. If no api_action_class Config option is set, the default RPC::ExtDirect::API::Action class will be used.
get_action_by_name
-
Instance method. Returns the Action object for the corresponding action name, or
undef
. Accepts one ordered argument (action name). get_action_by_package
-
Instance method. Returns the Action object for the corresponding package, or
undef
. Accepts one ordered argument (package name). add_method
-
Instance method. Add a new Ext.Direct Method object to the current API tree, creating an Action for it if necessary. Accepts named arguments in a hash.
Parameters:
action
-
Action name to add the Method to. Either this or
package
parameter below is mandatory. package
-
Package name of the Action to add the Method to. Either this or
action
parameter above is mandatory. method
-
Name of the Method to add.
- other
-
The rest of the arguments is passed directly to the Method constructor. If no api_method_class Config option is set, the default RPC::ExtDirect::API::Method class will be used.
get_method_by_name
-
Instance method. Return the Method object for the corresponding Action and Method name. Accepts two ordered arguments:
Action name to look up the Method in.
Method name.
add_hook
-
Instance method. Adds a new Hook object to the current API tree. Accepts named arguments in a hash.
For global hooks, only
type
andcode
parameters are needed. For Action level hooks, eitherpackage
oraction
parameter is needed as well. For Method level hooks, themethod
parameter is required in addition to the above.If api_hook_class Config option is not set, the default RPC::ExtDirect::API::Hook class will be used to instantiate the Hook object.
Parameters:
package
-
Package for which the hook is added. Optional for global hooks. This or
action
parameter is mandatory for Action or Method level hooks. action
-
Action name for which the hook is added. Optional for global hooks. This or
package
parameter is mandatory for Action or Method level hooks. method
-
Method name for which the hook is added. Optional for package and global hooks.
type
-
Hook type. The list of hook types supported by the API class is returned by the "HOOK_TYPES" method. See "TYPES" in RPC::ExtDirect::API::Hook for more information on hook types.
This parameter is mandatory.
code
-
Hook code, or absence thereof. See "code" in RPC::ExtDirect::API::Hook.
This parameter is mandatory.
get_hook
-
Instance method. Returns the Hook object for a given criteria. Accepts named arguments in a hash.
When looking up Method level hook,
action
orpackage
parameter is mandatory, as well as themethod
name and hooktype
. For Action level hooks,action
orpackage
is required to look up the Action, andtype
for the hook. For global hooks, onlytype
is required.Parameters:
action
-
Action name to return the Hook object for. This or
package
parameter is required for Action or Method level hooks, and is optional for global hooks. package
-
Package name of the Action to return the Hook object for. This or
action
parameter is required for Action or Method level hooks, and is optional for global hooks. method
-
Method name to return the Hook object for. Optional for Action and global level hooks.
type
-
Type of the hook to return. This parameter is required.
get_poll_handlers
-
Instance method. Returns the list of Method objects for all Poll handlers for every Action in the current API tree.
This method does not accept any arguments.
ACCESSOR METHODS
For RPC::ExtDirect::API, the following accesor methods are provided:
config
-
Return the current RPC::ExtDirect::Config instance held in this API object, or set a new one.
before
-
Return the global
before
Hook object if set, or assign a new one. See "HOOKS" in RPC::ExtDirect for more information. instead
-
Return the global
instead
Hook object if set, or assign a new one. See "HOOKS" in RPC::ExtDirect for more information. after
-
Return the global
after
Hook object if set, or assign a new one. See "HOOKS" in RPC::ExtDirect for more information.
SEE ALSO
More information on the configuration options can be found in RPC::ExtDirect::Config documentation.