How to declare SOPE methods.
What methods a SOPE object provides is specified in the
product.plist of a SOPE "product bundle". For each
method SOPE will register a callable "invocation"
object in the appropriate SoClass. This is different to
Objective-C and similiar to Python (methods are just
regular objects!).
In addition, for Objective-C classes, the classes are
scanned for methods.
TODO: write much more ;-)
If you want to learn more on how methods are invoked by
different protocols, take a look at
method dispatchers.
Automatic methods discovered by scanning
When an Objective-C class is registered as a SoClass,
SOPE scans the class hierarchy for "action" methods,
pretty much like in WODirectAction objects.
SOPE will expose all methods which end in "Action" or
"Action:" like "doItAction:" or "saveAction".
Discovered methods will be registered in the peer
SoClass as SoSelectorInvocation objects.
Note: automatic methods are discouraged in favor of
explicit declarations in product.plist. But in practice
they require less formal coding ;-)
This is implemented in
NGObjWeb/SoObjects/SoObjCClass.m
Q: Why don't we expose _any_ method?
A: In theory we could export any method to the web, and
actually this is done by Zope. The issue is that in this
case all methods (like -dealloc or -retain!) must be
properly protected by the security system which is
tedious and error prone.
So we found it more "secure" to enforce some naming
scheme or manual declaration in product.plist.
Tip: HTTP "methods"
The SoMethodDispatcher tries to invoke HTTP methods as
SOPE methods if available. So you can implement
"- GETAction:", "- DELETEAction:", etc to get triggered
by the appropriate HTTP methods.
Note that if no HTTP method is found, SoMethodDispatcher
will look for the "default method" and invoke that. This
is usually preferred over direct HTTP implementation/
Argument Types
To further understand methods, one needs to be aware
of different styles of passing arguments to methods.
The two major types are "positional arguments" and
"keyword arguments".
Keyword Arguments
For example if you submit an HTML form, parameters are
passed in as keywords arguments where ordering is not
relevant and the arguments are identified by their name:
http://myhost/myapp/so/doIt?a=5&b=3
This calls the method doIt with the arguments
a and b. The ordering cannot be ensured (at least
in no environment I know since everyone stores the query parameters
in a hashtable which does not preserve the ordering).
Positional Arguments
XML-RPC is the example which only allows calls using
positional arguments, eg:
c = server.add(5, 10);
Sidenote: Python methods
The handling of parameters is actually one of the
reasons why Python matches Zope so extremely well.
Python methods can be called in both styles. Sample:
def doIt(a, b)
doIt(b=10, a=5); # ordering is irrelevant, found by name
doIt(5, 10); # positional args
Further Python has support for optional positional and
keyword arguments (*args and **kwargs)
which is also great for dealing with additional
"webservice" parameters.
Special Case
TODO: talk about SOAP XML input and WebDAV messages.
Builtin Method Types
SOPE has builtin invocation classes for the common cases
(you can always add your own!). That is, it has
classes to invoke WOComponent's as "methods" and to
invoke selectors and WODirectAction's as methods.
The concept is a bit hard to grasp if you were thinking
OO in terms of a specific language (like Java or ObjC)
before. In SOPE a method can be anything which is
"callable".
As a simple example take a "view" method which can be
applied on all kinds of objects. Such a method exposed
through the web would probably get implemented as a
WOComponent.
SoSelectorInvocation / 'selector' key
Selector parameters are positional parameters by nature.
Don't be confused by Objective-C keyword selectors, the
keywords are actually part of the selector, not an argument
name (eg "- addThis:5 toThat:8" is selector
"addThis:toThat:" with two positional args 5 and 8).
methods = {
doIt = {
selector = "doIt:";
};
addIt = {
selector = {
name = "add:and:";
addContextParameter = NO;
};
};
addItMore = {
selector = {
name = "add:and:";
addContextParameter = NO;
arguments = {
SOAP = (
"Body/loginRequest/auth/username/!textValue",
"Body/loginRequest/auth/password/!textValue"
);
};
};
};
};
The first style with just the string as the value implies
the addContextParameter parameter set to
YES. That is, the "-doIt:" method will get no
parameters but the WOContext as the argument.
The second style works with positional parameters, as
submitted by the XML-RPC
method dispatcher.
It will receive two positional parameters and no context
(you can add the context as a third argument by setting the
addContextParameter to YES).
Finally the third option specifies how arguments are
extracted from a SOAP request. It performs skyrix-xml/DOM
queries on the SOAP envelope (which is passed in the
context by the dispatcher).
Note: all that will be extended a lot. Basically we need
to convert keyword parameters to a dictionary parameter or
to positional parameters.
Stay tuned.
Instances are created in
NGObjWeb/SoObjects/SoProductClassInfo.m
(-makeInvocationForMethodNamed:manifest:)
SoPageInvocation / 'pageName' key
WOComponent parameters are keyword parameters by nature
(parameters would be regular component values applied by
KVC).
methods = {
edit = {
pageName = "AppointmentEditor";
};
save = {
pageName = "AppointmentEditor";
actionName = "save";
}
login = {
pageName = "LoginPage";
actionName = "login";
arguments = {
SOAP = {
login = "Body/loginRequest/auth/username/!textValue";
password = "Body/loginRequest/auth/password/!textValue";
};
};
};
};
Page invocations are created if the pageName key
is found. The only optional argument is actionName
which specifies the name of a selector to trigger in
"direct action style".
That is, the "save" actionName will call a method
called "- (id<WOActionResult>)saveAction" on the
component.
Similiar to direct actions, defaultAction will be
called if no actionName is present. The default
implementation of this method just returns self
which will result in the component being rendered.
Parameters are not automatically applied on WOComponents,
at least not in the moment ;-) To retrieve keyword
parameters you just use the WODirectAction methods
- takeFormValuesForKeys:... etc or WORequest's
- formValueForKey: methods.
Note: this may change in the future, probably we
extend the declaration to include parameter specs
Update: added SOAP parameter handling to page invocations. It basically
matches the selector invocation support, it will extract values from the
XML and apply them to keyword parameters (via KVC).
Instances are created in
NGObjWeb/SoObjects/SoProductClassInfo.m
(-makePageInvocationForMethodNamed:manifest:)
WODirectActionPubInvocation
Hm, this is apparently unsupported in the moment?