DESCRIPTION

This module provides comprehensive support for implementing classes using the inside-out object model.

This module implements inside-out objects as anonymous scalar references that are blessed into a class with the scalar containing the ID for the object (usually a sequence number). Object data (i.e., fields) are stored within the class's package in either arrays indexed by the object's ID, or hashes keyed to the object's ID.

The virtues of the inside-out object model over the blessed hash object model have been extolled in detail elsewhere. See the informational links under "SEE ALSO". Briefly, inside-out objects offer the following advantages over blessed hash objects:

Encapsulation

Object data is enclosed within the class's code and is accessible only through the class-defined interface.

Field Name Collision Avoidance

Inheritance using blessed hash classes can lead to conflicts if any classes use the same name for a field (i.e., hash key). Inside-out objects are immune to this problem because object data is stored inside each class's package, and not in the object itself.

Compile-time Name Checking

A common error with blessed hash classes is the misspelling of field names:

$obj->{'coment'} = 'Say what?'; # Should be 'comment' not 'coment'

As there is no compile-time checking on hash keys, such errors do not usually manifest themselves until runtime.

With inside-out objects, data is accessed using methods, the names of which are checked by the Perl compiler such that any typos are easily caught using perl -c.

This module offers all the capabilities of Class::Std with the following additional key advantages:

Speed

When using arrays to store object data, Object::InsideOut objects are as much as 40% faster than blessed hash objects for fetching and setting data, and even with hashes they are still several percent faster than blessed hash objects.

For the same types of operations, Object::InsideOut objects are from 2 to 6 times faster than Class::Std objects.

Threads

Object::InsideOut is thread safe, and thoroughly supports sharing objects between threads using threads::shared. Class::Std is not usable in threaded applications (or applications that use fork under ActivePerl).

Flexibility

Allows control over object ID specification, accessor naming, parameter name matching, and more.

Runtime Support

Supports classes that may be loaded at runtime (i.e., using eval { require ...; };). This makes it usable from within mod_perl, as well. Also supports dynamic creation of object fields during runtime.

Perl 5.6

Usable with Perl 5.6.0 and later. Class::Std is only usable with Perl 5.8.1 and later.

Exception Objects

As recommended in Perl Best Practices, Object::InsideOut uses Exception::Class for handling errors in an OO-compatible manner.

Object Serialization

Object::InsideOut has built-in support for object dumping and reloading that can be accomplished in either an automated fashion or through the use of class-supplied subroutines. Serialization using Storable is also supported.

Foreign Class Inheritance

Object::InsideOut allows classes to inherit from foreign (i.e., non-Object::InsideOut) classes, thus allowing you to sub-class other Perl class, and access their methods from your own objects.

Class Declarations

To use this module, your classes will start with use Object::InsideOut;:

package My::Class: {
use Object::InsideOut;
...
}

Sub-classes inherit from base classes by telling Object::InsideOut what the parent class is:

Object::InsideOut acts as a replacement for the base pragma: It loads the parent module(s), calls their import functions, and sets up the sub-class's @ISA array. Therefore, you must not use base ... yourself, or try to set up @ISA arrays.

If a parent class takes parameters, enclose them in an array ref (mandatory) following the name of the parent class:

Field Declarations

Object data fields consist of arrays within a class's package into which data are stored using the object's ID as the array index. An array is declared as being an object field by following its declaration with the :Field attribute:

my @info :Field;

Object data fields may also be hashes:

my %data :Field;

However, as array access is as much as 40% faster than hash access, you should stick to using arrays. (See "Object ID" concerning when hashes may be required.)

(The case of the word Field does not matter, but by convention should not be all lowercase.)

Object Creation

Objects are created using the ->new() method which is exported by Object::InsideOut to each class:

my $obj = My::Class->new();

Classes do not implement their own ->new() method. Class-specific object initialization actions may be handled by :Init labeled methods (see "Object Initialization").

The initialization methods for both classes in the above will get 'foo' => 'bar', My::Class will also get 'param' => 'value', and Parent::Class will also get 'data' => 'info'. In this scheme, class-specific parameters will override general parameters specified at a higher level:

My::Class will get 'default' => 'bar', and Parent::Class will get 'default' => 'baz'.

Calling new on an object works, too, and operates the same as calling new for the class of the object (i.e., $obj->new() is the same as ref($obj)->new()).

NOTE: You cannot create objects from Object::InsideOut itself:

# This is an error
# my $obj = Object::InsideOut->new();

In this way, Object::InsideOut is not an object class, but functions more like a pragma.

Object Cloning

Copies of objects can be created using the ->clone() method which is exported by Object::InsideOut to each class:

my $obj2 = $obj->clone();

When called without arguments, ->clone() creates a shallow copy of the object, meaning that any complex data structures (i.e., refs) stored in the object will be shared with its clone.

Calling ->clone() with a true argument:

my $obj2 = $obj->clone(1);

creates a deep copy of the object such that no internal data is shared between the objects.

Deep cloning can also be controlled at the field level. See "Field Cloning" below for more details.

Object Initialization

Object initialization is accomplished through a combination of an :InitArgs labeled hash (explained in detail in the next section), and an :Init labeled subroutine.

The :InitArgs labeled hash specifies the parameters to be extracted from the argument list supplied to the ->new() method. These parameters are then sent to the :Init labeled subroutine for processing:

(The case of the words InitArgs and Init does not matter, but by convention should not be all lowercase.)

This :Init labeled subroutine will receive two arguments: The newly created object requiring further initialization (i.e., $self), and a hash ref of supplied arguments that matched :InitArgs specifications.

Data processed by the subroutine may be placed directly into the class's field arrays (hashes) using the object's ID (i.e., $$self):

which handles converting the data to a shared format when needed for applications using threads::shared.

Object Initialization Argument Specifications

The parameters to be handled by the ->new() method are specified in a hash that is labeled with the :InitArgs attribute.

The simplest parameter specification is just a tag:

my %init_args :InitArgs = (
'DATA' => '',
);

In this case, if a key => value pair with an exact match of DATA for the key is found in the arguments sent to the ->new() method, then 'DATA' => value will be included in the argument hash ref sent to the :Init labeled subroutine.

Rather than counting on exact matches, regular expressions can be used to specify the parameter:

my %init_args :InitArgs = (
'Param' => qr/^PARA?M$/i,
);

In this case, the argument key could be any of the following: PARAM, PARM, Param, Parm, param, parm, and so on. If a match is found, then 'Param' => value is sent to the :Init subroutine. Note that the :InitArgs hash key is substituted for the original argument key. This eliminates the need for any parameter key pattern matching within the :Init subroutine.

With more complex parameter specifications, the syntax changes. Mandatory parameters are declared as follows:

Can also be specified as Num or Number. This uses Scalar::Util::looks_like_number to test the input value.

List

This type permits a single value (that is then placed in an array ref) or an array ref.

A class name

The parameter's type must be of the specified class. For example, My::Class.

Other reference type

The parameter's type must be of the specified reference type (as returned by ref()). For example, CODE.

The first two types above are case-insensitive (e.g., 'NUMERIC', 'Numeric', 'numeric', etc.); the last two are case-sensitive.

The Type keyword can also be paired with a code reference to provide custom type checking. The code ref can either be in the form of an anonymous subroutine, or it can be derived from a (publicly accessible) subroutine. The result of executing the code ref on the initializer should be a boolean value.

In this case, when the host parameter is found, it is automatically put into the @hosts array, and a 'HOSTS' => value pair is not sent to the :Init subroutine. In fact, if you specify fields for all your parameters, then you don't even need to have an :Init subroutine! All the work will be taken care of for you.

(In the above, Regex may be Regexp or just Re, and Default may be Defaults or Def. They and the other specifier keys are case-insensitive, as well.)

Setting Data

Object::InsideOut automatically exports a method called set to each class. This method should be used in class code to put data into object field arrays/hashes whenever there is the possibility that the class code may be used in an application that uses threads::shared.

As mentioned above, data can be put directly into an object's field array (hash) using the object's ID:

$field[$$self] = $data;
# or
$field{$$self} = $data;

However, in a threaded application that uses data sharing (i.e., uses threads::shared), $data must be converted into shared data so that it can be put into the field array (hash). The ->set() method handles all those details for you.

The ->set() method, requires two arguments: A reference to the object field array/hash, and the data (as a scalar) to be put in it:

$self->set(\@field, $data);
# or
$self->set(\%field, $data);

To be clear, the ->set() method is used inside class code; not application code. Use it inside any object methods that set data in object field arrays/hashes.

In the event of a method naming conflict, the ->set() method can be called using its fully-qualified name:

$self->Object::InsideOut::set(\@field, $data);

Automatic Accessor Generation

As part of the "Field Declarations", you can optionally specify the automatic generation of accessor methods.

Accessor Naming

You can specify the generation of a pair of standard-named accessor methods (i.e., prefixed by get_ and set_):

my @data :Field('Standard' => 'data');

The above results in Object::InsideOut automatically generating accessor methods named get_data and set_data. (The keyword Standard is case-insensitive, and can be abbreviated to Std.)

For the above, you specify the full name of the accessor(s) (i.e., no prefix is added to the given name(s)). (The Get and Set keywords are case-insensitive.)

You can specify the automatic generation of a combined get/set accessor method:

my @comment :Field('Accessor' => 'comment');

which would be used as follows:

# Set a new comment
$obj->comment("I have no comment, today.");
# Get the current comment
my $cmt = $obj->comment();

(The keyword Accessor is case-insensitive, and can be abbreviated to Acc or can be specified as get_set or Combined or Combo or Mutator.)

Set Accessor Return Value

For any of the automatically generated methods that perform set operations, the default for the method's return value is the value being set (i.e., the new value).

The Return keyword allows you to modify the default behavior. The other options are to have the set accessor return the old (previous) value (or undef if unset):

my @data :Field('Set' => 'set_data', 'Return' => 'Old');

or to return the object itself:

my @data :Field('Set' => 'set_data', 'Return' => 'Object');

Returning the object from a set method allows it to be chained to other methods:

$obj->set_data($data)->do_something();

If desired, you can explicitly specify the default behavior of returning the new value:

my @data :Field('Set' => 'set_data', 'Return' => 'New');

(Return may be abbreviated to Ret; Previous, Prev and Prior are synonymous with Old; and Object may be abbreviated to Obj and is also synonymous with Self. All these are case-insensitive.)

Accessor Type Checking

You may, optionally, direct Object::InsideOut to add type-checking code to the set/combined accessor:

my @level :Field('Accessor' => 'level', 'Type' => 'Numeric');

Available types are:

Numeric

Can also be specified as Num or Number. This uses Scalar::Util::looks_like_number to test the input value.

List or Array

This type permits the accessor to accept multiple value (that are then placed in an array ref) or a single array ref.

Array_ref

This specifies that the accessor can only accept a single array reference. Can also be specified as Arrayref.

Hash

This type allows multiple key => value pairs (that are then placed in a hash ref) or a single hash ref.

Hash_ref

This specifies that the accessor can only accept a single hash reference. Can also be specified as Hashref.

A class name

The accessor will only accept a value of the specified class. For example, My::Class.

Other reference type

The accessor will only accept a value of the specified reference type (as returned by ref()). For example, CODE.

The types above are case-insensitive (e.g., 'NUMERIC', 'Numeric', 'numeric', etc.), except for the last two.

The Type keyword can also be paired with a code reference to provide custom type checking. The code ref can either be in the form of an anonymous subroutine, or a full-qualified subroutine name. The result of executing the code ref on the input argument(s) should be a boolean value.

Field Cloning

Object cloning can be controlled at the field level such that only specified fields are deep copied when ->clone() is called without any arguments. This is done by adding another specifier to the :Field attribute:

Object ID

By default, the ID of an object is derived from a sequence counter for the object's class hierarchy. This should suffice for nearly all cases of class development. If there is a special need for the module code to control the object ID (see Math::Random::MT::Auto as an example), then an :ID labeled subroutine can be specified:

The ID returned by your subroutine can be any kind of regular scalar (e.g., a string or a number). However, if the ID is something other than a low-valued integer, then you will have to architect all your classes using hashes for the object fields.

Within any class hierarchy only one class may specify an :ID subroutine.

Object Replication

Object replication occurs explicitly when the ->clone() method is called on an object, and implicitly when threads are created in a threaded application. In nearly all cases, Object::InsideOut will take care of all the details for you.

In rare cases, a class may require special handling for object replication. It must then provide a subroutine labeled with the :Replicate attribute. This subroutine will be sent three arguments: The parent and the cloned objects, and a flag:

In the case of thread cloning, $flag will be set to 'CLONE', and the $parent object is just an un-blessed anonymous scalar reference that contains the ID for the object in the parent thread.

When invoked via the ->clone() method, $flag may be either an empty string which denotes that a shallow copy is being produced for the clone, or $flag may be set to 'deep' indicating a deep copy is being produced.

The :Replicate subroutine only needs to deal with the special replication processing needed by the object: Object::InsideOut will handle all the other details.

Object Destruction

Object::InsideOut exports a DESTROY method to each class that deletes an object's data from the object field arrays (hashes). If a class requires additional destruction processing (e.g., closing filehandles), then it must provide a subroutine labeled with the :Destroy attribute. This subroutine will be sent the object that is being destroyed:

The :Destroy subroutine only needs to deal with the special destruction processing: The DESTROY method will handle all the other details of object destruction.

Cumulative Methods

Normally, methods with the same name in a class hierarchy are masked (i.e., overridden) by inheritance - only the method in the most-derived class is called. With cumulative methods, this masking is removed, and the same named method is called in each of the classes within the hierarchy. The return results from each call (if any) are then gathered together into the return value for the original method call. For example,

My top class is My::Class
I'm also a My::Foo
I was created as a My::Child

When called in a list context (as in the above), the return results of cumulative methods are accumulated, and returned as a list.

In a scalar context, a results object is returned that segregates the results from the cumulative method calls by class. Through overloading, this object can then be dereferenced as an array, hash, string, number, or boolean. For example, the above could be rewritten as:

Class My::Class reports:
My top class is My::Class
Class My::Child reports:
I was created as a My::Child
Class My::Foo reports:
I'm also a My::Foo

As illustrated above, Cumulative methods are tagged with the :Cumulative attribute (or :Cumulative(top down)), and propagate from the top down through the class hierarchy (i.e., from the base classes down through the child classes). If tagged with :Cumulative(bottom up), they will propagated from the object's class upwards through the parent classes.

Note that this directionality is the reverse of Class::Std which defaults to bottom up, and uses BASE FIRST to mean from the base classes downward through the children. (I eschewed the use of the term BASE FIRST because I felt it was ambiguous: base could refer to the base classes at the top of the hierarchy, or the child classes at the base (i.e., bottom) of the hierarchy.)

Chained Methods

In addition to :Cumulative, Object::InsideOut provides a way of creating methods that are chained together so that their return values are passed as input arguments to other similarly named methods in the same class hierarchy. In this way, the chained methods act as though they were piped together.

For example, imagine you had a method called format_name that formats some text for display:

And you decide that you'd like to perform some formatting of your own, and then have all the parent methods apply their own formatting. Normally, if you have a single parent class, you'd just call the method directly with $self-SUPER::format_name($name)>, but if you have more than one parent class you'd have to explicitly call each method directly:

So passing in someone's name to format_name in Customer would cause leading and trailing whitespace to be removed, then the name to be properly cased, and finally whitespace to be compressed to a single space. The resulting $name would be returned to the caller.

The default direction is to chain methods from the base classes at the top of the class hierarchy down through the child classes. You may use the attribute :Chained(top down) to make this more explicit.

If you label the method with the :Chained(bottom up) attribute, then the chained methods are called starting with the object's class and working upwards through the class hierarchy, similar to how :Cumulative(bottom up) works.

Unlike :Cumulative methods, :Chained methods return a scalar when used in a scalar context; not a results object.

Automethods

There are significant issues related to Perl's AUTOLOAD mechanism that cause it to be ill-suited for use in a class hierarchy. Therefore, Object::InsideOut implements its own :Automethod mechanism to overcome these problems

Classes requiring AUTOLOAD-type capabilities must provided a subroutine labeled with the :Automethod attribute. The :Automethod subroutine will be called with the object and the arguments in the original method call (the same as for AUTOLOAD). The :Automethod subroutine should return either a subroutine reference that implements the requested method's functionality, or else undef to indicate that it doesn't know how to handle the request.

Using its own AUTOLOAD subroutine (which is exported to every class), Object::InsideOut walks through the class tree, calling each :Automethod subroutine, as needed, to fulfill an unimplemented method call.

The name of the method being called is passed as $_ instead of $AUTOLOAD, and does not have the class name prepended to it. If the :Automethod subroutine also needs to access the $_ from the caller's scope, it is available as $CALLER::_.

Automethods can also be made to act as "Cumulative Methods" or "Chained Methods". In these cases, the :Automethod subroutine should return two values: The subroutine ref to handle the method call, and a string designating the type of method. The designator has the same form as the attributes used to designate :Cumulative and :Chained methods:

Note: The OPTIONAL code above for installing the generated handler as a method should not be used with :Cumulative or :Chained Automethods.

Object Serialization

my $array_ref = $obj->dump();

my $string = $obj->dump(1);

Object::InsideOut exports a method called dump to each class that returns either a Perl or a string representation of the object that invokes the method.

The Perl representation is returned when ->dump() is called without arguments. It consists of an array ref whose first element is the name of the object's class, and whose second element is a hash ref containing the object's data. The object data hash ref contains keys for each of the classes that make up the object's hierarchy. The values for those keys are hash refs containing key => value pairs for the object's fields. For example:

The name for an object field (data and life in the example above) can be specified as part of the field declaration using the NAME keyword:

my @life :Field('Name' => 'life');

If the NAME keyword is not present, then the name for a field will be either the tag from the :InitArgs array that is associated with the field, its get method name, its set method name, or, failing all that, a string of the form ARRAY(0x...) or HASH(0x...).

When called with a true argument, ->dump() returns a string version of the Perl representation using Data::Dumper.

Note that using Data::Dumper directly on an inside-out object will not produce the desired results (it'll just output the contents of the scalar ref). Also, if inside-out objects are stored inside other structures, a dump of those structures will not contain the contents of the object's fields.

In the event of a method naming conflict, the ->dump() method can be called using its fully-qualified name:

my $dump = $obj->Object::InsideOut::dump();

my $obj = Object::InsideOut->pump($data);

Object::InsideOut-pump()> takes the output from the ->dump() method, and returns an object that is created using that data. If $data is the array ref returned by using $obj->dump(), then the data is inserted directly into the corresponding fields for each class in the object's class hierarchy. If If $data is the string returned by using $obj->dump(1), then it is evaled to turn it into an array ref, and then processed as above.

If any of an object's fields are dumped to field name keys of the form ARRAY(0x...) or HASH(0x...) (see above), then the data will not be reloadable using Object::InsideOut-pump()>. To overcome this problem, the class developer must either add Name keywords to the :Field declarations (see above), or provide a :Dumper/:Pumper pair of subroutines as described below.

:Dumper Subroutine Attribute

If a class requires special processing to dump its data, then it can provide a subroutine labeled with the :Dumper attribute. This subroutine will be sent the object that is being dumped. It may then return any type of scalar the developer deems appropriate. Most likely this would be a hash ref containing key => value pairs for the object's fields. For example,

Just be sure not to call your :Dumper subroutine dump as that is the name of the dump method exported by Object::InsideOut as explained above.

:Pumper Subroutine Attribute

If a class supplies a :Dumper subroutine, it will most likely need to provide a complementary :Pumper labeled subroutine that will be used as part of creating an object from dumped data using Object::InsideOut::pump(). The subroutine will be supplied the new object that is being created, and whatever scalar was returned by the :Dumper subroutine. The corresponding :Pumper for the example :Dumper above would be:

Object::InsideOut also supports object serialization using the Storable module. There are two methods for specifying that a class can be serialized using Storable. The first method involves adding Storable to the Object::InsideOut declaration in your package:

package My::Class; {
use Object::InsideOut qw(Storable);
...
}

and adding use Storable; in your application. Then you can use the ->store() and ->freeze() methods to serialize your objects, and the retrieve() and thaw() subroutines to deserialize them.

Dynamic Field Creation

Normally, object fields are declared as part of the class code. However, some classes may need the capability to create object fields on-the-fly, for example, as part of an :Automethod. Object::InsideOut provides a class method for this:

The first argument is the class to which the field will be added. The second argument is a string containing the name of the field preceeded by either a @ or % to declare an array field or hash field, respectively. The third argument is a string containing key => value pairs used in conjunction with the :Field attribute for generating field accessors.

Restricted and Private Methods

Access to certain methods can be narrowed by use of the :Restricted and :Private attributes. :Restricted methods can only be called from within the class's hierarchy. :Private methods can only be called from within the method's class.

Without the above attributes, most methods have public access. If desired, you may explicitly label them with the :Public attribute.

Such permissions apply to both of the get and set accessors created on a field. If different permissions are required on an accessor pair, then you'll have to create the accessors yourself, using the :Restricted and :Private attributes when applicable:

Hidden Methods

For subroutines marked with the following attributes:

:ID

:Init

:Replicate

:Destroy

:Automethod

:Dumper

:Pumper

Object::InsideOut normally renders them uncallable (hidden) to class and application code (as they should normally only be needed by Object::InsideOut itself). If needed, this behavior can be overridden by adding the PUBLIC, RESTRICTED or PRIVATE keywords following the attribute:

NOTE: A bug in Perl 5.6.0 prevents using these access keywords. As such, subroutines marked with the above attributes will be left with public access.

NOTE: The above cannot be accomplished by using the corresponding attributes. For example:

# sub _init :Init :Private # Wrong syntax - doesn't work

Object Coercion

Object::InsideOut provides support for various forms of object coercion through the overload mechanism. For instance, if you want an object to be usable directly in a string, you would supply a subroutine in your class labeled with the :Stringify attribute:

Coercing an object to a scalar (:Scalarify) is not supported as $$obj is the ID of the object and cannot be overridden.

Foreign Class Inheritance

Object::InsideOut supports inheritance from foreign (i.e., non-Object::InsideOut) classes. This means that your classes can inherit from other Perl class, and access their methods from your own objects.

One method of declaring foreign class inheritance is to add the class name to the Object::InsideOut declaration inside your package:

package My::Class; {
use Object::InsideOut qw(Foreign::Class);
...
}

This allows you to access the foreign class's static (i.e., class) methods from your own class. For example, suppose Foreign::Class has a class method called foo. With the above, you can access that method using My::Class->foo() instead.

Thus, with the above, if Foreign::Class has an object method called bar, you can call that method from your own objects:

package main;
my $obj = My::Class->new();
$obj->bar();

Object::InsideOut handles the dispatching of the ->bar() method call using the internally held inherited object (in this case, $foreign_obj).

Multiple inheritance is supported, as well: You can call the ->inherit() method multiple times, or make just one call with all the objects to be inherited from.

->inherit() is a restricted method. In other words, you cannot use it on an object outside of code belonging to the object's class tree (e.g., you can't call it from application code).

In the event of a method naming conflict, the ->inherit() method can be called using its fully-qualified name:

$self->Object::InsideOut::inherit($obj);

my @objs = $self->heritage();

my $obj = $self->heritage($class);

my @objs = $self->heritage($class1, $class2, ...);

Your class code can retrieve any inherited objects using the ->heritage() method. When called without any arguments, it returns a list of any objects that were stored by the calling class using the calling object. In other words, if class My::Class uses object $obj to store foreign objects $fobj1 and $fobj2, then later on in class My::Class, $obj->heritage() will return $fobj1 and $fobj2.

->heritage() can also be called with one or more class name arguments. In this case, only objects of the specified class(es) are returned.

In the event of a method naming conflict, the ->heritage() method can be called using its fully-qualified name:

my @objs = $self->Object::InsideOut::heritage();

$self->disinherit($class [, ...])

$self->disinherit($obj [, ...])

The ->disinherit() method disassociates (i.e., deletes) the inheritance of foreign object(s) from an object. The foreign objects may be specified by class, or using the actual inherited object (retrieved via ->heritage(), for example).

The call is only effective when called inside the class code that established the initial inheritance. In other words, if an inheritance is set up inside a class, then disinheritance can only be done from inside that class.

In the event of a method naming conflict, the ->disinherit() method can be called using its fully-qualified name:

$self->Object::InsideOut::disinherit($obj [, ...])

NOTE: With foreign inheritance, you only have access to class and object methods. The encapsulation of the inherited objects is strong, meaning that only the class where the inheritance takes place has direct access to the inherited object. If access to the inherited objects themselves, or their internal hash fields (in the case of blessed hash objects) is needed outside the class, then you'll need to write your own accessors for that.

LIMITATION: You cannot use fully-qualified method names to access foreign methods. Thus, the following will not work:

my $obj = My::Class->new();
$obj->Foreign::Class::bar();

Normally, you shouldn't ever need to do the above: $obj->bar() would suffice.

The only time this may be an issue is when the native class overrides an inherited foreign class's method (e.g., My::Class has its own ->bar() method). Such overridden methods are not directly callable. If such overriding is intentional, then this should not be an issue: No one should be writing code that tries to by-pass the override. However, if the overriding is accidently, then either the native method should be renamed, or the native class should provide a wrapper method so that the functionality of the overridden method is made available under a different name.

THREAD SUPPORT

For Perl 5.8.1 and later, this module fully supports threads (i.e., is thread safe), and supports the sharing of Object::InsideOut objects between threads using threads::shared.

To use Object::InsideOut in a threaded application, you must put use threads; at the beginning of the application. (The use of require threads; after the program is running is not supported.) If object sharing it to be utilized, then use threads::shared; should follow.

If you just use threads;, then objects from one thread will be copied and made available in a child thread.

The addition of use threads::shared; in and of itself does not alter the behavior of Object::InsideOut objects. The default behavior is to not share objects between threads (i.e., they act the same as with use threads; alone).

To enable the sharing of objects between threads, you must specify which classes will be involved with thread object sharing. There are two methods for doing this. The first involves setting a ::shared variable (inside a BEGIN block) for the class prior to its use:

I have tried to make the messages and information returned by the error objects as informative as possible. Suggested improvements are welcome. Also, please bring to my attention any conditions that you encounter where an error occurs as a result of Object::InsideOut code that doesn't generate an Exception::Class object. Here is one such error:

Invalid ARRAY/HASH attribute

This error indicates you forgot the following in your class's code:

use Object::InsideOut qw(Parent::Class ...);

BUGS AND LIMITATIONS

You cannot overload an object to a scalar context (i.e., can't :SCALARIFY).

You cannot use two instances of the same class with mixed thread object sharing in same application.

Cannot use attributes on subroutine stubs (i.e., forward declaration without later definition) with :Automethod:

Due to limitations in the Perl parser, you cannot use line wrapping with the :Field attribute.

If a set accessor accepts scalars, then you can store any inside-out object type in it. If its Type is set to HASH, then it can store any blessed hash object.

There are bugs associated with threads::shared that may prevent you from using foreign inheritance with shared objects, or storing objects inside of shared objects.

For Perl 5.6.0 through 5.8.0, a Perl bug prevents package variables (e.g., object attribute arrays/hashes) from being referenced properly from subroutine refs returned by an :Automethod subroutine. For Perl 5.8.0 there is no workaround: This bug causes Perl to core dump. For Perl 5.6.0 through 5.6.2, the workaround is to create a ref to the required variable inside the :Automethod subroutine, and use that inside the subroutine ref:

For Perl 5.8.1 through 5.8.4, a Perl bug produces spurious warning messages when threads are destroyed. These messages are innocuous, and can be suppressed by adding the following to your application code:

It is known that thread support is broken in ActiveState Perl 5.8.4 on Windows. (It is not known which other version of ActivePerl may be affected.) The best solution is to upgrade your version of ActivePerl. Barring that, you can tell CPAN to force the installation of Object::InsideOut, and use it in non-threaded applications.

REQUIREMENTS

Scalar::Util v1.10 or later. It is possible to install a pure perl version of Scalar::Util, however, it will be missing the weaken() function which is needed by Object::InsideOut. You'll need to upgrade your version of Scalar::Util to one that supports its XS code.