File Coverage

blib/lib/Data/Hopen/G/Runnable.pm
Criterion Covered Total %
statement 49 52 96.1
branch 10 10 100.0
condition 8 8 100.0
subroutine 13 14 100.0
pod 2 2 100.0
total 82 86 97.6


line stmt bran cond sub pod time code
1             # Data::Hopen::G::Runnable - parent class for anything runnable in a hopen graph
2             package Data::Hopen::G::Runnable;
3 14     14   7175 use strict;
  14         31  
  14         423  
4 14     14   73 use Data::Hopen::Base;
  14         26  
  14         86  
5              
6             our $VERSION = '0.000019';
7              
8 14     14   3693 use Data::Hopen;
  14         31  
  14         755  
9 14     14   3509 use Data::Hopen::Scope::Hash;
  14         33  
  14         638  
10 14     14   107 use Data::Hopen::Util::Data qw(forward_opts);
  14         29  
  14         673  
11 14     14   86 use Data::Hopen::Util::NameSet;
  14         27  
  14         400  
12 14     14   77 use Hash::Merge;
  14         57  
  14         616  
13              
14             # Docs {{{1
15              
16             =head1 NAME
17              
18             Data::Hopen::G::Runnable - parent class for runnable things in a hopen graph
19              
20             =head1 SYNOPSIS
21              
22             Anything with L inherits from this. TODO should this be a role?
23              
24             =head1 ATTRIBUTES
25              
26             =head2 need
27              
28             (B)
29             Inputs this Runnable requires.
30             A L, with the restriction that C may not
31             contain regexes. ("Sorry, I can't run unless you give me every variable
32             in the world that starts with Q." I don't think so!)
33             Or maybe later an arrayref? TODO.
34              
35             =head2 scope
36              
37             If defined, a L that will have the final say on the
38             data used by L. This is the basis of the fine-grained override
39             mechanism in hopen.
40              
41             =head2 want
42              
43             (B)
44             Inputs this Runnable accepts but does not require.
45             A L, which may include regexes.
46             Or maybe later an arrayref? TODO.
47              
48             =cut
49              
50             # }}}1
51              
52 14     14   84 use parent 'Data::Hopen::G::Entity';
  14         29  
  14         99  
53             use Class::Tiny {
54             # NOTE: want and need are not currently used.
55 0         0 want => sub { Data::Hopen::Util::NameSet->new },
56 0         0 need => sub { Data::Hopen::Util::NameSet->new },
57              
58 58         605 scope => sub { Data::Hopen::Scope::Hash->new },
59 14     14   1461 };
  14         30  
  14         158  
60              
61             =head1 FUNCTIONS
62              
63             =head2 run
64              
65             Run the operation, whatever that means. Returns a new hashref.
66             Usage:
67              
68             my $hrOutputs = $op->run([options])
69              
70             Options are:
71              
72             =over
73              
74             =item -context
75              
76             A L or subclass including the inputs the caller wants to
77             pass to the Runnable. The L of the Runnable itself may override
78             values in the C.
79              
80             =item -visitor
81              
82             If given, an instance that supports C and C calls.
83             A L instance invokes those calls after processing each
84             goal or other node, respectively. They are invoked I the goal or
85             node has run. They are, however, given access to the L
86             that the node used for its inputs, in the C<$node_inputs> parameter. Example:
87              
88             $visitor->visit_goal($goal, $node_inputs);
89              
90             The return value from C or C is ignored.
91              
92             =item -nocontext
93              
94             If C<< -nocontext=>1 >> is specified, don't link a context scope into
95             this one. May not be specified together with C<-context>.
96              
97             =back
98              
99             See the source for this function, which contains as an example of setting the
100             scope.
101              
102             =cut
103              
104             sub run {
105 114     114 1 28029 my ($self, %args) = getparameters('self', [qw(; context visitor nocontext)], @_);
106 114         7967 my $context_scope = $args{context}; # which may be undef - that's OK
107 114 100 100     899 croak "Can't combine -context and -nocontext" if $args{context} && $args{nocontext};
108              
109             # Link the outer scope to our scope
110 113 100       2791 my $saver = $args{nocontext} ? undef : $self->scope->outerize($context_scope);
111              
112 113     79   773 hlog { '->', ref($self), $self->name, 'input', Dumper($self->scope->as_hashref) } 3;
  79         370  
113              
114 113         1133 my $retval = $self->_run(forward_opts(\%args, {'-'=>1}, qw[visitor]));
115              
116 105 100       758 die "$self\->_run() did not return a hashref" unless ref $retval eq 'HASH';
117             # Prevent errors about `non-hashref 1` or `invalid key`.
118              
119 104     74   698 hlog { '<-', ref $self, $self->name, 'output', Dumper($retval) } 3;
  74         302  
120              
121 104         868 return $retval;
122             } #run()
123              
124             =head2 _run
125              
126             The internal method that implements L. Must be implemented by
127             subclasses. When C<_run> is called, C<< $self->scope >> has been hooked
128             to the context scope, if any.
129              
130             The only parameter is C<-visitor>, which is always passed by name
131             (C<< -visitor=>$v >>). C<_run> is always called in scalar context,
132             and B return a new hashref.
133              
134             I recommend starting your C<_run> function with:
135              
136             my ($self, %args) = getparameters('self', [qw(; visitor)], @_);
137              
138             and working from there.
139              
140             =cut
141              
142             sub _run {
143             # uncoverable subroutine
144 0     0   0 die('Unimplemented'); # uncoverable statement
145             }
146              
147             =head2 passthrough
148              
149             Returns a new hashref of this Runnable's local values, as defined
150             by L. Usage:
151              
152             my $hashref = $runnable->passthrough([-context => $outer_scope]);
153             # To use $outer_scope as the context
154             my $hashref = $runnable->passthrough(-nocontext => 1);
155             # To ignore the context
156              
157             Other valid options include L<-levels|Data::Hopen::Scope/$levels>.
158              
159             =cut
160              
161             sub passthrough {
162 57     57 1 1538 my ($self, %args) = getparameters('self', ['*'], @_);
163 57         3487 my $outer_scope = $args{context}; # which may be undef - that's OK
164 57 100 100     281 croak "Can't combine -context and -nocontext" if $args{context} && $args{nocontext};
165              
166             # Link the outer scope to our scope
167 56 100       183 my $saver = $args{nocontext} ? undef : $self->scope->outerize($outer_scope);
168              
169             # Copy the names
170 56   100     195 my $levels = $args{levels} // 'local';
171 56         105 my @names = @{$self->scope->names(-levels=>$levels)};
  56         1160  
172 56         786 my $retval = {};
173 56         3728 $retval->{$_} = $self->scope->find($_, -levels=>$levels) foreach @names;
174              
175 56         215 return $retval;
176             } #passthrough()
177              
178             1;
179             __END__