File Coverage

blib/lib/Getopt/Chain/Declare.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Getopt::Chain::Declare;
2              
3 5     5   430612 use strict;
  5         14  
  5         172  
4 5     5   27 use warnings;
  5         10  
  5         211  
5              
6             =head1 NAME
7              
8             Getopt::Chain::Declare - Syntactic sugar for command-line processing like svn and git
9              
10             =head1 SYNPOSIS
11              
12             package My::Command;
13              
14             use Getopt::Chain::Declare;
15              
16             start [qw/ verbose|v /]; # These are "global"
17             # my-command --verbose ...
18              
19             # my-command ? initialize ... --> my-command help initialize ...
20             rewrite qr/^\?(.*)/ => sub { "help ".($1||'') };
21              
22             # NOTE: Rewriting applies to the command sequence, NOT options
23              
24             # my-command about ... --> my-command help about
25             rewrite [ ['about', 'copying'] ] => sub { "help $1" };
26              
27             # my-command initialize --dir=...
28             on initialize => [qw/ dir|d=s /], sub {
29             my $context = shift;
30              
31             my $dir = $context->option( 'dir' )
32              
33             # Do initialize stuff with $dir
34             };
35              
36             # my-command help
37             on help => undef, sub {
38             my $context = shift;
39              
40             # Do help stuff ...
41             # First argument is undef because help
42             # doesn't take any options
43            
44             };
45              
46             under help => sub {
47              
48             # my-command help create
49             # my-command help initialize
50             on [ [ qw/create initialize/ ] ] => undef, sub {
51             my $context = shift;
52              
53             # Do help for create/initialize
54             # Both: "help create" and "help initialize" go here
55             };
56              
57             # my-command help about
58             on 'about' => undef, sub {
59             my $context = shift;
60              
61             # Help for about...
62             };
63              
64             # my-command help copying
65             on 'copying' => undef, sub {
66             my $context = shift;
67              
68             # Help for copying...
69             };
70              
71             # my-command help ...
72             on qr/^(\S+)$/ => undef, sub {
73             my $context = shift;
74             my $topic = $1;
75              
76             # Catch-all for anything not fitting into the above...
77            
78             warn "I don't know about \"$topic\"\n"
79             };
80             };
81              
82             # ... elsewhere ...
83              
84             My::Command->new->run( [ @arguments ] )
85             My::Command->new->run # Just run with @ARGV
86              
87             =head1 DESCRIPTION
88              
89             For more information, see L<Getopt::Chain>
90              
91             =cut
92              
93 5     5   22805 use Moose();
  0            
  0            
94             use Moose::Exporter;
95              
96             Moose::Exporter->setup_import_methods(
97             with_caller => [qw/ context start on rewrite under /],
98             also => [qw/ Moose /],
99             );
100              
101             sub init_meta {
102             shift;
103             return Moose->init_meta( @_, base_class => 'Getopt::Chain', metaclass => 'Getopt::Chain::Declare::Meta::Class' );
104             }
105              
106             sub context {
107             my $caller = shift;
108             $caller->meta->context_from( @_ );
109             }
110              
111             sub start {
112             my $caller = shift;
113             $caller->meta->start( @_ );
114             }
115              
116             sub on {
117             my $caller = shift;
118             $caller->meta->on( @_ );
119             }
120              
121             sub under {
122             my $caller = shift;
123             $caller->meta->under( @_ );
124             }
125              
126             sub rewrite {
127             my $caller = shift;
128             $caller->meta->rewrite( @_ );
129             }
130              
131             package Getopt::Chain::Declare::Recorder;
132              
133             use Moose;
134             use MooseX::AttributeHelpers;
135              
136             has _replay_list => qw/metaclass Collection::Array is ro isa ArrayRef/, default => sub { [] }, provides => {qw/
137             push record
138             elements replay_list
139             /};
140              
141             our $BUILDER;
142              
143             sub replay {
144             my $self = shift;
145             my $builder = shift;
146              
147             {
148             local $BUILDER = $builder;
149              
150             for my $replay ($self->replay_list) {
151             if ( ref $replay eq 'ARRAY' ) {
152             my @replay = @$replay;
153             my $method = shift @replay;
154             $builder->$method( @replay );
155             }
156             else {
157             # It's a "child" package
158             $replay->replay( $builder );
159             }
160             }
161             }
162             }
163              
164             sub do_or_record {
165             my $self = shift;
166             my $method = shift;
167              
168             if ($BUILDER) {
169             $BUILDER->$method( @_ );
170             }
171             else {
172             $self->record( [ $method => @_ ] );
173             }
174             }
175              
176             package Getopt::Chain::Declare::Meta::Class;
177              
178             use Moose;
179             use MooseX::AttributeHelpers;
180              
181             extends qw/Moose::Meta::Class/;
182              
183             has recorder => qw/is ro lazy_build 1/, handles => [qw/ replay do_or_record /];
184             sub _build_recorder {
185             return Getopt::Chain::Declare::Recorder->new;
186             }
187              
188             around new_object => sub {
189             my $around = shift;
190             my $meta = shift;
191              
192             my @arguments = map { $_ => $meta->$_ } grep { defined $meta->$_ } qw/ context_from /;
193              
194             my $self = $around->( $meta, @arguments, 1 == @_ && ref $_[0] eq 'HASH' ? %{ $_[0] } : @_ );
195             my $builder = $self->builder;
196              
197             $meta->recorder->replay( $builder );
198              
199             return $self;
200             };
201              
202             has context_from => qw/is rw/;
203             sub start { shift->do_or_record( start => @_ ) }
204             sub on { shift->do_or_record( on => @_ ) }
205             sub under { shift->do_or_record( under => @_ ) }
206             sub rewrite { shift->do_or_record( rewrite => @_ ) }
207              
208             package Getopt::Chain::Declare::Branch;
209              
210             use Moose();
211             use Moose::Exporter;
212              
213             my %IMPORT_ARGUMENTS; # Yes, an ugly hack
214              
215             {
216             my ($import, $unimport) = Moose::Exporter->build_import_methods(
217             with_caller => [qw/ start on rewrite under /],
218             also => [qw/ Moose /],
219             );
220              
221             sub import {
222             my $class = shift; # 'under' or 'redispatch' or ...
223             my $caller = caller();
224              
225             $IMPORT_ARGUMENTS{$caller} = [ @_ ];
226              
227             goto &$import;
228             }
229              
230             no warnings 'once';
231             *unimport = $unimport;
232             }
233              
234             sub init_meta {
235             shift;
236             return Moose->init_meta( @_, base_class => 'Getopt::Chain', metaclass => 'Getopt::Chain::Declare::Branch::Meta::Class' );
237             }
238              
239             sub start {
240             my $caller = shift;
241             $caller->meta->start( @_ );
242             }
243              
244             sub on {
245             my $caller = shift;
246             $caller->meta->on( @_ );
247             }
248              
249             sub under {
250             my $caller = shift;
251             $caller->meta->under( @_ );
252             }
253              
254             sub rewrite {
255             my $caller = shift;
256             $caller->meta->rewrite( @_ );
257             }
258              
259             1;
260              
261             package Getopt::Chain::Declare::Branch::Meta::Class;
262              
263             use Moose;
264             use Getopt::Chain::Carp;
265              
266             extends qw/Moose::Meta::Class/;
267              
268             has getopt_chain_parent_class => qw/is ro lazy_build 1/;
269             sub _build_getopt_chain_parent_class {
270             my $self = shift;
271             my ($class) = $self->linearized_isa;
272             my @class = split m/::/, $class;
273             pop @class;
274             join '::', @class;
275             }
276              
277             sub getopt_chain_parent_meta {
278             return shift->getopt_chain_parent_class->meta;
279             }
280              
281             sub import_arguments {
282             my $self = shift;
283              
284             my ($class) = $self->linearized_isa;
285            
286             croak "No import arguments for \"$class\"" unless my $arguments = $IMPORT_ARGUMENTS{$class};
287              
288             return @$arguments;
289             }
290              
291             has recorder => qw/is ro lazy_build 1/, handles => [qw/ do_or_record /];
292             sub _build_recorder {
293             require Getopt::Chain::Declare;
294             return Getopt::Chain::Declare::Recorder->new;
295             }
296              
297             sub replay {
298             my $self = shift;
299             my $parent_builder = shift;
300              
301             my @arguments = $self->import_arguments;
302             my $match = $arguments[0];
303              
304             # We *could* redispatch here, but ...
305             $parent_builder->under( $match => sub {
306             $self->recorder->replay( $parent_builder );
307             } );
308             }
309              
310             has registered => qw/is rw default 0/;
311             before do_or_record => sub {
312             my $self = shift;
313             return if $self->registered;
314             $self->getopt_chain_parent_meta->recorder->record( $self ); # "Register" ourself
315             $self->registered( 1 );
316             };
317              
318             sub start { shift->do_or_record( start => @_ ) }
319             sub on { shift->do_or_record( on => @_ ) }
320             sub under { shift->do_or_record( under => @_ ) }
321             sub rewrite { shift->do_or_record( rewrite => @_ ) }
322              
323             1;