File Coverage

blib/lib/MooX/ConfigFromFile/Role/SortedByFilename.pm
Criterion Covered Total %
statement 34 34 100.0
branch 9 10 100.0
condition 3 3 100.0
subroutine 5 5 100.0
pod n/a
total 51 52 100.0


line stmt bran cond sub pod time code
1             package MooX::ConfigFromFile::Role::SortedByFilename;
2              
3 2     2   1926 use strict;
  2         5  
  2         67  
4 2     2   14 use warnings;
  2         6  
  2         154  
5              
6             our $VERSION = '0.009';
7              
8 2     2   20 use File::Basename ();
  2         16  
  2         70  
9              
10 2     2   19 use Moo::Role;
  2         16  
  2         17  
11              
12             requires "raw_loaded_config";
13              
14             sub _build_filename_sorted_loaded_config
15             {
16 9     9   275 my ($next, $class, $params) = @_;
17              
18 9 100       62 defined $params->{raw_loaded_config} or $params->{raw_loaded_config} = $class->_build_raw_loaded_config($params);
19 9 100       31342 return [] if !@{$params->{raw_loaded_config}};
  9         45  
20              
21 8 100       38 defined $params->{config_dirs} or $params->{config_dirs} = $class->_build_config_dirs($params);
22 8 100       732 defined $params->{config_extensions} or $params->{config_extensions} = $class->_build_config_extensions($params);
23              
24 8         4616 my %config_dir_order = map { $params->{config_dirs}->[$_] . "/" => $_ } 0 .. $#{$params->{config_dirs}};
  38         137  
  8         28  
25              
26             [
27             ## no critic (BuiltinFunctions::RequireSimpleSortBlock)
28             sort {
29 18         35 my @a = %{$a};
  18         54  
30 18         32 my @b = %{$b};
  18         39  
31 18         40 my ($fa, $pa, $sa) = File::Basename::fileparse($a[0], map { "." . $_ } @{$params->{config_extensions}});
  84         939  
  18         37  
32 18         63 my ($fb, $pb, $sb) = File::Basename::fileparse($b[0], map { "." . $_ } @{$params->{config_extensions}});
  84         797  
  18         40  
33             # uncoverable branch true
34 18 50 100     151 $fa cmp $fb || $sa cmp $sb || $config_dir_order{$pa} <=> $config_dir_order{$pb};
35 8         24 } @{$params->{raw_loaded_config}}
  8         48  
36             ];
37             }
38              
39             around _build_sorted_loaded_config => \&_build_filename_sorted_loaded_config;
40              
41             1;
42              
43             =head1 NAME
44              
45             MooX::ConfigFromFile::Role::SortedByFilename - allows filename based sort algorithm for multiple config files
46              
47             =head1 SYNOPSIS
48              
49             package MyApp::Cmd::TPau;
50              
51             use DBI;
52             use Moo;
53             use MooX::Cmd with_configfromfile => 1;
54            
55             with "MooX::ConfigFromFile::Role::SortedByFilename";
56             with "MooX::ConfigFromFile::Role::HashMergeLoaded";
57              
58             # ensure hashes are merged by top-most scalar wins
59             around _build_config_merge_behavior => sub { 'RIGHT_PRECEDENT' };
60              
61             has csv => (is => "ro", required => 1);
62              
63             sub execute
64             {
65             my $self = shift;
66             DBI->connect("DBI::csv:", undef, undef, $self->csv);
67             }
68              
69             __END__
70             $ cat etc/myapp.json
71             {
72             "csv": {
73             "f_ext": ".csv/r",
74             "f_dir": "data"
75             "csv_sep_char": ";",
76             "csv_class": "Text::CSV_XS"
77             }
78             }
79             $cat etc/myapp-tpau.json
80             {
81             "csv": {
82             "f_dir": "data/tpau"
83             }
84             }
85              
86             =head1 DESCRIPTION
87              
88             This is an additional role for MooX::ConfigFromFile to allow merging loaded
89             configurations in a more predictable order: filename > extension > path.
90             (Read: When the filename is identical, the extensions are compared, when
91             they are identical, the locations are compared).
92              
93             While filename and file extension are compared on character basis, the
94             sort order of the file locations (path) is based on precedence in
95             L or L, respectively. This order
96             is defined internally in C<@File::ConfigDir::extensible_bases>.
97              
98             =head1 ATTRIBUTES
99              
100             =head2 sorted_loaded_config
101              
102             This role modifies the builder for I by sorting
103             the loaded files from I by filename, extension
104             and location in the filesystem, respectively.
105              
106             Let's assume the affected setup has a I interface named C
107             with I like C provides them, too. And the company
108             using the application does it well by defining staging environments
109             like C (Development), C (Testing), C (Integration) and
110             C (Production). For the example, the I shall be
111             C and C.
112              
113             This will give you possible Cs of
114              
115             # main command
116             ['oper']
117             ['oper', 'dev']
118             ['oper', 'test']
119             ['oper', 'int']
120             ['oper', 'prod']
121             # deploy sub-command
122             ['oper', 'deploy']
123             ['oper', 'deploy', 'dev']
124             ['oper', 'deploy', 'test']
125             ['oper', 'deploy', 'int']
126             ['oper', 'deploy', 'prod']
127             # report sub-command
128             ['oper', 'report']
129             ['oper', 'report', 'dev']
130             ['oper', 'report', 'test']
131             ['oper', 'report', 'int']
132             ['oper', 'report', 'prod']
133              
134             This will result in (let's further assume, developers prefer C,
135             operators prefer C) following possible config filenames:
136              
137             [
138             # main command
139             'oper.json',
140             'oper.yaml',
141             'oper-dev.json',
142             'oper-dev.yaml',
143             'oper-test.json',
144             'oper-test.yaml',
145             'oper-int.json',
146             'oper-int.yaml',
147             'oper-prod.json',
148             'oper-prod.yaml',
149             # deploy sub-command
150             'oper-deploy.json',
151             'oper-deploy.yaml',
152             'oper-deploy-dev.json',
153             'oper-deploy-dev.yaml',
154             'oper-deploy-test.json',
155             'oper-deploy-test.yaml',
156             'oper-deploy-int.json',
157             'oper-deploy-int.yaml',
158             'oper-deploy-prod.json',
159             'oper-deploy-prod.yaml',
160             # report sub-command
161             'oper-report.json',
162             'oper-report.yaml',
163             'oper-report-dev.json',
164             'oper-report-dev.yaml',
165             'oper-report-test.json',
166             'oper-report-test.yaml',
167             'oper-report-int.json',
168             'oper-report-int.yaml',
169             'oper-report-prod.json',
170             'oper-report-prod.yaml',
171             ]
172              
173             For a particular invoking (C in C stage) following
174             files exists:
175              
176             [
177             '/etc/oper.json', # global configuration by developers
178             '/etc/oper.yaml', # global configuration by operating policy
179             '/opt/ctrl/etc/oper.json', # vendor configuration by developers
180             '/opt/ctrl/etc/oper.yaml', # vendor configuration by operating policy
181             '/opt/ctrl/etc/oper-int.yaml', # vendor configuration by stage operating policy
182             '/opt/ctrl/etc/oper-report.yaml', # vendor configuration by report operating team
183             '/home/usr4711/oper-report.yaml', # usr4711 individual configuration (e.g. for template adoption)
184             ]
185              
186             The default sort algorithm will deliver
187              
188             [
189             "/etc/oper.json",
190             "/etc/oper.yaml",
191             "/home/usr4711/oper-report.yaml",
192             "/opt/ctrl/etc/oper-int.yaml",
193             "/opt/ctrl/etc/oper-report.yaml",
194             "/opt/ctrl/etc/oper.json",
195             "/opt/ctrl/etc/oper.yaml"
196             ]
197              
198             This role will change the sort algorithm to deliver
199              
200             [
201             "/etc/oper.json",
202             "/opt/ctrl/etc/oper.json",
203             "/etc/oper.yaml",
204             "/opt/ctrl/etc/oper.yaml",
205             "/opt/ctrl/etc/oper-int.yaml",
206             "/opt/ctrl/etc/oper-report.yaml",
207             "/home/usr4711/oper-report.yaml"
208             ]
209              
210             Which will cause that all policy configuration will override the
211             developer defaults and user configuration override policy settings.
212              
213             =head1 AUTHOR
214              
215             Jens Rehsack, C<< >>
216              
217             =head1 ACKNOWLEDGEMENTS
218              
219             =head1 LICENSE AND COPYRIGHT
220              
221             Copyright 2018 Jens Rehsack.
222              
223             This program is free software; you can redistribute it and/or modify it
224             under the terms of either: the GNU General Public License as published
225             by the Free Software Foundation; or the Artistic License.
226              
227             See L for more information.
228              
229             =cut
230