AccessorFacade

NAME

AccessorFacade - turn indivdual get/set subroutines into a single read/write object attribute.

SYNOPSIS


    use AccessorFacade;
    use NativeCall;

    class Shout is repr('CPointer') {

        sub shout_set_host(Shout, Str) returns int32 is native('libshout') { * }
        sub shout_get_host(Shout) returns Str is native('libshout') { * }

        method host() is rw is attribute-facade(&shout_set_host, &shout_get_host) { }

        ...
    }

DESCRIPTION

This module was initially designed to reduce the boiler plate code in a native library binding that became something like:

class Shout is repr('CPointer') {
sub shout_set_host(Shout, Str) returns int32 is native('libshout') { * }
        sub shout_get_host(Shout) returns Str is native('libshout') { * }
method host() is rw {
            Proxy.new(
                FETCH => sub ($) {
                    shout_get_host(self);
                },
                STORE   =>  sub ($, $host is copy ) {
                    explicitly-manage($host);
                    shout_set_host(self, $host);
                }
            );
        }
...
}

That is the library API provides a sort of "object oriented" mechanism to set and get attributes on an opaque object instance that was returned by another "constructor" function. Because the object is an opaque CPointer it can only have subroutines and methods and not private data or attributes. The intent of the code is to provide fake "attributes" with rw methods (which is similar to how public rw attributes are provided.)

The above code will be reduced with the use of AccessorFacade to:


    class Shout is repr('CPointer') {

        sub shout_set_host(Shout, Str) returns int32 is native('libshout') { * }
        sub shout_get_host(Shout) returns Str is native('libshout') { * }

        method host() is rw is attribute-facade(&shout_get_host, &shout_set_host) { }

        ...
    }

Named arguments are also supported so the above method could also be written as:


    method host() is rw is attribute-facade(setter => &shout_set_host, getter => &shout_get_host) { }

(The call to explicitly manage is omitted for simplicity but how this is achieved is described in the documentation.) Libshout has a significant number of these get/set pairs so there is a reduction of typing, copy and paste and hopefully programmer error.

Whilst this was designed primarily to work with a fixed native API, it is possible that it could be used to provide an OO facade to a plain Raku procedural library. The only requirement that there is a getter subroutine that accepts an object as its first argument and returns the attribute value and a setter subroutine that accepts the object and the value to be set (it may return a value to indicate success - how this is handled is descibed in the documentation.)

TRAIT APPLICATION

The trait attribute-facade should be applied to an object method with no arguments that has the rw trait, (if the method isn't rw then assignment simply won't work, no check is currently performed.) The body of the method should be empty, but will be discarded anyway if it isn't.

The arguments can be supplied as positional or named arguments.

The signatures of the trait can be thought of as being:

attribute-facade(Method:D: $method, &getter, &setter, &before?, &after?)
    attribute-facade(Method:D: $method, :&getter!, :&setter!, :&before?, :&after?)

The individual arguments are:

&getter

Named parameter getter.

This is the function that is called to retrieve the attribute value. It has exactly one argument which is the invocant of the method (i.e. self). Its return value will be the value of the method invocation.

&setter

Named parameter setter.

This is the function that will be called to set the attribute value (i.e. when it is assigned to.) It will be called with two arguments: the invocant (self) and the value to set. It may return a value which will be passed to &after if it is defined.

&before

Named parameter before.

If this is defined this will be called when the value is being set with the invocant and the value and its returned value will be passed to &setter instead of the original value, it is free to do what it likes as long as the resulting value is acceptable to the &setter.

This is how the explicitly-manage would be applied in the example above:


    sub managed($, Str $str is copy ) {
        explicitly-manage($str);
        $str;
    }

    method host() is rw is attribute-facade(&shout_get_host, &shout_set_host, &managed) { }

Or with named parameters:


    method host() is rw is attribute-facade(getter => &shout_get_host, setter => &shout_set_host, before => &managed) { }

It is of course free to perform a validation and throw an exception or whatever may be appropriate.

&after

Named parameter after.

This will be called after &setter with the invocant and the return value of &setter. It is primarily intended where the setter may return a value to indicate the success or otherwise of setting the attribute and this should be turned into an exception:


    sub check($, Int $rc ) {
        if $rc != OK {
            die "value was not set";
        }
    }

    method host() is rw is attribute-facade(&shout_get_host, &shout_set_host, Code, &check) { }

Note in the above example the Code type if used as a placeholder for the empty &before (this is due to the way the "arguments" to the trait are checked.)

This may be more conveniently written with named argument style:


    method host() is rw is attribute-facade(getter => &shout_get_host, setter => &shout_set_host, after => &check) { }

AccessorFacade v0.1.1

Provides a method trait to turn indivdual get/set subroutines into a single attribute

Authors

  • Jonathan Stowe

License

Artistic-2.0

Dependencies

Test Dependencies

Provides

  • AccessorFacade

Documentation

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.