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 sope-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?