NAME

Plack::Middleware::MangleEnv - Mangle request environment at will

VERSION

This document describes Plack::Middleware::MangleEnv version 0.001.

SYNOPSIS

use Plack::Middleware::MangleEnv;

my $mw = Plack::Middleware::MangleEnv->new(

   # overriding is the default behaviour

   # straight value, must be a plain SCALAR (no refs)
   var_name    => 'a simple, overriding value',

   # any value can be wrapped, even refs
   some_value  => [ \@whatever ],

   # or you can be just explicit
   alternative => { value => $whatever },

   # you can read stuff from %ENV
   from_ENV    => { ENV => 'PLACK_ENV' },

   # or read other variables from $env
   from_env    => { env => 'psgi.url_scheme' },

   # turn override into defaulting, works with value, env and ENV
   de_fault    => { value => $something, override => 0 },

   # get rid of a variable, inconditionally. You can pass
   # no value or be explicit about your intent
   delete_pliz => [],
   delete_me   => { remove => 1 },

   # use subroutines for maximum flexibility.
   change_me1  => sub { ... },
   change_me2  => { sub => sub { ... } },

);

# you can also pass the key/value pairs as a hash reference
# associated to a key named 'manglers'. This is necessary e.g. if
# you want to set a variable in $env with name 'app' (or 'manglers'
# itself)
my $mw2 = Plack::Middleware::MangleEnv->new(
   manglers => {
      what => 'EVER',
      who  => 'are you?',
   }
);

# when evaluation order or repetition is important... use an array
# reference for 'manglers'
my $mw3 = Plack::Middleware::MangleEnv->new(
   manglers => [
      same_as => 'before', # set this at the beginning...
      what => {env => 'same_as'},
      ...,
      same_as => [], # ... and delete this at the end
   ]
);

DESCRIPTION

This module allows you to mangle Plack's $env that is passed along to the sequence of apps, taking values from:

  • direct configuration;

  • values from %ENV;

  • other values in $env itself;

  • subroutines.

How Variables Are Set

The end goal of this middleware module is to manipulate $env by adding, changing or deleting its items.

You can pass the different actions to be performed as key-value pairs. They can either appear directly upon invoking the middleware, as in the following example:

my $mw = Plack::Middleware::MangleEnv->new(
   var_name    => 'a simple, overriding value',
   some_value  => [ \@whatever ],
   alternative => { value => $whatever },
   # ... you get the idea
);

or wrap these pairs inside either an hash or an array reference whose key is manglers, like in the following examples:

my $mw_h = Plack::Middleware::MangleEnv->new(
   manglers => {
      var_name    => 'a simple, overriding value',
      some_value  => [ \@whatever ],
      alternative => { value => $whatever },
      # ... you get the idea
   }
);

my $mw_a = Plack::Middleware::MangleEnv->new(
   manglers => [
      var_name    => 'a simple, overriding value',
      some_value  => [ \@whatever ],
      alternative => { value => $whatever },
      # ... you get the idea
   ]
);

Although more verbose, this last approach with an array reference is important because it allows you to:

  • define the exact order of evaluation for mangling actions;

  • define multiple actions for the same key, possibly at different stages;

  • use keys manglers and app, if you need them.

There's a wide range of possible values that you can set associated to a key:

Simple scalar
key => 'some simple, non-reference scalar',

a non-reference scalar is always taken as-is and then set in $env.

Array reference
key => [],
key => [ { 'a non' => 'trivial scalar' } ],

when you pass an array reference, it can be either empty (in which case the associated key will be removed from $env) or contain exactly one value, which will be set into $env.

This alternative allows you to set any scalar, not just non-reference ones; so the following examples will do what they say:

# set key to an array ref with numbers 1..3 inside
key => [ [1..3] ], # note: array ref inside array ref!

# set key to a hash reference, literally
key => [ { a => 'b', c => 'd' } ],

# set key to a sub reference, literally
key => [ sub { 'I go with key!' } ],
Sub reference
key => sub { ... },

the sub reference will be called and its return value used to figure out the value to associate to the key. See "Sub Reference Interface" for details on the expected interface for the sub;

Hash reference

allows you to be verbosely clear about what you want, in addition to giving you knobs to modify the behaviour. The allowed keys are the following:

env

points to a string that will be used to extract the value from $env itself. Useful if you want to change the name of a parameter;

ENV

points to a string that will be used to extract the value from %ENV. Useful if you want to get some variables from the environment.

list

points to a hash of configurations to build and handle a list of other sources (i.e. env, ENV or value).

It collects data from a list of sources, optionally flatteninig them (if they are array or hash references), filtering them (e.g. to set default values for undef or empty strings, or removing unwanted elements), then optionally joininig them or passing them to sprintf. See "generate_hash_manglers_list" for the details on this hash and of each element in sources.

For example, consider the following definition:

...
connect_string => {
   list => {
      default => [], # remove undefined values
      join => ':',   # join with ":"
      sources => [
         {ENV => 'HOST'},
         {ENV => 'PORT'}
      ],
   }
}
...

The following applies:

%ENV = (HOST => 'localhost'); # leave PORT undefined
--> $env->{connect_string} = 'localhost';

%ENV = (HOST => 'localhost', PORT => 80);
--> $env->{connect_string} = 'localhost:80';

Something similar happens with sprintf, although the format is stricter in this case and we have to address this with defaults:

...
connect_string => {
   list => {
      default => [], # remove undefined values
      sprintf => '%s:%s',
      sources => [
         {ENV => 'HOST', default => 'www.example.com'},
         {ENV => 'PORT', default => 80},
      ],
   }
}
...

%ENV = ();
--> $env->{connect_string} = 'www.example.com:80';

%ENV = (HOST => 'localhost', PORT => 443);
--> $env->{connect_string} = 'localhost:443';

In addition to plain strings, you can also set either join or sprintf (never the two at the same time!) with a source specification with env, ENV or value:

...
sprintf_template => '%s:%s', # ends up in $env
connect_string => {
   list => {
      default => [], # remove undefined values
      sprintf => {env => 'sprintf_template'},
      sources => [
         {ENV => 'HOST', default => 'www.example.com'},
         {ENV => 'PORT', default => 80},
      ],
   }
}
...
# same as before here
override

boolean flag that indicates whether the new value overrides a previous one, if any. Set to a false value to avoid overriding an existing value, while still being able to provide a default one if the key is missing from $env.

Defaults to a true value;

sub

set a subroutine, see "Sub Reference Interface" for details;

value

set a value that is not a simple plain scalar:

# set key to an array ref with numbers 1..3 inside
key => { value => [1..3] },

# set key to a hash reference, literally
key => { value => { a => 'b', c => 'd' } },

# set key to a sub reference, literally
key => { value => sub { 'I go with key!' } },

Exactly one of the keys env, ENV, list, sub and value MUST appear in the hash reference. For obvious reasons, you cannot provide more of them, otherwise a conflict would arise.

Sub Reference Interface

The most flexible way to mangle $env is through a subroutine. It can be provided either directly associated to the key, or through the sub sub-key in the hash associated to the key.

The provided subroutine reference will be called like this:

sub {
   my ($current_value, $env, $key) = @_;
   # do what you need
   return @something;
}

The sub can modify $env at will, e.g. by adding new keys or removing other ones based on your specific logic.

If you don't return anything, or the undef value, the corresponding $key in $env will be left untouched, keeping its previous value (if any). Otherwise, you are supposed to return one single value that can be:

  • not an array reference, in which case it is used as the value associated to $key in $env;

  • an array reference. If the array is empty, the $key is removed from $env; otherwise, it MUST contain exactly one value, used to set the key $key in $env (which also allows you to set as output an array reference, even an empty one).

Examples:

# nothing happens with this
sub { return }

# key is removed from $env
sub { return [] }

# key is set to an empty array in $env
sub { return [[]] }

METHODS

The following methods are implemented as part of the interface for a Plack middleware. Although you can override them... there's probably little sense in doing this!

call
prepare_app

Methods described in the following subsections can be overridden or used in derived classes. The various generate*_manglers functions have the plural form because they can potentially return a list of manglers; in this module, anyway, each of them returns one single mangler per call.

generate_array_manglers

my @manglers = $obj->generate_array_manglers($key, $aref, $defaults);

generate manglers starting from an array definition. $aref MUST be an ARRAY reference. Depending on the number of elements in @$aref:

generate_code_manglers

my @manglers = $obj->generate_code_manglers($key, $sub, $defaults);

generate manglers from a sub definition. $sub MUST be a CODE reference.

The provided sub is wrapped using "wrap_code" to set the right behaviour around $sub, then the output mangler is returned as follows:

[$key => {%$defaults, wrapsub => $wrapped_sub}]

generate_hash_manglers

my @manglers = $obj->generate_hash_manglers($key, $hash, $defaults);

generate manglers from a hash definition. $hash MUST be a HASH reference. This is actually a dispatcher for the different methods named generate_hash_manglers_*, which can be overridden or added in derived classes to support further conversion types.

generate_hash_manglers_ENV

my @manglers = $obj->generate_hash_manglers_ENV($key, $name, $opts);

generate mangler for taking $ENV{$name}.

generate_hash_manglers_env

my @manglers = $obj->generate_hash_manglers_env($key, $name, $opts);

generate mangler for taking $env-{$name}>.

generate_hash_manglers_list

my @manglers = $obj->generate_hash_manglers_env($key, $cfg, $opts);

generate mangler for generating a list of elements from hash configuration %$cfg. The following keys are supported:

default

a default value to assign if the source element is not present or has an undefined value. If it is an array reference, it follows the same rules explained at "Array reference". If an item in sources has a default configuration, that takes precedence. If not present, the same as setting [] is assumed (i.e. undefined values will be removed).

default_on_empty

boolean, apply the default above to empty values too, in addition to undefined values. Defaults to 0;

flatten

boolean, turn array reference values into a list. Defaults to 0;

join

specification of a source (see below for the source specification provided for key sources) where it's possible to take a string for joining the elements of the list together. In this case, a single string will be returned.

remove_if

array reference to strings that will be removed when found;

sources

array reference of hashes, explained below.

The different source specifications in sources can take all the keys above as an overriding specialization (with the exception of sources), and the following additional ones:

env
ENV
value

mutually exclusive keys indicating where the value should be taken from.

generate_hash_manglers_remove

Same as "generate_remove_manglers".

generate_hash_manglers_sub

Same as "generate_code_manglers".

generate_hash_manglers_value

my @manglers =
  $obj->generate_hash_manglers_value($key, $value, $opts);

generate mangler for taking $value.

generate_immediate_manglers

my @manglers =
  $obj->generate_immediate_manglers($type, $key, $value, $opts);

generates this mangler:

[$key => {%$opts, $type => $value}]

i.e. the standard mangler for setting something immediately handled.

Note that this has the initial value for $type, differently from other generate*_manglers functions.

generate_manglers

my @manglers = $obj->generate_manglers($key, $value, $defaults);

Generate zero, one or more manglers, dispatching to the proper function depending on the type of $value. $defaults is a hash reference holding default values for the generated mangler, e.g. setting override to 1 by default.

At the moment, all generation methods return exactly one mangler per call. This can of course change in derived classes, hence the returned value can contain any number of items.

This method does the following dispatching based on ref($value):

If you want to override it (e.g. to add support for different types, or change the default ones described above) you might augment it like in the following example:

# suppose we want to do something with Regexp references

package Plack::Middleware::MangleEnv::Derived;
use parent 'Plack::Middleware::MangleEnv';
sub generate_manglers {
   my $self = shift;

   return $self->generate_regex_manglers(@_)
     if ref($_[1]) eq 'Regexp';

   return $self->SUPER::generate_manglers(@_);
}
sub generate_regex_manglers {
   my ($self, $key, $regex, $defaults) = @_;
   my $sub = sub {
      defined(my $value = shift) or return; # do nothing if undef
      my ($capture) = $value =~ m{$regex};
      return $capture;
   };
   my $wrapsub = $self->wrap_code($sub);
   return [$key => {%$defaults, wrapsub => $wrapsub}];
}
1;

generate_remove_manglers

my @manglers =
  $obj->generate_remove_manglers($key, $value, $defaults);

convenience function to generate manglers for removing. Such manglers are supposed to have this form:

[ $key => { remove => 1 } ]

and this function does exactly this, ignoring $defaults and checking that $value is empty if it is a hash reference (it is used by "generate_hash_manglers" behind the scenes, so this checks that there are no further keys in the input mangler definition).

get_values_from_source

my @values = $obj->get_values_from_source($env, $spec);

runtime helper that expands the $spec according to the rules explained in "generate_hash_manglers_list" for each source.

normalize_source

my $normal = $obj->normalize_source($source, $defaults);

normalizes a source (see "generate_hash_manglers_list") turning the pair with key env, ENV or value into two pair, one with key type and another one with key value. Example:

{ env => 'whatever' }  -->  { type => 'env', value => 'whatever' }

Values for options (e.g. default, default_on_empty, etc.) are taken from hash reference $source and, if absent, from $defaults.

push_manglers

$obj->push_manglers->(@manglers);

add the provided @manglers to the list of manglers that will be used at runtime.

Used by prepare_app to populate the list of runtime manglers from the provided inputs. For every input definition of a mangler, "generate_manglers" is called and its output fed to this method, like this:

my @manglers = $obj->generate_manglers(...);
$obj->push_manglers(@manglers);

You might want to override this method if you want to further process all the generated manglers, like this:

package Plack::Middleware::MangleEnv::Derived;
use parent 'Plack::Middleware::MangleEnv';
sub push_manglers {
   my $self = shift;
   my @manglers = map { do_something($_) } @_;
   return $self->SUPER::push_manglers(@manglers);
}
...

stringified_list

my @strings = $obj->stringified_list(@list);

convenience function to generate a list of strings suitable for logging. Defined element are escaped and put into single quotes, while undef is rendered as the string undef (without quoting).

wrap_code

my $wrapped_sub = $obj->wrap_code($sub);

wrap a code sub adhering to the "Sub Reference Interface" to implement the behaviour described in the same subsection. It is used by both "generate_code_manglers" and "generate_hash_manglers" to wrap input subs.

BUGS AND LIMITATIONS

Report bugs either through RT or GitHub (patches welcome).

SEE ALSO

Plack, Plack::Middleware::ForceEnv, Plack::Middleware::ReverseProxy, Plack::Middleware::SetEnvFromHeader, Plack::Middleware::SetLocalEnv.

AUTHOR

Flavio Poletti <polettix@cpan.org>

COPYRIGHT AND LICENSE

Copyright (C) 2016 by Flavio Poletti <polettix@cpan.org>

This module is free software. You can redistribute it and/or modify it under the terms of the Artistic License 2.0.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.