File Coverage

blib/lib/MooX/Attribute/ENV.pm
Criterion Covered Total %
statement 56 57 100.0
branch 23 28 82.1
condition 2 3 66.6
subroutine 15 15 100.0
pod n/a
total 96 103 94.1


line stmt bran cond sub pod time code
1             package MooX::Attribute::ENV;
2              
3             our $VERSION = '0.04';
4              
5             # this bit would be MooX::Utils but without initial _ on func name
6 2     2   139130 use strict;
  2         8  
  2         49  
7 2     2   9 use warnings;
  2         4  
  2         36  
8 2     2   8 use Moo ();
  2         3  
  2         19  
9 2     2   808 use Moo::Role ();
  2         14447  
  2         52  
10 2     2   13 use Carp qw(croak);
  2         3  
  2         1271  
11             #use base qw(Exporter);
12             #our @EXPORT = qw(override_function);
13             sub _override_function {
14 4     4   11 my ($target, $name, $func) = @_;
15 4 50       41 my $orig = $target->can($name) or croak "Override '$target\::$name': not found";
16 4 50       31 my $install_tracked = Moo::Role->is_role($target) ? \&Moo::Role::_install_tracked : \&Moo::_install_tracked;
17 4     26   110 $install_tracked->($target, $name, sub { $func->($orig, @_) });
  26     26   12695  
        26      
18             }
19             # end MooX::Utils;
20              
21             my %target2attr2envkey;
22             sub import {
23 2     2   19 my $target = scalar caller;
24             _override_function($target, 'has', sub {
25 7     7   26 my ($orig, $namespec, %opts) = @_;
26 7         19 my ($other_opts, $env_opts) = _partition_opts(\%opts);
27 7         31 $orig->($namespec, %$other_opts);
28 7 100       37442 return if !keys %$env_opts; # non env
29 6 50       16 for my $name (ref $namespec ? @$namespec : $namespec) {
30 6         31 my $envkey = _generate_key($name, \%opts, $target);
31 6         28 $target2attr2envkey{$target}{$name} = $envkey;
32             }
33 2         12 });
34             _override_function($target, 'BUILDARGS', sub {
35 19     19   56 my ($orig, $class, @args) = @_;
36 19 100 66     71 my %args = @args == 1 && ref($args[0]) eq 'HASH' ? %{$args[0]} : @args;
  1         3  
37 19         28 for my $attr (keys %{ $target2attr2envkey{$target} }) {
  19         53  
38 71 50       107 next if exists $args{$attr};
39 71         113 my $value = _lookup_env($target2attr2envkey{$target}{$attr});
40 71 100       129 $args{$attr} = $value if defined $value;
41             }
42 19         55 return $class->$orig(\%args);
43 2         111 });
44             }
45              
46             sub _lookup_env {
47 71 100   71   121 my @env_keys = ref $_[0] eq 'ARRAY' ? @{ $_[0] } : $_[0];
  6         13  
48 71         88 foreach my $envkey ( @env_keys ) {
49 73 100       129 return $ENV{$envkey} if exists $ENV{$envkey};
50 65 100       139 return $ENV{uc $envkey} if exists $ENV{uc $envkey};
51             }
52 55         71 undef;
53             }
54              
55             my @KEYS = qw(env env_key env_prefix env_package_prefix);
56             sub _partition_opts {
57 7     7   13 my ($opts) = @_;
58 7         20 my (%opts, %env_opts) = %$opts;
59 7         39 $env_opts{$_} = delete $opts{$_} for grep defined $opts{$_}, @KEYS;
60 7         19 (\%opts, \%env_opts);
61             }
62              
63             sub _generate_key {
64 6     6   18 my ($attr, $opts, $target) = @_;
65 6 100       14 return $attr if $opts->{env};
66 4 100       14 return $opts->{env_key} if $opts->{env_key};
67 2 100       6 return "$opts->{env_prefix}_$attr" if $opts->{env_prefix};
68 1 50       4 if ($opts->{env_package_prefix}) {
69 1         3 $target =~ s/:+/_/g;
70 1         4 return "${target}_$attr";
71             }
72 0           undef; # uncoverable statement
73             }
74              
75             =head1 NAME
76              
77             MooX::Attribute::ENV - Allow Moo attributes to get their values from %ENV
78              
79             =begin markdown
80              
81             # PROJECT STATUS
82              
83             | OS | Build status |
84             |:-------:|--------------:|
85             | Linux | [![Build Status](https://travis-ci.org/mohawk2/moox-attribute-env.svg?branch=master)](https://travis-ci.org/mohawk2/moox-attribute-env) |
86              
87             [![CPAN version](https://badge.fury.io/pl/moox-attribute-env.svg)](https://metacpan.org/pod/MooX::Attribute::ENV) [![Coverage Status](https://coveralls.io/repos/github/mohawk2/moox-attribute-env/badge.svg?branch=master)](https://coveralls.io/github/mohawk2/moox-attribute-env?branch=master)
88              
89             =end markdown
90              
91             =head1 SYNOPSIS
92              
93             package MyMod;
94             use Moo;
95             use MooX::Attribute::ENV;
96             # look for $ENV{attr_val} and $ENV{ATTR_VAL}
97             has attr => (
98             is => 'ro',
99             env_key => 'attr_val',
100             );
101             # look for $ENV{attr_val} and $ENV{next_val}, in that order
102             has some => (
103             is => 'ro',
104             env_key => [ 'attr_val', 'next_val' ],
105             );
106             # looks for $ENV{otherattr} and $ENV{OTHERATTR}, then any default
107             has otherattr => (
108             is => 'ro',
109             env => 1,
110             default => 7,
111             );
112             # looks for $ENV{xxx_prefixattr} and $ENV{XXX_PREFIXATTR}
113             has prefixattr => (
114             is => 'ro',
115             env_prefix => 'xxx',
116             );
117             # looks for $ENV{MyMod_packageattr} and $ENV{MYMOD_PACKAGEATTR}
118             has packageattr => (
119             is => 'ro',
120             env_package_prefix => 1,
121             );
122              
123             $ perl -MMyMod -E 'say MyMod->new(attr => 2)->attr'
124             # 2
125             $ ATTR_VAL=3 perl -MMyMod -E 'say MyMod->new->attr'
126             # 3
127             $ OTHERATTR=4 perl -MMyMod -E 'say MyMod->new->otherattr'
128             # 4
129              
130             =head1 DESCRIPTION
131              
132             This is a L extension. It allows other attributes for L. If
133             any of these are given, then L is wrapped so that values
134             for object attributes can, if not supplied in the normal construction
135             process, come from the environment.
136              
137             The environment will be searched for either the given case, or upper case,
138             version of the names discussed below.
139              
140             When a prefix is mentioned, it will be prepended to the mentioned name,
141             with a C<_> in between.
142              
143             =head1 ADDITIONAL ATTRIBUTES
144              
145             =head2 env
146              
147             Boolean. If true, the name is the attribute, no prefix.
148              
149             =head2 env_key
150              
151             String. If true, the name is the given value, no prefix.
152              
153             or
154              
155             ArrayRef. A list of names that will be checked in given order.
156              
157             =head2 env_prefix
158              
159             String. The prefix is the given value.
160              
161             =head2 env_package_prefix
162              
163             Boolean. If true, use as the prefix the current package-name, with C<::>
164             replaced with C<_>.
165              
166             =head1 AUTHOR
167              
168             Ed J, porting John Napiorkowski's excellent L.
169              
170             =head1 LICENCE
171              
172             The same terms as Perl itself.
173              
174             =cut
175              
176             1;