Airlock

 view release on metacpan or  search on metacpan

docs/superpowers/plans/2026-10-02-airlock-phase-1.md  view on Meta::CPAN


=method find

    my $row = $memory->find( hash => $hash );
    my $row = $memory->find( user_code => 'BCDFGHJK' );

A copy of the matching row, or nothing.

=cut

sub update {
  my ( $self, $hash, $from_state, $changes ) = @_;
  $self->_same_process;
  my $row = $self->_rows->{$hash} or return 0;
  return 0 unless $row->{state} eq $from_state;
  $row->{$_} = $changes->{$_} for keys %$changes;
  return 1;
}

=method update

docs/superpowers/plans/2026-10-02-airlock-phase-1.md  view on Meta::CPAN

  my ( $self, $field, $value ) = @_;
  croak __PACKAGE__.'->find by '.$field.' is not supported' unless $field eq 'hash' || $field eq 'user_code';
  return unless defined $value;
  return $self->_dbh->selectrow_hashref(
    'SELECT '.join( ', ', $self->_fields ).' FROM '.$self->table.' WHERE '.$field.' = ?', undef, $value
  );
}

# The WHERE on the old state is the whole point: of two concurrent redeems
# only one statement changes a row.
sub update {
  my ( $self, $hash, $from_state, $changes ) = @_;
  my %known  = map { $_ => 1 } $self->_fields;
  my @fields = sort keys %$changes;
  croak __PACKAGE__.'->update unknown field' if grep { !$known{$_} } @fields;
  my $changed = $self->_dbh->do(
    'UPDATE '.$self->table.' SET '.join( ', ', map { $_.' = ?' } @fields ).' WHERE hash = ? AND state = ?',
    undef, @{$changes}{@fields}, $hash, $from_state
  );
  return $changed > 0 ? 1 : 0;
}

docs/superpowers/plans/2026-10-02-airlock-phase-1.md  view on Meta::CPAN

sub find {
  my ( $self, $field, $value ) = @_;
  croak __PACKAGE__.'->find by '.$field.' is not supported' unless $field eq 'hash' || $field eq 'user_code';
  return unless defined $value;
  my $row = $self->_rs->search( { $field => $value } )->single or return;
  return { $row->get_columns };
}

# One UPDATE ... WHERE hash = ? AND state = ?. A resultset update returns the
# number of rows it changed, as '0E0' when there were none.
sub update {
  my ( $self, $hash, $from_state, $changes ) = @_;
  return $self->_rs->search( { hash => $hash, state => $from_state } )->update( { %$changes } ) > 0 ? 1 : 0;
}

sub purge {
  my ( $self, $before ) = @_;
  return $self->_rs->search( { expires => { '<' => $before } } )->delete + 0;
}

sub as_subs {

examples/lib/AirlockExample/StoreDBI.pm  view on Meta::CPAN

  my ( $self, $field, $value ) = @_;
  croak __PACKAGE__.'->find by '.$field.' is not supported' unless $field eq 'hash' || $field eq 'user_code';
  return unless defined $value;
  return $self->_dbh->selectrow_hashref(
    'SELECT '.join( ', ', $self->_fields ).' FROM '.$self->table.' WHERE '.$field.' = ?', undef, $value
  );
}

# The WHERE on the old state is the whole point: of two concurrent redeems
# only one statement changes a row.
sub update {
  my ( $self, $hash, $from_state, $changes ) = @_;
  my %known  = map { $_ => 1 } $self->_fields;
  my @fields = sort keys %$changes;
  croak __PACKAGE__.'->update unknown field' if grep { !$known{$_} } @fields;
  # a reference to a number means "add it", done by the database in one statement
  my @set = map { ref $changes->{$_} eq 'SCALAR' ? $_.' = '.$_.' + ?' : $_.' = ?' } @fields;
  my $changed = $self->_dbh->do(
    'UPDATE '.$self->table.' SET '.join( ', ', @set ).' WHERE hash = ? AND state = ?',
    undef, ( map { ref eq 'SCALAR' ? $$_ : $_ } @{$changes}{@fields} ), $hash, $from_state
  );

examples/lib/AirlockExample/StoreDBIO.pm  view on Meta::CPAN

sub find {
  my ( $self, $field, $value ) = @_;
  croak __PACKAGE__.'->find by '.$field.' is not supported' unless $field eq 'hash' || $field eq 'user_code';
  return unless defined $value;
  my $row = $self->_rs->search( { $field => $value } )->single or return;
  return { $row->get_columns };
}

# One UPDATE ... WHERE hash = ? AND state = ?. A resultset update returns the
# number of rows it changed, as '0E0' when there were none.
sub update {
  my ( $self, $hash, $from_state, $changes ) = @_;
  # a reference to a number means "add it", done by the database in one statement
  my %set = map {
    my $value = $changes->{$_};
    $_ => ref $value eq 'SCALAR' ? \[ $_.' + ?', $$value ] : $value
  } keys %$changes;
  return $self->_rs->search( { hash => $hash, state => $from_state } )->update( \%set ) > 0 ? 1 : 0;
}

sub purge {

lib/Airlock/Store/Memory.pm  view on Meta::CPAN

  croak __PACKAGE__.'->find by '.$field.' is not supported'
    unless $field eq 'hash' || $field eq 'user_code';
  return unless defined $value;
  for my $row ( values %{ $self->_rows } ) {
    return { %$row } if defined $row->{$field} && $row->{$field} eq $value;
  }
  return;
}


sub update {
  my ( $self, $hash, $from_state, $changes ) = @_;
  $self->_same_process;
  my $row = $self->_rows->{$hash} or return 0;
  return 0 unless $row->{state} eq $from_state;
  for my $field ( keys %$changes ) {
    my $value = $changes->{$field};
    $row->{$field} = ref $value eq 'SCALAR' ? ( $row->{$field} // 0 ) + $$value : $value;
  }
  return 1;
}



( run in 0.600 second using v1.01-cache-2.11-cpan-02e1dfdd527 )