| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Data::Hopen::G::GraphBuilder - fluent interface for building graphs |
|
2
|
|
|
|
|
|
|
package Data::Hopen::G::GraphBuilder; |
|
3
|
1
|
|
|
1
|
|
116976
|
use Data::Hopen; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
76
|
|
|
4
|
1
|
|
|
1
|
|
6
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
24
|
|
|
5
|
1
|
|
|
1
|
|
5
|
use Data::Hopen::Base; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
6
|
|
|
6
|
1
|
|
|
1
|
|
280
|
use Exporter 'import'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
68
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
49
|
our @EXPORT; BEGIN { @EXPORT=qw(make_GraphBuilder); } |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our $VERSION = '0.000021'; |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
use Class::Tiny { |
|
13
|
1
|
|
|
|
|
7
|
name => 'ANON', # Name is optional; it's here so the |
|
14
|
|
|
|
|
|
|
# constructor won't croak if you use one. |
|
15
|
|
|
|
|
|
|
dag => undef, # The current G::DAG instance |
|
16
|
|
|
|
|
|
|
node => undef, # The last node added |
|
17
|
1
|
|
|
1
|
|
724
|
}; |
|
|
1
|
|
|
|
|
2344
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
1
|
|
|
1
|
|
1407
|
use Class::Method::Modifiers qw(install_modifier); |
|
|
1
|
|
|
|
|
2033
|
|
|
|
1
|
|
|
|
|
78
|
|
|
20
|
1
|
|
|
1
|
|
7
|
use Getargs::Mixed; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
54
|
|
|
21
|
1
|
|
|
1
|
|
6
|
use Scalar::Util qw(refaddr); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1006
|
|
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# Docs {{{1 |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 NAME |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Data::Hopen::G::GraphBuilder - fluent interface for building graphs |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
A GraphBuilder wraps a L and a current |
|
32
|
|
|
|
|
|
|
L. It permits building chains of nodes in a |
|
33
|
|
|
|
|
|
|
fluent way. For example, in an L hopen file: |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# $Build is a Data::Hopen::G::DAG created by App::hopen |
|
36
|
|
|
|
|
|
|
use language 'C'; |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
my $builder = $Build->C::compile(file => 'foo.c'); |
|
39
|
|
|
|
|
|
|
# Now $builder holds $Build (the DAG) and a node created by |
|
40
|
|
|
|
|
|
|
# C::compile(). |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head2 name |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
An optional name, in case you want to identify your Builder instances. |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=head2 dag |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
The current L instance, if any. |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=head2 node |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
The current L instance, if any. |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head1 INSTANCE FUNCTIONS |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=cut |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# }}}1 |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=head2 add |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
Adds a node to the graph. Returns the node. Note that this B |
|
65
|
|
|
|
|
|
|
change the builder's current node (L). |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=cut |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub add { |
|
70
|
0
|
|
|
0
|
1
|
|
my ($self, %args) = getparameters('self', ['node'], @_); |
|
71
|
0
|
|
|
|
|
|
$self->dag->add($args{node}); |
|
72
|
0
|
|
|
|
|
|
return $args{node}; |
|
73
|
|
|
|
|
|
|
} #add() |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=head2 default_goal |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Links the most recent node in the chain to the default goal in the DAG. |
|
78
|
|
|
|
|
|
|
If the DAG does not have a default goal, adds one called "all". |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
As a side effect, calling this function clears the builder's record of the |
|
81
|
|
|
|
|
|
|
current node and returns C. The idea is that this function |
|
82
|
|
|
|
|
|
|
will be used at the end of a chain of calls. Clearing state in this way |
|
83
|
|
|
|
|
|
|
reduces the chance of unintentionally connecting nodes. |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=cut |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
sub default_goal { |
|
88
|
0
|
0
|
|
0
|
1
|
|
my $self = shift or croak 'Need an instance'; |
|
89
|
0
|
0
|
|
|
|
|
croak "Need a node to link to the goal" unless $self->node; |
|
90
|
|
|
|
|
|
|
|
|
91
|
0
|
|
0
|
|
|
|
my $goal = $self->dag->default_goal // $self->dag->goal('all'); |
|
92
|
0
|
|
|
|
|
|
$self->dag->add($self->node); # no harm in it - DAG::add() is idempotent |
|
93
|
0
|
|
|
|
|
|
$self->dag->connect($self->node, $goal); |
|
94
|
|
|
|
|
|
|
|
|
95
|
0
|
|
|
|
|
|
$self->node(undef); # Less likely to leak state between goals. |
|
96
|
|
|
|
|
|
|
|
|
97
|
0
|
|
|
|
|
|
return undef; |
|
98
|
|
|
|
|
|
|
# Also, if this is the last thing in an App::hopen hopen file, |
|
99
|
|
|
|
|
|
|
# whatever it returns gets recorded in MY.hopen.pl. Therefore, |
|
100
|
|
|
|
|
|
|
# return $self would cause a copy of the whole graph to be dropped into |
|
101
|
|
|
|
|
|
|
# MY.hopen.pl, which would be a Bad Thing. |
|
102
|
|
|
|
|
|
|
} #default_goal() |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=head2 goal |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
Links the most recent node in the chain to the given goal in the DAG. |
|
107
|
|
|
|
|
|
|
Clears the builder's record of the current node and returns undef. |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=cut |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub goal { |
|
112
|
0
|
0
|
|
0
|
1
|
|
my $self = shift or croak 'Need an instance'; |
|
113
|
0
|
0
|
|
|
|
|
my $goal_name = shift or croak 'Need a goal name'; |
|
114
|
0
|
0
|
|
|
|
|
croak "Need a node to link to the goal" unless $self->node; |
|
115
|
|
|
|
|
|
|
|
|
116
|
0
|
|
|
|
|
|
my $goal = $self->dag->goal($goal_name); |
|
117
|
0
|
|
|
|
|
|
$self->dag->add($self->node); # no harm in it - DAG::add() is idempotent |
|
118
|
0
|
|
|
|
|
|
$self->dag->connect($self->node, $goal); |
|
119
|
|
|
|
|
|
|
|
|
120
|
0
|
|
|
|
|
|
$self->node(undef); # Less likely to leak state between goals. |
|
121
|
|
|
|
|
|
|
|
|
122
|
0
|
|
|
|
|
|
return undef; # undef: See comment in goal() |
|
123
|
|
|
|
|
|
|
} #goal() |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=head2 to |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Connect one node to another, where both are wrapped in Cs. |
|
128
|
|
|
|
|
|
|
Usage: |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
$builder_1->to($builder_2); |
|
131
|
|
|
|
|
|
|
# No $builder_1->node has an edge to $builder_2->node |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
Returns C, because chaining would be ambiguous. For example, |
|
134
|
|
|
|
|
|
|
in the snippet above, would the chain continue from C<$builder_1> or |
|
135
|
|
|
|
|
|
|
C<$builder_2>? |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
Does not change the state of either GraphBuilder. |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=cut |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub to { |
|
142
|
0
|
|
|
0
|
1
|
|
my ($self, %args) = parameters('self', [qw(dest)], @_); |
|
143
|
|
|
|
|
|
|
croak 'Destination is not a ' . __PACKAGE__ |
|
144
|
0
|
0
|
|
|
|
|
unless $args{dest}->DOES(__PACKAGE__); |
|
145
|
|
|
|
|
|
|
croak 'Cannot connect nodes from different graphs' |
|
146
|
0
|
0
|
|
|
|
|
if refaddr($self->dag) != refaddr($args{dest}->dag); |
|
147
|
|
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
|
$self->dag->connect($self->node, $args{dest}->node); |
|
149
|
0
|
|
|
|
|
|
return undef; |
|
150
|
|
|
|
|
|
|
} #to() |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=head1 STATIC FUNCTIONS |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head2 make_GraphBuilder |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Given the name of a subroutine, wrap the given subroutine for use in a |
|
157
|
|
|
|
|
|
|
GraphBuilder chain such as that shown in the L. Usage: |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub worker { |
|
160
|
|
|
|
|
|
|
my $graphbuilder = shift; |
|
161
|
|
|
|
|
|
|
... |
|
162
|
|
|
|
|
|
|
return $node; # Will automatically be linked into the chain |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
make_GraphBuilder 'worker'; |
|
166
|
|
|
|
|
|
|
# now worker can take a DAG or GraphBuilder, and the |
|
167
|
|
|
|
|
|
|
# return value will be the GraphBuilder. |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
The C subroutine is called in scalar context. |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=cut |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub _wrapper; |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub make_GraphBuilder { |
|
176
|
0
|
|
|
0
|
1
|
|
my $target = caller; |
|
177
|
0
|
0
|
|
|
|
|
my $funcname = shift or croak 'Need the name of the sub to wrap'; # yum |
|
178
|
|
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
|
install_modifier $target, 'around', $funcname, \&_wrapper; |
|
180
|
|
|
|
|
|
|
} #make_GraphBuilder() |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# The "around" modifier |
|
183
|
|
|
|
|
|
|
sub _wrapper { |
|
184
|
0
|
0
|
|
0
|
|
|
my $orig = shift or die 'Need a function to wrap'; |
|
185
|
0
|
0
|
|
|
|
|
croak "Need a parameter" unless @_; |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# Create the GraphBuilder if we don't have one already. |
|
188
|
0
|
|
|
|
|
|
my $self = shift; |
|
189
|
|
|
|
|
|
|
$self = __PACKAGE__->new(dag=>$self) |
|
190
|
0
|
0
|
|
|
|
|
unless eval { $self->DOES(__PACKAGE__) }; |
|
|
0
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
croak "Parameter must be a DAG or Builder" |
|
192
|
0
|
0
|
|
|
|
|
unless eval { $self->dag->DOES('Data::Hopen::G::DAG') }; |
|
|
0
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
|
|
194
|
0
|
|
|
|
|
|
unshift @_, $self; # Put the builder on the arg list |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# Call the worker |
|
197
|
0
|
|
|
|
|
|
my $worker_retval = &{$orig}; # @_ passed to code |
|
|
0
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# If we got a node, remember it. |
|
200
|
0
|
0
|
0
|
|
|
|
if(ref $worker_retval && eval { $worker_retval->DOES('Data::Hopen::G::Node') } ) { |
|
|
0
|
|
|
|
|
|
|
|
201
|
0
|
|
|
|
|
|
$self->dag->add($worker_retval); # Link it into the graph |
|
202
|
0
|
0
|
|
|
|
|
$self->dag->connect($self->node, $worker_retval) if $self->node; |
|
203
|
|
|
|
|
|
|
|
|
204
|
0
|
|
|
|
|
|
$self->node($worker_retval); # It's now our current node |
|
205
|
|
|
|
|
|
|
} |
|
206
|
|
|
|
|
|
|
|
|
207
|
0
|
|
|
|
|
|
return $self; |
|
208
|
|
|
|
|
|
|
}; #_wrapper() |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
1; |
|
211
|
|
|
|
|
|
|
__END__ |