Aniki

 view release on metacpan or  search on metacpan

lib/Aniki.pm  view on Meta::CPAN

        my $result = $self->_fetch_by_sth($sth, $table_name, $columns);
        $self->fetch_and_attach_relay_data($table_name, $prefetch, $result->rows);

        $txn->rollback if defined $txn; ## for read only
        return $result;
    }

    my $sth = $self->execute($sql, @$bind);

    # When the return value is never used, should not create object
    # case example: use `FOR UPDATE` query for global locking
    unless (defined wantarray) {
        $sth->finish();
        return;
    }

    return $self->_fetch_by_sth($sth, $table_name, $columns);
}

sub _fetch_by_sth {
    my ($self, $sth, $table_name, $columns) = @_;

lib/Aniki/Filter.pm  view on Meta::CPAN

package Aniki::Filter;
use 5.014002;

use namespace::autoclean;
use Mouse v2.4.5;

has global_inflators => (
    is      => 'ro',
    default => sub { [] },
);

has global_deflators => (
    is      => 'ro',
    default => sub { [] },
);

has global_triggers => (
    is      => 'ro',
    default => sub { +{} },
);

has table_inflators => (
    is      => 'ro',
    default => sub { +{} },
);

has table_deflators => (

lib/Aniki/Filter.pm  view on Meta::CPAN

);

has table_triggers => (
    is      => 'ro',
    default => sub { +{} },
);

sub _identity { $_[0] }
sub _normalize_column2rx { ref $_[0] eq 'Regexp' ? $_[0] : qr/\A\Q$_[0]\E\z/m }

sub add_global_inflator {
    my ($self, $column, $code) = @_;
    my $rx = _normalize_column2rx($column);
    push @{ $self->global_inflators } => [$rx, $code];
}

sub add_global_deflator {
    my ($self, $column, $code) = @_;
    my $rx = _normalize_column2rx($column);
    push @{ $self->global_deflators } => [$rx, $code];
}

sub add_global_trigger {
    my ($self, $event, $code) = @_;
    push @{ $self->global_triggers->{$event} } => $code;
}

sub add_table_inflator {
    my ($self, $table_name, $column, $code) = @_;
    my $rx = _normalize_column2rx($column);
    push @{ $self->table_inflators->{$table_name} } => [$rx, $code];
}

sub add_table_deflator {
    my ($self, $table_name, $column, $code) = @_;

lib/Aniki/Filter.pm  view on Meta::CPAN

    my %row = %$row;

    my $trigger = $self->get_trigger_callback($event, $table_name);
    return $trigger->(\%row);
}

sub get_inflate_callback {
    my ($self, $table_name, $column) = @_;
    unless (exists $self->{__inflate_callbacks_cache}->{$table_name}->{$column}) {
        my $callback;
        for my $pair (@{ $self->global_inflators }) {
            my ($rx, $code) = @$pair;
            $callback = $code if $column =~ $rx;
        }
        for my $pair (@{ $self->table_inflators->{$table_name} }) {
            my ($rx, $code) = @$pair;
            $callback = $code if $column =~ $rx;
        }
        $self->{__inflate_callbacks_cache}->{$table_name}->{$column} = $callback;
    }
    return $self->{__inflate_callbacks_cache}->{$table_name}->{$column};
}

sub get_deflate_callback {
    my ($self, $table_name, $column) = @_;
    unless (exists $self->{__deflate_callbacks_cache}->{$table_name}->{$column}) {
        my $callback;
        for my $pair (@{ $self->global_deflators }) {
            my ($rx, $code) = @$pair;
            $callback = $code if $column =~ $rx;
        }
        for my $pair (@{ $self->table_deflators->{$table_name} }) {
            my ($rx, $code) = @$pair;
            $callback = $code if $column =~ $rx;
        }
        $self->{__deflate_callbacks_cache}->{$table_name}->{$column} = $callback;
    }
    return $self->{__deflate_callbacks_cache}->{$table_name}->{$column};
}

sub get_trigger_callback {
    my ($self, $event, $table_name) = @_;

    unless (exists $self->{__trigger_callback_cache}->{$table_name}->{$event}) {
        my @triggers = (
            @{ $self->table_triggers->{$table_name}->{$event} || [] },
            @{ $self->global_triggers->{$event} || [] },
        );

        my $trigger = \&_identity;
        for my $cb (reverse @triggers) {
            my $next = $trigger;
            $trigger = sub { $cb->($_[0], $next) };
        }
        $self->{__trigger_callback_cache}->{$table_name}->{$event} = $trigger;
    }

lib/Aniki/Filter/Declare.pm  view on Meta::CPAN

}

sub _inflate {
    my $filter = shift;
    return sub ($&) {## no critic
        my ($column, $code) = @_;
        if (defined $TARGET_TABLE) {
            $filter->add_table_inflator($TARGET_TABLE, $column, $code);
        }
        else {
            $filter->add_global_inflator($column, $code);
        }
    };
}

sub _deflate {
    my $filter = shift;
    sub ($&) {## no critic
        my ($column, $code) = @_;
        if (defined $TARGET_TABLE) {
            $filter->add_table_deflator($TARGET_TABLE, $column, $code);
        }
        else {
            $filter->add_global_deflator($column, $code);
        }
    };
}

sub _trigger {
    my $filter = shift;
    sub ($&) {## no critic
        my ($event, $code) = @_;
        if (defined $TARGET_TABLE) {
            $filter->add_table_trigger($TARGET_TABLE, $event, $code);
        }
        else {
            $filter->add_global_trigger($event, $code);
        }
    };
}

sub _instance {
    my $filter = shift;
    return sub { $filter };
}

1;

lib/Aniki/Filter/Declare.pm  view on Meta::CPAN

            my $name = shift;
            return uc $name;
        };

        deflate name => sub {
            my $name = shift;
            return lc $name;
        };
    };

    # define inflate/deflate filters in global context. (apply to all tables)
    inflate qr/_at$/ => sub {
        my $datetime = shift;
        return Time::Moment->from_string($datetime.'Z', lenient => 1);
    };

    deflate qr/_at$/ => sub {
        my $datetime = shift;
        return $datetime->at_utc->strftime('%F %T') if blessed $datetime and $datetime->isa('Time::Moment');
        return $datetime;
    };

t/filter/declare/basic.t  view on Meta::CPAN

        };

        deflate foo => sub {
            my $value = shift;
            return "hoge_deflate_$value";
        };
    };

    inflate bar => sub {
        my $value = shift;
        return "global_inflate_$value";
    };

    deflate bar => sub {
        my $value = shift;
        return "global_deflate_$value";
    };
};

my $filter = MyProj::DB::Filter->instance;

subtest table => sub {
    is $filter->inflate_row(hoge => { foo => 'foo_value' })->{foo}, 'hoge_inflate_foo_value';
    is $filter->deflate_row(hoge => { foo => 'foo_value' })->{foo}, 'hoge_deflate_foo_value';
    is $filter->inflate_row(hoge => { foo => 'foo_value' })->{foo}, 'hoge_inflate_foo_value';
    is $filter->deflate_row(hoge => { foo => 'foo_value' })->{foo}, 'hoge_deflate_foo_value';
    is $filter->inflate_row(fuga => { foo => 'foo_value' })->{foo}, 'foo_value';
    is $filter->deflate_row(fuga => { foo => 'foo_value' })->{foo}, 'foo_value';
    is $filter->inflate_row(fuga => { foo => 'foo_value' })->{foo}, 'foo_value';
    is $filter->deflate_row(fuga => { foo => 'foo_value' })->{foo}, 'foo_value';
};

subtest global => sub {
    is $filter->inflate_row(hoge => { bar => 'bar_value' })->{bar}, 'global_inflate_bar_value';
    is $filter->deflate_row(hoge => { bar => 'bar_value' })->{bar}, 'global_deflate_bar_value';
    is $filter->inflate_row(hoge => { bar => 'bar_value' })->{bar}, 'global_inflate_bar_value';
    is $filter->deflate_row(hoge => { bar => 'bar_value' })->{bar}, 'global_deflate_bar_value';
    is $filter->inflate_row(fuga => { bar => 'bar_value' })->{bar}, 'global_inflate_bar_value';
    is $filter->deflate_row(fuga => { bar => 'bar_value' })->{bar}, 'global_deflate_bar_value';
    is $filter->inflate_row(fuga => { bar => 'bar_value' })->{bar}, 'global_inflate_bar_value';
    is $filter->deflate_row(fuga => { bar => 'bar_value' })->{bar}, 'global_deflate_bar_value';
};

done_testing;

t/filter/deflate/basic.t  view on Meta::CPAN

use strict;
use warnings;
use utf8;

use Test::More;
use Aniki::Filter;

subtest 'global deflator only' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_global_deflator(foo => sub {
        my $value = shift;
        return "global_$value";
    });
    is $filter->deflate_row(hoge => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->deflate_row(fuga => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->deflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
    is $filter->deflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
};

subtest 'table deflator only' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_table_deflator(hoge => foo => sub {
        my $value = shift;
        return "hoge_$value";
    });
    is $filter->deflate_row(hoge => { foo  => 'foo_value' })->{foo},   'hoge_foo_value';
    is $filter->deflate_row(fuga => { foo  => 'foo_value' })->{foo},   'foo_value';
    is $filter->deflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
    is $filter->deflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
};

subtest 'table and global deflator' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_global_deflator(foo => sub {
        my $value = shift;
        return "global_$value";
    });
    $filter->add_table_deflator(hoge => foo => sub {
        my $value = shift;
        return "hoge_$value";
    });
    is $filter->deflate_row(hoge => { foo  => 'foo_value' })->{foo},   'hoge_foo_value';
    is $filter->deflate_row(fuga => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->deflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
    is $filter->deflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
};

done_testing();

t/filter/deflate/regex.t  view on Meta::CPAN

use strict;
use warnings;
use utf8;

use Test::More;
use Aniki::Filter;

subtest 'global deflator only' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_global_deflator(qr/foo/ => sub {
        my $value = shift;
        return "global_$value";
    });
    is $filter->deflate_row(hoge => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->deflate_row(fuga => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->deflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'global_foo2_value';
    is $filter->deflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'global_foo2_value';
    is $filter->deflate_row(hoge => { bar  => 'bar_value' })->{bar},   'bar_value';
    is $filter->deflate_row(fuga => { bar  => 'bar_value' })->{bar},   'bar_value';
};

subtest 'table deflator only' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_table_deflator(hoge => qr/foo/ => sub {
        my $value = shift;
        return "hoge_$value";
    });
    is $filter->deflate_row(hoge => { foo  => 'foo_value' })->{foo},   'hoge_foo_value';
    is $filter->deflate_row(fuga => { foo  => 'foo_value' })->{foo},   'foo_value';
    is $filter->deflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'hoge_foo2_value';
    is $filter->deflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
    is $filter->deflate_row(hoge => { bar  => 'bar_value' })->{bar},   'bar_value';
    is $filter->deflate_row(fuga => { bar  => 'bar_value' })->{bar},   'bar_value';
};

subtest 'table and global deflator' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_global_deflator(qr/foo/ => sub {
        my $value = shift;
        return "global_$value";
    });
    $filter->add_table_deflator(hoge => qr/foo/ => sub {
        my $value = shift;
        return "hoge_$value";
    });
    is $filter->deflate_row(hoge => { foo  => 'foo_value' })->{foo},   'hoge_foo_value';
    is $filter->deflate_row(fuga => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->deflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'hoge_foo2_value';
    is $filter->deflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'global_foo2_value';
    is $filter->deflate_row(hoge => { bar  => 'bar_value' })->{bar},   'bar_value';
    is $filter->deflate_row(fuga => { bar  => 'bar_value' })->{bar},   'bar_value';
};

done_testing();

t/filter/deflate_and_inflate.t  view on Meta::CPAN

use Test::More;
use Aniki::Filter;

my @PATTERN = (
    [hoge => { foo  => 'foo_value'  }],
    [fuga => { foo  => 'foo_value'  }],
    [hoge => { foo2 => 'foo2_value' }],
    [fuga => { foo2 => 'foo2_value' }],
);

subtest 'global' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_global_inflator(foo => sub {
        my $value = shift;
        return "global_inflate_$value";
    });
    $filter->add_global_deflator(foo => sub {
        my $value = shift;
        return "global_deflate_$value";
    });

    for my $pattern (@PATTERN) {
        my ($table, $row) = @$pattern;
        my ($column) = keys %$row;
        is $filter->deflate_row($table, $row)->{$column}, $column eq 'foo' ? 'global_deflate_foo_value' : $row->{$column};
        is $filter->inflate_row($table, $row)->{$column}, $column eq 'foo' ? 'global_inflate_foo_value' : $row->{$column};
    }
};

subtest 'table' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_table_inflator(hoge => foo => sub {
        my $value = shift;
        return "hoge_inflate_$value";
    });
    $filter->add_table_deflator(hoge => foo => sub {

t/filter/inflate/basic.t  view on Meta::CPAN

use strict;
use warnings;
use utf8;

use Test::More;
use Aniki::Filter;

subtest 'global inflator only' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_global_inflator(foo => sub {
        my $value = shift;
        return "global_$value";
    });
    is $filter->inflate_row(hoge => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->inflate_row(fuga => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->inflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
    is $filter->inflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
};

subtest 'table inflator only' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_table_inflator(hoge => foo => sub {
        my $value = shift;
        return "hoge_$value";
    });
    is $filter->inflate_row(hoge => { foo  => 'foo_value' })->{foo},   'hoge_foo_value';
    is $filter->inflate_row(fuga => { foo  => 'foo_value' })->{foo},   'foo_value';
    is $filter->inflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
    is $filter->inflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
};

subtest 'table and global inflator' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_global_inflator(foo => sub {
        my $value = shift;
        return "global_$value";
    });
    $filter->add_table_inflator(hoge => foo => sub {
        my $value = shift;
        return "hoge_$value";
    });
    is $filter->inflate_row(hoge => { foo  => 'foo_value' })->{foo},   'hoge_foo_value';
    is $filter->inflate_row(fuga => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->inflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
    is $filter->inflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
};

done_testing();

t/filter/inflate/regex.t  view on Meta::CPAN

use strict;
use warnings;
use utf8;

use Test::More;
use Aniki::Filter;

subtest 'global inflator only' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_global_inflator(qr/foo/ => sub {
        my $value = shift;
        return "global_$value";
    });
    is $filter->inflate_row(hoge => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->inflate_row(fuga => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->inflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'global_foo2_value';
    is $filter->inflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'global_foo2_value';
    is $filter->inflate_row(hoge => { bar  => 'bar_value' })->{bar},   'bar_value';
    is $filter->inflate_row(fuga => { bar  => 'bar_value' })->{bar},   'bar_value';
};

subtest 'table inflator only' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_table_inflator(hoge => qr/foo/ => sub {
        my $value = shift;
        return "hoge_$value";
    });
    is $filter->inflate_row(hoge => { foo  => 'foo_value' })->{foo},   'hoge_foo_value';
    is $filter->inflate_row(fuga => { foo  => 'foo_value' })->{foo},   'foo_value';
    is $filter->inflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'hoge_foo2_value';
    is $filter->inflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'foo2_value';
    is $filter->inflate_row(hoge => { bar  => 'bar_value' })->{bar},   'bar_value';
    is $filter->inflate_row(fuga => { bar  => 'bar_value' })->{bar},   'bar_value';
};

subtest 'table and global inflator' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_global_inflator(qr/foo/ => sub {
        my $value = shift;
        return "global_$value";
    });
    $filter->add_table_inflator(hoge => qr/foo/ => sub {
        my $value = shift;
        return "hoge_$value";
    });
    is $filter->inflate_row(hoge => { foo  => 'foo_value' })->{foo},   'hoge_foo_value';
    is $filter->inflate_row(fuga => { foo  => 'foo_value' })->{foo},   'global_foo_value';
    is $filter->inflate_row(hoge => { foo2 => 'foo2_value' })->{foo2}, 'hoge_foo2_value';
    is $filter->inflate_row(fuga => { foo2 => 'foo2_value' })->{foo2}, 'global_foo2_value';
    is $filter->inflate_row(hoge => { bar  => 'bar_value' })->{bar},   'bar_value';
    is $filter->inflate_row(fuga => { bar  => 'bar_value' })->{bar},   'bar_value';
};

done_testing();

t/filter/trigger/basic.t  view on Meta::CPAN

use strict;
use warnings;
use utf8;

use Test::More;
use Aniki::Filter;

subtest 'global trigger only' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_global_trigger(insert => sub {
        my ($row, $next) = @_;
        $row->{baz}++;
        return $next->($row);
    });
    is $filter->apply_trigger(insert => hoge => { foo  => 'foo_value' })->{baz},  1;
    is $filter->apply_trigger(insert => fuga => { foo  => 'foo_value' })->{baz},  1;
    is $filter->apply_trigger(insert => hoge => { foo2 => 'foo2_value' })->{baz}, 1;
    is $filter->apply_trigger(insert => fuga => { foo2 => 'foo2_value' })->{baz}, 1;
};

t/filter/trigger/basic.t  view on Meta::CPAN

        my ($row, $next) = @_;
        $row->{baz}++;
        return $next->($row);
    });
    is $filter->apply_trigger(insert => hoge => { foo  => 'foo_value' })->{baz},  1;
    is $filter->apply_trigger(insert => fuga => { foo  => 'foo_value' })->{baz},  undef;
    is $filter->apply_trigger(insert => hoge => { foo2 => 'foo2_value' })->{baz}, 1;
    is $filter->apply_trigger(insert => fuga => { foo2 => 'foo2_value' })->{baz}, undef;
};

subtest 'table and global trigger' => sub {
    my $filter = Aniki::Filter->new();
    $filter->add_table_trigger(hoge => insert => sub {
        my ($row, $next) = @_;
        $row->{baz}++;
        return $next->($row);
    });
    $filter->add_global_trigger(insert => sub {
        my ($row, $next) = @_;
        $row->{baz}++;
        return $next->($row);
    });
    is $filter->apply_trigger(insert => hoge => { foo  => 'foo_value' })->{baz},  2;
    is $filter->apply_trigger(insert => fuga => { foo  => 'foo_value' })->{baz},  1;
    is $filter->apply_trigger(insert => hoge => { foo2 => 'foo2_value' })->{baz}, 2;
    is $filter->apply_trigger(insert => fuga => { foo2 => 'foo2_value' })->{baz}, 1;
};



( run in 1.010 second using v1.01-cache-2.11-cpan-49f99fa48dc )