move actions to a separate state attribute
Matt S Trout [Fri, 14 Feb 2014 10:24:49 +0000 (10:24 +0000)]
lib/DX/Lib/FS.pm
lib/DX/Op/HasAction.pm [new file with mode: 0644]
lib/DX/Op/ModifyAction.pm
lib/DX/Op/ProposeAction.pm
lib/DX/Result.pm
lib/DX/Role/Action.pm
lib/DX/RuleSet.pm
lib/DX/State.pm

index be78481..00b5420 100644 (file)
@@ -9,8 +9,6 @@ use DX::SetOver;
 use Moo;
 
 our @RULES = (
-  [ has_action => [ qw(Thing Action) ],
-    [ prop => 'Thing', \'required_action', 'Action' ] ],
   [ path_status => [ qw(PS) ],
     [ member_of => 'PS', \'path_status' ] ],
   [ path => [ qw(PS P) ],
diff --git a/lib/DX/Op/HasAction.pm b/lib/DX/Op/HasAction.pm
new file mode 100644 (file)
index 0000000..7fee15a
--- /dev/null
@@ -0,0 +1,28 @@
+package DX::Op::HasAction;
+
+use Safe::Isa;
+use Moo;
+
+has arg_spec => (is => 'ro', required => 1);
+
+with 'DX::Role::Op';
+
+sub run {
+  my ($self, $state) = @_;
+  my @arg_spec = @{$self->arg_spec};
+  ($state, my %vars) = $self->_expand_args(
+    $state, Thing => $arg_spec[0], Action => $arg_spec[1]
+  );
+  die "Thing must be bound" unless $vars{Thing}->is_bound;
+  my $thing = $state->resolve_value($vars{Thing});
+  if ($thing->$_does('DX::Role::Fact') and $thing->has_required_action) {
+    return $state->bind_value(
+      $vars{Action}->id,
+      $state->actions->{$thing->required_action}
+    )->add_dependencies($vars{Action}->id => $vars{Thing}->id)
+     ->then($self->next);
+  }
+  return $state->backtrack;
+}
+
+1;
index 57696b4..a4975b4 100644 (file)
@@ -1,5 +1,6 @@
 package DX::Op::ModifyAction;
 
+use Safe::Isa;
 use DX::ObservationRequired;
 use Moo;
 
@@ -18,8 +19,10 @@ sub run {
   my ($self, $state) = @_;
   ($state, my %args) = $self->_expand_args($state, %{$self->_arg_map});
   my ($subject, @vars) = @args{sort keys %args};
-  die "Can't alter action on $subject" unless
-    my $orig_action = $state->resolve_value($subject)->required_action;
+  my $subject_fact = $state->resolve_value($subject);
+  die "Subject not a fact" unless $subject_fact->$_does('DX::Role::Fact');
+  die "Subject has no action" unless $subject_fact->has_required_action;
+  my $orig_action = $state->actions->{$subject_fact->required_action};
   my @deps = $state->action_dependencies(
     @{$orig_action->dependencies},
     map $_->id, @vars
@@ -31,9 +34,12 @@ sub run {
   my $action = $self->builder->(@builder_args)
                     ->but(dependencies => \@deps);
   my ($fact_type, $value) = $action->expected_effect;
-  my $final_value = $value->but(required_action => $action);
+  my $final_value = $value->but(required_action => $action->id);
   my $fact_set = $state->facts->{$fact_type}->with_value($final_value);
-  $state->but(facts => { %{$state->facts}, $fact_type => $fact_set })
+  $state->but(
+            facts => { %{$state->facts}, $fact_type => $fact_set },
+            actions => { %{$state->actions}, $action->id => $action },
+          )
         ->then($self->next);
 }
 
index b1aa7f3..e63eb40 100644 (file)
@@ -21,11 +21,12 @@ sub run {
   my @deps = $state->action_dependencies(map $_->id, @vars);
   my $action = $self->builder->(map $state->resolve_value($_), @vars)
                     ->but(dependencies => \@deps);
+  my ($rec_state, $action_id) = $state->record_action($action);
   my ($fact_type, $value) = $action->expected_effect;
-  my $final_value = $value->but(required_action => $action);
+  my $final_value = $value->but(required_action => $action_id);
   my $fact_set = $state->facts->{$fact_type}->with_value($final_value);
-  $state->but(facts => { %{$state->facts}, $fact_type => $fact_set })
-        ->then($self->next);
+  $rec_state->but(facts => { %{$state->facts}, $fact_type => $fact_set })
+            ->then($self->next);
 }
 
 1;
index ddfb626..d409b55 100644 (file)
@@ -12,12 +12,9 @@ sub var_names {
 sub actions {
   my ($self) = @_;
   my $state = $self->_state;
-  my $by_id = $self->_state->by_id;
-  return map $_->required_action,
-           grep $_->has_required_action,
-             grep $_->$_does('DX::Role::Fact'),
-               map $state->resolve_value($_),
-                 values %$by_id;
+  my $actions = $state->actions;
+  my @act = @{$actions}{sort keys %$actions};
+  return @act; # separate array to get correct scalar context return
 }
 
 sub independent_actions {
index 113dd44..3b7367a 100644 (file)
@@ -2,6 +2,8 @@ package DX::Role::Action;
 
 use Moo::Role;
 
+has id => (is => 'ro');
+
 has dependencies => (is => 'ro', default => sub { [] });
 
 has was_run => (is => 'rw');
index baa9586..1559d49 100644 (file)
@@ -16,6 +16,7 @@ use DX::Op::Materialize;
 use DX::Op::Prop;
 use DX::Op::Exists;
 use DX::Op::Predicate;
+use DX::Op::HasAction;
 use List::Util qw(reduce);
 
 has rules => (is => 'ro', default => sub { {} });
@@ -136,4 +137,9 @@ sub _expand_op_exists {
   );
 }
 
+sub _expand_op_has_action {
+  my ($self, @args) = @_;
+  DX::Op::HasAction->new(arg_spec => \@args);
+}
+
 1;
index 2f0e2b0..95f49f6 100644 (file)
@@ -24,6 +24,8 @@ has facts => (is => 'ro');
 
 has dependencies => (is => 'ro', default => sub { {} });
 
+has actions => (is => 'ro', default => sub { {} });
+
 sub scope_var {
   my ($self, $name) = @_;
   $self->by_id->{$self->scope->{$name}};
@@ -61,6 +63,18 @@ sub expand_vars {
   ), %vars;
 }
 
+sub record_action {
+  my ($self, $action) = @_;
+  my %id_gen = %{$self->id_gen};
+  my ($type) = (ref($action) =~ /([^:]+)$/);
+  my $id = join('_', $type, ++($id_gen{$type}||='000'));
+  my $recorded = $action->but(id => $id);
+  $self->but(
+    id_gen => \%id_gen,
+    actions => { %{$self->actions}, $id => $recorded }
+  ), $id;
+}
+
 sub assign_vars {
   my ($self, %vars) = @_;
   my ($state, %expanded) = $self->expand_vars(%vars);
@@ -188,11 +202,11 @@ sub action_dependencies {
   while (my $id = shift @queue) {
     $seen{$id}++;
     my $value = $self->resolve_value($by_id->{$id});
-    push @found, $id if $value->$_does('DX::Role::Fact')
-                        and $value->has_required_action;
+    push @found, $value if $value->$_does('DX::Role::Fact')
+                           and $value->has_required_action;
     push @queue, grep !$seen{$_}, keys %{$deps->{$id}};
   }
-  return @found;
+  return map $_->required_action, @found;
 }
 
 sub copy_vars {