New registration functions for pure pipes

I suspect many of those who viewed the recent enhancements to Nephtali that promote pure pipe functions thought the example code was terribly verbose. Frankly, it is, for the most typical situations. With that admission, let me say that there is a method to my madness.

I firmly believe developers should strive to account for as much complexity as possible in the early stages of development.  After establishing a strong, extensible foundation from which to work, we can then examine various use cases of developers to identify the most common needs, and work to provide tools (general functions, API enhancements, hooks, plugins, etc.) that facilitate these high-traffic scenarios.

General pipes

In terms of Nephtali pipes, I first created a general pipe registration function. While using the general pipe can sometimes require significant amounts of code, it is extensible to every situation I’ve encountered while using Nephtali.

n\pipe\register(
    $name = 'general_pipe',
    $function = function($markup, $args, $signatures)
    {
        // make it happen 🙂
        return n\view\render($view = 'default', $markup);
    }
);

Action pipes

After working with Nephtali for a while, I realized that I could greatly facilitate one particular use case: performing specific actions if/when sets of ports are present on a request. This lead to the development of action pipes.

n\pipe\register_action(
    $name = 'user',
    $actions = array(
        n\port\signature(n\val('update_user')) => function($markup)
        {
            // typically have port validation code here and db update, but omitted for brevity
            return n\view\render($view = 'update', $markup);
        },
        n\port\signature(n\val('insert_user')) => function($markup)
        {
            // typically have port validation code here and db insert, but omitted for brevity
            return n\view\render($view = 'insert', $markup);
        }
    )
);

General pure pipes

Most recently, Nephtali had added the ability to work with functions that, at least in terms of syntactic appearance, are pure. The benefit to this type of approach is that unit testing a function that appears syntactically pure can be done with relative ease, as you can push in any set of values to a pipe’s function at test time to ensure that the function’s logic is correct.

n\pipe\register(
    $name = 'user',
    $function = function($markup, $args, $signatures)
    {
        if ($signatures['update']['is_valid']) {
            if ($signatures['update']['errors']) {
                return n\view\render($view = 'feedback', $markup, $signatures['update']['errors']);
            } else {
                if ($args['update']) {
                    return n\view\render($view = 'default', $markup, $rows = array('message' => 'User updated.'));
                } else {
                    return n\view\render($view = 'error', $markup);
                }
            }
        } else
        if ($signatures['insert']['is_valid']) {
            if ($signatures['insert']['errors']) {
                return n\view\render($view = 'feedback', $markup, $signatures['insert']['errors']);
            } else {
                if ($args['insert']) {
                    return n\view\render($view = 'default', $markup, $rows = array('message' => 'User inserted.'));
                } else {
                    return n\view\render($view = 'error', $markup);
                }
            }
        } else {
            return '';
        }
    },
    $opts = array(
        'signatures' => array(
            'update' => array('id','name'),
            'insert' => array('name')
	),
        $args = array(
            'update' => function($signatures) {
                return n\sql\action\update($table_name = 'users', $inputs = $signatures['update']['values']);
            },
            'insert' => function($signatures) {
                return n\sql\action\insert($table_name = 'users', $inputs = $signatures['insert']['values']);
            }
        )
    )
);

I think we can agree that the above example is painfully verbose, especially when one compares this code to equivalent code in other web frameworks. However, the above code is also very capable, and extends to situations where one may wish to manually work with many different sets of ports and sub-pipes, whilst keeping the ability to maintain the purity of the function (again, the purity I’m speaking of is the syntactic purity, which affords easy testing of the functions logic and correctness, as you can pass in any array of values to the $args array at test time.)

Now, having worked with the above code for several weeks, I’ve developed two functions that handle the most typical use cases.

Pure display pipes

One of the most common use cases is wanting to display a feedback view if one or more of the ports that the view requires is/are invalid, display a default view if there is data available, display an empty view if there is no data, or display an error view if there is an error during the processing of the pipe. This can now be accomplished through use of the n\pipe\register_pure_display() function.

The pure display pipe expects a default, empty, error, and feedback view present in the markup for the pipe. And, just as with the other pipe functions, you can also pass in an optional $opts argument to set the databinding (form or placeholder); whitelists; override the default, feedback, or empty functions; etc.

n\pipe\register_pure_display(
    $name = 'users',
    $rows_func = function($port_vals){
        return n\sql\source\query($query = 'SELECT * FROM users');
    }
);

Pure action pipes

As noted above, wanting to performing specific actions if/when sets of ports are present on a request is a common use case for Nephtali users, and the n\pipe\register_pure_action() function  facilitates this workflow utilizing the new pure capabilities. The first action to have a valid signature (all of the ports are present, but not necessarily valid) will be executed.

The pure action pipe expects the markup to contain a feedback view for displaying error messages if any of the ports are invalid, a status view for displaying the status message (databound to its {message} placeholder within the <!–data–> region), and an error view.

n\pipe\register_pure_action(
    $name = 'user',
    $actions = array(
        'update' => array(
            'io_func' => function($port_vals){
                return n\sql\action\update($table_name = 'users', $inputs = $port_vals);
            },
            'signature' => array('id', 'name'),
            'message' => 'User updated.'
        )
        'insert' => array(
            'io_func' => function($port_vals){
                return n\sql\action\insert($table_name = 'users', $inputs = $port_vals);
            },
            'signature' => array('name'),
            'message' => 'User inserted.'
        )
    )
);

Conclusion

Although not officially released yet, you can pull the changes from trunk to start utilizing the new pure functions to speed your development now. I won’t be changing the API at this point, although I may add a couple other features before making the next release. Hopefully you’ll find the new functions as useful as I do, and with this work out of the way, the next step will be to integrate unit testing directly within Nephtali.