Skip to content

Commit

Permalink
Code and test updates
Browse files Browse the repository at this point in the history
Code changes (in package order):
- Data::Hopen::hnew(): provide more accurate error messages.
- Data::Hopen::loadfrom(): no longer require a list of stems.
- Data::Hopen::G::DAG: pass the node inputs to visit_{node,goal}; change
  some exception text
- Data::Hopen::G::Entity: custom stringification for easier debugging
- Added Data::Hopen::G::NoOp

- !!! >>> Data::Hopen::Scope: when merging predecessor-node outputs
  into successor-node inputs, DO NOT CLONE.  This way blessed references
  can be successfully passed between nodes. <<< !!!

- Data::Hopen::Util::NameSet: Tweaked code so 100% coverage
  is achievable.
- Added Data::Hopen::Visitor
- Updated documentation

Test changes:
- Added t/lib/PackagesInThisFile, which is a crazy way of doing
  subtests, each in its own package.

WIP: a way to merge hashes, cloning non-blessed references but not
     references.
  • Loading branch information
cxw42 authored and Chris White committed May 26, 2019
1 parent 60a9822 commit 4875f4a
Show file tree
Hide file tree
Showing 46 changed files with 877 additions and 144 deletions.
3 changes: 3 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
Revision history for Data-Hopen

0.000013 2019-05-26
Functional changes throughout

0.000012 2019-02-28
No changes - stable release of 0.000011

Expand Down
19 changes: 12 additions & 7 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ lib/Data/Hopen/G/Goal.pm
lib/Data/Hopen/G/GraphBuilder.pm
lib/Data/Hopen/G/Link.pm
lib/Data/Hopen/G/Node.pm
lib/Data/Hopen/G/NoOp.pm
lib/Data/Hopen/G/Op.pm
lib/Data/Hopen/G/OutputOp.pm
lib/Data/Hopen/G/Runnable.pm
Expand All @@ -22,6 +23,7 @@ lib/Data/Hopen/Scope/Overrides.pm
lib/Data/Hopen/Util/Data.pm
lib/Data/Hopen/Util/Filename.pm
lib/Data/Hopen/Util/NameSet.pm
lib/Data/Hopen/Visitor.pm
LICENSE.md
Makefile.PL
MANIFEST This list of files
Expand All @@ -34,24 +36,27 @@ t/001-entity.t
t/002-link.t
t/003-node.t
t/004-goal.t
t/005-op.t
t/006-collect-op.t
t/007-hnew.t
t/008-nameset.t
t/009-hopen-constants.t
t/010-scope-hash.t
t/011-scope-env.t
t/012-scope-nested.t
t/020-dag.t
t/021-dag-single-goal.t
t/022-dag-visitor.t
t/023-dag-warnings.t
t/024-dag-merge-node-inputs.t
t/030-ordered_pred.t
t/100-op.t
t/101-collect-op.t
t/120-dag.t
t/121-dag-single-goal.t
t/122-dag-visitor.t
t/123-dag-warnings.t
t/124-dag-merge-node-inputs.t
t/998-sig-die.t
t/999-extras-for-coverage.t
t/dir200/inner.hopen.pl
t/dir200/inner/.hopen.pl
t/dir200/inner/z.hopen.pl
t/lib/HopenTest.pm
t/lib/PackagesInThisFile.pm
xt/boilerplate.t
xt/manifest.t
xt/pod-coverage.t
Expand Down
4 changes: 4 additions & 0 deletions MANIFEST.SKIP
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# WIP files
^lib.Data.Hopen.Util.MergeWithoutCloneBlessed\.pm
^t.005-merge-without-clone-blessed\.NOTYET

# Miscellaneous
^\.editorconfig
^\.[^\\\/]*\.yml
Expand Down
9 changes: 8 additions & 1 deletion Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,18 @@ my %opts = (
'Pod::Text' => '0', # pod2text
},
TEST_REQUIRES => {
'Capture::Tiny' => '0',
'Carp' => '0',
'Exporter' => '0',
'Import::Into' => '0',
'List::AutoNumbered' => '0.000006',
'Quote::Code' => '1.0102',
'rlib' => '0',
'Scalar::Util' => '0',
'Test::Deep' => '0.084', # for superhashof
'Sub::Identify' => '0.14',
'Test::Deep' => '0.098', # for superhashof, Test::Deep::NoTest
## 'Test::Directory' => '0.02', # for subdirs
'Test::Fatal' => '0.014',
'Test::More' => '0',
# Test::TempDir::Tiny? If so, remove Test::Directory dependency?
'Test::Warn' => '0.35', # for metadata
Expand All @@ -188,6 +193,7 @@ my %opts = (
#'Algorithm::Dependency' => '1.106',
# - Probably don't need this; we can use Graph::topological_sort().

#'base' => '0', # as used by Hash::Merge
'Carp' => '0',

'Class::Method::Modifiers' => '2.10', # most recent code change
Expand All @@ -200,6 +206,7 @@ my %opts = (


## 'Class::XPath' => '1.4',
#'Clone::Choose' => '0.008', # as used by Hash::Merge
'Config' => '0',
#'Cwd' => '0',

Expand Down
59 changes: 46 additions & 13 deletions lib/Data/Hopen.pm
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use Data::Hopen::Util::NameSet;
use Getargs::Mixed;
use Storable ();

our $VERSION = '0.000013'; # TRIAL
our $VERSION = '0.000013';

# Docs {{{1

Expand Down Expand Up @@ -97,11 +97,27 @@ is the same as
Data::Hopen::G::DAG->new( name => 'foo' );
If the provided name does not include a double-colon, it is first tried after
C<Data::Hopen::G::>. It is then tried in C<Data::Hopen::> and as a
complete package name. The first one that succeeds is used.
The first parameter (C<$class>) is an abbreviated package name. It is tried
as the following, in order. The first one that succeeds is used.
The first parameter must be a part of a class name, and the second parameter
=over
=item 1.
C<Data::Hopen::G::$class>. This is tried only if C<$class>
does not include a double-colon.
=item 2.
C<Data::Hopen::$class>
=item 3.
C<$class>
=back
The second parameter
must be the name of the new instance. All other parameters are passed
unchanged to the relevant constructor.
Expand All @@ -112,16 +128,22 @@ sub hnew {
my @stems = ('Data::Hopen::G::', 'Data::Hopen::', '');
shift @stems if $class =~ /::/;

my $found_class = false;

foreach my $stem (@stems) {
my $instance = eval {
eval "require $stem$class";
"$stem$class"->new('name', @_)
# put 'name' in front of the name parameter.
};
eval "require $stem$class";
next if $@;
$found_class = "$stem$class";
my $instance = "$found_class"->new('name', @_);
# put 'name' in front of the name parameter.
return $instance if $instance;
}

croak "Could not find class for $class";
if($found_class) {
croak "Could not create instance for $found_class";
} else {
croak "Could not find class for $class";
}
} #hnew()

=head2 loadfrom
Expand All @@ -131,13 +153,14 @@ sub hnew {
my $fullname = loadfrom($name[, @stems]);
Returns the full name of the loaded package, or falsy on failure.
If C<@stems> is omitted, no stem is used, i.e., C<$name> is tried as-is.
=cut

sub loadfrom {
my $class = shift or croak 'Need a class';

foreach my $stem (@_) {
foreach my $stem (@_, '') {
eval "require $stem$class";
return "$stem$class" unless $@;
}
Expand All @@ -157,10 +180,15 @@ Each line is prefixed with C<'# '> for the benefit of test runs.
The list is in C<{}> so that it won't be evaluated if logging is turned off.
It is a full block, so you can run arbitrary code to decide what to log.
If the block returns an empty list, hlog will not produce any output.
However, if the block returns at least one element, hlog will produce at
least a C<'# '>.
The message will be output only if L</$VERBOSE> is at least the given minimum
verbosity level (1 by default).
If C<< $VERBOSE > 2 >>, the filename and line from which hlog was called
will also be printed.
=cut

sub hlog (&;$) {
Expand All @@ -172,7 +200,12 @@ sub hlog (&;$) {

chomp $log[$#log] if $log[$#log];
# TODO add an option to number the lines of the output
say STDERR (join(' ', @log)) =~ s/^/# /gmr;
my $msg = (join(' ', @log)) =~ s/^/# /gmr;
if($VERBOSE>2) {
my ($package, $filename, $line) = caller;
$msg .= " (at $filename:$line)";
}
say STDERR $msg;
} #hlog()

=head2 isMYH
Expand Down
6 changes: 4 additions & 2 deletions lib/Data/Hopen/Base.pm
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package Data::Hopen::Base;
use parent 'Exporter';
use Import::Into;

our $VERSION = '0.000013'; # TRIAL
our $VERSION = '0.000013';

# Pragmas
use 5.014;
Expand Down Expand Up @@ -36,7 +36,9 @@ our @EXPORT = qw(true false);

#DEBUG
BEGIN {
$SIG{'__DIE__'} = sub { Carp::confess(@_) } unless $SIG{'__DIE__'};
unless($SIG{'__DIE__'}) {
$SIG{'__DIE__'} = sub { Carp::confess(@_) };
}
#$Exporter::Verbose=1;
}

Expand Down
5 changes: 5 additions & 0 deletions lib/Data/Hopen/G.pod
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,9 @@ edges is expressed in a DAG. A DAG has zero or more goals
(L<Data::Hopen::G::Goal>) that represent named activities expressed
in the DAG.

=head1 VISITORS

L<Data::Hopen::G::DAG/run> can take a C<visitor> parameter. The visitor
should be an instance of a concrete subclass of L<Data::Hopen::Visitor>.

=cut
4 changes: 2 additions & 2 deletions lib/Data/Hopen/G/CollectOp.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
package Data::Hopen::G::CollectOp;
use Data::Hopen::Base;

our $VERSION = '0.000013'; # TRIAL
our $VERSION = '0.000013';

use parent 'Data::Hopen::G::Op';
use Class::Tiny {
Expand All @@ -17,7 +17,7 @@ use Storable ();

=head1 NAME
Data::Hopen::G::CollectOp - a no-op
Data::Hopen::G::CollectOp - a passthrough operation
=head1 SYNOPSIS
Expand Down
19 changes: 11 additions & 8 deletions lib/Data/Hopen/G/DAG.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
package Data::Hopen::G::DAG;
use Data::Hopen::Base;

our $VERSION = '0.000013'; # TRIAL
our $VERSION = '0.000013';

use parent 'Data::Hopen::G::Op';
use Class::Tiny {
Expand Down Expand Up @@ -34,7 +34,9 @@ use Data::Hopen::G::Node;
use Data::Hopen::G::CollectOp;
use Data::Hopen::Util::Data qw(forward_opts);
use Data::Hopen::OrderedPredecessorGraph;
use Getargs::Mixed; # parameters, which doesn't permit undef
use Hash::Merge;
use Scalar::Util qw(refaddr);
use Storable ();

# Class data {{{1
Expand Down Expand Up @@ -157,9 +159,9 @@ sub _run {
# Remove _final from the order for now - I don't yet know what it means
# to traverse _final.
warn "Last item in order isn't _final! This might indicate a bug in hopen, or that some graph edges are missing."
unless $QUIET or $order[$#order] == $self->_final;
unless $QUIET or refaddr $order[$#order] == refaddr $self->_final;

@order = grep { $_ != $self->_final } @order;
@order = grep { refaddr $_ != refaddr $self->_final } @order;

# --- Check for non-connected ops, and goals with no inputs ---

Expand Down Expand Up @@ -228,6 +230,7 @@ sub _run {
hlog { ' -- no links' };
$node_inputs->merge(%{$pred->outputs});
# TODO specify which set these are.
# Use the predecessor's identity as the set.
next;
}

Expand All @@ -237,6 +240,7 @@ sub _run {
my $link_inputs = Data::Hopen::Scope::Hash->new->put(%{$hrPredOutputs});
# All links get the same outer scope --- they are parallel,
# not in series.
# TODO use the predecessor's identity as the set.
$link_inputs->outer($self->scope);
# The links run at the same scope level as the node.
$link_inputs->local(true);
Expand Down Expand Up @@ -265,14 +269,14 @@ sub _run {

# Give the visitor a chance, and stash the results if necessary.
if(eval { $node->DOES('Data::Hopen::G::Goal') }) {
$args{visitor}->visit_goal($node) if $args{visitor};
$args{visitor}->visit_goal($node, $node_inputs) if $args{visitor};

# Save the result if there is one. Don't save {}.
# use $node->outputs, not $step_output, since the visitor may
# alter $node->outputs.
$retval->{$node->name} = $node->outputs if keys %{$node->outputs};
} else {
$args{visitor}->visit_node($node) if $args{visitor};
$args{visitor}->visit_node($node, $node_inputs) if $args{visitor};
}

} #foreach node in topo-sort order
Expand Down Expand Up @@ -384,10 +388,9 @@ Returns the node, for the sake of chaining.
=cut

sub add {
my $self = shift or croak 'Need an instance';
my $node = shift or croak 'Need a node';
my ($self, undef, $node) = parameters('self', ['node'], @_);
return if $self->_graph->has_vertex($node);
hlog { __PACKAGE__, 'adding', Dumper($node) } 2;
hlog { __PACKAGE__, $self->name, 'adding', Dumper($node) } 2;

$self->_graph->add_vertex($node);
#$self->_node_by_name->{$node->name} = $node if $node->name;
Expand Down
Loading

0 comments on commit 4875f4a

Please sign in to comment.