Skip to content

awncorp/rewire

Repository files navigation

NAME

Rewire - Dependency Injection

ABSTRACT

Dependency Injection Container for Perl 5

SYNOPSIS

use Rewire;

my $services = {
  filetemp => {
    package => 'File/Temp'
  },
  tempfile => {
    package => 'Mojo/File',
    argument => { '$service' => 'filetemp' }
  }
};

my $rewire = Rewire->new(services => $services);

$rewire->resolve('tempfile');

DESCRIPTION

This package provides methods for using dependency injection, and building objects and values.

INTEGRATES

This package integrates behaviors from:

Data::Object::Role::Buildable

Data::Object::Role::Proxyable

LIBRARIES

This package uses type constraints from:

Types::Standard

SCENARIOS

This package supports the following scenarios:

$callback

use Rewire;

my $services = {
  io => {
    package => 'IO/Handle'
  },
  log => {
    package => 'Mojo/Log',
    argument => {
      format => { '$callback' => 'io' }
    }
  },
};

my $rewire = Rewire->new(
  services => $services
);

This package supports resolving services as callbacks to be passed around and/or resolved by other services. The $callback directive is used to specify the name of a service to be resolved and passed as an argument.

$envvar

use Rewire;

my $services = {
  file => {
    package => 'Mojo/File',
    argument => { '$envvar' => 'home' }
  }
};

my $rewire = Rewire->new(
  services => $services
);

This package supports inlining environment variables as arguments to services. The $envvar directive is used to specify the name of an environment variable, and can also be used in metadata for reusability.

$function

use Rewire;

my $services = {
  temp => {
    package => 'File/Temp'
  },
  file => {
    package => 'Mojo/File',
    argument => { '$function' => 'temp#tempfile' }
  }
};

my $rewire = Rewire->new(
  services => $services
);

This package supports inlining the result of a service resolution and function call as arguments to services. The # delimited $function directive is used to specify the name of an existing service on the right-hand side, and an arbitrary function to be call on the result on the left-hand side.

$metadata

use Rewire;

my $metadata = {
  home => '/home/ubuntu'
};

my $services = {
  file => {
    package => 'Mojo/File',
    argument => { '$metadata' => 'home' }
  }
};

my $rewire = Rewire->new(
  metadata => $metadata,
  services => $services
);

This package supports inlining configuration data as arguments to services. The $metadata directive is used to specify the name of a stashed configuration value or data structure.

$method

use Rewire;

my $services = {
  temp => {
    package => 'File/Temp'
  },
  file => {
    package => 'Mojo/File',
    argument => { '$method' => 'temp#filename' }
  }
};

my $rewire = Rewire->new(
  services => $services
);

This package supports inlining the result of a service resolution and method call as arguments to services. The # delimited $method directive is used to specify the name of an existing service on the right-hand side, and an arbitrary method to be call on the result on the left-hand side.

$routine

use Rewire;

my $services = {
  temp => {
    package => 'File/Temp'
  },
  file => {
    package => 'Mojo/File',
    argument => { '$routine' => 'temp#tempfile' }
  }
};

my $rewire = Rewire->new(
  services => $services
);

This package supports inlining the result of a service resolution and routine call as arguments to services. The # delimited $routine directive is used to specify the name of an existing service on the right-hand side, and an arbitrary routine to be call on the result on the left-hand side.

$service

use Rewire;

my $services = {
  io => {
    package => 'IO/Handle'
  },
  log => {
    package => 'Mojo/Log',
    argument => {
      handle => { '$service' => 'io' }
    }
  },
};

my $rewire = Rewire->new(
  services => $services
);

This package supports inlining resolved services as arguments to other services. The $service directive is used to specify the name of a service to be resolved and passed as an argument.

arguments

use Rewire;

my $metadata = {
  applog => '/var/log/rewire.log'
};

my $services = {
  mojo_log => {
    package => 'Mojo/Log',
    argument => {
      path => { '$metadata' => 'applog' },
      level => 'warn'
    },
    argument_as => 'list'
  }
};

my $rewire = Rewire->new(
  services => $services,
  metadata => $metadata
);

This package supports providing static and/or dynamic arguments during object construction from metadata or other services.

builder

use Rewire;

my $services = {
  mojo_date => {
    package => 'Mojo/Date',
    builder => [
      {
        method => 'new',
        return => 'self'
      },
      {
        method => 'to_datetime',
        return => 'result'
      }
    ]
  }
};

my $rewire = Rewire->new(
  services => $services,
);

This package supports specifying multiple build steps as function, method, and routine calls and chaining them together.

config

use Rewire;

my $metadata = {
  home => '/home/ubuntu'
};

my $services = {
  tempfile => {
    package => 'Mojo/File',
    argument => { '$metadata' => 'home' }
  }
};

my $rewire = Rewire->new(
  services => $services,
  metadata => $metadata
);

This package supports configuring services and metadata in the service of building objects and values.

constructor

use Rewire;

my $services = {
  mojo_date => {
    package => 'Mojo/Date',
    constructor => 'new'
  }
};

my $rewire = Rewire->new(
  services => $services
);

This package supports specifying constructors other than the traditional new routine. A constructor is always called with the package name as the invocant.

extends

use Rewire;

my $services = {
  io => {
    package => 'IO/Handle'
  },
  log => {
    package => 'Mojo/Log',
    argument => {
      handle => { '$service' => 'io' }
    }
  },
  development_log => {
    package => 'Mojo/Log',
    extends => 'log',
    builder => [
      {
        method => 'new',
        return => 'self'
      },
      {
        method => 'path',
        argument => '/tmp/development.log',
        return => 'none'
      },
      {
        method => 'level',
        argument => 'debug',
        return => 'none'
      }
    ]
  },
  production_log => {
    package => 'Mojo/Log',
    extends => 'log',
    builder => [
      {
        method => 'new',
        return => 'self'
      },
      {
        method => 'path',
        argument => '/tmp/production.log',
        return => 'none'
      },
      {
        method => 'level',
        argument => 'warn',
        return => 'none'
      }
    ]
  },
  staging_log => {
    package => 'Mojo/Log',
    extends => 'development_log',
  },
  testing_log => {
    package => 'Mojo/Log',
    extends => 'log',
  },
};

my $rewire = Rewire->new(
  services => $services
);

This package supports extending services in the definition of other services, recursively compiling service configurations and eventually executing the requested compiled service.

function

use Rewire;

my $services = {
  foo_sum => {
    package => 'Mojo/Util',
    function => 'md5_sum',
    argument => 'foo',
  }
};

my $rewire = Rewire->new(
  services => $services,
);

This package supports specifying construction as a function call, which when called does not provide an invocant.

lifecycle

use Rewire;

my $metadata = {
  home => '/home/ubuntu'
};

my $services = {
  tempfile => {
    package => 'Mojo/File',
    argument => { '$metadata' => 'home' },
    lifecycle => 'singleton'
  }
};

my $rewire = Rewire->new(
  services => $services,
  metadata => $metadata
);

This package supports different lifecycle options which determine when services are built and whether they're persisted.

metadata

use Rewire;

my $metadata = {
  homedir => '/home',
  tempdir => '/tmp'
};

my $services = {
  home => {
    package => 'Mojo/Path',
    argument => { '$metadata' => 'homedir' },
  },
  temp => {
    package => 'Mojo/Path',
    argument => { '$metadata' => 'tempdir' },
  }
};

my $rewire = Rewire->new(
  services => $services,
  metadata => $metadata
);

This package supports specifying data and structures which can be used in the construction of multiple services.

method

use Rewire;

my $services = {
  mojo_url => {
    package => 'Mojo/URL',
    argument => 'https://perl.org',
    method => 'new'
  }
};

my $rewire = Rewire->new(
  services => $services,
);

This package supports specifying construction as a method call, which when called provides the package or object instance as the invocant.

proxyable

use Rewire;

my $services = {
  home => {
    package => 'Mojo/Path',
    argument => '/home',
  },
  temp => {
    package => 'Mojo/Path',
    argument => '/tmp',
  }
};

my $rewire = Rewire->new(
  services => $services
);

# resolve services via method calls
[
  $rewire->home, # i.e. $rewire->process('home')
  $rewire->temp  # i.e. $rewire->process('temp')
]

This package supports the resolution of services using a single method call. This is enabled by intercepting method calls and proxying them to the "process" method.

routine

use Rewire;

my $services = {
  mojo_url => {
    package => 'Mojo/URL',
    argument => 'https://perl.org',
    routine => 'new'
  }
};

my $rewire = Rewire->new(
  services => $services,
);

This package supports specifying construction as a function call, which when called provides the package as the invocant.

service

my $metadata = {
  home => '/home/ubuntu'
};

my $services = {
  tempfile => {
    package => 'Mojo/File',
    argument => { '$metadata' => 'home' },
    lifecycle => 'eager'
  }
};

my $rewire = Rewire->new(
  services => $services,
  metadata => $metadata
);

This package supports defining services to be constructed on-demand or automatically on instantiation.

ATTRIBUTES

This package has the following attributes:

context

context(CodeRef)

This attribute is read-only, accepts (CodeRef) values, and is optional.

engine

engine(InstanceOf["Data::Object::Space"])

This attribute is read-only, accepts (InstanceOf["Data::Object::Space"]) values, and is optional.

metadata

metadata(HashRef)

This attribute is read-only, accepts (HashRef) values, and is optional.

services

services(HashRef)

This attribute is read-only, accepts (HashRef) values, and is optional.

METHODS

This package implements the following methods:

config

config() : HashRef

The config method returns the configuration based on the services and metadata attributes.

  • config example #1

      # given: synopsis
    
      $rewire->config;
    

process

process(Str $name, Any $argument, Maybe[Str] $argument_as) : Any

The process method processes and returns an object or value based on the service named but where the arguments are provided ad-hoc. Note: This method is meant to be used to construct services ad-hoc and as such bypasses caching and lifecycle effects.

  • process example #1

      # given: synopsis
    
      $rewire->process('tempfile', 'rewire.tmp');
    
  • process example #2

      use Rewire;
    
      my $metadata = {
        logfile => '/var/log/rewire.log',
      };
    
      my $services = {
        mojo_log => {
          package => 'Mojo/Log',
          argument => { '$metadata' => 'logfile' },
        }
      };
    
      my $rewire = Rewire->new(
        services => $services,
        metadata => $metadata
      );
    
      $rewire->process('mojo_log', {
        level => 'fatal',
        path => { '$metadata' => 'logfile' }
      });
    
  • process example #3

      use Rewire;
    
      my $metadata = {
        logfile => '/var/log/rewire.log',
      };
    
      my $services = {
        mojo_log => {
          package => 'Mojo/Log',
          builder => [
            {
              method => 'new',
              return => 'self'
            }
          ]
        }
      };
    
      my $rewire = Rewire->new(
        services => $services,
        metadata => $metadata
      );
    
      $rewire->process('mojo_log', {
        level => 'fatal',
        path => { '$metadata' => 'logfile' }
      });
    

resolve

resolve(Str $name) : Any

The resolve method resolves and returns an object or value based on the service named. Note: This method is recommended to be used to construct services as defined by the configuration and as such doesn't not allow passing additional arguments.

  • resolve example #1

      # given: synopsis
    
      $rewire->resolve('tempfile');
    
  • resolve example #2

      use Rewire;
    
      my $services = {
        mojo_log => {
          package => 'Mojo/Log',
          argument => {
            level => 'fatal',
            path => '/var/log/rewire.log'
          },
        }
      };
    
      my $rewire = Rewire->new(
        services => $services,
      );
    
      $rewire->resolve('mojo_log');
    
  • resolve example #3

      package Dynamic;
    
      sub import;
    
      sub AUTOLOAD {
        bless {};
      }
    
      sub DESTROY {
        ; # noop
      }
    
      package main;
    
      use Rewire;
    
      my $services = {
        dynamic => {
          package => 'Dynamic',
          builder => [
            {
              method => 'new',
              return => 'self'
            },
            {
              method => 'missing_method',
              return => 'result'
            }
          ],
        }
      };
    
      my $rewire = Rewire->new(
        services => $services,
      );
    
      $rewire->resolve('dynamic');
    

validate

validate() : Object

The validate method validates the configuration and throws an exception if invalid, otherwise returns itself.

  • validate example #1

      # given: synopsis
    
      $rewire->validate;
    

AUTHOR

Al Newkirk, awncorp@cpan.org

LICENSE

Copyright (C) 2011-2019, Al Newkirk, et al.

This is free software; you can redistribute it and/or modify it under the terms of the The Apache License, Version 2.0, as elucidated in the "license file".

PROJECT

Wiki

Project

Initiatives

Milestones

Contributing

Issues