File Coverage

blib/lib/Bread/Board/LazyLoader/Supersite.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package # hide from PAUSE
2             Bread::Board::LazyLoader::Supersite;
3              
4             # DEPRECATED - use Bread::Board::LazyLoader qw(load_container)
5              
6 1     1   14736 use strict;
  1         1  
  1         25  
7 1     1   3 use warnings;
  1         1  
  1         21  
8              
9             # ABSTRACT: loads the proper IOC root with your Bread::Board setup
10              
11              
12 1     1   401 use Class::Load;
  1         15365  
  1         51  
13 1     1   7 use Carp qw(croak);
  1         1  
  1         43  
14 1     1   435 use Module::Find qw(findsubmod);
  1         1307  
  1         50  
15 1     1   396 use Bread::Board::LazyLoader;
  0            
  0            
16              
17             sub _throw {
18             croak join '', __PACKAGE__, '->import: ', @_, "\n";
19             }
20              
21             sub import {
22             my $this = shift;
23              
24             # sites is a list of subroutines building the container
25             my @sites = _get_sites(@_);
26              
27             no strict 'refs';
28             *{ caller . '::' . 'root' } = sub {
29             my $this = shift;
30              
31             # there may be more than one site
32             my ( $first, @next ) = reverse @sites;
33             my $root = $first->(@_);
34             $root = $_->($root) for @next;
35             return $root;
36             };
37             }
38              
39             sub _load_module_site {
40             my ( $module) = @_;
41              
42             Class::Load::load_class($module);
43             return sub {
44             $module->root(@_);
45             };
46             }
47              
48             sub _load_file_site {
49             my ($file) = @_;
50              
51             my $loader = Bread::Board::LazyLoader->new;
52             $loader->add_file($file);
53             return sub {
54             $loader->build(@_);
55             };
56             }
57              
58             # the variable may contain more than one site (either module or file) separated by semicolon
59             sub _load_var_sites {
60             my ($content) = @_;
61              
62             my @content = split /;/, $content;
63             return
64             map { m{/} ? _load_file_site($_) : _load_module_site($_); } @content;
65             }
66              
67             sub _get_sites {
68             return @_ == 1 && ref $_[0] eq 'ARRAY'
69              
70             # array ref
71             ? map { _get_sites($_) } @{ shift() }
72              
73             # hashref
74             : ( @_ == 1 && ref $_[0] eq 'HASH' ) ? _get_sites( %{ shift() } )
75             : _get_site(@_);
76             }
77              
78             sub _get_site {
79             my %args = @_;
80              
81             my $env_var = $args{env_var};
82             if (my $site = $env_var && $ENV{$env_var}){
83             return _load_var_sites( $site );
84             }
85              
86             if (my $file = delete $args{file}){
87             return _load_file_site( $file );
88             }
89              
90             my $site = delete $args{site}
91             or _throw "No site argument supplied";
92              
93             if (! ref $site ){
94             return _load_module_site($site);
95             }
96             elsif ( ref $site eq 'HASH' ){
97             # we select the only site which fulfills the condition
98             my $prefix = $site->{prefix};
99             my $filter = $site->{filter};
100              
101             $prefix && $filter or _throw "Invalid site argument $site";
102              
103             return _load_only_module($prefix, $filter);
104             }
105             else {
106             _throw "Invalid site argument $site";
107             }
108              
109             }
110              
111             # there must be just one site module $prefix:: conforming the selection
112             # for example Manggis::Site::<name> module where name starts with lowercase (cz, sk)
113             sub _load_only_module {
114             my ($prefix, $filter) = @_;
115              
116             my $select =
117             ref $filter eq 'Regexp' ? sub { $_ =~ $filter }
118             : ref $filter eq 'CODE' ? $filter
119             : _throw "Inapropriate filter $filter";
120             my @sites = grep {
121             my ($name) = /^${prefix}::(.*)/;
122             local $_ = $name;
123             $select->($name);
124             } findsubmod($prefix);
125              
126             _throw "No site module $prefix\:\:* conforming your selection found\n" if !@sites;
127             _throw "More than one site module $prefix\:\:* found (" . join( ', ', @sites ) . ')'
128             if @sites > 1;
129             return _load_module_site($sites[0]); # "found as the only proper $prefix\:\:* installed module");
130             }
131              
132             1;
133              
134             # vim: expandtab:shiftwidth=4:tabstop=4:softtabstop=0:textwidth=78:
135              
136             __END__
137              
138             =pod
139              
140             =encoding UTF-8
141              
142             =head1 NAME
143              
144             Bread::Board::LazyLoader::Supersite - loads the proper IOC root with your Bread::Board setup
145              
146             =head1 VERSION
147              
148             version 0.13
149              
150             =head1 SYNOPSIS
151              
152             package MyApp::IOC;
153             use strict;
154             use warnings;
155              
156             use Bread::Board::LazyLoader::SuperSite
157             env_var => 'MY_APP_SITE',
158             site => {
159             prefix => 'My::Site',
160             filter => qr{^[a-z]}
161             };
162              
163             # in scripts, psgi apps
164              
165             use MyApp::IOC;
166              
167             my $root = MyApp::Root->root;
168             $root->fetch('Scripts/MyApp-Web')->get()->run();
169              
170             =head1 DESCRIPTION
171              
172             This module creates a single proxy subroutine for IOC root,
173             which may be loaded from different modules (for example
174             national specific).
175              
176             Better with example:
177              
178             We have two instances of our application, czech and slovak, with IOC roots
179             implemented in C<< MyApp::Site::cz->root >> and C<< MyApp::Site::sk->root >>.
180             In each instance of our app only one this modules is installed.
181              
182             Most of the scripts (tests, psgi files, ...) referencing the IOC root
183             are not nationally specific, so we prefer them to use some common name.
184              
185             Having defined "dispatcher" ioc module like this:
186              
187             package MyApp::IOC;
188             use strict;
189              
190             use Bread::Board::LazyLoader::SuperSite
191             site => {
192             prefix => 'MyApp::Site',
193             filter => qr{^[a-z]},
194             };
195              
196             1;
197              
198             We can use C<< MyApp::IOC->root >> uniformly to get our IOC root of the application,
199             which returns either C<< MyApp::Site::cz->root >> or C<< MyApp::Site::sk->root >> depending
200             on site.
201              
202             Import looks through all C<< MyApp::Site::* >> installed modules and tries to find one
203             with next part starting with lowercase letter (lowercase, so that our base IOC module C<< MyApp::Site::Core >> is not found).
204             There must be exactly one such module or C<< use Bread::Board::LazyLoader::Supersite >> fails.
205              
206             =head2 import parameters
207              
208             use Bread::Board::LazyLoader::Supersite %params;
209              
210             =over 4
211              
212             =item env_var=NAME
213              
214             The content of environment variable (if set) is used as site module (the one with root method).
215             There may be more than one modules separated by semicolon inside env var.
216              
217             With
218              
219             package MyApp::IOC;
220             use strict;
221             use warnings;
222              
223             use Bread::Board::LazyLoader::SuperSite env_var => 'MY_APP_SITE';
224              
225             and
226              
227             MY_APP_SITE='MyApp::Site::Sandbox;MyApp::Site::cz'
228              
229             then
230              
231             MyApp::IOC->root
232              
233             returns
234              
235             MyApp::Site::Sandbox->root( MyApp::Site::cz->root )
236              
237             Environment variable may even contain paths:
238              
239             MY_APP_SITE="$HOME/app/sandbox.ioc;MyApp::Site::cz"
240              
241             With C<< $HOME/app/sandbox.ioc >>
242              
243             use Bread::Board;
244             sub {
245             my $c = shift;
246              
247             # $c here is MyApp::Site::cz->root
248              
249             container $c => as {
250             service dbh => some_mocked_dbh();
251             };
252             };
253              
254             If C<env_var> option is used (and the appropriate variable set), it has priority over C<site> option.
255              
256             =item site
257              
258             Used either like C<< site => $module >> or C<< site => { prefix => $module_prefix, filter => $name_filter_re } >>.
259              
260             Dispatches the C<< root >> to appropriate module. There may be just one.
261              
262             =back
263              
264             =head1 AUTHOR
265              
266             Roman Daniel <roman@daniel.cz>
267              
268             =head1 COPYRIGHT AND LICENSE
269              
270             This software is copyright (c) 2016 by Roman Daniel.
271              
272             This is free software; you can redistribute it and/or modify it under
273             the same terms as the Perl 5 programming language system itself.
274              
275             =cut