File Coverage

blib/lib/Net/CLI/Interact/ActionSet.pm
Criterion Covered Total %
statement 12 83 14.4
branch 0 30 0.0
condition 0 15 0.0
subroutine 4 13 30.7
pod 4 5 80.0
total 20 146 13.7


line stmt bran cond sub pod time code
1             package Net::CLI::Interact::ActionSet;
2             $Net::CLI::Interact::ActionSet::VERSION = '2.400002';
3 1     1   21 use Moo;
  1         2  
  1         6  
4 1     1   349 use Sub::Quote;
  1         3  
  1         83  
5 1     1   17 use MooX::Types::MooseLike::Base qw(InstanceOf ArrayRef CodeRef RegexpRef);
  1         3  
  1         69  
6 1     1   444 use Net::CLI::Interact::Action;
  1         3  
  1         1047  
7              
8             with 'Net::CLI::Interact::Role::Iterator';
9              
10             has default_continuation => (
11             is => 'rw',
12             isa => InstanceOf['Net::CLI::Interact::ActionSet'],
13             predicate => 1,
14             );
15              
16             has current_match => (
17             is => 'rw',
18             isa => ArrayRef[RegexpRef],
19             predicate => 1,
20             coerce => quote_sub(q{ (ref qr// eq ref $_[0]) ? [$_[0]] : $_[0] }),
21             );
22              
23             sub BUILDARGS {
24 0     0 0   my ($class, @rest) = @_;
25              
26             # accept single hash ref or naked hash
27 0 0         my $params = (ref {} eq ref $rest[0] ? $rest[0] : {@rest});
28              
29 0 0 0       if (exists $params->{actions} and ref $params->{actions} eq ref []) {
30 0           foreach my $a (@{$params->{actions}}) {
  0            
31 0 0         if (ref $a eq 'Net::CLI::Interact::ActionSet') {
32 0           push @{$params->{_sequence}}, @{ $a->_sequence };
  0            
  0            
33 0           next;
34             }
35              
36 0 0         if (ref $a eq 'Net::CLI::Interact::Action') {
37 0           push @{$params->{_sequence}}, $a;
  0            
38 0           next;
39             }
40              
41 0 0         if (ref $a eq ref {}) {
42 0           push @{$params->{_sequence}},
  0            
43             Net::CLI::Interact::Action->new($a);
44 0           next;
45             }
46              
47 0           die "don't know what to do with a: '$a'\n";
48             }
49 0           delete $params->{actions};
50             }
51              
52 0           return $params;
53             }
54              
55             sub clone {
56 0     0 1   my $self = shift;
57             return Net::CLI::Interact::ActionSet->new({
58 0 0         actions => [ map { $_->clone } @{ $self->_sequence } ],
  0 0          
  0 0          
59             ($self->_has_callbacks ? (_callbacks => $self->_callbacks) : ()),
60             ($self->has_default_continuation ? (default_continuation => $self->default_continuation) : ()),
61             ($self->has_current_match ? (current_match => $self->current_match) : ()),
62             });
63             }
64              
65             # store params to the set, used when send is passed via sprintf
66             sub apply_params {
67 0     0 1   my ($self, @params) = @_;
68              
69 0           $self->reset;
70 0           while ($self->has_next) {
71 0           my $next = $self->next;
72 0           $next->params([splice @params, 0, $next->num_params]);
73             }
74             }
75              
76             has _callbacks => (
77             is => 'rw',
78             isa => ArrayRef[CodeRef],
79             default => sub { [] },
80             predicate => 1,
81             );
82              
83             sub register_callback {
84 0     0 1   my $self = shift;
85 0           $self->_callbacks([ @{$self->_callbacks}, shift ]);
  0            
86             }
87              
88             sub execute {
89 0     0 1   my $self = shift;
90              
91 0           $self->_pad_send_with_match;
92 0           $self->_forward_continuation_to_match;
93 0           $self->_do_exec;
94 0           $self->_marshall_responses;
95             }
96              
97             sub _do_exec {
98 0     0     my $self = shift;
99              
100 0           $self->reset;
101 0           while ($self->has_next) {
102 0           $_->($self->next) for @{$self->_callbacks};
  0            
103             }
104             }
105              
106             # pad out the Actions with match Actions if needed between send pairs.
107             sub _pad_send_with_match {
108 0     0     my $self = shift;
109 0           my $match = Net::CLI::Interact::Action->new({
110             type => 'match', value => $self->current_match,
111             });
112              
113 0           $self->reset;
114 0           while ($self->has_next) {
115 0           my $this = $self->next;
116 0 0         my $next = $self->peek or last; # careful...
117 0 0 0       next unless $this->type eq 'send' and $next->type eq 'send';
118              
119 0           $self->insert_at($self->idx + 1, $match->clone);
120             }
121              
122             # always finish on a match
123 0 0         if ($self->last->type ne 'match') {
124 0           $self->insert_at($self->count, $match->clone);
125             }
126             }
127              
128             # carry-forward a continuation beacause it's the match which really does the
129             # heavy lifting.
130             sub _forward_continuation_to_match {
131 0     0     my $self = shift;
132              
133 0           $self->reset;
134 0           while ($self->has_next) {
135 0           my $this = $self->next;
136 0 0         my $next = $self->peek or last; # careful...
137 0   0       my $cont = ($this->continuation || $self->default_continuation);
138 0 0 0       next unless $this->type eq 'send'
      0        
139             and $next->type eq 'match'
140             and defined $cont;
141              
142 0           $next->continuation($cont);
143             }
144             }
145              
146             # marshall the responses so as to move data from match to send
147             sub _marshall_responses {
148 0     0     my $self = shift;
149              
150 0           $self->reset;
151 0           while ($self->has_next) {
152 0           my $send = $self->next;
153 0 0         my $match = $self->peek or last; # careful...
154 0 0         next unless $match->type eq 'match';
155              
156             # remove echoed command from the beginning
157 0           my $cmd = quotemeta( sprintf $send->value, @{ $send->params } );
  0            
158 0           (my $output = $match->response_stash) =~ s/^${cmd}[\t ]*(?:\r\n|\r|\n)?//s;
159 0           $send->response($output);
160             }
161             }
162              
163             1;
164              
165             =pod
166              
167             =for Pod::Coverage BUILDARGS has_current_match has_default_continuation
168              
169             =head1 NAME
170              
171             Net::CLI::Interact::ActionSet - Conversation of Send and Match Actions
172              
173             =head1 DESCRIPTION
174              
175             This class is used internally by L<Net::CLI::Interact> and it's unlikely that
176             an end-user will need to make use of ActionSet objects directly. The interface
177             is documented here as a matter of record.
178              
179             An ActionSet comprises a sequence (usefully, two or more) of
180             L<Actions|Net::CLI::Interact::Action> which describe a conversation with a
181             connected network device. Actions will alternate between type C<send> and
182             C<match>, perhaps not in their original
183             L<Phrasebook|Net::CLI::Interact::Phrasebook> definition, but certainly by the
184             time they are used.
185              
186             If the first Action is of type C<send> then the ActionSet is a normal sequence
187             of "send a command" then "match a response", perhaps repeated. If the first
188             Action is of type C<match> then the ActionSet represents a C<continuation>,
189             which is the method of dealing with paged output.
190              
191             =head1 INTERFACE
192              
193             =head2 default_continuation
194              
195             An ActionSet (C<match> then C<send>) which will be available for use on all
196             commands sent from this ActionSet. An alternative to explicitly describing the
197             Continuation sequence within the Phrasebook.
198              
199             =head2 current_match
200              
201             A stash for the current Prompt (regular expression reference) which
202             L<Net::CLI::Interact> expects to see after each command. This is passed into
203             the constructor and is used when padding Match Actions into the ActionSet (see
204             C<execute>, below).
205              
206             =head2 clone
207              
208             Returns a new ActionSet which is a shallow clone of the existing one. All the
209             reference based slots will share data, but you can add (for example) a
210             C<current_match> without affecting the original ActionSet. Used when preparing
211             to execute an ActionSet which has been retrieved from the
212             L<Phrasebook|Net::CLI::Interact::Phrasebook>.
213              
214             =head2 apply_params
215              
216             Accepts a list of parameters which will be used when C<sprintf> is called on
217             each Send Action in the set. You must supply sufficient parameters as a list
218             for I<all> Send Actions in the set, and they will be popped off and stashed
219             with the Action(s) according to how many are required.
220              
221             =head2 register_callback
222              
223             Allows the Transport to be registered
224             such that when the ActionSet is executed, commands are sent to the registered
225             callback subroutine. May be called more than once, and on execution each of
226             the callbacks will be run, in turn and in order.
227              
228             =head2 execute
229              
230             The business end of this class, where the sequence of Actions is prepared for
231             execution and then control passed to the Transport. This process is split into
232             a number of phases:
233              
234             =over 4
235              
236             =item Pad C<send> with C<match>
237              
238             The Phrasebook allows missing out of the Match statements between Send
239             statements, when they are expected to be the same as the C<current_match>.
240             This phase inserts Match statements to restore a complete ActionSet
241             definition.
242              
243             =item Forward C<continuation> to C<match>
244              
245             In the Phrasebook a user defines a Continuation (C<match>, then C<send>)
246             following a Send statement (because it deals with the response to the sent
247             command). However they are actually used by the Match, as it's the Match which
248             captures output.
249              
250             This phase copies Continuation ActionSets from Send statements to following
251             Match statements in the ActionSet. It also performs a similar action using the
252             C<default_continuation> if one is set and there's no existing Continuation
253             configured.
254              
255             =item Callback(s)
256              
257             Here the registered callbacks are executed (i.e. data is sent to the
258             Transport).
259              
260             =item Marshall Responses
261              
262             Finally, responses which are stashed in the Match Actions are copied back to
263             the Send actions, as more logically they are responses to commands sent. The
264             ActionSet is now ready for access to retrieve the C<last_response> from the
265             device.
266              
267             =back
268              
269             =head1 COMPOSITION
270              
271             See the following for further interface details:
272              
273             =over 4
274              
275             =item *
276              
277             L<Net::CLI::Interact::Role::Iterator>
278              
279             =back
280              
281             =cut
282