Aniki

 view release on metacpan or  search on metacpan

lib/Aniki.pm  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;

=head1 DESCRIPTION

Aniki is ORM.
Lite, but powerful.

=head2 FEATURES

=over 4

=item Small & Simple

You can read codes easily.

=item Object mapping

Inflates rows to L<Aniki::Result::Collection> object.
And inflates row to L<Aniki::Row> object.

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

=item Raw SQL support

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

=item Query builder

Aniki includes query builder powered by L<SQL::Maker>.
L<SQL::Maker> is fast and secure SQL builder.

=item Fork safe & Transaction support

Aniki includes L<DBI> handler powered by L<DBIx::Handler>.

=item Error handling

Easy to handle execution errors by C<handle_error> method.
You can override it.

=item Extendable

You can extend Aniki by L<Mouse::Role>.
Aniki provides some default plugins as L<Mouse::Role>.

=back

=head2 RELATIONSHIP

Aniki supports relationship.
Extracts relationship from schema class.

Example:

    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 C<author> has many C<modules>.
So you can access C<author> row object to C<modules>.

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

Also C<module> has a C<author>.
So you can access C<module> row object to C<author> also.

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

And you can pre-fetch related rows.

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

=head1 SETUP

Install Aniki from CPAN:

    cpanm Aniki

And run C<install-aniki> command.

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

C<install-aniki> creates skeleton modules.

=head1 METHODS

=head2 CLASS METHODS

=head3 C<setup(%args)>

Initialize and customize Aniki class.
C<schema> is required. Others are optional.

=head4 Arguments

=over 4

=item schema : ClassName

=item handler : ClassName

=item filter : ClassName

=item row : ClassName

=item result : ClassName

=item query_builder : ClassName

=back

=head3 C<use_prepare_cached>

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

=head3 C<use_strict_query_builder>

If this method returns true value, Aniki enables L<SQL::Maker>'s strict mode.
This method returns true value default.
So you need to disable L<SQL::Maker>'s strict mode, override it and return false value.

SEE ALSO: L<The JSON SQL Injection Vulnerability|http://blog.kazuhooku.com/2014/07/the-json-sql-injection-vulnerability.html>

=head3 C<preload_all_row_classes>

Preload all row classes.

=head3 C<preload_all_result_classes>



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