File Coverage

lib/Module/Build/Pluggable.pm
Criterion Covered Total %
statement 76 82 92.6
branch 9 12 75.0
condition n/a
subroutine 17 18 94.4
pod 0 3 0.0
total 102 115 88.7


line stmt bran cond sub pod time code
1             package Module::Build::Pluggable;
2 7     7   35582 use strict;
  7         56  
  7         524  
3 7     7   66 use warnings;
  7         39  
  7         544  
4 7     7   497 use 5.008001;
  7         41  
  7         647  
5             our $VERSION = '0.10';
6 7     7   13639 use Module::Build;
  7         1046078  
  7         694  
7              
8             our $SUBCLASS;
9             our $OPTIONS;
10             our @REQUIRED_PLUGINS;
11              
12 7     7   7386 use Data::OptList;
  7         60685  
  7         93  
13 7     7   252 use Data::Dumper; # as serializer.
  7         19  
  7         364  
14 7     7   9629 use Module::Load ();
  7         7648  
  7         156  
15 7     7   4633 use Module::Build::Pluggable::Util;
  7         20  
  7         8074  
16              
17             sub import {
18 13     13   1625     my $class = shift;
19 13         40     my $pkg = caller(0);
20 13 100       556     return unless @_;
21                 
22 6         36     my $optlist = Data::OptList::mkopt(\@_);
23 6         212        @REQUIRED_PLUGINS = map { _mkpluginname($_) } grep !/^\+/, map { $_->[0] } @$optlist;
  6         20  
  7         35  
24 6         18        $optlist = [map { [ _mkpluginname($_->[0]), $_->[1] ] } @$optlist];
  7         21  
25              
26 6         19     _author_requires(map { $_->[0] } @$optlist);
  7         23  
27              
28 6         25     push @$OPTIONS, @$optlist;
29              
30 6         28     $SUBCLASS = Module::Build->subclass(
31                     code => _mksrc(),
32                 );
33             }
34              
35             sub _author_requires {
36 6     6   13     my @devmods = @_;
37 6         12     my @not_available;
38 6         15     for my $mod (@devmods) {
39             ## no critic.
40 7 50       461         eval qq{require $mod} or push @not_available, $mod;
41             # need to diag $@ if an error message is not "Can't locate..."?
42                 }
43 6 50       194     if (@not_available) {
44 0         0         print qq{# The following modules are not available.\n};
45 0         0         print qq{# `$^X $0 | cpanm` will install them:\n};
46 0         0         print $_, "\n" for @not_available;
47 0         0         print "\n";
48 0         0         exit -1;
49                 }
50             }
51              
52             sub _mksrc {
53 6     6   12     my $data = do {
54 6         15         local $Data::Dumper::Terse = 1;
55 6         16         local $Data::Dumper::Indent = 0;
56 6         39         Data::Dumper::Dumper($OPTIONS);
57                 };
58 6         886     return sprintf(q{
59             use Module::Build::Pluggable;
60             sub resume {
61             my $class = shift;
62             my $self = $class->SUPER::resume(@_);
63             Module::Build::Pluggable->call_triggers_all('build', $self, %s);
64             $self;
65             }
66             }, $data);
67             }
68              
69             sub _mkpluginname {
70 13     13   17     my $module = shift;
71 13 100       53     $module = $module =~ s/^\+// ? $module : "Module::Build::Pluggable::$module";
72 13         41     $module;
73             }
74              
75             sub new {
76 6     6 0 31     my $class = shift;
77 6         67     my %args = @_;
78 6         93     $class->call_triggers_all('prepare', undef, $OPTIONS, \%args);
79 6         91     my $builder = $SUBCLASS->new(%args);
80 6         523918     my $self = bless { builder => $builder }, $class;
81 6         121     $self->_init();
82 6         74     $self->call_triggers_all('configure', $builder, $OPTIONS);
83 6         908     return $self;
84             }
85              
86             sub _init {
87 6     6   39     my $self = shift;
88             # setup (build|configure) requires
89 6         84     for my $module (@REQUIRED_PLUGINS) {
90 6         36         for my $type (qw/configure_requires build_requires/) {
91 12         513             Module::Build::Pluggable::Util->add_prereqs(
92                             $self->{builder},
93                             $type,
94                             $module, $module->VERSION,
95                         );
96                     }
97                 }
98             }
99              
100             sub call_triggers_all {
101 13     13 0 172079     my ($class, $type, $builder, $options, $args) =@_;
102 13         92     for my $opt (@$options) {
103 15         51         my ($module, $opts) = @$opt;
104 15         83         $class->call_trigger($type, $builder, $module, $opts, $args);
105                 }
106             }
107              
108             sub call_trigger {
109 15     15 0 41     my ($class, $type, $builder, $module, $opts, $args) =@_;
110              
111 15         132     Module::Load::load($module);
112 15 50       1764     my $plugin = $module->new(builder => $builder, %{ $opts || +{} });
  15         332  
113 15         57     my $method = "HOOK_$type";
114 15 100       273     if ($plugin->can($method)) {
115 8         75         $plugin->$method($args);
116                 }
117             }
118              
119 0     0   0 sub DESTROY { }
120              
121             our $AUTOLOAD;
122             sub AUTOLOAD {
123 8     8   59     my $self = shift;
124 8         241     $AUTOLOAD =~ s/.*:://;
125 8         161     return $self->{builder}->$AUTOLOAD(@_);
126             }
127              
128             1;
129             __END__
130            
131             =encoding utf8
132            
133             =for stopwords pluggability
134            
135             =head1 NAME
136            
137             Module::Build::Pluggable - Module::Build meets plugins
138            
139             =head1 SYNOPSIS
140            
141             use Module::Build::Pluggable (
142             'Repository',
143             'ReadmeMarkdownFromPod',
144             'PPPort',
145             );
146            
147             my $builder = Module::Build::Pluggable->new(
148             ... # normal M::B args
149             );
150             $builder->create_build_script();
151            
152             =head1 DESCRIPTION
153            
154             Module::Build::Pluggable adds pluggability for Module::Build.
155            
156             =head1 HOW CAN I WRITE MY OWN PLUGIN?
157            
158             Module::Build::Pluggable call B<HOOK_prepare> on preparing arguments for C<< Module::Build->new >>, B<HOOK_configure> on configuration step, and B<HOOK_build> on build step.
159            
160             That's all.
161            
162             And if you want a help, you can use L<Module::Build::Pluggable::Base> as base class.
163            
164             =head1 AUTHOR
165            
166             Tokuhiro Matsuno E<lt>tokuhirom AAJKLFJEF@ GMAIL COME<gt>
167            
168             =head1 SEE ALSO
169            
170             This module built on L<Module::Build>.
171            
172             =head1 LICENSE
173            
174             Copyright (C) Tokuhiro Matsuno
175            
176             This library is free software; you can redistribute it and/or modify
177             it under the same terms as Perl itself.
178            
179             =cut
180