NAME

Class::Property - Perl implementation of class properties.

VERSION

Version 1.002

SYNOPSIS

This module allows you to easily create properties for your class. It supports default, custom and lazy properties. Basically, properties are just a fancy way to access object's keys, generally means $foo->some_property is equal to $foo->{'some_property'}.

General syntax:

package Foo;
use Class::Property;

property(
    'name' => { 'get' => undef, 'set' => undef },               # creates default RW property, fastests
    'age' => { 'get' => undef },                                # creates default RO property
    'salary' => { 'set' => undef },                             # creates default WO property
    'weight' => { 'get' => \&weight_getter, 'set' => undef },   # creates RW property with custom getter and default setter
    'family' => { 'get_lazy' => \&read_family },                # creates RO property with lazy init method 
);

After you've created properties for your class, you may use them as lvalues:

use Foo;

my $foo = Foo->new();

$foo->name = 'Yourname';
printf 'The age of %s is %s', $foo->name, $foo->age;    

Usually you'll need to use general syntax when you want to use custom getters/setters and/or lazy properties. To make your code cleaner, there are few helper methods:

rw_property( 'street', 'house', 'flat' );   # creates 3 RW properties 
ro_property( 'sex', 'height' );             # creates 2 RO properties 
wo_property( 'relation' );                  # creates WO property

API

Default properties

Default properties are the fastest way to make your code cleaner. They just mapping object's hash keys to class methods and returns them as lvalues. Just use:

rw_property( ... property names ... );

Read-only and write-only properties

Default RO and WO properties works slower, because they are using wrapper class, but they control access to the object properties. Both croaks on attempt to write to RO property or read WO one. Currently, perl doesn't allow to restrict access to the object hash keys, but you will use only properties in your code, you'll have additional control. You may create such properties with helper methods:

ro_property( ... property names ... );

# or

wo_property( ... property names ... );

Custom getters/accessors

Sometimes you need to do something special on reading property, count reads, for example. This may be done via custom getter:

property( 'phone_number' => {'get' => \&my_getter, 'set' => undef} );

sub my_getter
{
    my($self) = @_;
    
    $self->{'somecounter'}++;
    
    return $self->{'phone_number'};
}

The property above will use default way to set data and call your own method to get it. Custom getter being invoked as object method (with $self passed) and it must return requested value.

Custom setters/mutators

Sometimes you need to do something special on writing property, validate, for example. This may be done via custom setter:

property( 'phone_number' => {'get' => undef, 'set' => \&my_setter} );

sub my_setter
{
    my($self, $value) = @_;
    
    ... validation or exception is here...
    
    $self->{'phone_number'} = $value;
    
    return;
}

The property above will use default way to get data and call your own method to set it. Custom setter being invoked with two arguments - reference to an object and value to set. Setter responsible to store data in proper place.

Lazy properties

Lazy properties may be useful in situations, when reading data is resourseful or slow and it may be never used in current piece of code. For example, you may have database with persons and their relations:

package Person;
use Class::Property;

property(
    'family' => { 'get_lazy' => \&read_family, 'set' => undef },
);

sub read_family
{
    my($self) = @_;
    
    ... reading family members from database... 
    
    return $family;
}

...

my $me = Person->new(...);

my $family = $me->family;       # first occurance, invokes read_family in background (resourceful)

print_family( $me->family );    # other occurances, takes family from default place (fast)

Such class will have a lazy property: family. If some code will try to access this object's property a first time, your method read_family will be invoked and it's result will be stored in default place. Further accesses won't invoke init function, property will behave as non-lazy.

BENCHMARKING

Here is a comparision of different properties and alternatives as Class::Accessor and Class::Accessor::Fast

 1. Direct hash read           :  1 wallclock secs ( 0.78 usr +  0.00 sys =  0.78 CPU) @ 12820512.82/s (n=10000000)
 2. Direct hash write          :  0 wallclock secs ( 0.80 usr +  0.00 sys =  0.80 CPU) @ 12562814.07/s (n=10000000)
 3. Class::Property rw read    :  3 wallclock secs ( 2.54 usr +  0.00 sys =  2.54 CPU) @ 3930817.61/s (n=10000000)
 4. Class::Property rw write   :  2 wallclock secs ( 2.26 usr +  0.00 sys =  2.26 CPU) @ 4420866.49/s (n=10000000)
 5. Class::Accessor::Fast read :  4 wallclock secs ( 3.28 usr +  0.00 sys =  3.28 CPU) @ 3052503.05/s (n=10000000)
 6. Class::Accessor::Fast write:  4 wallclock secs ( 4.06 usr +  0.00 sys =  4.06 CPU) @ 2465483.23/s (n=10000000)
 7. Class::Property lrw read   :  6 wallclock secs ( 5.82 usr +  0.00 sys =  5.82 CPU) @ 1718508.33/s (n=10000000)
 8. Class::Property lrw write  :  4 wallclock secs ( 5.60 usr +  0.00 sys =  5.60 CPU) @ 1785395.47/s (n=10000000)
 9. Class::Accessor read       :  6 wallclock secs ( 6.83 usr +  0.00 sys =  6.83 CPU) @ 1463486.02/s (n=10000000)
10. Class::Accessor write      :  8 wallclock secs ( 8.03 usr +  0.00 sys =  8.03 CPU) @ 1244709.98/s (n=10000000)
11. Class::Property ro read    : 16 wallclock secs (15.26 usr +  0.00 sys = 15.26 CPU) @ 655436.85/s (n=10000000)
12. Class::Property wo write   : 14 wallclock secs (14.18 usr +  0.00 sys = 14.18 CPU) @ 705168.89/s (n=10000000)
13. Class::Property crw read   : 18 wallclock secs (17.19 usr +  0.00 sys = 17.19 CPU) @ 581699.73/s (n=10000000)
14. Class::Property crw write  : 20 wallclock secs (19.58 usr +  0.00 sys = 19.58 CPU) @ 510777.40/s (n=10000000)    

Results shows that:

  • Default properties works 3 times slower than direct access, but still faster than Class::Accessor::Fast accessors.

  • Lazy properties works 7.2 times slower than direct access, but still faster than Class::Accessor accessors.

  • RO and WO properties works 18.6 times slower than direct access.

  • Custom properties works 23.2 times slower than direct access.

LICENSE

This module is published under the terms of the MIT license, which basically means "Do with it whatever you want". For more information, see the LICENSE file that should be enclosed with this distributions. A copy of the license is (at the time of writing) also available at http://www.opensource.org/licenses/mit-license.php.

SEE ALSO

AUTHOR

Copyright (C) 2015 by Alexandr Evstigneev (hurricup@evstigneev.com)