The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Rope - Tied objects

VERSION

Version 0.13

SYNOPSIS

        package Knot;

        use Rope;

        prototyped (
                bend_update_count => 0,
                loops => 1,
                hitches => 10,
                ...

        );

        properties (
                bends => {
                        type => sub { $_[0] =~ m/^\d+$/ ? $_[0] : die "$_[0] != integer" },
                        value => 10,
                        initable => 1,
                        configurable => 1,
                        enumerable => 1,
                        required => 1,
                        trigger => sub {
                                my ($self, $value) = @_;
                                $self->{bend_update_count}++;
                                return $value;
                        }
                },
                ...
        );

        function add_loops => sub {
                my ($self, $loop) = @_;
                $self->{loops} += $loop;
        };

        1;

...

        my $k = Knot->new();

        say $k->{loops}; # 1;
        
        $k->{add_loops}(5);

        say $k->{loops}; # 6;

        $k->{add_loops} = 5; # errors

DESCRIPTION

Rope is an Object Orientation system that is built on top of perls core hash tying implementation. It extends the functionality which is available with all the modern features you would expect from an modern OO system. This includes clear class and role definitions. With Rope you also get out of the box sorted objects, where the order you define persists.

CONFIGURE PROPERTIES

initable

If set to a true value then this property can be initialised during the object creationg, when calling ->new(%params). If set to false then on initialisation the code will die with a relevant error when you try to initialise it. (Cannot initalise Object ($name) property ($key) as initable is not set to true.)

writeable

If set to a true value then this property value can be updated after initialisation with any other value. If set to false then the code will die with a relevant error. If writeable is true then configurable is not checked and redundent. (Cannot set Object ($name) property ($key) it is only readable)

configurable

If set to a true value then this property value can be updated after initialisation with a value that matches the type of the existing. If you try to set a value which is not of the same type the code will die with a relevant error. (Cannot change Object ($name) property ($key) type). If you set to false and writeable is also false you will get the same error as writeable false.

enumerable

If set to a true value then the property will be enumerable when you itterate the object for example when you call keys %{$self}. If set to false then the property will be hidden from any itteration. Note also that when itterating your object keys are already ordered based on the order they were assigned.

required

If set to a true value then this property is required at initialisation, either by a value key being set or via passing into ->new. I would suggest using this in conjunction with initable when you require a value is passed. If no value is passed and required is set to true then the code will die with a relevant error. (Required property ($key) in object ($object) not set)

type

The type property/key expects a code ref to be passed, all values that are then set either during initialisation or writing will run through this ref. Rope expects that you return the final value from this ref so you can use coercion via a type.

builder

The buidler property/key expects either a code ref or a scalar that represents the name of a sub routine in your class (currently not functions/properties but may extend in the future). It expects the value for that property to be returned from either the code ref or sub routine. Within a builder you can also add new properties to your object by extending the passed defintion, when extending this way I would suggest using ++$_[0]->{keys} to set the index so that sorting is persistent further down the line.

trigger

The trigger property/key expects a code ref that expects the value for that property to be returned so it can also be used for coecion on setting of a property. Two values are passed into a trigger the first being $self which allows you to access and manipulate the existing objects other properties, the second is the value that is being set for the property being called.

A trigger is also called on DELETE, in this case you will get three params, the second being the text 'DELETE', the third being the last set value.

index

The index property/key expects an integer, if you do not set then this integer it's automatically generated and associated to the property. You will only want to set this is you always want to have a property last when itterating

KEYWORDS

property

Extends the current object definition with a single new property

        property one => (
                initable => 1,
                writeable => 0,
                enumerable => 1,
                builder => sub {
                        return 200;
                }
        );

properties

Extends the current object definition with multiple new properties

        properties (
                two => {
                        type => sub { $_[0] =~ m/^\d+$/ ? $_[0] : die "$_[0] != integer" },
                        value => 10,
                        initable => 1,
                        configurable => 1,
                        enumerable => 1,
                        required => 1
                },
                ...
        );

prototyped

Extends the current object definition with multiple new properties where initable, writable and enumerable are all set to a true value.

        prototyped (
                two => 10
                ...
        );

function

Extends the current object definition with a new property that acts as a function. A function has initable, writeable, enumerable and configurable all set to false so it cannot be changed/set once the object is instantiated.

        function three => sub {
                my ($self, $param) = @_;
                ...
        };

NOTE: traditional sub routines work and should be inherited also.

        sub three {
                my ($self, $param) = @_;
                ...
        }

extends

The extends keyword allows you to extend your current definition with another object, your objext will inherit all the properties of that extended object.

        package Ping;
        use Rope;
        extends 'Pong';

with

The with keyword allows you to include roles in your current object definition, your objext will inherit all the properties of that role.

        package Ping;
        use Rope;
        with 'Pong';

requires

The requires keyword allows you to define properties which are required for either a role or an object, it works in both directions.

        package Pong;
        use Rope::Role;
        requires qw/host/;
        function ping => sub { ... };
        function pong => sub { ... };

        package Ping;
        use Rope;
        requires qw/ping pong/;
        with 'Pong';
        prototyped (
                host => '...'
        );

locked

The locked keyword can be used to lock your Object so that it can no longer be extended with new properties/keys.

        package Locked;

        use Rope;

        property one => ( ... );

        locked;

        1;

Once you have an object initialised you can toggle whether it is locked by calling 'locked' with either a true or false value.

        $obj->locked(1);

        $obj->locked(0);

METHODS

new

Along with class definitions you can also generate object using Rope itself, the options are the same as described above.

        my $knot = Rope->new({
                name => 'Knot',
                properties => [
                        loops => 1,
                        hitches => {
                                type => Int,
                                value => 10,
                                initable => 0,
                                configurable => 0,
                        },
                        add_loops => sub {
                                my ($self, $loop) = @_;
                                $self->{loops} += $loop;
                        }
                ]
        });

        my $with = Rope->new({
                use => [ 'Rope::Autoload' ],
                with => [ 'Knot' ],
                requires => [ qw/loops hitches add_loops/ ],
                properties => [ bends => { type => Int, initable => 1, configurable => 1 }, ... ]
        }, bends => 5);

        $knot->{loops};
        $with->loops;

destroy

For objects that are running in a long process, like under a web server, you will want to explicitly call destroy on your objects as to achieve the correcting scoping a reference has to be held in memory. If running under a script the problem will not exists as destruction happens when that script ends.

        $knot->destroy();

OBJECT CLASS DEFINITION

        package Builder;

        use Rope;

        property one => (
                initable => 1,
                writeable => 0,
                enumerable => 1,
                builder => sub {
                        return 200;
                }       
        );

        property two => (
                writeable => 0,
                enumerable => 0,
                builder => sub {
                        $_[0]->{properties}->{three} = {
                                value => 'works',
                                writeable => 0,
                                index => ++$_[0]->{keys}
                        };
                        return $_[0]->{properties}->{one}->{value} + 11;
                }
        );

        1;

OBJECT ROLE DEFINITION

        package Builder::Role;

        use Rope::Role;

        property one => (
                initable => 1,
                writeable => 0,
                enumerable => 1,
                builder => sub {
                        return 200;
                }       
        );

        1;

AUTOLOADING

If you do not enjoy accessing properties as hash keys and would instead like to access them as package routines then you can simply include Rope::Autoload and this will use perls internal AUTOLOAD functionality to expose your properties as routines.

        package Builder;

        use Rope;
        use Rope::Autoload;

        ...

        1;

So you can write

        $builder->thing = 10;

Instead of

        $builder->{thing} = 10;

TYPES

Rope also includes additional helpers for defining properties with fixed types, see Rope::Type for more information. ( internally that uses Type::Standard for the actual type checking. )

        package Knot;
         
        use Rope;
        use Rope::Type qw/int/;
         
        int loops => 1;
        int hitches => 10;

        1;

AUTHOR

LNATION, <email at lnation.org>

BUGS

Please report any bugs or feature requests to bug-rope at rt.cpan.org, or through the web interface at https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Rope. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Rope

You can also look for information at:

ACKNOWLEDGEMENTS

LICENSE AND COPYRIGHT

This software is Copyright (c) 2023 by LNATION.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)