The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
=head1 NAME

Message::Passing::Manual::Components - Writing inputs, outputs and filters

=head1 Writing your own scripts

The supplied L<message-pass> script is useful for testing, and is also fine
for production use in simple cases, however, for less simple cases (for example
scripts with multiple inputs or outputs), then the default script isn't suitable.

=head2 Like the message-pass script

If you just want to override some of the behavior of the message-pass script,
to provide different details etc, then this is easy to do by subclassing
the script.

An example of doing this is:

    package Message::Passing::Script::WebHooks;
    use Moo;
    use MooX::Options;
    use namespace::clean -except => 'meta';

    extends 'Message::Passing';

    option '+encoder' => (
        init_arg => undef,
        default => '+Message::Passing::Filter::Encoder::Null',
        nogetopt => 1,
    );

    option '+output' => (
        nogetopt => 1,
    );

    __PACKAGE__->start( output => 'WebHooks' ) unless caller;

This shows overriding the default command line options, as this script is
dedicated to L<Message::Passing::Output::WebHooks>.

=head2 Different scripts

If you want a more complex example, rather than just overriding some of the functionality from
the default script, then you're better off writing your own script.

See L<Message::Passing:Role::Script>, for a basic role you are likely to want to use to do this.
You'll also want to use L<Message::Passing::DSL>, as shown in the example documentation for
the script role..

If you are writing your own script, want some components to be completely configurable
(as per the default script), then see L<Message::Passing::Role::CLIComponent>, which
implements attributes to help you do this in the same way as the normal script (i.e.
C<thing>, and C<thing_options>).

=head1 Writing Filters

A filter is just a class which consumes both L<Message::Passing::Role::Input> and
L<Message::Passing::Role::Output>.

Simple filters can just consume L<Log::Stash::Role::Filter>, and implement
a C<filter> method. Please see the documentation for that Role for more
information.

More complex filters can compose the input and output roles themselves, and
consume / emit messages as they choose. For a simple example of this type
of filter, see L<Message::Passing::Filter::Delay>.

=head2 Encoders and Decoders

Encoders and Decoders are just implemented the same as standard filters.

The only difference is the default namespace supplied by the DSL, which
appends C<Encoder::> or C<Decoder::> to the standard filter prefix.

=head1 Writing Inputs and Outputs

The interface for both inputs and outputs is conceptually very simple, however there are
some gotchas to watch out for which are described below.

=head2 Use common attributes.

Please try to keep the names of your component's attributes in keeping with the other
inputs and outputs in the framework.

To help with this, a number of simple roles with attributes you may want are included in
the distribution:

=over

=item L<Message::Passing::Role::HasHostnameAndPort>

=item L<Message::Passing::Role::HasUsernameAndPassword>

=back

=head2 MUST by asynchronous.

Your input or output B<MUST NOT> block in the course of it's normal operation. You should use L<AnyEvent>
to make your input or output asynchronous.

If you are trying to convert a synchronous module into being an input, then you can often make it
'asynchronous enough' by grabbing the file descriptor and setting up an IO watcher on it.
L<Message::Passing::Input::Freeswitch> is an example of an input implemented like this.

=head2 Connecting to a server.

If your input or output connects to a server, you should be using the connection manager role
supplied to manage this connection, rather than trying to manage it in your component directly.

This is so that users can have multiple inputs and outputs which share the same connection,
which is both possible and desirable with a number of protocols.

Roles are provided to help component authors with this, please see the documentation in:

=over

=item L<Message::Passing::Role::HasAConnection> - for your component

=item L<Message::Passing::Role::ConnectionManager> - to implement your connection manager.

=back

For example code using these roles, see L<Message::Passing::STOMP>, which implements a simple
example.

=head1 AUTHOR, COPYRIGHT & LICENSE

See L<Message::Passing>.

=cut