Aniki

 view release on metacpan or  search on metacpan

README.md  view on Meta::CPAN

    use Time::Moment;

    # define inflate/deflate filters in table context.
    table author => sub {
        inflate name => sub {
            my $name = shift;
            return uc $name;
        };

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

    inflate qr/_at$/ => sub {
        my $datetime = shift;
        $datetime =~ tr/ /T/;
        $datetime .= 'Z';
        return Time::Moment->from_string($datetime);
    };

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

package MyProj::DB {
    use Mouse v2.4.5;
    extends qw/Aniki/;

    __PACKAGE__->setup(
        schema => 'MyProj::DB::Schema',
        filter => 'MyProj::DB::Filter',
        row    => 'MyProj::DB::Row',
    );
};

package main {
    my $db = MyProj::DB->new(connect_info => ["dbi:SQLite:dbname=:memory:", "", ""]);
    $db->execute($_) for split /;/, MyProj::DB::Schema->output;

    my $author_id = $db->insert_and_fetch_id(author => { name => 'songmu' });

    $db->insert(module => {
        name      => 'DBIx::Schema::DSL',
        author_id => $author_id,
    });
    $db->insert(module => {
        name      => 'Riji',
        author_id => $author_id,
    });

    my $module = $db->select(module => {
        name => 'Riji',
    }, {
        limit => 1,
    })->first;
    say '$module->name:         ', $module->name;         ## Riji
    say '$module->author->name: ', $module->author->name; ## SONGMU

    my $author = $db->select(author => {
        name => 'songmu',
    }, {
        limit    => 1,
        prefetch => [qw/modules/],
    })->first;

    say '$author->name:   ', $author->name;                 ## SONGMU
    say 'modules[]->name: ', $_->name for $author->modules; ## DBIx::Schema::DSL, Riji
};

1;
```

# DESCRIPTION

Aniki is ORM.
Lite, but powerful.

## FEATURES

- Small & Simple

    You can read codes easily.

- Object mapping

    Inflates rows to [Aniki::Result::Collection](https://metacpan.org/pod/Aniki::Result::Collection) object.
    And inflates row to [Aniki::Row](https://metacpan.org/pod/Aniki::Row) object.

    You can change result class, also we can change row class.
    Aniki dispatches result/row class by table. (e.g. `foo` table to `MyDB::Row::Foo`)

- Raw SQL support

    Supports to execute raw `SELECT` SQL and fetch rows of result.
    Of course, Aniki can inflate to result/row also.

- Query builder

    Aniki includes query builder powered by [SQL::Maker](https://metacpan.org/pod/SQL::Maker).
    [SQL::Maker](https://metacpan.org/pod/SQL::Maker) is fast and secure SQL builder.

- Fork safe & Transaction support

    Aniki includes [DBI](https://metacpan.org/pod/DBI) handler powered by [DBIx::Handler](https://metacpan.org/pod/DBIx::Handler).

- Error handling

    Easy to handle execution errors by `handle_error` method.
    You can override it.

- Extendable

    You can extend Aniki by [Mouse::Role](https://metacpan.org/pod/Mouse::Role).
    Aniki provides some default plugins as [Mouse::Role](https://metacpan.org/pod/Mouse::Role).

## RELATIONSHIP

Aniki supports relationship.
Extracts relationship from schema class.

Example:

```perl
use 5.014002;
package MyProj::DB::Schema {
    use DBIx::Schema::DSL;

    create_table 'module' => columns {
        integer 'id', primary_key, auto_increment;
        varchar 'name';
        integer 'author_id';

        add_index 'author_id_idx' => ['author_id'];

        belongs_to 'author';
    };

    create_table 'author' => columns {
        integer 'id', primary_key, auto_increment;
        varchar 'name', unique;
    };
};
```

A `author` has many `modules`.
So you can access `author` row object to `modules`.

```perl
my $author = $db->select(author => { name => 'songmu' })->first;
say 'modules[]->name: ', $_->name for $author->modules; ## DBIx::Schema::DSL, Riji
```

Also `module` has a `author`.
So you can access `module` row object to `author` also.

```perl
my $module = $db->select(module => { name => 'Riji' })->first;
say "Riji's author is ", $module->author->name; ## SONGMU
```

And you can pre-fetch related rows.

```perl
my @modules = $db->select(module => {}, { prefetch => [qw/author/] });
say $_->name, "'s author is ", $_->author->name for @modules;
```

# SETUP

Install Aniki from CPAN:

```
cpanm Aniki
```

And run `install-aniki` command.

```
install-aniki --lib=./lib MyApp::DB
```

`install-aniki` creates skeleton modules.

# METHODS

## CLASS METHODS

### `setup(%args)`

Initialize and customize Aniki class.
`schema` is required. Others are optional.

#### Arguments

- schema : ClassName
- handler : ClassName
- filter : ClassName
- row : ClassName
- result : ClassName
- query\_builder : ClassName

### `use_prepare_cached`

If this method returns true value, Aniki uses `preare_cached`.
This method returns true value default.
So you don't need to use `preare_cached`, override it and return false value.

### `use_strict_query_builder`

If this method returns true value, Aniki enables [SQL::Maker](https://metacpan.org/pod/SQL::Maker)'s strict mode.
This method returns true value default.
So you need to disable [SQL::Maker](https://metacpan.org/pod/SQL::Maker)'s strict mode, override it and return false value.

SEE ALSO: [The JSON SQL Injection Vulnerability](http://blog.kazuhooku.com/2014/07/the-json-sql-injection-vulnerability.html)

### `preload_all_row_classes`

Preload all row classes.

### `preload_all_result_classes`

Preload all result classes.

### `guess_result_class($table_name) : ClassName`



( run in 0.737 second using v1.01-cache-2.11-cpan-a5162978ef8 )