File Coverage

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 15     15   8743 use strict;
  15         35  
  15         576  
4 15     15   73 use Data::Hopen::Base;
  15         28  
  15         106  
5              
6             our $VERSION = '0.000021';
7              
8 15     15   4972 use Data::Hopen;
  15         37  
  15         947  
9 15     15   4736 use Data::Hopen::Scope::Hash;
  15         44  
  15         827  
10 15     15   97 use Data::Hopen::Util::Data qw(forward_opts);
  15         31  
  15         923  
11 15     15   86 use Data::Hopen::Util::NameSet;
  15         26  
  15         411  
12 15     15   80 use Hash::Merge;
  15         28  
  15         776  
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 15     15   78 use parent 'Data::Hopen::G::Entity';
  15         40  
  15         106  
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         630 scope => sub { Data::Hopen::Scope::Hash->new },
59 15     15   2010 };
  15         30  
  15         157  
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 243883 my ($self, %args) = getparameters('self', [qw(; context visitor nocontext)], @_);
106 114         9252 my $context_scope = $args{context}; # which may be undef - that's OK
107 114 100 100     900 croak "Can't combine -context and -nocontext" if $args{context} && $args{nocontext};
108              
109             # Link the outer scope to our scope
110 113 100       3786 my $saver = $args{nocontext} ? undef : $self->scope->outerize($context_scope);
111              
112 113     79   993 hlog { '->', ref($self), $self->name, 'input', Dumper($self->scope->as_hashref) } 3;
  79         339  
113              
114 113         1218 my $retval = $self->_run(forward_opts(\%args, {'-'=>1}, qw[visitor]));
115              
116 105 100       817 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   856 hlog { '<-', ref $self, $self->name, 'output', Dumper($retval) } 3;
  74         372  
120              
121 104         1274 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 1689 my ($self, %args) = getparameters('self', ['*'], @_);
163 57         4069 my $outer_scope = $args{context}; # which may be undef - that's OK
164 57 100 100     431 croak "Can't combine -context and -nocontext" if $args{context} && $args{nocontext};
165              
166             # Link the outer scope to our scope
167 56 100       233 my $saver = $args{nocontext} ? undef : $self->scope->outerize($outer_scope);
168              
169             # Copy the names
170 56   100     198 my $levels = $args{levels} // 'local';
171 56         98 my @names = @{$self->scope->names(-levels=>$levels)};
  56         1627  
172 56         828 my $retval = {};
173 56         4250 $retval->{$_} = $self->scope->find($_, -levels=>$levels) foreach @names;
174              
175 56         282 return $retval;
176             } #passthrough()
177              
178             1;
179             __END__