[Chicago-talk] undef and Moose types

Jonathan Rockway jon-chicagotalk at jrock.us
Fri Apr 29 22:36:45 PDT 2011


* On Fri, Apr 29 2011, Sean Blanton wrote:
> Yeah, the 'Maybe' works great, and thanks for pointing me to the doc.
>
> My ssh_builder looks like your safe_ssh, not sure what you meant. I
> have one &ssh_builder for all (86!) classes. If a new class wants an
> ssh connection, it consumes the 'SSH' role with the ssh attribute
> populated by &ssh_builder.

This means you want dependency injection.  Classes should *not* know how
to build things that they have; that's the job for some other part of
the program.

Bread::Board is a Moose-based module for dependency injection.  You can
write something like this:

    use Bread::Board;
    my $c = container 'MyApp' as {
        service 'ssh' => (
            class     => 'Net::SSH',
            lifecycle => 'Singleton', # if you want the same
                                      # instance each time
        );

        service 'LogInAndDeleteEverything' => (
            class        => 'MyApp::Action::LogInAndDeleteEverything',
            dependencies => [ depends_on('ssh') ],
        );
    };

    my $action = $c->resolve( service => 'LogInAndDeleteEverything' );
    $action->execute; # everything is deleted

Your MyApp::Action::LogInAndDeleteEverything would look something like:

    package MyApp::Action::LogInAndDeleteEverything;
    use Moose;

    has 'ssh' => (
        is => 'ro',
        isa => 'Net::SSH',
        required => 1,
    );

    sub execute { ... }

Bread::Board also has support for type mapping, so theoretically there
won't be much boilerplate even if you have 86 classes that all need the
'ssh' service.  I haven't used this feature much, however.

In addition to class injection, there is also block injection, where you
can use a coderef to build the instance.  Sometimes convenient.

It's also worth noting that services can have parameters and that you
can pass in args during "resolve", so if your code looked something
like:

    has 'ssh' => ( ... as above ... );

    has 'chdir_to' => ( ... );

Your service could be changed to:

    service 'LogInAndDeleteEverything' => ( # remind me never to use such
                                            # a long name for an example!
        class        => 'MyApp::Action::LogInAndDeleteEverything',
        dependencies => [ depends_on('ssh') ],
        parameters   => { chdir_to => { default => '/' } },
    );

Then, if you want to delete /etc, you would say:

    my $action = $c->resolve(
        service    => 'LogInAndDeleteEverything',
        parameters => { chdir_to => '/etc' },
    );
    $action->execute; # BAI.

Anyway, Bread::Board is pretty simple and easy to hack on, so it should
be possible to do what you want without making a mess.

One more thing: you can make diagrams of your services and your
dependencies:

https://github.com/jrockway/BreadBoard/commit/83de2f946b0af5a129146a085abfc95173ed0130

Finally, I used Bread::Board::Service as an example in the README here:

https://github.com/jrockway/graphviz-hasa

Pretty!

--
print just => another => perl => hacker => if $,=$"


More information about the Chicago-talk mailing list