Stefano Mazzocchi wrote:

Reinhard Poetz wrote:

Answer: It depends on the order of declaring your scripts in <map:flow/>. The first helper() method declared will be found.




But there is only one helper() method per block!?



Yes. Therefore we need something more sohpisticated than imports.


I'm having a hard time following this conversation, as it seems to me that this is another instance of something that is becoming an anti-pattern: "stating the solution before stating the problem".

The problem that we discuss was stated by Reinhard in http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=110519369529959&w=2, and then discussed in the rest of that thread (http://marc.theaimsgroup.com/?t=110467878200001&r=1&w=2). Glen changed name of the thread to something more relevant for the topic and we continued to discuss it http://marc.theaimsgroup.com/?t=110533379600002&r=1&w=2. I tried to explain the issues and point to the use case in http://marc.theaimsgroup.com/?t=110533379600002&r=1&w=2 again, as a service to newcommers to the discussion, and make a new trial explaining the issues below.


The problem is that you want to use some functionality defined in another block.

Fair enough, since that's what blocks are: isolated service providers.

One of the design decisions with blocks is that *NO FILE* will ever be exposed by the blocks directly.

There is practically no way in the world you are going to change my mind on that, so consider it a permanent -1 for a block to expose direct file access.

I agree completely with that, and have discussed how to achieve shielding of block internals earlier in the thread.


Usecase
=======

Now the use case that Reinhard and I have discussed is:

The idea is that I in a number of my webapps want to use the same handling of new customers. And therefore I have written a block "Customer" containing among other things a flowscript function:

function newCustomer() {
 var customer = new Customer(defaultCustomer.xml);
 cocoon.sendPageAndWait("nameAndAdress");
 ...
 cocoon.sendPageAndWait("preferences");
 ...
 cocoon.sendPageAndWait("spamPreferenses");
 ...
 return customer;
}

That handles a dialog with a new customer and returns an object that contains the result of the interaction.

Then we might want to use this functionality in another block for e-business that among other things uses the newCustomer function. That could e.g. look like:

function buy() {
 ...
 var customer = newCustomer();
 ...
 cocoon.sendPageAndWait("shoppingCart");
 ...
}

IMO this is a relevant use case for blocks and flow.

Several Contexts
================

After having seen the use case the question is what to do about it. First it is important to note that Customer:newCustomer() contain a number of references to resources within the Customer block: the file defaultCustomer.xml and the possibly internal pipelines nameAndAdress, preferences, spamPreferenses. A consequence of this is that even if the function is executed in the context of another block, like the e-business block above, all its relative URIs must be resolved in the context of the Customer block.

So using flowscripts from other blocks is more complicated that using flowscripts from the same block (the current situation). When using a flowscript from the same block you just call it in the current context that is descripbed by the FOM. When you use a flowscript from another block it must be exectuted in the context of the "FOM" from the other block. So the situation is like in object oriented programing the block is an object and the flowscript function is a method on it.

Sylvain have recoginzed that the handling of block (and sitemap) specific contexts is necessary for implementing VPCs and blocks, and discussed the concepts and its implementation in http://marc.theaimsgroup.com/?t=110064560900003&r=1&w=2. I have sugested that this can be interpreted in terms of static and dynamic binding, a refinement of the parameter passing strategy and polymorphic sitemaps in that thread.

Implementation Approaches
=========================

Returning to implementation of the usecase above. We have discussed some solutions, it can be solved by calling the flowscript function in a VPC pipeline defined in block. This VPC pipeline can then be used in other places following Sylvains design. We need some slightly modificated cocoon.sendPageAndWait function that handles foreign VPCs. This is the solution that I initially and Glen more persistently proposed.

The problem with that solution is how to pass the return value. It could be done by seting a session variable from the flowscript variable, but neither I nor Reinhard liked that solution. Communication between components by using global variables is not my favorite way of building systems. It scales badly, the communication between components becomes rather implicit and we have a risk that different blocks gets session variable name collisions.

Another solution would be to introduce some kind of return value for VPC pipelines and a third would be package the exported flowscript function as some function like thing that is easy to use from flowscripts. Reinhard and I have discussed the third option.

Here there are a number of questions:

* How to export functions from blocks. My opinion is that they should be explicitly enumerated in the VPC section is the block

* How to use them from another block, provided that they are exported. Reihnard proposed: cocoon.block.Customer.newCustomer();, which I find a good solution as it solves the problem that the function must be called in the context of its block. It solves the problem with lack of name spaces in JS and it also showes the object oriented nature of the situation.

* How to implement it.

* Other shielding issues. E.g. what about the web continuations that are created during the execution of a block flowscript: http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=110537204229024&w=2

                                                         --- o0o ---

Blocks *may* expose their sitemap pipelines but nobody said that they couldn't expose flow functions too (which are the flow equivalent of sitemap pipelines)

Now, blocks have different composition patterns:

 - dependency
 - extension

the two will, IMO, result in different ways of composing flow functions.

Dependency can make available, either implicity or explicitly thru a cocoon.importDependencies() the block's dependencies.

Since javascript has no notion of "private/public" for functions, we could specify that functions that start with _ are "private", while the other ones are "public". Or something like that.

For extension, we could have automatic overload, so that if block A has flow with function blah() and block B extends block A with function blah() you can do

 block b:
  blah() {
   cocoon.super()
   print "blah b"
  }

 block a:
  blah() {
   print "blah a"
  }

if block b extends block a, calling blah() on b will result in

 blah a
 blah b

These are certainly also important use cases.

There is *NO* need for direct file access and there is

Agree

*NO* need for namespaces in javascript.

Combining blocks from several manufacturers will be more complicated if there are name collsions. But by using the exported functions as a method on the "block object", that problem disapears.


I understand the above might not be easy to implement, but implementation difficulty should *never* drive design decisions.

I tend to live after that policy, but my employers have not always been happy with that approach ;)


                                                         --- o0o ---

HTH

/Daniel



Reply via email to