File Coverage

blib/lib/CLI/Osprey.pm
Criterion Covered Total %
statement 82 87 94.2
branch 12 18 66.6
condition 1 3 33.3
subroutine 15 15 100.0
pod n/a
total 110 123 89.4


line stmt bran cond sub pod time code
1             package CLI::Osprey;
2 4     4   356755 use strict;
  4         17  
  4         152  
3 4     4   19 use warnings;
  4         7  
  4         167  
4              
5             # ABSTRACT: MooX::Options + MooX::Cmd + Sanity
6             our $VERSION = '0.06'; # VERSION
7             our $AUTHORITY = 'cpan:ARODLAND'; # AUTHORITY
8              
9 4     4   18 use Carp 'croak';
  4         6  
  4         200  
10 4     4   25 use Module::Runtime 'use_module';
  4         6  
  4         25  
11 4     4   214 use Scalar::Util qw(reftype);
  4         8  
  4         192  
12              
13 4     4   1767 use Moo::Role qw(); # only want class methods, not setting up a role
  4         31685  
  4         108  
14              
15 4     4   2095 use CLI::Osprey::InlineSubcommand ();
  4         15  
  4         2284  
16              
17             my @OPTIONS_ATTRIBUTES = qw(
18             option option_name format short repeatable negatable spacer_before spacer_after doc long_doc format_doc order hidden
19             );
20              
21             sub import {
22 8     8   3051 my (undef, @import_options) = @_;
23 8         22 my $target = caller;
24              
25 8         21 for my $method (qw(with around has)) {
26 24 50       162 next if $target->can($method);
27 0         0 croak "Can't find the method '$method' in package '$target'. CLI::Osprey requires a Role::Tiny-compatible object system like Moo or Moose.";
28             }
29              
30 8         23 my $with = $target->can('with');
31 8         25 my $around = $target->can('around');
32 8         21 my $has = $target->can('has');
33              
34 8 50       65 if ( ! Moo::Role->is_role( $target ) ) { # not in a role
35 8 50       1558 eval "package $target;\n" . q{
36             sub _osprey_options {
37             my $class = shift;
38             return $class->maybe::next::method(@_);
39             }
40              
41             sub _osprey_config {
42             my $class = shift;
43             return $class->maybe::next::method(@_);
44             }
45              
46             sub _osprey_subcommands {
47             my $class = shift;
48             return $class->maybe::next::method(@_);
49             }
50             1;
51             } || croak($@);
52             }
53              
54 8         40 my $osprey_config = {
55             preserve_argv => 1,
56             abbreviate => 1,
57             prefer_commandline => 1,
58             @import_options,
59             };
60              
61             $around->(_osprey_config => sub {
62 26     26   569 my ($orig, $self) = (shift, shift);
63 26         386 return $self->$orig(@_), %$osprey_config;
64 8         56 });
65              
66 8         10246 my $options_data = { };
67 8         18 my $subcommands = { };
68              
69             my $apply_modifiers = sub {
70 14 100   14   115 return if $target->can('new_with_options');
71 8         31 $with->('CLI::Osprey::Role');
72             $around->(_osprey_options => sub {
73 16         358 my ($orig, $self) = (shift, shift);
74 16         231 return $self->$orig(@_), %$options_data;
75 8         9586 });
76             $around->(_osprey_subcommands => sub {
77 26         544 my ($orig, $self) = (shift, shift);
78 26         398 return $self->$orig(@_), %$subcommands;
79 8         2687 });
80 8         36 };
81              
82 8         17 my $added_order = 0;
83              
84             my $option = sub {
85 1     1   11 my ($name, %attributes) = @_;
86              
87 1         8 $has->($name => _non_option_attributes(%attributes));
88 1         296 $options_data->{$name} = _option_attributes($name, %attributes);
89 1         3 $options_data->{$name}{added_order} = ++$added_order;
90 1         7 $apply_modifiers->();
91 8         52 };
92              
93             my $subcommand = sub {
94 5     5   29 my ($name, $subobject) = @_;
95              
96 5 50 33     28 if (ref($subobject) && reftype($subobject) eq 'CODE') {
97 0         0 my @args = @_[2 .. $#_];
98 0         0 $subobject = CLI::Osprey::InlineSubcommand->new(
99             name => $name,
100             method => $subobject,
101             @args,
102             );
103             }
104             else {
105 5 100       23 use_module($subobject) unless $osprey_config->{on_demand};
106             }
107              
108 5         54 $subcommands->{$name} = $subobject;
109 5         16 $apply_modifiers->();
110 8         40 };
111              
112 8 50       29 if (my $info = $Role::Tiny::INFO{$target}) {
113 0         0 $info->{not_methods}{$option} = $option;
114 0         0 $info->{not_methods}{$subcommand} = $subcommand;
115             }
116              
117             {
118 4     4   36 no strict 'refs';
  4         7  
  4         1219  
  8         13  
119 8         13 *{"${target}::option"} = $option;
  8         36  
120 8         13 *{"${target}::subcommand"} = $subcommand;
  8         31  
121             }
122              
123 8         25 $apply_modifiers->();
124              
125 8         2621 return;
126             }
127              
128             sub _non_option_attributes {
129 1     1   4 my (%attributes) = @_;
130 1         2 my %filter_out;
131 1         12 @filter_out{@OPTIONS_ATTRIBUTES} = ();
132             return map {
133 2         12 $_ => $attributes{$_}
134             } grep {
135 1         4 !exists $filter_out{$_}
  4         11  
136             } keys %attributes;
137             }
138              
139             sub _option_attributes {
140 1     1   4 my ($name, %attributes) = @_;
141              
142 1 50       6 unless (defined $attributes{option}) {
143 1         4 ($attributes{option} = $name) =~ tr/_/-/;
144             }
145 1         4 my $ret = {};
146 1         4 for (@OPTIONS_ATTRIBUTES) {
147 13 100       28 $ret->{$_} = $attributes{$_} if exists $attributes{$_};
148             }
149 1         5 return $ret;
150             }
151              
152             1;
153              
154             __END__