File Coverage

blib/lib/Object/Hybrid.pm
Criterion Covered Total %
statement 575 701 82.0
branch 140 320 43.7
condition 76 296 25.6
subroutine 140 150 93.3
pod 4 14 28.5
total 935 1481 63.1


line stmt bran cond sub pod time code
1             package Object::Hybrid;
2            
3             #use 5.006;
4            
5 1     1   16440 use strict qw[vars subs];
  1         3  
  1         198  
6             $Object::Hybrid::VERSION = '0.03_5';
7            
8             =head1 NAME
9            
10             Object::Hybrid - promote Perl primitives (hashes, scalars, arrays, and filehandles), either tie()d or not, to become hybrid objects
11            
12             =head1 WARNING
13            
14             Any specific interface that Object::Hybrid exposes may change (as it already did) until version 1.0 is reached.
15            
16             =head1 SYNOPSIS
17            
18             Here (and everywhere in this documentation) notion of "primitive" refers to hash, scalar, array, or filehandle (i.e. perltie types), either tie()d or non-tie()d, bless()ed or non-bless()ed.
19            
20             Promote $primitive to become hybrid object:
21            
22             use Object::Hybrid qw(promote); # declare promote() for use
23             promote $primitive;
24            
25             Or (especially if you prefer to not export anything) use new() constructor...
26            
27             use Object::Hybrid;
28             $hybrid = new Object::Hybrid $primitive; # $primitive becomes hybrid object
29             $hybrid = Object::Hybrid->new($primitive); # same
30            
31             NOTE: tie()d primitive must be tie()d before promote(). If it needs to be tie()d later, the mutable_class => 1 argument to promote() should be used (see L).
32            
33             After that $primitive remains the same, but it is now bless()ed as object that exposes corresponding perltie methods, so that the following, for example, become interchangeable for B %$primitive:
34            
35             $primitive->{foo};
36             $primitive->FETCH('foo'); # same
37             $primitive->fetch('foo'); # same
38            
39             Also, in case of tie()d primitive instead of:
40            
41             tied(%$primitive)->method();
42            
43             just be tied()less:
44            
45             $primitive->method();
46            
47             The later call will also not fail due to "can't find method" if method() is not defined by tied(%$primitive), becoming no-op that returns empty list or undef() scalar, making it easier to write code portable accross multiple different tied(%$primitive) classes as well as non-tie()d primitives. In contrast, $primitive->METHOD() aliase call is not similarly "fail-safe" and will raise an exception if neither FETCH() nor fetch() are defined.
48            
49             In case non-tied() primitives need to be interchangeable with tied() ones that have extended tied() interface, instead of cumbersome (possibly repeating many times) tied()-conditional access expression:
50            
51             tied(%$primitive) ?
52             tied(%$primitive)->FETCH('foo', @args)
53             : $primitive->{foo};
54            
55             just:
56            
57             $primitive->FETCH('foo', @args);
58            
59             or faster:
60            
61             $primitive->fast->FETCH('foo', @args);
62            
63             However, for non-tie()d primitives the above tied()-conditional switch expression may still be significantly faster, so that it is still preferred for hot paths (tight loops, etc.). For speed and other tradeoffs involved see L and L sections.
64            
65             If $FH is a plain filehandle or tiehandle tied to class that implements stat(), ftest() (and others) and self() method, then the following simple code need not to discriminate between plain filehandle and tiehandle:
66            
67             promote $FH;
68            
69             $FH->stat();
70             $FH->ftest('-X');
71            
72             # same in indirect method notation:
73             STAT $FH;
74             FTEST $FH '-X';
75            
76             # or sometimes self() method can be used for that end too:
77             stat $FH->self;
78             -X $FH->self;
79            
80             =head1 DESCRIPTION
81            
82             Some applications need to accept both plain primitives as well as tie()d primitives with additional extended interface available through tied() object. For example, application cache may be allowed to optionally use either screamingly fast plain hash or some highly evolved persistent hashes tie()d to disk storage (like DB_File, etc.). There are many similar examples for filehandles, arrays and even scalars. Those are cases when Object::Hybrid combined with simple coding style can make code that handles those primitives compatible across whole spectrum, from plain primitives to all types of extended tied() primitives. There are also other uses, including working around gaps in tiehandle implementation, adding some fancy operations to primitives without using tie() as well as plain syntactic sugar.
83            
84             In the context of this module hybrid object is defined as a Perl object that represents its own bless()ed primitive (i.e. the primitive it is implemented with, currently hash, scalar, array, or filehandle). According to this definition, hybrid object can be seen as both primitive and object at the same time. In general case, it is a violation of object encapsulation to access object's underlying bless()ed primitive directly (at least outside of class's methods), but in special case of hybrid objects it is perfectly ok to do so - no violation of encapsulation takes place.
85            
86             Hybrid objects are instances of the class that is referred to as "hybrid class". This module implements default hybrid class and exports promote() function that bless()es Perl's primitives (hash, scalar, array, or filehandle) into either default or user-specified (custom) hybrid class to make them hybrid objects.
87            
88             Promoting primitive to become hybrid (i.e. bless()ing it into hybrid class) simply adds object interface to primitive and is a way to extend Perl primitives that is compatible with and complementary to another major way of extending primitives - perltie API.
89            
90             Specifically, advantages of promote()ing primitives are:
91            
92             =over
93            
94             =item Compatibility
95            
96             Hybrid object has corresponding perltie methods interface for accessing underlying bless()ed primitive that object is implemented with (e.g. tiehash methods for accessing underlying bless()ed hash, etc.). Moreover, for hybrid object the following, for example, are equivalent and interchangeable for B primitives:
97            
98             $hybrid->{foo};
99             $hybrid->FETCH('foo'); # same
100             $hybrid->fetch('foo'); # same
101            
102             Promoting primitives to become hybrids allows the same simple portable, non-specific code to manipulate (be compatible with) anything ranging from plain primitives to highly extended tie()d primitives as it unifies their interfaces and make them interchangeable.
103            
104             For example, if same code is required to accept and handle both plain hashes, i.e. fast, in-memory hashes, and tie()d hashes with extended perltie interface, e.g. slow persistent hashes tie()d to disk storage, then it is useful to promote() each of those hashes to become hybrid. Whenever plain access is required the following code:
105            
106             $hybrid->{foo};
107            
108             will work for both in-memory and persistent hashes, and is really fast in case of in-memory hash. And in case you need to use extended interface, something like next code will also work for promote()d both in-memory and persistent hashes:
109            
110             $hybrid->FETCH('foo', @args);
111            
112             $hybrid->can('custom_method')
113             and $hybrid->custom_method();
114            
115             For performnce comparison of various interface options see L section.
116            
117             Despite promoting primitives to become hybrids turn them into Perl objects, compatibility with arbitrary Perl objects in practice has little value, since code that manipulates objects usually assume objects to be of very specific class.
118            
119             =item tied()less access
120            
121             Accessing tied() interface of tie()d primitive no longer requires cumbersome (possibly conditional) tied() call, i.e. instead of:
122            
123             tied(%$hybrid)->method();
124            
125             one can write:
126            
127             $hybrid->method(); # same
128             $hybrid->fast->method(); # same, but may be faster
129            
130             For performnce comparison of various interface options see L and L sections.
131            
132             =item Incomplete tie() implementation workaround
133            
134             Currently tie()ing filehandles is still incomplete in Perl: sysopen(), truncate(), flock(), fcntl(), stat() and -X cannot currently be trapped. However, both tieclasses and hybrid classes can define corresponding methods or use self() method (see L) to not distinguish between primitives for that matter:
135            
136             promote $FH;
137            
138             $FH->stat();
139             $FH->ftest('-X');
140            
141             # same in indirect method notation:
142             STAT $FH;
143             FTEST $FH '-X';
144            
145             # or sometimes self() method can be used for that end too:
146             stat $FH->self;
147             -X $FH->self;
148            
149             =item Operator overloading
150            
151             Custom hybrid classes can be used for overloading operators on primitives. However, unfortunately such hybrid classes currently can only be used to promote() non-tied() primitives (see L).
152            
153             =back
154            
155             Object::Hybrid is a lightweight pure Perl module with no dependencies beyond core.
156            
157             =head1 Stop reading now (or how to use this documentation)
158            
159             Usually, there is no need to read any of the following documentation to use Object::Hybrid - you can stop reading at this point. What you have read so far, or even just self-explanatory SYNOPSIS, is enough in most cases. The following documentation covers optional features that need not to be learned for using Object::Hybrid in most usual case (e.g. occasionally).
160            
161             =head1 C
162            
163             use Object::Hybrid; # exports nothing
164             use Object::Hybrid $feature; # enables single named feature
165             use Object::Hybrid %options; # most general form
166            
167             The following features are supported:
168            
169             use Object::Hybrid 'promote';
170             use Object::Hybrid feature => 'promote'; # same
171             use Object::Hybrid feature => ['promote']; # same
172             use Object::Hybrid export => 'promote'; # same
173             use Object::Hybrid export => ['promote']; # same
174            
175             which exports (i.e. declares for use) the promote() function into caller's namespace.
176            
177             Next features depend on autobox pragma being installed (can be installed from CPAN archive):
178            
179             use Object::Hybrid 'autobox';
180             use Object::Hybrid feature => 'autobox'; # same
181             use Object::Hybrid feature => ['autobox']; # same
182             use Object::Hybrid autobox => Object::Hybrid->CLASS; # same, but can be custom hybrid class
183            
184             which will automatically promote() any primitive within the current scope, and "unpromote" them back beyond that scope. It is is equivalent to:
185            
186             use Object::Hybrid;
187             use autobox
188             HASH => Object::Hybrid->CLASS,
189             SCALAR => Object::Hybrid->CLASS,
190             ARRAY => Object::Hybrid->CLASS; # can be custom hybrid class instead
191            
192             And closely related is:
193            
194             use Object::Hybrid 'autopromote';
195             use Object::Hybrid feature => 'autopromote'; # same
196             use Object::Hybrid feature => ['autopromote']; # same
197             use Object::Hybrid autopromote => Object::Hybrid->CLASS; # same, but can be custom hybrid class
198            
199             which makes any method call on primitive in the lexical scope to automatically promote() that primitive.
200            
201             =head1 promote() function
202            
203             promote $primitive; # bless() to make $primitive a hybrid
204             promote $primitive => \%args; # same, but with named arguments
205             promote $primitive => %args; # same
206             promote $primitive => $class; # same, but with explicit $class to tie() to or bless() into
207             promote $primitive => $class, \%args; # same, but with named arguments
208             promote $primitive => $class, %args; # same
209            
210             In case $primitive is (or will later be) tied(), the tied() object is used as object interface of the hybrid (see L), unless custom hybrid $class is specified (see L).
211            
212             In any case, promote() never (re)tie()s primitive, only bless()es it.
213            
214             If $primitive specified is of type not currently supported by Object::Hybrid, exception is raised. If not defined($primitive) or no $primitive is specified, then exception is raised unless custom $class is specified (see L).
215            
216             The return value is a hybrid object, i.e. $primitive itself (may be useful if promote() is used in expressions).
217            
218             The class that primitive is bless()ed into by promote() is generated based on the type of primitive, whether it is tied(), and using custom $class, if any is specified. User should not assume anything about that resulting ref($primitive) class, except:
219            
220             Object::Hybrid->Class->is(ref($primitive));
221             ref($primitive)->isa($class); # in case $class was specified
222            
223             If promote() is called on already bless()ed $primitive, i.e. on object, it is equivalent to as if promote() was called on non-bless()ed $primitive with ref($primitive) passed as $class:
224            
225             promote bless $primitive => $class;
226             promote $primitive => $class; # same
227            
228             The "mutable_class" option commands whether the hybrid class used for specific primitive is mutable or not. The "mutable_class" => 1 values makes hybrid class mutable, allowing tie() on the primitive to happen after promote(). If hybrid gets tied()d or untie()d, its object interface immediately changes accordingly. Otherwise in case of immutable class, if tie() is called after promote(), perltie methods of tied() class cannot be called on hybrid. If "mutable_class" is undef(), mutable class is used for tied() primitives, immutable otherwise. The explicit "mutable_class" => 0 value makes hybrid class immutable - in theory this allows to use hybrid object interface different from tied() interface, which may be useful in some special cases, but may also violate hybrid equivalence requirement in case of tie()d primitives. Using this option leads to promote() bless()ing primitive into universal Object::Hybrid::CLASS instead of type-specific hybrid class, and this hurts performance very significantly. The semantics of this option is same for custom hybrid class, except in addition "mutable_class" => 1 should be set when custom class defines perltie methods.
229            
230             Note that mutable class significantly reduce performance in case of non-tied primitives.
231            
232             =head2 Custom hybrid $class
233            
234             If custom hybrid $class is specified and either $primitive is not tied() or not_tied => 1 option is given, then $primitive is bless()ed to hybrid class that inherits from $class.
235            
236             If however, C<< Object::Hybrid::Class->is($class) >> is true (means $class is the complete hybrid class implementation, then .$primitive is simply bless()ed into $class.
237            
238             Custom $class must be type-specific for given $primitive, so the type-conditional expression for $class may need to be used by caller.
239            
240             If custom hybrid $class is specified without $primitive or with not defined($primitive), then $primitive of the type expected by $class is autovivified. If $class do not allow to determine what $primitive type it expects, then exception is raised.
241            
242             =head1 Properties of hybrid objects
243            
244             The following are the properties of hybrid objects:
245            
246             =head2 Equivalent perltie API
247            
248             Hybrid object exposes perltie interface (perltie methods) for its underlying bless()ed primitive B that interface is equivalent to directly accessing underlying primitive - B tie()d and not tie()d. This interface equivalence is what makes, say, next two lines to have exactly same effect for hash hybrids:
249            
250             $hybrid->{foo};
251             $hybrid->FETCH('foo'); # same
252            
253             For performance comparison of various interface options see L section.
254            
255             =head2 Complete perltie API
256            
257             Currently tie()ing filehandles is still incomplete in Perl: sysopen(), truncate(), flock(), fcntl(), stat() and -X can't currently be trapped. However, hybrid object provides equivalent samename methods and their uppercase aliases (SYSOPEN(), TRUNCATE(), FLOCK(), FCNTL(), STAT() and FTEST()) to fill the gap and also allow compatibility with tiehandle classes that also implement them. This allows to write portable code that works around gaps in tiehandle implementation by B using methods on hybrids filehandles instead of Perl built-in functions, for example:
258            
259             promote $FH;
260            
261             $FH->stat();
262             $FH->ftest('-X');
263            
264             # same with indirect method calls...
265             STAT $FH;
266             FTEST $FH '-X';
267            
268             Thus, to avoid problems with gaps in tiehandle implementation simply always call methods on hybrids instead of Perl built-in functions.
269            
270             =head2 Delegation to tied() object
271            
272             In case of tied() primitives, hybrid object tries to delegate all method calls to tied() object. Delegation invokes called method on tied() object instead of hybrid. This allows invoking perltie methods B on hybrid directly (instead of on tied() object):
273            
274             $hybrid->STORE($value, @args);
275            
276             Delegation exposes entire interface of tied() object as hybrid object interface. Caller may use $hybrid->can('foo') to determine whether tied() object (if any) or hybrid implement certain methods.
277            
278             If tied() object provides no called method, delegation fails and then hybrid object falls back to calling its own samename method (called "fallback method") on hybrid itself. Fallback methods are simply same methods that would be called if hybrid is not tied(). This means methods of the non-tied() hybrid remain available if hybrid is tied(), unless tied() object exposes its own samename methods.
279            
280             In other words, tie()d hybrid may expose methods that tied() object do not provide. For example, since stat() is not a perltie method currently, it is unlikely to be implemented by tiehandle class, but it is provided by hybrid object to have portable C<< promote($tied_fh)->stat() >> workarounds for currently broken C.
281            
282             Note that delegation for standard perltie methods almost always works (no fallback), because normally tieclass does implement perltie methods.
283            
284             =head2 Method aliases
285            
286             Hybrid object provides altered-case aliases for all its methods (including lowercased aliases for all perltie methods).
287            
288             This feature is especially relevant in case when there are samename built-in functions for accessing primitives: shift(), exists(), seek(), etc. In this case and as general coding style for hybrids: the lower case should be used as functions or in direct method calls notation, while upper case can be used for indirect method call notation (later minimizes chances of indirect method notation colliding with non-parenthesized same name function calls with single scalar argument, like C). For example:
289            
290             seek $FH, 0, 0; # function call (coma after $FH, no extra arguments)
291             SEEK $FH 0, 0, @args; # indirect method call (no coma after $FH, @args extended interface)
292             $FH->seek(0, 0, @args); # direct method call (@args extended interface)
293            
294             In case of tie()d hybrid it is more efficient to call method that is defined by underlying tied() class, for example, FETCH() is likely to be faster than fetch() for tie()d primitives (as their tied() classes usually define no fetch(), just FETCH()). In all other cases aliases are equally efficient.
295            
296             Setting global C<$Object::Hybrid::Portable = 1> (usually local()ized to some block) changes behavior of aliases making them non-equivalent. Calls of lower-case aliases now do not fail (are "fail-safe") due to method being not defined by either hybrid class or underlying tied() class (if any), becoming no-op and returning empty list or undef scalar. This allows to write portable code that calls non-standard methods on tied() hybrids without (ab)using can() calls or eval{} wraps, which otherwise would make code cumbersome (e.g. in case of eval{} it is necessary to manually distinguish "can't find method" from errors in defined method, etc.). But it is of course risky too, as typos will not blow up, leading to silent error propagation, so that $Object::Hybrid::Portable = 1 should be used with care, after first testing code without it.
297            
298             In contrast, upper-case aliases are not similarly fail-safe under C<$Object::Hybrid::Portable = 1>, calling them is a fatal error if method, both lower-case and upper-case, is not defined, so that they can be used to ensure method is really called (mnemonics: insist on it being called by writing it in upper case). If, however, lower-case method is defined, the upper-case call will call it, not fail. For example:
299            
300             {
301             local $Object::Hybrid::Portable = 1;
302            
303             $hybrid->non_existing_method(); # will not fail due to "can't find method"
304             $hybrid->NON_EXISTING_METHOD(); # fatal "can't find method" error
305            
306             $hybrid->maybe_existing_method(); # will not fail due to "can't find method"
307             $hybrid->MAYBE_EXISTING_METHOD(); # may be a fatal "can't find method" error
308            
309             $filehandle_hybrid->fetch(); # will not fail due to "can't find method"
310             $filehandle_hybrid->FETCH(); # likely fatal "can't find method" error (since filehandles normally have no FETCH()), but will call fetch() (not fail) if fetch() happens to be defined
311             }
312            
313             =head2 call()
314            
315             $hybrid->call(method => @args);
316            
317             is the short form of:
318            
319             {
320             local $Object::Hybrid::Portable = 1;
321             $hybrid->method(@args);
322             }
323            
324             Except in case of call() caller() within &$method is one level deeper in the stack (which may be unexpected by methods that use caller()) and character case of method()'s name is irrelevant.
325            
326             =head2 fast()
327            
328             The fast() efficiently returns tied() object for tie()d invocant and invocant itself for non-tied. The fast() method is used for "manual" delegation to tied() object as a way of performance optimization:
329            
330             $hybrid->fast->FETCH('a'); # for tied() $hybrid is much faster than...
331             $hybrid->FETCH('a');
332            
333             $hybrid->fast->can('foo'); # for tied() $hybrid is much faster than...
334             $hybrid->can('foo');
335            
336             For non-tied hybrids, however, situation is reversed, but in absolute terms using fast() often pays off, especially where tied hybrids are more common throughput. The trade-off however is that $hybrid->fast->FETCH() syntax provides no method aliases, no fail-safety for non-defined methods in case of true $Object::Hybrid::Portable, and raises exception instead of falling back to calling samename hybrid's method in case tied() class defines no called method, so that using it is more risky and requires better knowledge of tied() classes involved.
337            
338             =head2 self() method
339            
340             The self() method returns underlying primitive: either bless()ed primitive of the hybrid object (i.e. hybrid object itself) or, if possible, real underlying primitive that is used by tied() object/class.
341            
342             Many tied() objects (like Tie::ExtraHash) transparently delegate operations on tie()d primitive to real primitive encapsulated somewhere inside that tied() object, using object just to store some additional state. If this is the case, tied() class may define self() as accessor for that underlying primitive to expose it to methods of the hybrid class and to users. The self() method allows to access that real primitive directly, falling back to hybrid's bless() primitive, if it is not possible or tied() class do not provide self() method. As a result, methods of custom hybrid class can have access to both tie()d bless()ed primitive (slow) and underlying real primitive (fast).
343            
344             The tied() class must not define self() if this may result in violation of encapsulation, i.e. if delegation to underlying real primitive is not transparent enough. Example of transparent tieclass that may define self() is Tie::ExtraHash. On the other hand some tieclasses, like Tie::StdHash, are so transparent that need not to define self() at all as default one is good for them.
345            
346             The self() method can have a number of useful applications, in particular to work around gaps in tiehandle implementation and to increase performance, as it allows hybrid methods to quickly bypass perltie layer and operate on underlying primitive directly, which may bring significant efficiency benefits, especially for some bulk operations.
347            
348             For example, since there is no yet perltie support for stat() and -X tests, called on tiehandle they do not propagate to underlying real filehandle, so they should be somehow propagated manually, but it requires knowing how to get underlying filehandle, if there is any, out of tied() object. Defining self() method in tieclass is supposed to do just that, and hybrid classes are expected to define self() method as well, so that portable code (assuming tied() classes define self()) can simply be:
349            
350             promote $FH;
351            
352             stat $FH->self;
353             -X $FH->self;
354            
355             # or nearly same using methods (default implementations of these methods also use self() under the hood):
356             STAT $FH;
357             FTEST $FH '-X';
358             $FH->stat();
359             $FH->ftest('-X');
360            
361             If tieclass defines self(), the sysopen(), truncate(), flock(), fcntl(), stat() and ftest() methods of corresponding tie()d hybrid object will operate correctly without tieclass implementing them.
362            
363             The Hybrid::Object makes no use of self() only it it is defined by tieclass of tie()d primitive, so that hybrids do not depend on tieclass implementing self() method.
364            
365             =head2 Optional bless() method
366            
367             If custom hybrid class defines bless() method, the C<< $hybrid_class->bless($primitive) >> is called instead of otherwise calling C. The bless() method is optional. It can be used as constructor/initializer for hybrid object, except it is to reuse $primitive (same as built-in bless()) instead of creating new.
368            
369             =head2 C<< Object::Hybrid->Class >>
370            
371             Hybrid objects can be recognized with the following test:
372            
373             Object::Hybrid->Class->is($hybrid);
374            
375             =head1 new() method
376            
377             $hybrid = new Object::Hybrid $primitive; # bless() to make $primitive a hybrid
378             $hybrid = new Object::Hybrid $primitive => \%args; # same, but with named arguments
379             $hybrid = new Object::Hybrid $primitive => %args; # same
380             $hybrid = new Object::Hybrid $primitive => $class; # same, but with explicit $class to tie() to or bless() into
381             $hybrid = new Object::Hybrid $primitive => $class, \%args; # same, but with named arguments
382             $hybrid = new Object::Hybrid $primitive => $class, %args; # same
383             $hybrid = new Object::Hybrid $class; # same, but $hybrid is constructed for a given class
384             $hybrid = new Object::Hybrid $class, \%args; # same, but with named arguments
385             $hybrid = new Object::Hybrid $class, %args; # same
386             $hybrid = new Object::Hybrid \%args; # same, but with named arguments
387             $hybrid = new Object::Hybrid %args; # same
388            
389             Or corresponding direct method call notation for any of the above can be used, for example:
390            
391             $hybrid = Object::Hybrid->new($primitive); # etc.
392            
393             The new() constructor promote()s $primitive to hybrid and returns it. It is roughly equivalent to:
394            
395             sub new { shift; return promote(@_) }
396            
397             Refer to promote() documentation.
398            
399             Note that new() do not construct object of Object::Hybrid class, even not $hybrid->isa('Object::Hybrid'), so beware.
400            
401             =head1 tie() method
402            
403             $tied = Object::Hybrid->tie( $primitive, $tieclass, @args); # for %$primitive same as...
404             $tied = tie(%$primitive, $tieclass, @args); # ... except $primitive also gets promote()d to hybrid
405            
406             =head1 Class() method
407            
408             Object::Hybrid relies on Class::Tag to store and query inheritable meta-data of hybrid classes, and uses Object::Hybrid::Class as tagger class in Class::Tag's terms. The Class() method always returns the name of that tagger class. This is primarily for convenience of using it in expressions. Refer to L for currently supported tags and their semantics.
409            
410             package Foo;
411             use Object::Hybrid::Class; # tags Foo as standalone hybrid class
412            
413             package Bar;
414             use Object::Hybrid::Class 'mutable_class'; # tags Bar as mutable hybrid class
415            
416             Object::Hybrid->Class eq 'Object::Hybrid::Class'; # true
417             Object::Hybrid->Class->is( 'Foo'); # true
418             Object::Hybrid->Class->mutable_class('Foo'); # false
419             Object::Hybrid->Class->mutable_class('Bar'); # true
420             Object::Hybrid->Class->is( 'Bar'); # false
421            
422             =head1 is() method
423            
424             promote $hybrid;
425             Object::Hybrid->is( $hybrid); # true
426             Object::Hybrid->Class->is( $hybrid); # same
427             Object::Hybrid->is( $not_hybrid); # false
428             Object::Hybrid->Class->is($not_hybrid); # same
429            
430             =head1 ref_*() methods
431            
432             These are utility methods useful to sort things out. Generally, all ref_foo() (ref_*()) methods return boolean that tells whether its argument is reference or not, but exact boolean value depends of the value of 'foo' suffix:
433            
434             =head2 ref_type() method
435            
436             Object::Hybrid->ref_type({}) eq 'HASH'; # true
437             Object::Hybrid->ref_type(bless {}, 'Foo') eq 'HASH'; # true
438            
439             and so on...
440            
441             =head2 ref_isa() method
442            
443             $obj = bless {}, 'Foo';
444             Object::Hybrid->ref_isa($obj); # true
445             Object::Hybrid->ref_isa($obj) eq $obj; # true
446             Object::Hybrid->ref_isa($obj, 'Foo') eq $obj; # true
447             Object::Hybrid->ref_isa($obj, 'Bar'); # false
448             Object::Hybrid->ref_isa({}); # false
449            
450             and so on...
451            
452             This method is useful to try some unknown $thing at hands that it is too uncertain to call $thing->isa('Foo') on it. It returns true, more specifically passes through its argument (for use in expressions and chained calls) if reference is blessed and, if second argument defined, isa() of second argument type (exact type can be obtained with ref(ref_isa($var)) instead). Otherwise returns false. More specifically, returns 0 for blessed references, '' for non-blessed references and undef for non-references.
453            
454             =head2 ref_tied() method
455            
456             $tied = tie %$primitive, 'Foo';
457             Object::Hybrid->ref_tied($primitive) eq $tied; # true
458             Object::Hybrid->ref_tied({}) eq '0'; # true
459             Object::Hybrid->ref_tied(sub{}) eq ''; # true, since sub{} is not tie()able
460             Object::Hybrid->ref_tied('string') eq ''; # true, since 'string' is not tie()able
461            
462             and so on...
463            
464             =head1 Subclassing Object::Hybrid
465            
466             Subclassing Object::Hybrid and overriding new() in the subclass will automatically override promote() exported by that subclass, so there is no need to explicitly redefine promote() in subclass.
467            
468             =head1 Perltie classes
469            
470             Hybrid objects are out of the box compatible with any valid tieclass.
471            
472             However, to support workarounds for "gaps" in perltie implementation, tieclasses may need to meet additional requirements - those are some of requirements that hybrid classes already comply with, intended specifically to work around perltie implementation gaps, namely: L and L. Currently tie()ing filehandles is still incomplete in Perl, so these requirements mainly apply to tiehandle classes. The most simple tiehandle classes, like Tie::StdHandle (loaded by "use Tie::Handle"), already comply with this requirements, as for them default self() and other methods provided by default hybrid class are good enough. A bit more complex tiehandle classes need just to implement self() method. If defining self() is not possible in case of more complex tiehandle classes, additional SYSOPEN(), TRUNCATE(), FLOCK(), FCNTL(), STAT() and FTEST() methods may need to be implemented as workarounds by tiehandle class.
473            
474             Since tie() do not pass primitive to be tie()d to TIE*() constructor, TIE*() cannot be made to optionally promote() that primitive. Instead, tieclass can expose promote() as one of its methods allowing user to promote primitives or expose Object::Hybrid->tie() method analog for built-in tie() that both tie()s and promote() given primitive. This, however, should probably not be dove via subclassing.
475            
476             =head1 Operator overloading
477            
478             Custom hybrid classes can be used for overloading operators on promote()d primitives. However, unfortunately hybrid classes with overloaded operators currently can only be used to promote() non-tied() primitives. This is because currently overload pragma is broken - bless()ing tie()d primitive into such class will implicitly untie() it. Should this be fixed in the future, operator overloading can be used without this limitation.
479            
480             However, even used on non-tied() primitives operator overloading is very powerful and can have some interesting (and possibly even useful) applications. In particular, overloading of dereference operators allows to achieve effects somewhat similar to using tie(), like "back-door" state similar to that of tied() objects. It is even possible to have "hybrid primitive" that is simultaneously hash, scalar, array, subroutine and glob (however such hybrid class may violate equivalence requirement as FETCH(0) need to be equivalent to $hybrid->{0} and $hybrid->[0] at the same time).
481            
482             =head1 Performance
483            
484             The performance preferences for hash hybrids are (in order from fastest to slowest):
485            
486             $non_blessed->{foo} # ~ 1_700_000/s
487             $nontied_hybrid->{foo}; # ~ 1_700_000/s
488             $nontied_hybrid->FETCH('foo'); # ~ 300_000/s
489             $nontied_hybrid->fast->FETCH('foo'); # ~ 200_000/s (a bit slower despite fast())
490             tied(%$tied_hybrid)->FETCH('foo'); # ~ 230_000/s
491             $tied_hybrid->fast->FETCH('foo'); # ~ 150_000/s
492             $tied_hybrid->{foo}; # ~ 60_000/s
493             $tied_hybrid->FETCH('foo'); # ~ 25_000/s
494            
495             This results are based on bench/benchmark_hash.pl script available in the Object::Hybrid distribution and assume immutable hybrid class (the default, see further in this documentation) and Tie::StdHash-like simple FETCH() on the {foo => 1} hash.
496            
497             Above results suggests that tied()-conditional access switching expression is (currently) the fastest solution that is to be used in hot paths (tight loops, etc.) and it requires no promote()ing of primitive to hybrid:
498            
499             tied(%$primitive) ?
500             tied(%$primitive)->FETCH('foo') # ~230_000/s
501             : $primitive->{foo}; # ~1_700_000/s
502            
503             However, using this construct repeatedly may be too cumbersome, so out of hot paths (tight loops, etc.) promote()ing to hybrid can be used to simplify code while retaining and enhancing its portability across various tie()d and non-tie()d primitives.
504            
505             For hybrid interfaces performance varies widely depending on whether it is a tied or non-tied respectively:
506            
507             $hybrid->{foo}; # ~ 60_000 - 1_700_000/s
508             $hybrid->FETCH('foo'); # ~ 25_000 - 300_000/s
509             $hybrid->fast->FETCH('foo'); # ~ 150_000 - 200_000/s
510            
511             Consequently, use of any of these should be decided based on the projected use mix of tied vs. non-tied primitives.
512            
513             =head1 TODO
514            
515             Currently tests cover only tiehashes and tiehandles, there should be tests for other types as well.
516            
517             As soon as (and if) Object::Hybrid interface stabilizes enough, its version is to jump to 1.0.
518            
519             =head1 SEE ALSO
520            
521             The C pragma is another way of doing somewhat similar, but not the same things, so autobox and Object::Hybrid are not mutually substitutive. However, if C pragma is installed, then either "autobox" or "autopromote" feature can be enabled - see L.
522            
523             Objects of standard IO::Handle class and its subclasses (such as IO::File or IO::Socket) are hybrid objects according to general hybrid object definition used by Object::Hybrid, but they are incompatible with promote()d hybrids. However, it should be possible to promote() IO::Handle object to become compatible hybrid.
524            
525             =head1 SUPPORT
526            
527             Send bug reports, patches, ideas, suggestions, feature requests or any module-related information to L. They are welcome and each carefully considered.
528            
529             In particular, if you find certain portions of this documentation either unclear, complicated or incomplete, please let me know, so that I can try to make it better.
530            
531             If you have examples of a neat usage of Object::Hybrid, drop a line too.
532            
533             =head1 AUTHOR
534            
535             Alexandr Kononoff (L)
536            
537             =head1 COPYRIGHT AND LICENSE
538            
539             Copyright (c) 2010 Alexandr Kononoff (L). All rights reserved.
540            
541             This program is free software; you can use, redistribute and/or modify it either under the same terms as Perl itself or, at your discretion, under following Simplified (2-clause) BSD License terms:
542            
543             Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
544            
545             * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
546             * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
547            
548             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
549            
550             =cut
551            
552 1     1   5 no warnings;
  1         2  
  1         37  
553            
554 1     1   518 use Object::Hybrid::Class (); # Object::Hybrid itself is not a hybrid class
  1         4  
  1         2924  
555            
556             sub CLASS_MUTABLE () { 'Object::Hybrid::CLASS' }
557             sub CLASS () { 'Object::Hybrid::CLASS' }
558             sub CLASS_HASH () { 'Object::Hybrid::HASH2' }
559             sub CLASS_HASH_NONTIED () { 'Object::Hybrid::HASH' }
560             sub CLASS_SCALAR () { 'Object::Hybrid::SCALAR2' }
561             sub CLASS_SCALAR_NONTIED () { 'Object::Hybrid::SCALAR' }
562             sub CLASS_ARRAY () { 'Object::Hybrid::ARRAY2' }
563             sub CLASS_ARRAY_NONTIED () { 'Object::Hybrid::ARRAY' }
564             sub CLASS_HANDLE () { 'Object::Hybrid::GLOB2' }
565             sub CLASS_HANDLE_NONTIED () { 'Object::Hybrid::GLOB' }
566             sub CLASS_AUTOPROMO () { 'Object::Hybrid::AUTOPROMOTE' }
567             sub FRONTAL () { 'FRONTAL' }
568             sub Class () { 'Object::Hybrid::Class' }
569            
570             sub frontclass_name {
571 51     51 0 1951 my (undef, $class, $primitive) = @_;
572 51 50 66     281 return join '_', $class, ref $primitive ? _ref_type($primitive) : $primitive||(), FRONTAL
573             }
574            
575             my %class4type = (
576             HASH => CLASS_HASH,
577             ARRAY => CLASS_ARRAY,
578             SCALAR => CLASS_SCALAR,
579             GLOB => CLASS_HANDLE,
580             );
581            
582             my %nontied_class4type = (
583             HASH => CLASS_HASH_NONTIED,
584             ARRAY => CLASS_ARRAY_NONTIED,
585             SCALAR => CLASS_SCALAR_NONTIED,
586             GLOB => CLASS_HANDLE_NONTIED,
587             );
588            
589             my %AD;
590            
591             __PACKAGE__->import(qw(promote));
592             sub import {
593 2     2   9 my $self = shift;
594            
595             # interface...
596 2 50       13 my $opt
    50          
    50          
597             = @_ > 1 ? {@_}
598             : !@_ ? { }
599             : ref $_[0] eq 'HASH' ? $_[0]
600             : { feature => [$_[0]] };
601            
602             # normalize %$opt...
603 2         3 foreach my $list (qw(feature export autopromote)) {
604             next
605 6 100       10 if !exists $opt->{$list};
606             ref $opt->{$list} eq 'ARRAY'
607             or $opt->{$list}
608 2 50       6 = [$opt->{$list}];
609             }
610            
611 2         2 my @goto;
612            
613             # process features first...
614 2 50       5 foreach my $feature (ref $opt->{feature} eq 'ARRAY' ? @{$opt->{feature}} : $opt->{feature}) {
  2         3  
615 2 50       5 if ($feature eq 'promote') { push @{$opt->{export}}, $feature }
  2         3  
  2         4  
616            
617             # mutually exclusive features...
618 2 50       7 if ($feature eq 'autobox') {
    50          
    50          
619             #_load_class($self->CLASS);
620 0         0 _load_class($self->CLASS_HASH);
621 0         0 _load_class($self->CLASS_SCALAR);
622 0         0 _load_class($self->CLASS_ARRAY);
623            
624             require
625 0         0 autobox;
626             autobox::import( ref($self)||$self,
627             HASH => $opt->{$feature}||$self->CLASS_HASH, # method instead of constant, for subclassing...
628             SCALAR => $opt->{$feature}||$self->CLASS_SCALAR,
629             #GLOB => $opt->{$feature}||$self->CLASS_HANDLE, # not supported by autobox
630 0   0     0 ARRAY => $opt->{$feature}||$self->CLASS_ARRAY, );
      0        
      0        
      0        
631             }
632             elsif ($feature eq 'autopromote') {
633             require
634 0         0 autobox;
635 0   0     0 autobox::import( ref($self)||$self,
636             HASH => CLASS_AUTOPROMO,
637             SCALAR => CLASS_AUTOPROMO,
638             #GLOB => CLASS_AUTOPROMO, # not supported by autobox
639             ARRAY => CLASS_AUTOPROMO, );
640 0         0 my $autoload
641             = __PACKAGE__ . '::AUTOLOAD';
642 0         0 *{ CLASS_AUTOPROMO . '::AUTOLOAD' } = sub{
643 0   0 0   0 $self->new($_[0], @{$opt->{$feature}}||());
644 0         0 $$autoload =~ s/^.*:://;
645 0 0       0 goto &{ $_[0]->can($$autoload)
  0         0  
646             or croak(_cant_locate_object_method($_[0], $$autoload)) };
647 0         0 };
648             }
649 0         0 elsif ($feature eq 'HASTE') { $opt->{$feature} = 1; }
650             }
651            
652             # process options...
653 2 50       4 if ($opt->{HASTE}) {
654 0         0 $Object::Hybrid::HASTE = 1;
655             }
656            
657 2 50       6 if ($opt->{export}) {
658 2         1 my @symbols;
659 2 50       4 foreach my $symbol (@{$opt->{export}||[]}) {
  2         4  
660 2 50       3 if ( $symbol eq 'promote' ) {
661 2         12 *{join '::', scalar caller, $symbol}
662 2     52   5 = sub{ unshift @_, $self; goto &{ $self->can(qw(new)) } };
  52         17060  
  52         59  
  52         299  
663             }
664 0         0 else { push @symbols, $symbol }
665             }
666            
667 2 50 33     6 if (@symbols
      33        
668 2   33     15 or @{(ref($self)||$self).'::EXPORT'}
669 2         7 or @{ __PACKAGE__.'::EXPORT'}) {
670             require
671 0         0 Exporter;
672 0 0       0 Exporter::export_to_level(1, $self, @symbols) or
673             Exporter::export_to_level(1, __PACKAGE__, @symbols); # "inheritance" of export, subclasses can define their own @EXPORTs,
674             }
675             }
676            
677 2 50       16 if (@goto) {
678 0         0 @_ = @goto;
679 0         0 goto &{shift(@_)};
  0         0  
680             }
681             }
682            
683 54     54 0 215 sub is { Object::Hybrid::Class->is($_[1]) }
684            
685             sub new {
686 113 50   113 0 19782 @_>1 or croak("Error: Nothing to promote");
687 113         188 my $self = shift;
688 113         147 my $primitive = $_[0]; # keep $_[0] to retain alias and autovivification ability...
689 113         129 my ($args, $class);
690 113 50       507 ref $_[1] eq 'HASH' ? $args : $class = splice @_, 1, 1 unless @_ - 2*int(@_/2); # @_ is odd
    100          
691 113         483 %$args = (%$args, @_[1..$#_]);
692            
693             # be idempotent...
694 113 50 100     590 return $primitive
695             if ref $primitive eq ($class||CLASS);
696            
697 113 50 66     227 ! _ref_isa($primitive)
698             or $self->Class->is( $primitive)
699             or $class = ref $primitive;
700 113         258 my $tied_primitive = _ref_tied($primitive);
701            
702             #_load_class(CLASS) if $class and $class->isa(CLASS);
703            
704 113         174 my $primitive_type;
705            
706 113 100       197 if ($class) {
707             _can_tie($class, $primitive) # autovivifies $primitive
708 30 50 33     64 or $class4type{$primitive_type ||= _ref_type($primitive)}
      66        
      33        
709             or $self->Class->is($class)
710             or croak("Error: Wrong hybrid class $class, either not labled as such or defines no perltie methods for '$primitive' primitive");
711             }
712            
713 113 50       269 ref $primitive
714             or croak("Error: No primitive to promote");
715 113 50 66     363 $class4type{$primitive_type ||= _ref_type($primitive)}
716             or croak("Error: Can't promote unsupported non-tie()able primitive $primitive");
717            
718             my $mutable_class
719             = defined $args->{mutable_class}
720             ? $args->{mutable_class}
721 113 50       545 : defined $self->Class->mutable_class($class)
    100          
722             ? $self->Class->mutable_class($class)
723             : $tied_primitive;
724            
725 113 100       188 if ($class) {
726 30 50       103 unless ( $self->Class->is($class) ) { # use $class as subclass...
727 30         84 my $make_class = join '', $self->frontclass_name($class, $primitive_type);
728 30         203 @{ $make_class . '::ISA' } or
729 3         49 @{ $make_class . '::ISA' }
730             =( $class
731             , $mutable_class ? ()
732             : $nontied_class4type{$primitive_type}||()
733 30 50 33     31 , $class4type{$primitive_type}||() );
    100 33        
734 30         59 $class = $make_class;
735             }
736             }
737             else {
738 83 100       155 if ( $mutable_class ) {
739 74         119 $class = $class4type{$primitive_type};
740             }
741             else {
742 9         30 my $make_class = $self->frontclass_name($class4type{$primitive_type});
743 9         71 @{ $make_class . '::ISA' } or
744 2         28 @{ $make_class . '::ISA' }
745             =( $nontied_class4type{$primitive_type}||()
746 9 100 33     11 , $class4type{$primitive_type}||() );
      33        
747 9         13 $class = $make_class;
748             }
749             }
750             _load_class($class4type{$primitive_type}
751 113         285 , $nontied_class4type{$primitive_type}); # custom hybrid class may subclass them, so load anyway
752            
753             my
754 113         2959 $bless = $class->can('bless');
755 113 50       286 $bless ? $class->$bless($primitive)
756             : bless $primitive, $class;
757            
758 113   66     1003 $_[0] ||= $primitive; # autovivify in case of method call (otherwise prototype constraint)
759 113         558 return $primitive
760             }
761            
762             sub tie {
763             return undef
764 12 50   12 1 7730 if !ref $_[1];
765            
766             my $tied
767 12         92 = $_[1] =~ m'(?:^|=)HASH' ? tie( %{$_[1]}, @_[2..$#_] )
768 0         0 : $_[1] =~ m'(?:^|=)SCALAR' ? tie( ${$_[1]}, @_[2..$#_] )
769 0         0 : $_[1] =~ m'(?:^|=)ARRAY' ? tie( @{$_[1]}, @_[2..$#_] )
770 12 0       87 : $_[1] =~ m'(?:^|=)GLOB' ? tie( *{$_[1]}, @_[2..$#_] )
  0 0       0  
    0          
    50          
    50          
771             : undef
772             or return undef;
773            
774 12         113 Object::Hybrid->new($_[1]);
775 12         30 return $tied
776             }
777            
778             sub _load_class {
779 113     113   198 foreach (@_) {
780 226 50       342 next if !$_;
781 226 100       481 if (ref $AD{$_} eq 'CODE') { &{$AD{$_}} }
  113         111  
  113         205  
782             else {
783             eval( !exists $AD{$_} ? "require $_"
784 1 50 0 1 0 5 : $AD{$_} );
  1 0 0 1 0 1  
  1 0 0 42 0 3  
  1 100   0   7  
  1 50   10   2  
  1 50   5   6  
  113 0   109   2589  
  10 50   0   58  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  10         113  
  10         273  
  12         76  
  0         0  
  6         13  
  6         19  
  6         80  
  6         39  
  4         27  
  12         242  
  4         9  
  4         41  
  4         29  
  0         0  
  0         0  
  12         79  
  0         0  
  0         0  
  4         30  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  12         72  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  10         75  
  4         11  
  4         50  
  4         13  
  4         36  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  18         109  
  0         0  
  5         108  
  109         8540  
  0         0  
  0         0  
785 113         294 undef $AD{$_}; # be idempotent
786 113 50       311 !$@ or croak("Error: Can't load $_: $@");
787             }
788             }
789 113         326 return( (grep $_, @_)[0] ) # same as $_[0] || $_[1] || ...
790             }
791            
792 0     0 0 0 sub can_tie { shift; _can_tie(@_) }
  0         0  
793             sub _can_tie { # if not given, autovivifies (in place) primitive of type that can be tie()d with given class
794             #my ($tie_to, $primitive) = @_;
795             return undef
796 30 50   30   55 if !$_[0];
797             #or ref $_[0]
798             #and !_ref_isa($_[0]);
799            
800             return ref $_[1]
801 26         82 ? eval{ $_[0]->can( "TIE"._ref_type($_[1])) } ? $_[1] : undef
802 4         22 : eval{ $_[0]->can(qw(TIEHASH)) } ? \%{$_[1]}
  4         20  
803 0         0 : eval{ $_[0]->can(qw(TIESCALAR)) } ? \${$_[1]}
  0         0  
804 0         0 : eval{ $_[0]->can(qw(TIEARRAY)) } ? \@{$_[1]}
  0         0  
805 30 100       84 : eval{ $_[0]->can(qw(TIEHANDLE)) } ? \*{$_[1]}
  0 0       0  
  0 0       0  
    0          
    50          
    100          
806             : undef
807             }
808            
809 0     0 1 0 sub ref_tied { shift; _ref_tied(@_) }
  0         0  
810             sub _ref_tied2 { # this is much faster at least for tied hashes, but for non-tied tries all variants (still may be faster)
811 0 0   0   0 return undef if !ref $_[0];
812             return eval{ tied( %{$_[0]} ) }
813             || eval{ tied( ${$_[0]} ) }
814             || eval{ tied( @{$_[0]} ) }
815 0   0     0 || eval{ tied( *{$_[0]} ) }
816             || undef
817             }
818             sub _ref_tied {
819             return undef
820 113 100   113   515 if !ref $_[0];
821             return $_[0] =~ m'(?:^|=)HASH' ? tied( %{$_[0]} )||0
822             : $_[0] =~ m'(?:^|=)SCALAR' ? tied( ${$_[0]} )||0
823             : $_[0] =~ m'(?:^|=)ARRAY' ? tied( @{$_[0]} )||0
824 109 50 100     542 : $_[0] =~ m'(?:^|=)GLOB' ? tied( *{$_[0]} )||0
    50 0        
    50 0        
    100 100        
825             : undef
826             }
827            
828 0     0 1 0 sub ref_type { shift; _ref_type(@_) }
  0         0  
829             sub _ref_type {
830 139 50   139   250 return undef if !ref $_[0];
831 139 100       631 return $1 if $_[0] =~ /=(\w+)/;
832 83         486 return ref $_[0]
833             }
834            
835 0     0 1 0 sub ref_isa { shift; _ref_isa(@_) }
  0         0  
836             sub _ref_isa {
837 113 100   113   299 return undef if !ref $_[0];
838 109 100       429 return '' if exists $class4type{ref $_[0]};
839 44 50 33     131 return 0 if defined $_[1] and !$_[0]->isa($_[1]);
840 44         332 return $_[0]
841             }
842            
843 10     10 0 54 sub croak { require Carp; goto &Carp::croak; }
  10         1636  
844            
845 739 100   739   9434 sub _alter_case { $_[0] =~ /[A-Z]/ ? lc($_[0]) : uc($_[0]) };
846 0     0 0 0 sub method_alias { _alter_case($_[1]) }
847            
848             sub methods {
849 117     117 0 189 shift;
850 117         94 my $subs;
851 117 100       592 ref $_[0] eq 'HASH' ? ($subs) : %$subs = @_;
852 117         216 my $caller = caller;
853 117         412 foreach my $method (keys %$subs) {
854             # explicit aliases...
855             ref $subs->{$method} eq 'CODE'
856             or ref( $subs->{$method}
857 383 50 33     882 = $subs->{$subs->{$method}}) eq 'CODE'
858             or next;
859            
860             # implicit altered-case aliases...
861 383         501 my $method2 = _alter_case($method);
862 383         304 my $goto;
863 383         2245 *{join '::', $caller, $method } =
864 383         320 *{join '::', $caller, $method2} = $subs->{$method};
  383         2529  
865            
866             }
867             }
868            
869             sub _cant_locate_object_method {
870 10   33 10   94 join '', "Object::Hybrid: Can't locate object method \""
      33        
871             , $_[1], "\" via package \""
872             , ref($_[0])||$_[0], "\" (perhaps you forgot to load \""
873             , ref($_[0])||$_[0], "\"?) "
874             }
875            
876             my $CLASS_MUTABLE = <<'CLASS_MUTABLE';
877            
878             $INC{ INCKEY_REPLACE } ||= 1;
879             package PACKAGE_REPLACE;
880            
881             use Object::Hybrid::Class; # just labeling
882            
883             sub can;
884             #sub isa;
885            
886             Object::Hybrid->methods(
887             SELF => sub {
888            
889             return $_[0]
890             },
891             fast => sub {
892             return TIED_REPLACE || $_[0];
893            
894             # next is fast only for tie()d primitives, especially hashes as they come first in conditional, but traped exceptions within eval{} are slow things down unacceptably for plaing primitives...
895             return eval{ tied( %{$_[0]} ) }
896             || eval{ tied( ${$_[0]} ) }
897             || eval{ tied( @{$_[0]} ) }
898             || eval{ tied( *{$_[0]} ) }
899             || $_[0];
900            
901             # next is hoplessly slow, even after inlining _ref_type()...
902             return $_[0] if !ref $_[0];
903             #my $type = Object::Hybrid::_ref_type( $_[0] );
904             my $type = $_[0] =~ /=(\w+)/ ? $1 : ref $_[0];
905             return $type eq 'HASH' ? tied( %{$_[0]} ) || $_[0]
906             : $type eq 'SCALAR' ? tied( ${$_[0]} ) || $_[0]
907             : $type eq 'ARRAY' ? tied( @{$_[0]} ) || $_[0]
908             : $type eq 'GLOB' ? tied( *{$_[0]} ) || $_[0]
909             : $_[0];
910             },
911             call => sub{
912             @_ > 1
913             or Object::Hybrid::croak("Error: Nothing to call");
914            
915             local $Object::Hybrid::Portable = 1;
916             my $method = lc(splice(@_, 1, 1));
917             return shift->$method(@_) # is ok, except for caller()
918             },
919             );
920            
921             #my $AUTOLOAD = \&AUTOLOAD;
922             sub AUTOLOAD {
923             package Object::Hybrid; # to not qualify _ref_tied(), _ref_type(), croak(), etc...
924            
925             ( my $METHOD = $PACKAGE_REPLACE::AUTOLOAD ) =~ s/^.*:://;
926             my $METHOD_is_lc = ($METHOD !~ /[A-Z]/);
927             my $SUB_METHOD;
928            
929             #goto &{ *{ $PACKAGE_REPLACE::AUTOLOAD }
930             goto &{ *{ join '::', ref($_[0])||$_[0], $METHOD }
931             = $SUB_METHOD
932             = sub{
933             my $swap
934             ; $swap = splice(@_, 0, 1, $swap)
935             if $swap = TIED_REPLACE;
936            
937             $METHOD eq 'can' and my $can_method = $_[1];
938            
939             my
940             $sub_method;
941             $sub_method
942             = !$can_method && $Object::Hybrid::HASTE
943             ? return shift->$METHOD(@_)
944             : $_[0]->UNIVERSAL::can( $can_method||$METHOD )
945             || $_[0]->UNIVERSAL::can(_alter_case($can_method||$METHOD))
946             if $swap
947             or $can_method;
948            
949             if (!$sub_method) {
950             splice(@_, 0, 1, $swap) if $swap; # revert swap, if any
951             #my
952             #$subclass;
953             #$subclass = $class4type{_ref_type($_[0])}||'FOO', # instead inlining...
954             #$subclass = $class4type{!ref $_[0] ? undef : $_[0] =~ /=(\w+)/ ? $1 : ref $_[0]}||'FOO',
955             $sub_method
956             = SUBCLASS_REPLACE->UNIVERSAL::can( $can_method||$METHOD )
957             || SUBCLASS_REPLACE->UNIVERSAL::can(_alter_case($can_method||$METHOD));
958             }
959            
960             return $sub_method if $can_method;
961            
962             $sub_method
963             or $METHOD_is_lc and $Object::Hybrid::Portable and return # lower-case aliases are fail-safe for compartibility
964             or croak( _cant_locate_object_method($_[0], $METHOD) );
965            
966             $sub_method ne $SUB_METHOD # this case is hopefully excluded by above logic, but it may screw up
967             #and defined(&$sub_method) # here goto() to not defined(&method) is ok as it may be autoloadable in tied() class or otherwise
968             or croak( join '', "Undefined method \""
969             , $METHOD, "\" called via package \""
970             , ref($_[0])||$_[0], "\"");
971            
972             goto &$sub_method
973             } };
974             }
975            
976             CLASS_MUTABLE
977            
978             sub _compile_class {
979 113     113   222 my ($CLASS_MUTABLE, $PACKAGE, $SUBCLASS, $TIED) = @_;
980 113         581 (my $INCKEY = $PACKAGE . '.pm') =~ s/::/\//g;
981            
982 113         976 $CLASS_MUTABLE =~ s/PACKAGE_REPLACE/$PACKAGE/g;
983 113         877 $CLASS_MUTABLE =~ s/INCKEY_REPLACE/'$INCKEY'/g;
984 113         668 $CLASS_MUTABLE =~ s/SUBCLASS_REPLACE/$SUBCLASS/g;
985 113         677 $CLASS_MUTABLE =~ s/TIED_REPLACE/$TIED/g;
986 1 50 33 1   6 eval $CLASS_MUTABLE;
  1 50 33 1   1  
  1 50 33 1   4  
  1 50 33 1   4  
  1 50 33 1   1  
  1 50 0 1   3  
  1 50 0 1   4  
  1 0 33 1   1  
  1 50 0 1   3  
  1 0 0 1   5  
  1 0 0 1   2  
  1 0 0 1   4  
  1 0 0 1   5  
  1 0 0 1   1  
  1 0 0 1   4  
  1 0 0 1   6  
  1 0 0 1   1  
  1 0 0 1   4  
  1 100 66 1   4  
  1 100 100 1   1  
  1 50 66 1   3  
  1 50 66 1   5  
  1 100 66 1   1  
  1 100 50 1   4  
  1 100 66 1   4  
  1 0 0 1   2  
  1 50 0 1   3  
  1 0 0 1   5  
  1 0 0 1   1  
  1 0 0 1   4  
  1 0 0 1   6  
  1 0 0 1   1  
  1 0 0 1   3  
  1 0 0 1   4  
  1 0 0 1   2  
  1 0 0 1   3  
  1 0 0 1   5  
  1 0 0 1   2  
  1 0 33 1   3  
  1 50 66 1   5  
  1 0 66 1   1  
  1 100 33 1   3  
  1 50 0 1   6  
  1 50 0 1   1  
  1 100 33 1   4  
  1 100 0 1   5  
  1 100 0 1   1  
  1 50 0 1   4  
  1 0 33 1   6  
  1 50 33 1   2  
  1 50 66 1   3  
  1 50 50 1   4  
  1 0 33 1   2  
  1 50 0 1   3  
  1 50 0 1   5  
  1 50 0 1   2  
  1 50 33 1   3  
  1 0 33 1   4  
  1 0 66 1   1  
  1 50 50 1   4  
  1 50 33 1   6  
  1 0 0 1   2  
  1 50 33 1   5  
  1 50 100 1   10  
  1 50 66 1   2  
  1 50 66 1   9  
  1 0 0 1   9  
  1 0 0 1   2  
  1 100 0 1   6  
  1 50 0 1   7  
  1 50   1   1  
  1 50   1   4  
  1 100   1   5  
  1 100   1   1  
  1 50   1   5  
  1 0   1   6  
  1 0   1   2  
  1     1   4  
  1     1   5  
  1     1   2  
  1     1   3  
  1     1   8  
  1     1   1  
  1     1   5  
  1     1   6  
  1     1   2  
  1     1   4  
  1     1   5  
  1     1   2  
  1     1   3  
  1     1   6  
  1     1   1  
  1     1   4  
  1     1   4  
  1     1   2  
  1     1   3  
  1     1   5  
  1     1   1  
  1     1   4  
  1     1   4  
  1     1   2  
  1     1   4  
  1     1   4  
  1     1   2  
  1     1   3  
  1     1   5  
  1     1   1  
  1     1   3  
  1     1   4  
  1     1   1  
  1     1   3  
  1     1   4  
  1     1   2  
  1     2   3  
  1     0   5  
  1     83   1  
  1         3  
  1         5  
  1         2  
  1         3  
  1         5  
  1         1  
  1         3  
  1         5  
  1         2  
  1         4  
  1         5  
  1         1  
  1         4  
  1         8  
  1         2  
  1         7  
  1         5  
  1         1  
  1         3  
  1         7  
  1         1  
  1         3  
  1         5  
  1         2  
  1         3  
  1         5  
  1         2  
  1         3  
  1         5  
  1         1  
  1         3  
  1         4  
  1         2  
  1         3  
  1         5  
  1         1  
  1         3  
  1         5  
  1         1  
  1         3  
  1         5  
  1         2  
  1         4  
  1         5  
  1         2  
  1         4  
  1         5  
  1         1  
  1         4  
  1         8  
  1         2  
  1         5  
  1         7  
  1         2  
  1         6  
  1         9  
  1         3  
  1         8  
  1         7  
  1         2  
  1         5  
  1         10  
  1         2  
  1         5  
  1         5  
  1         2  
  1         4  
  1         9  
  1         2  
  1         5  
  1         6  
  1         3  
  1         4  
  1         8  
  1         1  
  1         5  
  1         8  
  1         2  
  1         6  
  1         8  
  1         1  
  1         4  
  1         9  
  1         1  
  1         6  
  1         6  
  1         2  
  1         6  
  1         8  
  1         3  
  1         6  
  1         7  
  1         2  
  1         4  
  1         7  
  1         4  
  1         6  
  1         8  
  1         2  
  1         5  
  1         4  
  1         2  
  1         3  
  1         8  
  1         2  
  1         6  
  1         5  
  1         1  
  1         4  
  1         8  
  1         1  
  1         6  
  1         4  
  1         1  
  1         4  
  1         6  
  1         1  
  1         4  
  1         5  
  1         2  
  1         3  
  1         5  
  1         2  
  1         3  
  1         5  
  1         1  
  1         3  
  1         9  
  1         3  
  1         5  
  1         5  
  1         2  
  1         4  
  1         5  
  1         1  
  1         5  
  1         6  
  1         2  
  1         5  
  1         6  
  1         2  
  1         5  
  1         5  
  1         4  
  1         4  
  1         5  
  1         2  
  1         3  
  1         6  
  1         2  
  1         4  
  1         5  
  1         2  
  1         3  
  1         7  
  1         2  
  1         4  
  1         5  
  1         2  
  1         3  
  1         5  
  1         1  
  1         4  
  1         8  
  1         2  
  1         5  
  1         4  
  1         2  
  1         3  
  1         6  
  1         2  
  1         4  
  1         5  
  1         1  
  1         4  
  1         6  
  1         1  
  1         4  
  1         5  
  1         1  
  1         3  
  1         9  
  1         2  
  1         5  
  1         8  
  1         1  
  1         5  
  1         6  
  1         1  
  1         4  
  1         7  
  1         1  
  1         4  
  1         5  
  1         1  
  1         4  
  1         6  
  1         1  
  1         4  
  1         5  
  1         2  
  1         4  
  1         10  
  1         3  
  1         6  
  1         7  
  1         2  
  1         5  
  1         7  
  1         1  
  1         5  
  1         8  
  1         1  
  1         6  
  1         8  
  1         2  
  1         6  
  1         5  
  1         1  
  1         5  
  1         5  
  1         2  
  1         5  
  113         8915  
  2         9  
  2         6  
  2         4  
  2         2  
  2         9  
  2         22  
  3         4  
  3         3  
  3         17  
  3         9  
  3         4  
  3         55  
  3         8  
  3         10  
  3         30  
  3         7  
  3         10  
  3         9  
  3         65  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  83         93  
  83         66  
  83         342  
  83         149  
  83         61  
  83         889  
  83         138  
  9         17  
  9         98  
  83         169  
  78         123  
  74         154  
  74         199  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         5  
  1         3  
  1         3  
  1         19  
  12         322  
  0         0  
  0         0  
  0         0  
  0         0  
  84         5029  
  84         81  
  84         358  
  84         205  
  84         67  
  84         991  
  84         188  
  48         132  
  48         372  
  84         156  
  84         136  
  84         240  
  84         1170  
  12         15  
  12         13  
  12         38  
  12         31  
  12         13  
  12         49  
  12         24  
  12         18  
  12         134  
  12         23  
  12         80  
  0         0  
  0         0  
  4         6  
  4         5  
  4         24  
  4         12  
  4         5  
  4         36  
  4         9  
  4         8  
  4         69  
  4         12  
  4         83  
  0         0  
  0         0  
  234         13864  
  234         229  
  234         824  
  234         498  
  234         190  
  234         2382  
  234         508  
  134         262  
  134         771  
  234         881  
  0            
  0            
  0            
987 113 50       488 !$@ or die($@);
988             }
989            
990             $AD{CLASS_HASH()} = sub{ _compile_class($CLASS_MUTABLE, CLASS_HASH, CLASS_HASH_NONTIED, 'tied( %{$_[0]} )', ); };
991             $AD{CLASS_ARRAY()} = sub{ _compile_class($CLASS_MUTABLE, CLASS_ARRAY, CLASS_ARRAY_NONTIED, 'tied( @{$_[0]} )', ); };
992             $AD{CLASS_SCALAR()} = sub{ _compile_class($CLASS_MUTABLE, CLASS_SCALAR, CLASS_SCALAR_NONTIED, 'tied( ${$_[0]} )', ); };
993             $AD{CLASS_HANDLE()} = sub{ _compile_class($CLASS_MUTABLE, CLASS_HANDLE, CLASS_HANDLE_NONTIED, 'tied( *{$_[0]} )', ); };
994            
995             $AD{ 'Object::Hybrid::HASH' } = <<'CLASS';
996             $INC{ "Object\/Hybrid\/HASH.pm" } ||= 1;
997             package Object::Hybrid::HASH;
998            
999             use Object::Hybrid::Class; # just labeling
1000            
1001             sub can { $_[0]->UNIVERSAL::can($_[1]) } # override slow can() of mutable CLASS_HASH
1002            
1003             Object::Hybrid->methods({
1004             fast => sub { $_[0] },
1005             self => sub { $_[0] },
1006             TIEHASH => sub { bless {}, ref($_[0])||$_[0] },
1007             STORE => sub { $_[0]->{$_[1]} = $_[2] },
1008             FETCH => sub { $_[0]->{$_[1]} },
1009             FIRSTKEY => sub { my $a = scalar keys %{$_[0]}; each %{$_[0]} },
1010             NEXTKEY => sub { each %{$_[0]} },
1011             EXISTS => sub { exists $_[0]->{$_[1]} },
1012             DELETE => sub { delete $_[0]->{$_[1]} },
1013             CLEAR => sub { %{$_[0]} = () },
1014             SCALAR => sub { scalar %{$_[0]} },
1015             });
1016            
1017             sub DESTROY {}
1018            
1019             CLASS
1020            
1021             $AD{ 'Object::Hybrid::SCALAR' } = <<'CLASS';
1022             $INC{ "Object\/Hybrid\/SCALAR.pm" } = 1;
1023             package Object::Hybrid::SCALAR;
1024            
1025             use Object::Hybrid::Class; # just labeling
1026            
1027             sub can { $_[0]->UNIVERSAL::can($_[1]) } # override slow can() of mutable CLASS_SCALAR
1028            
1029             Object::Hybrid->methods({
1030             fast => sub { $_[0] },
1031             self => sub { $_[0] },
1032             TIESCALAR => sub {
1033             my $class = shift;
1034             my $instance = shift || undef;
1035             return bless \$instance => $class;
1036             },
1037             FETCH => sub { ${$_[0]} },
1038             STORE => sub { ${$_[0]} = $_[1] },
1039             });
1040            
1041             sub DESTROY { undef ${$_[0]} }
1042            
1043             CLASS
1044            
1045             $AD{ 'Object::Hybrid::ARRAY' } = <<'CLASS';
1046             $INC{ "Object\/Hybrid\/ARRAY.pm" } ||= 1;
1047             package Object::Hybrid::ARRAY;
1048            
1049             use Object::Hybrid::Class; # just labeling
1050            
1051             sub can { $_[0]->UNIVERSAL::can($_[1]) } # override slow can() of mutable CLASS_ARRAY
1052            
1053             Object::Hybrid->methods({
1054             fast => sub { $_[0] },
1055             self => sub { $_[0] },
1056             TIEARRAY => sub { bless [], $_[0] },
1057             FETCHSIZE => sub { scalar @{$_[0]} },
1058             STORESIZE => sub { $#{$_[0]} = $_[1]-1 },
1059             STORE => sub { $_[0]->[$_[1]] = $_[2] },
1060             FETCH => sub { $_[0]->[$_[1]] },
1061             CLEAR => sub { @{$_[0]} = () },
1062             POP => sub { pop(@{$_[0]}) },
1063             PUSH => sub { my $o = shift; push(@$o,@_) },
1064             SHIFT => sub { shift(@{$_[0]}) },
1065             UNSHIFT => sub { my $o = shift; unshift(@$o,@_) },
1066             EXISTS => sub { exists $_[0]->[$_[1]] },
1067             DELETE => sub { delete $_[0]->[$_[1]] },
1068             EXTEND => sub {},
1069             SPLICE => sub {
1070             my $ob = shift;
1071             my $sz = $ob->FETCHSIZE;
1072             my $off = @_ ? shift : 0;
1073             $off += $sz if $off < 0;
1074             my $len = @_ ? shift : $sz-$off;
1075             return splice(@$ob,$off,$len,@_);
1076             },
1077             });
1078            
1079             sub DESTROY {}
1080            
1081             CLASS
1082            
1083             $AD{ 'Object::Hybrid::GLOB' } = <<'CLASS';
1084             $INC{ "Object\/Hybrid\/GLOB.pm" } ||= 1;
1085             package Object::Hybrid::GLOB;
1086            
1087             use Object::Hybrid::Class; # just labeling
1088            
1089             sub can { $_[0]->UNIVERSAL::can($_[1]) } # override slow can() of mutable CLASS_HANDLE
1090            
1091             sub new {
1092             goto &{ $_[0]->can(qw(TIEHANDLE))
1093             ||Object::Hybrid::croak("Method not defined: new() / TIEHANDLE()") }
1094             }
1095            
1096             Object::Hybrid->methods({
1097             fast => sub { $_[0] },
1098             self => sub { $_[0] },
1099             TIEHANDLE => sub {
1100             my ($elf, $fh, @open_args) = @_;
1101            
1102             if ($fh eq '') {
1103             $fh = \do { local *HANDLE };
1104             } else {
1105             eval{ $fh = *$fh }, !$@ or Object::Hybrid::croak("Not a GLOB reference");
1106             }
1107            
1108             $fh->OPEN(@open_args) or Object::Hybrid::croak($!)
1109             if @open_args;
1110            
1111             return bless $fh, ref($elf)||$elf
1112             },
1113            
1114             OPEN => sub {
1115             defined $_[0]->FILENO
1116             and $_[0]->CLOSE;
1117            
1118             @_ == 2
1119             ? open($_[0], $_[1])
1120             : open($_[0], $_[1], $_[2]);
1121             },
1122            
1123             WRITE2 => sub {
1124             my $fh = $_[0];
1125             print $fh substr($_[1],0,$_[2])
1126             },
1127             WRITE => sub { my $fh = shift; write $fh },
1128             PRINT => sub { my $fh = shift; print $fh @_ },
1129             PRINTF => sub { my $fh = shift; printf $fh @_ },
1130            
1131             READ => sub { read $_[0], $_[1], $_[2] },
1132             READLINE => sub { my $fh = $_[0]; <$fh> },
1133             GETC => sub { getc $_[0] },
1134            
1135             EOF => sub { eof $_[0] },
1136             TELL => sub { tell $_[0] },
1137             FILENO => sub { fileno $_[0] },
1138             SEEK => sub { seek $_[0], $_[1], $_[2] },
1139             CLOSE => sub { close $_[0] },
1140             BINMODE => sub { binmode $_[0] },
1141            
1142             SYSOPEN => sub {
1143             eval {
1144             @_ >= 3 or Object::Hybrid::croak("Not enough arguments for sysopen()");
1145             @_ == 3 ? sysopen $_[0]->self, $_[1], $_[2] :
1146             @_ >= 4 ? sysopen $_[0]->self, $_[1], $_[2], $_[3] :();
1147             };
1148             !$@ or Object::Hybrid::croak($@);
1149             },
1150             FCNTL => sub {
1151             eval {
1152             @_ >= 3 or Object::Hybrid::croak("Not enough arguments for fcntl()");
1153             fcntl $_[0]->self, $_[1], $_[2];
1154             };
1155             !$@ or Object::Hybrid::croak($@);
1156             }, # TODO: same as for SYSOPEN()
1157             STAT => sub { stat $_[0]->self },
1158             FLOCK => sub { flock $_[0]->self, $_[1] },
1159             TRUNCATE => sub { truncate $_[0]->self, $_[1] },
1160             FTEST => sub {
1161             my $file = $_[0]->self;
1162             if ($_[1] =~ /^-\w$/) {
1163             eval "$_[1] \$file";
1164             !$@ or Object::Hybrid::croak($@);
1165             }
1166             else { Object::Hybrid::croak("Unknown argument to FTEST()") }
1167             },
1168            
1169             });
1170            
1171             #sub DESTROY;
1172             #sub UNTIE;
1173             sub DESTROY {}
1174            
1175             CLASS
1176            
1177             1;
1178