Release 1.0 Copyright ª1995, 1996 by TipTop Software. All Rights Reserved.

The most up-to-date version of this document is available at http://www.tiptop.com/WOPerl/Documentation/.


WOPerl

This is preliminary WOPerl documentation. This documentation currently assumes that you are familiar with Perl and Objective-C, and that you understand the WebObjects paradigm.

Web-Objective-Perl (WOPerl) is a WebObjects add-on which seamlessly integrates Perl into the WebObjects framework from NeXT Software, Inc. WOPerl is based on the Objective-Perl technology.

WOPerl extends the NeXT WebObjects framework so that you can program it in plain vanilla Perl. This allows you to use WebObjects without the need to learn any of the OpenStep object model, WebScript, the Objective-C language, or even the `Objective-' part of Objective-Perl. By using WOPerl you can leverage the power of the Perl programming language, and all already-existing Perl code.

WOPerl provides completely seamless interoperability with the rest of the WebObjects system. You can use any Objective-C or WebScript-implemented web component from WOPerl. Moreover, you can transparently use WOPerl-implemented web components from Objective-C or WebScript. In your WOPerl-implemented components, you can not only use any Perl-provided functionality (e.g., use networking Perl modules, interprocess communication modules, database interface modules, text processing modules, web-related modules, OLE automation extension module on NT, etc.), but you can also seamlessly use all NeXT-provided functionality (e.g., PDO, DOLE, EOF), as well as functionalities from your own class libraries!

WOPerl Product Line

WOPerl Lite
The Lite version runs with WebObjects Lite, Pro, or Enterprise. It is FREE, but it can only be used for limited time for evaluation purposes.

WOPerl Pro
The Pro version of WOPerl requires WebObjects Pro or Enterprise. In addition to the functionality provided by the Lite version, the Pro version provides extensibility:

WOPerl Pro Plus
The Pro Plus version requires WebObjects Pro or Enterprise. The Pro Plus version includes everything, i.e., the full Objective-Framework system:

The rest of this document explains basic WOPerl concepts.

Table of Contents

1. Objects and Messages
2. WOPerl Use in WebObjects Framework
3. Gory Details
4. Defining Component Classes in Other Languages
5. Running WOPerl Applications
6. Examples
7. Tips
8. Known Problems
9. Contact Information

1. Objects and Messages

IMPORTANT: When you use WOPerl there are two kinds of objects in the system: OpenStep objects and Perl object. We will use the term "Perl objects" to refer to native Perl objects (blessed hash references), and the term "OpenStep objects" to refer to OpenStep (Objective-C, Objective-Tcl, Objective-Perl, etc.) objects. Note that although there are two object systems involved when you use WOPerl, you don't need to understand the OpenStep object model (when you access OpenStep objects you access them as if they were Perl objects), although you should be aware that OpenStep objects are different.

In WOPerl, OpenStep objects are represented by a special Perl object type: ObjPerl::Object. Each OpenStep object accessible from WOPerl has a unique ObjPerl::Object representation. To send a message from Perl to an OpenStep object, use the standard Perl message syntax. If an OpenStep object $o responds to selector message:with:, this is how you can send this message (all three are equivalent):

	$o->messageWith($arg1,$arg2);	(**)
	$o->message_with_($arg1,$arg2);
	msg_send($o,'message:with:',$arg1,$arg2);

Note: The ObjPerl selector mapping functionality is turned ON by default in WOPerl. Hence "nice" message names such as (**) can be used.

In addition, you can send messages to OpenStep classes just as if they were Perl classes (packages). [This requires that you use the patched Perl available from TipTop Software.] For example:

	$v=NSObject->version;

Converting OpenStep objects to Perl objects and vice versa

The following table lists Perl types and corresponding OpenStep types.

Perl<-->OpenStep
scalar (number) <--> NSNumber
scalar (other) <--> NSString
array reference <--> NSArray
array ->-> NSArray
hash reference <--> NSDictionary
hash ->-> NSDictionary
object (blessed hash ref) <--> TTPerlObject
other <--> TTPerlLValue
ObjPerl::Object <--> other

To convert a Perl object to an OpenStep object, you can explicitly use the obj() function. Note that, by default, autoconversion is ON, hence you never need to explicitly convert Perl objects to OpenStep objects. E.g., if $o is an OpenStep object, and ­takeDict: is a method which takes one argument, an NSDictionary, you can simply say:

	$o->takeDict({ 'x' => 'y', 'a' => 'b' });

To convert an OpenStep object to a Perl object you need to explicitly use the pl() function. Note that OpenStep-to-Perl object conversion is not performed automatically! The reason for this is that it may not always be appropriate to do pl() conversion, in addition to the performance consideration. On the other hand, the system can always determine when a Perl entity needs to be converted to an OpenStep object; e.g., when sending a message to an OpenStep object which takes an object argument.

Applying the pl() function to a Perl entity (which is not an OpenStep object), yields the same unmodified Perl entity. Similarly, applying the obj() function to an OpenStep object yields the same unmodified OpenStep object.

Note that ObjPerl::Object is "overloaded" in Perl. As a consequence, you can use certain OpenStep objects (NSString, NSNumber, NSArray, and NSDictionary) as if they were Perl entities. Hence, you rarely need to explicitly use the pl() function to convert OpenStep objects to Perl entities. For example, if $s is an NSString:

	print "string: $s\n";

will print the value of the string. Similarly, NSNumber objects behave as Perl number data types:

	$n=NSNumber->numberWithInt(123);
	print "n+10: ", $n+10, "\n";
	$n2=2*$n;  # Note: $n2 is a Perl number

Furthermore, if $d is an NSDictionary (with string keys), you can simply access it as if it were a Perl hash reference! (This requires that you have patched Perl from TipTop Software.)

	@keys=keys(%$d);
	print "Dictionary keys: @keys\n";
	$d->{'mykey'}="My Value";
	print "Value stored: \"$d->{mykey}\",  ref: ",ref($d->{'mykey'}),
		",  class: ",$d->{'mykey'}->class,"\n";

Similarly, you can access NSArrays just as if they were Perl array references. For example, if $a is an NSArray, you can access its elements as Perl array elements:

	$a->[0]='whatever...";
	print "Zero element: $a->[0]\n";

[WARNING: (caveat) Perl 5 currently does not provide support for the following ($a is an NSArray):

	$count=$#$a+1;
	@arr=@$a;
	shift @$a;
	unshift @$a;
	push @$a;
  	pop @$a;

The above constructs do not work if $a is an NSArray.]

2. WOPerl Use in WebObjects Framework

To use WOPerl with WebObjects you simply create a .pl file inside .wo component bundle (i.e., you use .pl instead of .wos). Here is Main.pl, a replacement for Main.wo/Main.wos in the HelloWorld example:

  	# Perl classes are packages.
	# In this example we define Perl class WO::Main package WO::Main;
  	use WOPerl;

	# Define Perl method sayHello
  	sub sayHello {
    	  my($self)=@_;

	  # Global variable $WOApp is defined by WebObjects.  Note that
    	  # -pageWithName: expects an NSString.  Perl string "Hello" gets
    	  # automatically converted to the corresponding NSString object.
    	  # Note that $nextPage is an WO component controller object, i.e.,
    	  # OpenStep object (an ObjPerl::Object).
    	  my $nextPage=$WOApp->pageWithName("Hello");

  	  # $self->{'nameString'} is the value of instance variable
    	  # 'nameString' of this Perl object.
    	  $nextPage->setNameString($self->{'nameString'});
          # or: $nextPage->{'nameString'}=$name;
          # or: $nextPage->takeValueForKey($name,'nameString');

  	  # Action methods are supposed to return component controller objects.
   	  return $nextPage;
  	}

  	# We must return the package name, so that the WOPerl system knows
  	# which Perl object to instantiate (i.e., what the Perl package is).
  	'WO::Main';

Note that in this example we have not explicitly declared instance variables. To explicitly declare instance variable nameString:

	@IVARS=qw(nameString);

In a similar fashion, to define "persistent" instance variables p1 and p2, you do:

	@PERSISTENT=qw(p1 p2);

and to define "action" instance variables callback1 and callback2:

	@ACTIONS=qw(callback1 callback2);

In addition, in the main application script Application.pl, use @IVARS to declare "global" WebObjects variables, e.g.:

	@IVARS=qw(glb1 glb2);

and @SESSION to declare "session" variables:

	@SESSION=qw(svar1 svar2);

If instance variables are defined, the WOPerl system provides instance-variable-accessor methods for the corresponding OpenStep object.

Here is Hello.pl, a replacement for Hello.wo/Hello.wos in the Hello World example:

	# This is our Perl class:
	package WO::Hello;

	# Explicitly declare instance variable nameString.  As a result
	# of this declaration, the -nameString and -setNameString: accessor
	# methods are provided.  Hence, in the sayHello sub in Main.pl, we
	# can say $nextPage->setNameString(...);
	@IVARS=qw(nameString);

	'WO::Hello';

Invoking WOPerl-Implemented Methods

To invoke a method implemented in WOPerl, do just what you would expect:

	# File: MyComponent.pl
	package WO::MyComponent;

	use WOPerl;

	@IVARS=qw( ...  );

	sub doSomethingWith
	{

  	  my($self, $arg1)=@_;
  	  ...
	}

	sub doSomethingElse
	{

  	  my($self)=@_;
  	  ...

	  # Invoking doSomethingWith() from the same WOPerl implemented object:
	  # ###################################################################
  	  # $self is a Perl object
  	  $self->doSomethingWith("Hello World!");

  	  # ...
	}

	'WO::MyComponent';
	# EOF

	// Invoking doSomethingWith() from Obj-C or WebScript:
	// ###################################################

	component=[WOApp pageWithName:@"MyComponent"];
	[component doSomethingWith:@"Hello World!"];

	# Invoking doSomethingWith() from another WOPerl-implemented WebComponent:
	# ########################################################################

	$component=$WOApp->pageWithName("MyComponent");
	# $component is an OpenStep object with an embedded Perl object.
	# The following method invocation does not make any assumption how
	# $component is implemented (i.e., WOPerl, ObjC, WebScript, etc.):
	$component->doSomethingWith("Hello World!");

	# Invoking doSomethingWith() from another WOPerl-implemented WebComponent:
	# ########################################################################

	$component=$WOApp->pageWithName("MyComponent");
	$plobj=pl($component);
	# $plobj is the plain-Perl object which implements MyComponent.
	# NOTE: This assumes that MyComponent is implemented in WOPerl.
	# Otherwise, this would not work:
	$plobj->doSomethingWith("Hello World!");


	# Invoking doSomethingWith() from ObjTcl:
	# #######################################

	set component [$WOApp pageWithName: [@ "MyComponent"]]
	$component doSomethingWith: [@ "Hello World!"]

Caveat: You should not use _underscores in your WOPerl-implemented method (unless you use underscores in place of colons ':' ). You should also not use _underscores in instance variable names. If a method name or variable name contains an _underscore (other than leading _undersores) you have to use explicit msg_send() in order to send the corresponding message.

Method Names

Consider the following WOPerl-implemented method:

	sub methodOne { ... }

Note that in Perl subs (e.g., methods) can take any number of arguments. Hence, from ObjC or WebScript, &methodOne can be invoked as:

	[obj methodOne];
	[obj methodOne: a1];
	[obj methodOne: a1 : a2];
	// etc.

or from WOPerl as:

	$obj->methodOne;
	$obj->methodOne($a1);
	$obj->methodOne($a1, $a2);
	# etc.

If you want to restrict the number of arguments that can be passed into a WOPerl-implemented method, use undersores in place where you would use ':' in a method name. E.g.:

	sub methodTwo_with_ { ... }

This method now can be invoked from ObjC or WebScript as:

	[obj methodTwo: a1 with: a2];

or from WOPerl as:

	$obj->methodTwo_with_($a1,$a2);  # or even as:
	$obj->methodTwoWith($a1,$a2);

Instance Variable Access

To access keys (instance variables) of a web component from WOPerl, you can do:

  1. Access component keys (instance variables) using methods ­takeValue:forKey: and ­valueForKey:. For example, to set and read value of key "anIvar":
    	$component->takeValueForKey("Hello World!","anIvar");
    	print $component->valueForKey("anIvar"),"\n";

  2. Access component keys using methods ­setIvar: and ­ivar. These methods are automatically provided by WOPerl and WebScript:
    	$component->setAnIvar("Hello World!");
    	print $component->anIvar(),"\n";

  3. Access component keys directly. For example:
    	$component->{'anIvar'}="Hello World!";
    	print $component->{'anIvar'},"\n";
    (When you access component keys this way methods ­takeValue:forKey: / ­valueForKey: get invoked.)

  4. If $component is implemented in WOPerl, you can access its keys directly via embedded perl object. This is discouraged:
    	$plobj=pl($component);
    	$plobj->{'anIvar'}="Hello World!";
    	print $plobj->{'anIvar'},"\n";

3. Gory Details

Each WO component controller is represented by an OpenStep object. If your .wo component bundle contains a .pl file in it, the corresponding component controller will be an instance of the TTWOPerlComponentController class which contains an embedded Perl object. In the Perl domain, the embedded Perl object is an instance of the Perl package (class) that you define in the .pl file; i.e., the embedded Perl object is a hash reference blessed with your package, and its instance variables are the elements of the hash. The embedded Perl object is represented as an instance of TTPerlObject in the OpenStep domain.

Data "push-in" / "pull-out" conversion

When a TTWOPerlComponentController object along with its embedded Perl object (TTPerlObject) are created, instance variables of the Perl object are set (as specified in the .wod file). The values "pushed-in" by the WOF system are in the OpenStep domain (i.e., represented by OpenStep objects). At that time, these objects are converted to Perl entities (i.e., using the pl() conversion). When the transaction is completed, the values from Perl objects are "pulled-out" by the WOF system. I.e., the values are converted to OpenStep values using the obj() conversion.

Similarly, type conversion automatically occurs when Perl methods are invoked (e.g., from ObjC or WebScript); arguments are converted to Perl values (using the pl() conversion), and return type is converted to OpenStep value (using the obj() conversion).

The "push-in" / "pull-out" conversion is performed by default. The WOPerl system allows you turn it ON or OFF. The flags that control this behavior are 'rawSet' and 'rawGet':

Here is Main.pl with the 'rawSet' mode turned ON:

	package WO::Main;
	use WOPerl;
	@IVARS=qw(nameString);
	%FLAGS=(
		'rawSet' => 1,
	);

	sub sayHello {
	  my($self)=@_;
	  my $nextPage=$WOApp->pageWithName("Hello");

	  # Since 'rawSet' is 1, no automatic conversion is performed when data
	  # is pushed into perl object.  Hence, $name is actually an NSString
	  # instance, not a perl string!
	  my $name=$self->{'nameString'};
	  print "nameString class: ",$name->class->DESCR,"\n";
	  $nextPage->setNameString($name);
	  return $nextPage;
	}

	'WO::Main';

Request for comment: Should rawSet be the default behavior? E.g., this makes an important difference when NSArrays and NSDictionaries are pushed into a perl web component object: they are not converted into perl lists and hashes (respectively).

Memory management

OpenStep uses reference counting (retain/release) memory management semantics. When you store an OpenStep object into an instance variable of your Perl WO component object, the WOPerl system will make sure that the OpenStep object is retained while you need it (e.g., for the lifetime of your Perl object). Hence, you don't need to do explicit memory management of OpenStep objects when you store them in instance variables of your Perl WO component object.

Note that if you want to store OpenStep objects outside your Perl WO component objects you need to explicitly memory-manage those OpenStep objects. E.g., if you want to store an OpenStep object into a global variable:

	package WO::Example;
	use WOPerl;

	$aDict=NSMutableDictionary->dictionary->retain;
  	...

  	sub doSomethingAndSetADict {
    	  my($self, $newDict)=@_;
    	  ...
    	  # Release the previous object, and retain the new one:
 	  $aDict->autorelease;
    	  $aDict=$newDict->retain;

	  # No need to release/retain:
    	  $self->{'aDict'}=$newDict;
    	  ...
  	}

(This example is bad in a sense that you should not use global variables!)

Perl component object lifetime

The lifetime of WO component controller objects is the duration of a transaction (actually two transactions). I.e., WO component controller objects are created for each transaction, and deleted after the transaction is completed. The same holds true for embedded Perl objects: embedded Perl objects are created for each transaction and removed when the transaction is done.

4. Defining Component Classes in Other Languages

If you have the Objective-Framework system from TipTop Software, you can define WO component classes in ObjTcl or ObjPerl. To define an OpenStep class for a WO component in ObjTcl or ObjPerl, simply include a class definition in .objpl or .objtcl file in the component bundle. Note that the OpenStep class name must match the component name. E.g., in Main.objpl in Main.wo you must define the class named Main. See Examples/WOPerl/ObjTclHello.

5. Running WOPerl Applications

The post-install script creates a symbolic link so that /NextLibrary/WebObjects/Executables/DefaultApp is symbolically linked to /LocalDeveloper/Executables/DefaultApp. Hence, to run a WOPerl web application, run it just as you would run any other WebObjects application; i.e., connect to an appropriate URL using your favorite web browser. (Note that /LocalDeveloper/Executables/DefaultApp is a shell script which actually executes /LocalDeveloper/Examples/WOPerl/DefaultApp/WOPerlDefaultApp. Hence, when WebObjects adaptor runs DefaultApp, WOPerlDefaultApp actually gets started.)

Alternatively, you may want to run you web application by hand (e.g., for debugging purposes). To run a WOPerl web application by hand, type the following in a Unix shell window:

	WOPerlDefaultApp WOPerlAppName

For example to run WOPerl application <HTML_ROOT>/WebObjects/Examples/WOPerl/HelloWorld type:

	WOPerlDefaultApp Examples/WOPerl/HelloWorld

Then, connect to the app in a browser:

	http://localhost/cgi-bin/WebObjects/Examples/WOPerl/HelloWorld

See the WebObjects documentation "Serving WebObjects" for more info. See also script:

	/LocalDeveloper/Examples/WOPerl/DefaultApp/WOPerlDefaultApp

for advanced options.

6. Examples

WOPerl comes with a number of examples (originaly from NeXT) converted to WOPerl. If you are already familiar with WebObjects, you can learn a lot about WOPerl by comparing the WOPerl-implemented examples with the original ones.

Component:
http://localhost/cgi-bin/WebObjects/Examples/WOPerl/Component

CyberWind:
http://localhost/cgi-bin/WebObjects/Examples/WOPerl/CyberWind

DodgeLite:
http://localhost/cgi-bin/WebObjects/Examples/WOPerl/DodgeLite

EmployeeBook:
http://localhost/cgi-bin/WebObjects/Examples/WOPerl/EmployeeBook

HelloWorld:
http://localhost/cgi-bin/WebObjects/Examples/WOPerl/HelloWorld

ObjTclHello:
http://localhost/cgi-bin/WebObjects/Examples/WOPerl/ObjTclHello

TimeOff:
http://localhost/cgi-bin/WebObjects/Examples/WOPerl/TimeOff

Visitors:
http://localhost/cgi-bin/WebObjects/Examples/WOPerl/Visitors

7. Tips

Embedded Perl Objects. If a WO component is implemented in WOPerl (i.e., there is a .pl file in the .wo component bundle), the component is represented as an instance of TTWOPerlComponentController, which contains an embedded Perl object represented as TTPerlObject in OpenStep. If $web_obj is a component controller object (a TTWOPerlComponentController), to obtain the corresponding plain Perl object, simply:

	$perl_obj=pl($web_obj);

Similarly, if you have an embedded Perl object corresponding to a WO component, to get the corresponding OpenStep component object, simply:

	$web_obj=obj($perl_obj);

Debugging. Use Perl debugger, just as you would normally do with any Perl program. In addition, there are three trace flags which can be turned ON:

To turn all tracing ON, simply turn ON all three flags in your Perl package (class):

  	package WO::Whatever;
	use WOPerl;

 	%FLAGS=(
    	  'traceSet' => 1,
    	  'traceGet' => 1,
    	  'traceInvoke' => 1,
  	  );

  	@IVARS=...
  	...

8. Known Problems

9. Contact Information

TipTop Software
Email: info@tiptop.com
URL: http://www.tiptop.com
Tel: +1 (301) 656-3837 (9am--5pm US EST, Mon--Fri)
Fax: +1 (301) 656-8432
USPS: PO Box 30681, Bethesda, MD 20824, USA

Release 1.0 Copyright ª1995, 1996 by TipTop Software. All Rights Reserved.
###