File Coverage

blib/lib/MooX/Attribute/ENV.pm
Criterion Covered Total %
statement 53 55 96.3
branch 19 26 73.0
condition 1 3 33.3
subroutine 15 15 100.0
pod n/a
total 88 99 88.8


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