ActiveRecord-Simple

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

        * Typo fixes

0.51    2013-11-11
        + Added method's "last" & "first"
        + Added new class ActiveRecord::Simple::Tutorial with pod-documentation
        + Implemented schema-loader script called "mars"
        + Added method "count"
        + Added method "exists"
        + Added possibility to creating read-only objects: fetch({ read_only => 1 })
        + Added new syntax to method "fetch"
        + Added possibility to select only specific fields ("only")
        * Improved tests
        * Improved documentation
        * Fixed bugs

0.52    2013-11-26
        * Fixed the `fetch` behavior
        * Fixed tests

0.53    2014-05-12
        + Added method "increment" (thnx @lifeofguenter)

Changes  view on Meta::CPAN

        + Added ARS_TRACE
        + Created method "update" for quick objects update
        + New mars command "--upload"
        * Improved `find` and `count` methods, now you can use find({ id => [1, 2, 3] }) as '.. where id in (1, 2, 3)'
        * Improved error handling
        * Method `new` now takes simple hashes (not only hashrefs)
        * Improved documentation

0.80    2016-01-05
        + Added method "abstract"
        + Added method "select"
        + Added method "update"
        + Added method "abstract"
        + Added method "next"
        + Added "where in ... " condition to find
        + Added method "connect"
        * Improved error handling
        * Improved "new" method
        + Added LEFT JOIN
        * Optimization of data fetch
        * Improved documentation

Changes  view on Meta::CPAN

        - Classes
        - SQL::Traslator things

1.00    2018-01-02
        * New faster accessors
        * Improved tests
        * Updated sandbox
        + New option "make_columns_accessors"
        - script/mars
        - "abstract" method
        - "select" method
        * Improved documentation

1.10    2018-01-13
        * Fixed bugs
        + new class ActiveRecord::Simple::QueryManager
        + new "objects" method
        - method "find" [DEPRECATED]
        - method "all" [DEPRECATED]
        - method "get" [DEPRECATED]

lib/ActiveRecord/Simple.pm  view on Meta::CPAN

    my $pkey_val;
    my $sql_stm = qq{
        INSERT INTO "$table_name" ($field_names_str)
        VALUES ($values)
    };

    if ( $self->dbh->{Driver}{Name} eq 'Pg' ) {
        if ($primary_key) {
            $sql_stm .= ' RETURINIG ' . $primary_key if $primary_key;
            $sql_stm = ActiveRecord::Simple::Utils::quote_sql_stmt($sql_stm, $self->dbh->{Driver}{Name});
            $pkey_val = $self->dbh->selectrow_array($sql_stm, undef, @bind);
        }
        else {
            my $sth = $self->dbh->prepare(
                ActiveRecord::Simple::Utils::quote_sql_stmt($sql_stm, $self->dbh->{Driver}{Name})
            );

            $sth->execute(@bind);
        }
    }
    else {

lib/ActiveRecord/Simple/Find.pm  view on Meta::CPAN


    #my $self = $class->new();
    my $self = bless { class => $class } => $self_class;

    my $table_name = ($self->{class}->can('_get_table_name'))  ? $self->{class}->_get_table_name  : undef;
    my $pkey       = ($self->{class}->can('_get_primary_key')) ? $self->{class}->_get_primary_key : undef;

    croak 'can not get table_name for class ' . $self->{class} unless $table_name;
    #croak 'can not get primary_key for class ' . $self->{class} unless $pkey;

    $self->{prep_select_fields} //= [];
    $self->{prep_select_from}   //= [];
    $self->{prep_select_where}  //= [];

    my ($fields, $from, $where);

    if (!ref $param[0] && scalar @param == 1) {
        $fields = qq/"$table_name".*/;
        $from   = qq/"$table_name"/;
        $where  = qq/"$table_name"."$pkey" = ?/;

        $self->{BIND} = \@param
    }

lib/ActiveRecord/Simple/Find.pm  view on Meta::CPAN

        # find many by condition
        my $wherestr = shift @param;

        $fields = qq/"$table_name".*/;
        $from   = qq/"$table_name"/;
        $where  = $wherestr;

        $self->{BIND} = \@param;
    }

    push @{ $self->{prep_select_fields} }, $fields if $fields;
    push @{ $self->{prep_select_from} }, $from if $from;
    push @{ $self->{prep_select_where} }, $where if $where;

    return $self;
}

sub count {
    my $inv = shift;
    my $self = ref $inv ? $inv : $inv->new(@_);
    $self->{prep_select_fields} = [ 'COUNT(*)' ];
    if (@{ $self->{prep_group_by} || [] }) {
        my $table_name = $self->{class}->_get_table_name;
        push @{ $self->{prep_select_fields} }, map qq/"$table_name".$_/, @{ $self->{prep_group_by} };
        my @group_by = @{ $self->{prep_group_by} };
        s/"//g foreach @group_by;
        my @results;
        foreach my $item ($self->fetch) {
            push my @line, (count => $item->{'COUNT(*)'}), map { $_ => $item->{$_} } @group_by;
            push @results, { @line };
        }
        return @results;
    }
    else {

lib/ActiveRecord/Simple/Find.pm  view on Meta::CPAN

    ref $self or croak 'Create an object abstraction before using the modifiers. Use methods like `find`, `first`, `last` at the beginning';

    if ($self->{class}->can('_get_primary_key')) {
        my $pk = $self->{class}->_get_primary_key;
        push @fields, $pk if ! grep { $_ eq $pk } @fields;
    }

    my $table_name = $self->{class}->_get_table_name;
    my $mixins = $self->{class}->can('_get_mixins') ? $self->{class}->_get_mixins : undef;

    my @filtered_prep_select_fields =
        grep { $_ ne qq/"$table_name".*/ } @{ $self->{prep_select_fields} };
    for my $fld (@fields) {
        if ($mixins && grep { $_ eq $fld } keys %$mixins) {
            my $mixin = $mixins->{$fld}->($self->{class});
            $mixin .= qq/ AS $fld/ unless $mixin =~ /as\s+\w+$/i;
            push @filtered_prep_select_fields, $mixin;
        }
        else {
            push @filtered_prep_select_fields, qq/"$table_name"."$fld"/;
        }
    }

    $self->{prep_select_fields} = \@filtered_prep_select_fields;

    return $self;
}

# alias to only:
sub fields { shift->only(@_) }

sub order_by {
    my ($self, @param) = @_;

lib/ActiveRecord/Simple/Find.pm  view on Meta::CPAN

        my $relation = $self->{class}->_get_relations->{$rel_name}
            or next RELATION;

        next RELATION unless grep { $_ eq $relation->{type} } qw/one only/;
        my $rel_table_name = $relation->{class}->_get_table_name;
        my $rel_columns = $relation->{class}->_get_columns;

        REL_COLUMN:
        for (@$rel_columns) {
            next REL_COLUMN if ref $_;
            push @{ $self->{prep_select_fields} }, qq/"$rel_table_name"."$_" AS "JOINED_$rel_name\_$_"/;
        }

        if ($relation->{type} eq 'one') {
            my $join_sql = qq/LEFT JOIN "$rel_table_name" ON /;
            $join_sql .= qq/"$rel_table_name"."$relation->{params}{pk}"/;
            $join_sql .= qq/ = "$table_name"."$relation->{params}{fk}"/;

            push @{ $self->{prep_left_joins} }, $join_sql;
        }
    }

lib/ActiveRecord/Simple/Find.pm  view on Meta::CPAN


    $self->_finish_sql_stmt();
    $self->_quote_sql_stmt();

    return wantarray ? ($self->{SQL}, $self->{BIND}) : $self->{SQL};
}

sub exists {
    my ($self) = @_;

    $self->{prep_select_fields} = ['1'];
    $self->_finish_sql_stmt;
    $self->_quote_sql_stmt;

    my $sth = $self->dbh->prepare($self->{SQL});
    $sth->execute(@{ $self->{BIND} });

    return $sth->fetchrow_arrayref();
}


lib/ActiveRecord/Simple/Find.pm  view on Meta::CPAN

                $root_class_opts = $opts;
            }
            elsif ($opts->{class} eq $class) {
                $class_opts = $opts;
            }
        }

        my $self = $self_class->new($class, @{ $param->{where_statement} });

        my $connected_table_name = $class->_get_table_name;
        $self->{prep_select_from} = [ $param->{m_class}->_get_table_name ];

        push @{ $self->{prep_left_joins} },
            'JOIN ' . $connected_table_name . ' ON ' . $connected_table_name . '.' . $class->_get_primary_key . ' = '
                . $param->{m_class}->_get_table_name . '.' . $class_opts->{params}{fk};

        push @{ $self->{prep_select_where} },
            $root_class_opts->{params}{fk} . ' = ' . $param->{self}->{ $param->{root_class}->_get_primary_key };

        return $self;
    }
    else {
        my $self = $self_class->new($class, @{ $param->{where_statement} });

        my $connected_table_name = $class->_get_table_name;
        $self->{prep_select_from} = [ $param->{via_table} ];
        my $fk = ActiveRecord::Simple::Utils::class_to_table_name($class);
        $fk .= '_id';

        push @{ $self->{prep_left_joins} },
            'JOIN ' . $connected_table_name . ' ON ' . $connected_table_name . '.' . $class->_get_primary_key . ' = '
                . $param->{via_table} . '.' . $fk;

        my $fk2 = ActiveRecord::Simple::Utils::class_to_table_name($param->{root_class}) . '_id';

        push @{ $self->{prep_select_where} },
            $fk2 . ' = ' . $param->{self}->{ $param->{root_class}->_get_primary_key };

        return $self;
    }

}

sub _get_slice {
    my ($self, $time) = @_;

lib/ActiveRecord/Simple/Find.pm  view on Meta::CPAN


    $obj->{read_only} = 1 if defined $read_only;
    $obj->{isin_database} = 1;

    return $obj;
}

sub _finish_sql_stmt {
    my ($self) = @_;

    ref $self->{prep_select_fields} or croak 'Invalid prepare SQL statement';
    ref $self->{prep_select_from}   or croak 'Invalid prepare SQL statement';

    my $table_name = $self->{class}->_get_table_name;
    my @add = grep { $_ !~~ $self->{prep_select_fields} } map qq/"$table_name".$_/, @{ $self->{prep_group_by}||[] };
    push @{ $self->{prep_select_fields} }, @add;

    $self->{SQL} = "SELECT " . (join q/, /, @{ $self->{prep_select_fields} }) . "\n";
    $self->{SQL} .= "FROM " . (join q/, /, @{ $self->{prep_select_from} }) . "\n";

    if (defined $self->{prep_left_joins}) {
        $self->{SQL} .= "$_\n" for @{ $self->{prep_left_joins} };
        $self->{has_joined_table} = 1;
    }

    if (@{ $self->{prep_select_where}||[] }) {
        $self->{SQL} .= "WHERE\n";
        $self->{SQL} .= join " AND ", @{ $self->{prep_select_where} };
    }

    if (@{ $self->{prep_group_by}||[] }) {
        $self->{SQL} .= ' GROUP BY ';
        $self->{SQL} .= join q/, /, @{ $self->{prep_group_by} };
    }

    if (@{ $self->{prep_order_by}||[] }) {
        $self->{SQL} .= ' ORDER BY ';
        $self->{SQL} .= join q/, /, @{ $self->{prep_order_by} };

lib/ActiveRecord/Simple/Find.pm  view on Meta::CPAN

            next if $relation->{type} ne 'one';
            my $fk = $relation->{params}{fk};
            my $pk = $relation->{params}{pk};

            if (ref $param_hash->{$param_name} eq __PACKAGE__) {
                my $object = $param_hash->{$param_name};

                my $tmp_table = qq/tmp_table_/ . sprintf("%x", $object);
                my $request_table = $object->{class}->_get_table_name;

                $object->{prep_select_fields} = [qq/"$request_table"."$pk"/];
                $object->_finish_sql_stmt;

                push @$condition_pairs, qq/"$table_name"."$fk" IN (SELECT "$tmp_table"."$pk" from ($object->{SQL}) as $tmp_table)/;
                push @$bind, @{ $object->{BIND} } if ref $object->{BIND} eq 'ARRAY';
            }
            else {
                my $object = $param_hash->{$param_name};

                if (ref $object eq 'ARRAY') {
                    push @$bind, map $_->$pk, @$object;

lib/ActiveRecord/Simple/QueryManager.pm  view on Meta::CPAN


sub new { bless {}, shift }

sub all  { ActiveRecord::Simple::Find->new(shift->{caller})->fetch }
sub get  { ActiveRecord::Simple::Find->new(shift->{caller}, @_)->fetch }
sub find { ActiveRecord::Simple::Find->new(shift->{caller}, @_) }

sub sql_fetch_all {
    my ($self, $sql, @bind) = @_;

    my $data = $self->{caller}->dbh->selectall_arrayref($sql, { Slice => {} }, @bind);
    my @list;
    for my $row (@$data) {
        $self->{caller}->_mk_ro_accessors([keys %$row]);
        bless $row, $self->{caller};
        push @list, $row;
    }

    return \@list;
}

sub sql_fetch_row {
    my ($self, $sql, @bind) = @_;

    my $row = $self->{caller}->dbh->selectrow_hashref($sql, undef, @bind);
    $self->{caller}->_mk_ro_accessors([keys %$row]);
    bless $row, $self->{caller};

    return $row;
}

1;

__END__;

t/08-basic.t  view on Meta::CPAN


1;

package t::ClaSs3;

use base 'ActiveRecord::Simple';


package MockDBI;

sub selectrow_array { 1 }
sub do { 1 }
sub selectrow_hashref { { DUMMY => 'hash' } }
sub fetchrow_hashref { { DUMMY => 'hash' } }
sub prepare { bless {}, 'MockDBI' }
sub execute { 1 }
sub last_insert_id { 1 }
sub selectall_arrayref { [{ foo => 1  }, { bar => 2 }] }

1;

*ActiveRecord::Simple::dbh = sub {
    return bless { Driver => { Name => 'mysql' } }, 'MockDBI';
};

package main;

use Test::More;

t/12-connect.t  view on Meta::CPAN

ok(Customer->connect("dbi:SQLite:dbname=:memory:","",""), 'connect');

eval { require DBIx::Connector };
if ($@) {
	# There is no DBIx::Connector, use DBI/ARS::Connect
}
else {
	isa_ok $ActiveRecord::Simple::connector, 'DBIx::Connector';
}

my $hello = Customer->dbh->selectrow_array('SELECT "hello"');
is $hello, 'hello';

done_testing();

t/15-sql-row.t  view on Meta::CPAN

package Achievement;

our @ISA = qw/Schema/;

__PACKAGE__->auto_load();
__PACKAGE__->has_many(customers => 'Customer', { via => 'customer_achievement' });


package main;

ok my $one = Customer->objects->sql_fetch_all('select 1 as one, 2 as two');
is ref $one, 'ARRAY';
is scalar @$one, 1;

my $one1 = shift @$one;
isa_ok $one1, 'Customer';
ok $one1->one;
is $one1->one, 1;
ok $one1->two;
is $one1->two, 2;

eval { $one1->foo };
ok $@;

ok my $two = Customer->objects->sql_fetch_row('select 3 as three, 4 as four');
isa_ok $two, 'Customer';
ok $two->three;
is $two->three, 3;
eval { $two->five };
ok $@;

eval { $two->three(4) };
ok $@;
like $@, qr/read-only/;
#is $two->three, 3, 'still 3';



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