Resource::Silo - declarative lazy resource container library

Resource::Silo is a declarative lazy resource container library for Perl. It allows to declare resources such as configuration files, database connections, external service endpoints, and so on, in one place; acquire them on demand; cache them; and release in due order.

It may also be described as the single source of truth for the application's side effects.

For those unfamiliar with Bread::Board:

Hey, hey, hey, hey! A shiny new solution just arrived!

  • declare all of your application's resources / dependencies in one place using a simple DSL;
  • instantiate them on demand and only once, with little overhead;
  • override them with mocks in your tests & guard against unexpected side effects;
  • gain more fine-grained control via the ctl frontend.

For Bread::Board users:

  • the container object is blessed into a one-off container class;
  • service names become methods in said class, specifically lazy getters;
  • syntax for service declaration is more Moose-like;
  • the only service initializer available is block;
  • DI/IoC is not imposed onto the user but merely suggested;
  • service cleanup order may be specified;
  • services are reinitialized if a fork is detected;
  • services can be replaced with mocks for testing.

The usage/syntax examples:

Declaring a resource:

package My::App;
use Resource::Silo;

resource config => sub {
  require YAML::XS;
  YAML::XS::LoadFile( "/etc/myapp.yaml" );
};
resource dbh    => sub {
  require DBI;
  my $self = shift;
  my $conf = $self->config->{database};
  DBI->connect(
    $conf->{dbi}, $conf->{username}, $conf->{password}, { RaiseError => 1 }
  );
};
resource user_agent => sub {
    require LWP::UserAgent;
    LWP::UserAgent->new();
    # set your custon UserAgent header or SSL certificate(s) here
};

Resources with more options:

resource logger =>
  cleanup_order     => 9e9,     # destroy as late as possible
  init              => sub { ... };

resource schema =>
  derivative        => 1,        # merely a frontend to its dependencies
  init              => sub {
    my $self = shift;
    require My::App::Schema;
    return My::App::Schema->connect( sub { $self->dbh } );
  };

Declaring a parametric resource:

package My::App;
use Resource::Silo;

use Redis;
use Redis::Namespace;

my %known_namespaces = (
  lock    => 1,
  session => 1,
  user    => 1,
);

resource redis =>
    argument      => sub { $known_namespaces{ $_ } },
    init          => sub {
        my ($self, $name, $ns) = @_;
        Redis::Namespace->new(
            redis     => $self->redis,
            namespace => $ns,
        );
    };

resource redis_conn => sub {
    my $self = shift;
    Redis->new( server => $self->config->{redis} );
};

# later in the code
silo->redis;            # nope!
silo->redis('session'); # get a prefixed namespace

Using resources throughout the project:

use My::App qw(silo);

sub load_foo {
  my $id = shift;
  my $sql = q{SELECT * FROM foo WHERE foo_id = ?};
  silo->dbh->fetchrow_hashred( $sql, $id );
};

Using as a dependency injection:

package My::App::Stuff;
use Moo;
use My::App qw(silo);
has dbh => is => 'lazy', builder => sub { silo->dbh };

Overriding resources in test files:

use Test::More;
use My::App qw(silo);

silo->ctl->override( dbh => $temp_sqlite_connection );
silo->ctl->lock;

my $stuff = My::App::Stuff->new();
$stuff->frobnicate( ... );        # will only affect the sqlite instance

$stuff->ping_partner_api();       # oops! the user_agent resource wasn't
                                  # overridden, so there'll be an exception

Performing a Big Scary Update:

use My::App qw(silo);
my $dbh = silo->ctl->fresh('dbh');

$dbh->begin_work;
# any operations on $dbh won't interfere with normal usage
# of silo->dbh by other application classes.

Conclusion:

This module is still early in development. The API is not yet set in stone. Feel free to try it out and report the missing features that would make it more applicable to new projects as well as old, entrenched code bases.

Module::Lazy - postpone module loading until actually used

This module is designed to improve load times for applications with huge dependency footprint. It is somewhat similar to autouse, but more focused on object-oriented modules. See:

use Module::Lazy 'My::Module';

This creates a fake package My::Module with AUTOLOAD, DESTROY, can, and isa redefined.

my $object = My::Module->new;
my $value = My::Module::some_function();

Either of these triggers loading My::Module in full.

no Module::Lazy;

Preload all lazy methods right away, forbid further lazy-loading. This may be useful for long-running, mission-critical apps.

use Module::Lazy;
Module::Lazu->unimport();

Ditto, but at runtime (say after parsing command-line parameters and before daemonizing).

Sure, there are some caveats with using it, but maybe it's still helpful for your project. We managed to reduce test suite execution time from ~17 to ~9 minutes with this one.

namespace::local above, below, and around

My namespace::local module has got some new switches in v.0.04:

  • -above (0.04) emulates namespace::clean:

    package My::Package;
    use Lots::Of::Imports; # imports will only be available until end of scope
    use namespace::local -above;
    
    
    sub do_things { ... }; # this will not be hidden
    
  • -below (0.03) hides everything that follows it from outside view:

    package My::Package;
    sub public { ... };
    use namespace::local -below;
    sub private { ... }; # not available outside this module
    
  • default mode still confines imports and/or functions between the use line and end of scope:

    package My::Package;
    
    
    # cannot frobnicate here
    {
        use namespace::local;
        use Some::Crazy::DSL;
        frobnicate Foo => 42;
    }
    # can't frobnicate here, too
    

I also added a list of symbols that this module shall not be fiddling with, which is most likely incomplete.

Still it looks more like an Acme:: toy, to be honest. Who needs to hide their private functions? Just document the public ones to the point where nobody cares to look inside! =)

Have fun.

namespace::local: hiding utility functions in moo[se] roles

Ever since I came up with namespace::local, clearly inspired by namespace::clean, I was wondering who would need it in real life.

Now I seem to have found an actual application: roles. Roles must be very careful about what's in the namespace to avoid conflicts. So any private subs that are there to reduce repeating code should ideally be hidden from external observer.

And it is possible with namespace::local -below. The below switch is introduced in 0.03, and it allows the names defined below the use lines to propagate above the use line, but not past the end of scope. This can be seen as namespace::clean working in opposite direction.

Example: if you comment both use namespace::local lines, this code blows up.

#!perl

use 5.010;
use strictures;

{
    package Bar;
    use Moo::Role;

    sub bar {
        return _private(@_);
    };

    use namespace::local -below;    
    sub _private {
        return 42;
    };
};

{
    package Baz;
    use Moo::Role;

    sub baz {
        return _private(@_);
    };

    use namespace::local -below;
    sub _private {
        return 137;
    };
};

{
    package Foo;
    use Moo;

    with "Bar", "Baz";
};

my $foo = Foo->new;

say $foo->bar;
say $foo->baz;

Fave fun!

namespace::local - confine imports to current scope

namespace::clean (along with its cousins namespace::autoclean and namespace::sweep) allows to "forget" imported functions, only making them available for the current package. This is neat.

After looking at the inside of namespace::clean, I thought I could do an inside-out version that would erase imports that come after and not before it. A prototype turned out surprisingly easy to implement.

The usage is as follows:

package My::Package;

sub normal_funtion {
    # no traces of quux exist
};

sub function_with_import {
     use namespace::local;
     use Foo::Bar qw(quux); # some crazy prototyped DSL
     # quux available here
}; 

sub another_normal {
     # no traces of quux exist
};

I have at least one use case where importing a lot of functions into a single scope makes sense.

Do you have more?