File Coverage

blib/lib/Dist/Zilla/Plugin/ModuleBuildDatabase.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Dist::Zilla::Plugin::ModuleBuildDatabase;
2              
3 1     1   1811 use Moose;
  0            
  0            
4             use v5.10;
5             use File::chdir;
6             use Path::Class::Dir;
7             use File::Copy qw( copy );
8              
9             extends 'Dist::Zilla::Plugin::ModuleBuild';
10              
11             # ABSTRACT: build a Build.PL that uses Module::Build::Database
12             our $VERSION = '0.05'; # VERSION
13              
14              
15             has '+mb_class' => ( default => 'Module::Build::Database' );
16              
17             has 'mbd_database_type' => (
18             isa => 'Str',
19             is => 'rw',
20             default => 'SQLite',
21             );
22              
23             has 'mbd_extra_options' => (
24             isa => 'HashRef',
25             is => 'rw',
26             default => sub { { } },
27             );
28              
29             has '_notified' => (
30             isa => 'Int',
31             is => 'rw',
32             default => 0,
33             );
34              
35             around module_build_args => sub {
36             my $orig = shift;
37             my $self = shift;
38            
39             my %args = %{ $self->$orig(@_) };
40            
41             $args{database_type} = $self->mbd_database_type;
42            
43             while(my($k,$v) = each %{ $self->mbd_extra_options })
44             {
45             $args{$k} = $v;
46             }
47            
48             \%args;
49             };
50              
51             around BUILDARGS => sub {
52             my $orig = shift;
53             my $class = shift;
54             my $args = ref $_[0] eq 'HASH' ? (\%{$_[0]}) : ({@_});
55            
56             my $option_root = {};
57            
58             foreach my $key (keys %$args)
59             {
60             next unless $key =~ /^mbd_(database_(options|extensions)\..+)$/;
61             my @key = split /\./, $1;
62             my $value = delete $args->{$key};
63             my $opt = $option_root;
64             $opt = $opt->{shift @key} //= {} while @key > 1;
65             $opt->{$key[0]} = $value;
66             }
67            
68             $args->{mbd_extra_options} = $option_root;
69            
70             $class->$orig($args);
71             };
72              
73             sub mbd_build
74             {
75             my($self, $opt, $args) = @_;
76            
77             my $build_root = $opt->in
78             ? Path::Class::Dir->new($opt->in)
79             : $self->zilla->root->subdir('.build', 'mbd');
80            
81             if(-d $build_root)
82             {
83             $self->log("using existing build: $build_root");
84             $self->log("(run dzil clean to start from scratch)");
85             }
86             else
87             {
88             $self->log("mkdir -p $build_root");
89             $build_root->mkpath;
90             $self->log("building in $build_root");
91             $self->zilla->build_in($build_root);
92             $self->_run_in($build_root, [$^X, 'Build.PL']);
93             }
94             $self->_run_in($build_root, ['./Build', @$args]);
95             $self->_recurse($self->zilla->root->subdir('db'), $build_root->subdir('db'));
96             }
97              
98             sub _run_in
99             {
100             my($self, $dir, $cmd) = @_;
101            
102             local $CWD = $dir;
103             $self->log("% @$cmd");
104            
105             return $self->_run_in_mswin32($dir, $cmd) if $^O eq 'MSWin32';
106            
107             require AnyEvent;
108             require AnyEvent::Open3::Simple;
109            
110             my $done = AnyEvent->condvar;
111            
112             my $ipc = AnyEvent::Open3::Simple->new(
113             on_stdout => sub {
114             my($proc, $line) = @_;
115             $self->log("out: $line");
116             },
117             on_stderr => sub {
118             my($proc, $line) = @_;
119             $self->log("err: $line");
120             },
121             on_error => sub {
122             my($error) = @_;
123             $self->log("error starting process: $error");
124             $done->send(1);
125             },
126             on_exit => sub {
127             my($proc, $exit, $sig) = @_;
128             $self->log("exit: $exit") if $exit;
129             $self->log("signal: $sig") if $sig;
130             $done->send($exit || $sig);
131             },
132             );
133             $ipc->run(@$cmd);
134             $done->recv and $self->log_fatal("command failed");
135             }
136              
137             sub _run_in_mswin32
138             {
139             my($self, $dir, $cmd) = @_;
140             system(@$cmd) and $self->log_fatal("command failed");
141             }
142              
143             sub _recurse
144             {
145             my($self, $dist_root, $build_root) = @_;
146              
147             state $first = 1;
148            
149             foreach my $child ($build_root->children(no_hidden => 1))
150             {
151             my $name = $child->basename;
152             if($child->is_dir)
153             {
154             my $build_dir = $child;
155             my $dist_dir = $dist_root->subdir($name);
156             unless(-d $dist_dir)
157             {
158             $self->_notify;
159             $self->log("create $dist_dir/");
160             $dist_dir->mkpath;
161             }
162             $self->_recurse($dist_dir, $build_dir);
163             }
164             else
165             {
166             my $new = $child;
167             my $new_content = $new->slurp;
168             my $old = $dist_root->file($name);
169             if($new_content ne (eval { $old->slurp } // ''))
170             {
171             $self->_notify;
172             $self->log("copy $new => $old");
173             $old->openw->print($new_content);
174             }
175             }
176             }
177             }
178              
179             sub _notify
180             {
181             my($self) = @_;
182             return if $self->_notified;
183             $self->_notified(1);
184             $self->log("importing back:");
185             }
186              
187             1;
188              
189             __END__
190             =pod
191              
192             =head1 NAME
193              
194             Dist::Zilla::Plugin::ModuleBuildDatabase - build a Build.PL that uses Module::Build::Database
195              
196             =head1 VERSION
197              
198             version 0.05
199              
200             =head1 SYNOPSIS
201              
202             [ModuleBuildDatabase]
203             mbd_database_type = PostgreSQL
204             mbd_database_options.name = my_database_name
205             mbd_database_options.schema = my_schema_name
206             mbd_database_extensions.postgis.schema = public
207              
208             =head1 DESCRIPTION
209              
210             This plugin is a very light layer over L<Dist::Zilla::Plugin::ModuleBuild>
211             to support some of the eccentricities of L<Module::Build::Database>. It
212             allows you to specify the C<database_type>, C<database_options> and
213             C<database_extensions> in your C<dist.ini>. It also set the mb_class
214             to L<Module::Build::Database>.
215              
216             =head1 ATTRIBUTES
217              
218             This plugin understands all the attributes supported by L<Dist::Zilla::Plugin::ModuleBuild>,
219             with the minor caveat that the default for C<mb_class> is L<Module::Build::Database>
220             instead of L<Module::Build>. In addition it understands these options:
221              
222             =head2 mbd_database_type
223              
224             The type of database. Any value supported by L<Module::Build::Database>, which is, as
225             of this writing either C<PostgreSQL> or C<SQLite>. The details and ramifications of
226             using specific options are described in the L<Module::Build::Database::PostgreSQL>
227             and L<Module::Build::Database::SQLite> documentation.
228              
229             =head2 mbd_database_options
230              
231             Database options. This is a hash reference. This must be specified using the dot notation as in the example above.
232              
233             =head2 mbd_database_extensions
234              
235             Database extensions. This is a hash reference. This must be specified using the dot notation as in the example above.
236              
237             =head1 SEE ALSO
238              
239             L<Dist::Zilla::Plugin::ModuleBuild>,
240             L<Module::Build::Database>,
241             L<Module::Build::Database::PostgreSQL>,
242             L<Module::Build::Database::SQLite>
243              
244             =head1 AUTHOR
245              
246             Graham Ollis <plicease@cpan.org>
247              
248             =head1 COPYRIGHT AND LICENSE
249              
250             This software is copyright (c) 2013 by NASA GSFC.
251              
252             This is free software; you can redistribute it and/or modify it under
253             the same terms as the Perl 5 programming language system itself.
254              
255             =cut
256