[Chicago-talk] undef and Moose types

Sean Blanton sean at blanton.com
Thu May 12 09:08:44 PDT 2011


>
> 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.
>
> ...
>
>
>
It took some experimenting, but I've got something using Bread::Board that
is way better than I had and still meets my goals of maximizing reuse and
minimizing boiler plate.

Since most of my classes use the same resources, I have one app container
class that has app, log, db and ssh services defined. This container is
sub-classed from Bread::Board::Container a la
http://search.cpan.org/~stevan/Bread-Board-0.18/lib/Bread/Board/Manual/Concepts/Advanced.pod#Subclassing

<http://search.cpan.org/~stevan/Bread-Board-0.18/lib/Bread/Board/Manual/Concepts/Advanced.pod#___top>The
container uses a variable for the class name, and I pass that name to the
container class constructor and it creates all the resources and returns an
instance of the class I want. This gives me the reuse so I don't have to
create the same container for different application classes. I'll probably
have a few "standard" containers.

The dependency management between resources is divine. It's giving me the
ability to make cleaner, hierarchical services with more finely grained
encapsulation. I had been using Moose::Role's 'with' and 'require' which
provided primitive forms of encapsulation and dependency management, but the
shortcomings are now especially clear.

It's kind of hard to show a complete example in an email, but here's how to
get the app and run it, and then the container class from a working example.
Feedback always appreciated.

==========================
use app_cntnr;

my $c = app_cntnr->new(
name  => 'app_cntnr',
 class => 'app',
);

my $app = $c->resolve( service => 'application' );

$app->do_something();

==========================
package app_cntnr;

use Moose;
use Bread::Board;

extends 'Bread::Board::Container';

has 'class' => (
is       => 'ro',
isa      => 'Str',
 required => 1,
);

sub BUILD {
my $s = shift;

container $s => as {

service 'log_svc' => (
 class        => 'log_svc',
lifecycle    => 'Singleton',
 );

service 'db_svc' => (       #-- svc for the raw DBI database handle
 class        => 'db_svc',
dependencies => { log_svc => depends_on('log_svc'), }
 );

service 'exec_db_svc' => (  #-- interacts with the database
 class        => 'exec_db_svc',
dependencies => {
log_svc => depends_on('log_svc'),
 db_svc  => depends_on('db_svc'),
}
);

service 'ssh_svc' => (      #-- svc for the raw Net::SSH2 connection
class        => 'ssh_svc',
 dependencies => { log_svc => depends_on('log_svc'), }
);

service 'exec_ssh_svc' => ( #-- Interacts with remote OS over ssh
class        => 'exec_ssh_svc',
 dependencies => {
log_svc => depends_on('log_svc'),
ssh_svc  => depends_on('ssh_svc'),
 }
);

service 'application' => (
 class        => $s->class,
dependencies => {
log_svc  => depends_on('log_svc'),
 exec_db  => depends_on('exec_db_svc'),
exec_ssh => depends_on('exec_ssh_svc'),
 }
);
};
}

no Moose;

1;





Regards,
Sean




On Mon, May 2, 2011 at 12:43 PM, Sean Blanton <sean at blanton.com> wrote:

> Awesome, thanks for the detailed feedback.
>
>
>> 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.
>
>
> Time to review my OO patterns!
>
> Bread::Board is a Moose-based module for dependency injection.
>
> ...
>
>
> I'm going to start using it.
>
> Regards,
> Sean
>
> On Sat, Apr 30, 2011 at 12:36 AM, Jonathan Rockway <
> jon-chicagotalk at jrock.us> wrote:
>
>> * 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.
>>
>>
>> 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 $,=$"
>> _______________________________________________
>> Chicago-talk mailing list
>> Chicago-talk at pm.org
>> http://mail.pm.org/mailman/listinfo/chicago-talk
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.pm.org/pipermail/chicago-talk/attachments/20110512/8f0c7dd3/attachment.html>


More information about the Chicago-talk mailing list