Group
Extension

DBIx-Class-HashAccessor/lib/DBIx/Class/HashAccessor.pm

package DBIx::Class::HashAccessor;
our $AUTHORITY = 'cpan:GETTY';
# ABSTRACT: Helper functions to install accessors for serialized hash columns
$DBIx::Class::HashAccessor::VERSION = '0.001';
use strict;
use warnings;
use Package::Stash;

use parent 'DBIx::Class::Row';

sub add_hash_accessor {
  my ( $class, $accessor, $hash ) = @_;
  die 'require accessor and hash name' unless defined $accessor and defined $hash;
  die 'accessor can\'t be named like hash' if $accessor eq $hash;
  my $st = Package::Stash->new($class);

  $st->add_symbol('&'.$accessor,sub {
    my ( $self, @args ) = @_;
    die((ref $self).' does not support '.$hash) unless $self->can($hash);
    my %h = %{$self->$hash || {}};
    if (scalar @args == 1) {
      return $h{$args[0]};
    } elsif (scalar @args == 2) {
      $h{$args[0]} = $args[1];
      $self->$hash({ %h });
      return $args[1];
    } else {
      die $accessor.' function must get 1 or 2 args';
    }
  });

  $st->add_symbol('&'.$accessor.'_hash',sub {
    my ( $self, $key, @args ) = @_;
    die((ref $self).' does not support '.$hash) unless $self->can($hash);
    my %h = %{$self->$hash || {}};
    if (scalar @args == 1) {
      return ref $h{$key} eq 'HASH' ? $h{$key}->{$args[0]} : undef;
    } elsif (scalar @args == 2) {
      $h{$key} = {} unless exists $h{$key};
      return undef unless ref $h{$key} eq 'HASH';
      $h{$key}->{$args[0]} = $args[1];
      $self->$hash({ %h });
      return $args[1];
    } else {
      die $accessor.'_hash function must get 2 or 3 args';
    }
  });

  $st->add_symbol('&'.$accessor.'_hash_delete',sub {
    my ( $self, $key, $hash_key ) = @_;
    die((ref $self).' does not support '.$hash) unless $self->can($hash);
    my %h = %{$self->$hash || {}};
    if ($key && $hash_key) {
      return undef unless ref $h{$key} eq 'HASH';
      my $old_value = delete $h{$key}->{$hash_key};
      $self->$hash({ %h });
      return $old_value;
    } else {
      die $accessor.'_hash_delete function must get 2 args';
    }
  });

  $st->add_symbol('&'.$accessor.'_push',sub {
    my ( $self, $key, @elements ) = @_;
    die((ref $self).' does not support '.$hash) unless $self->can($hash);
    die $accessor.'_push function requires 1 arg' unless defined $key;
    my %h = %{$self->$hash || {}};
    my @array = defined $h{$key} ? ( @{$h{$key}} ) : ();
    push @array, @elements;
    $h{$key} = [ @array ];
    $self->$hash({ %h });
    return @elements;
  });

  $st->add_symbol('&'.$accessor.'_shift',sub {
    my ( $self, $key ) = @_;
    die((ref $self).' does not support '.$hash) unless $self->can($hash);
    die $accessor.'_shift function requires 1 arg' unless defined $key;
    my %h = %{$self->$hash || {}};
    my @array = defined $h{$key} ? ( @{$h{$key}} ) : ();
    return unless scalar @array;
    my $return = shift @array;
    $h{$key} = [ @array ];
    $self->$hash({ %h });
    return $return;
  });

  $st->add_symbol('&'.$accessor.'_in',sub {
    my ( $self, $key, $val ) = @_;
    die((ref $self).' does not support '.$hash) unless $self->can($hash);
    die $accessor.'_in function requires 2 args' unless defined $val && defined $key;
    my %h = %{$self->$hash || {}};
    my @array = defined $h{$key} ? ( @{$h{$key}} ) : ();
    for (@array) {
      return 1 if $val eq $_;
    }
    return 0;
  });

  $st->add_symbol('&'.$accessor.'_in_delete',sub {
    my ( $self, $key, $val ) = @_;
    die((ref $self).' does not support '.$hash) unless $self->can($hash);
    die $accessor.'_in_delete function requires 2 args' unless defined $val && defined $key;
    my %h = %{$self->$hash || {}};
    my @array = defined $h{$key} ? ( @{$h{$key}} ) : ();
    my @new_array;
    for my $old_val (@array) {
      push @new_array, $old_val unless $val eq $old_val;
    }
    $h{$key} = [ @new_array ];
    $self->$hash({ %h });
    return;
  });

  $st->add_symbol('&'.$accessor.'_delete',sub {
    my ( $self, $key ) = @_;
    die((ref $self).' does not support '.$hash) unless $self->can($hash);
    die $accessor.'_delete function requires 1 arg' unless defined $key;
    my %h = %{$self->$hash || {}};
    my $return = delete $h{$key};
    $self->$hash({ %h });
    return $return;
  });

  $st->add_symbol('&'.$accessor.'_exists',sub {
    my ( $self, $key ) = @_;
    die((ref $self).' does not support '.$hash) unless $self->can($hash);
    die $accessor.'_exists function requires 1 arg' unless defined $key;
    my %h = %{$self->$hash || {}};
    return exists $h{$key};
  });

}

1;

__END__

=pod

=head1 NAME

DBIx::Class::HashAccessor - Helper functions to install accessors for serialized hash columns

=head1 VERSION

version 0.001

=head1 SYNOPSIS

Your result class

  __PACKAGE__->load_components(
    'HashAccessor',
    'InflateColumn::Serializer',
    'Core'
  );
 
  __PACKAGE__->table('data');

  __PACKAGE__->add_columns(
    'data' => {
      'data_type' => 'VARCHAR',
      'size' => 255,
      'serializer_class' => 'JSON',
    }
  );

  __PACKAGE__->add_hash_accessor( da => 'data' );

In code

  # Access key in hash (no default hash needed)

  print $result->da_exists('key') ? 1 : 0; # exists
  $result->da('key',$new_value); # set
  print $result->da('key'); # get
  $result->da_delete('key'); # delete
  $result->da_hash('hash','key',22); # set inside hash
  print $result->da_hash('hash','key'); # get inside hash
  $result->da_hash_delete('hash','key'); # delete inside hash
  $result->da_push('array',@elements); # add to array (and create array if key isn't array)
  $result->da_shift('array'); # shift from array
  $result->da_in('array',$value); # value is in array
  $result->da_in_delete('array',$value); # find value in array and delete

=head1 DESCRIPTION

=head1 SUPPORT

IRC

  Join #dbix-class on irc.perl.org. Highlight Getty for fast reaction :).

Repository

  http://github.com/Getty/p5-dbix-class-hashaccessor
  Pull request and additional contributors are welcome

Issue Tracker

  http://github.com/Getty/p5-dbix-class-hashaccessor/issues

=head1 AUTHOR

Torsten Raudssus <torsten@raudss.us>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017 by Torsten Raudssus.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.