view release on metacpan or search on metacpan
lib/ActiveRecord/Simple.pm view on Meta::CPAN
sub auto_load {
my ($class) = @_;
my $table_name = class_to_table_name($class);
# 0. check the name
my $table_info_sth = $class->dbh->table_info('', '%', $table_name, 'TABLE');
$table_info_sth->fetchrow_hashref or croak "Can't find table '$table_name' in the database";
# 1. columns list
my $column_info_sth = $class->dbh->column_info(undef, undef, $table_name, undef);
my $cols = $column_info_sth->fetchall_arrayref({});
my @columns = ();
push @columns, $_->{COLUMN_NAME} for @$cols;
# 2. Primary key
my $primary_key_sth = $class->dbh->primary_key_info(undef, undef, $table_name);
my $primary_key_data = $primary_key_sth->fetchrow_hashref;
my $primary_key = ($primary_key_data) ? $primary_key_data->{COLUMN_NAME} : undef;
$class->table_name($table_name) if $table_name;
$class->primary_key($primary_key) if $primary_key;
$class->columns(@columns) if @columns;
}
sub connect {
my ($class, $dsn, $username, $password, $options) = @_;
eval { require DBIx::Connector };
lib/ActiveRecord/Simple.pm view on Meta::CPAN
#return unless $self->dbh;
croak "Undefined database handler" unless $self->dbh;
croak 'Object is read-only'
if exists $self->{read_only} && $self->{read_only} == 1;
my $save_param = {};
my $fields = $self->_get_columns;
my $pkey = ($self->can('_get_primary_key')) ? $self->_get_primary_key : undef;
FIELD:
for my $field (@$fields) {
next FIELD if defined $pkey && $field eq $pkey && !$self->{$pkey};
next FIELD if ref $field && ref $field eq 'HASH';
$save_param->{$field} = $self->{$field};
}
### Get additional fields from related objects:
for my $field (keys %$self) {
lib/ActiveRecord/Simple.pm view on Meta::CPAN
my $result;
if ($self->{isin_database}) {
$result = $self->_update($save_param);
}
else {
$result = $self->_insert($save_param);
}
$self->{need_to_save} = 0 if $result;
delete $self->{SQL} if $result;
return (defined $result) ? $self : undef;
}
sub update {
my ($self, $params) = @_;
my $fields = $self->_get_columns();
FIELD:
for my $field (@$fields) {
next FIELD if ! exists $params->{$field};
next FIELD if ! $params->{$field};
lib/ActiveRecord/Simple.pm view on Meta::CPAN
my $table_name = _what_is_the_table_name($self);
my $pkey = $self->_get_primary_key;
return unless $self->{$pkey};
my $sql = qq{
DELETE FROM "$table_name" WHERE $pkey = ?
};
$sql .= ' CASCADE ' if $param && $param->{cascade};
my $res = undef;
$sql = ActiveRecord::Simple::Utils::quote_sql_stmt($sql, $self->dbh->{Driver}{Name});
if ( $self->dbh->do($sql, undef, $self->{$pkey}) ) {
$self->{isin_database} = undef;
delete $self->{$pkey};
$res = 1;
}
return $res;
}
sub is_defined {
my ($self) = @_;
lib/ActiveRecord/Simple.pm view on Meta::CPAN
my $related_class = _get_related_class($relation);
#eval { load $related_class }; ### TODO: check module is loaded
#load $related_class;
#load $related_class unless is_loaded $related_class;
#mark_as_loaded $related_class;
load_module $related_class;
my $rel_type = undef;
while (my ($rel_key, $rel_opts) = each %{ $related_class->_get_relations }) {
next if $class ne _get_related_class($rel_opts);
$rel_type = $rel_opts->{type};
}
croak 'Oops! Looks like related class ' . $related_class . ' has no relations with ' . $class unless $rel_type;
$type .= $rel_type;
return $type;
}
sub _get_related_subclass {
my ($relation) = @_;
return undef if !ref $relation->{class};
my $subclass;
if (ref $relation->{class} eq 'HASH') {
$subclass = (keys %{ $relation->{class} })[0];
}
elsif (ref $relation->{class} eq 'ARRAY') {
$subclass = $relation->{class}[0];
}
return $subclass;
lib/ActiveRecord/Simple.pm view on Meta::CPAN
sub _insert {
my ($self, $param) = @_;
return unless $self->dbh && $param;
#my $table_name = $self->_table_name;
my $table_name = _what_is_the_table_name($self);
my @field_names = grep { defined $param->{$_} } sort keys %$param;
my $primary_key = ($self->can('_get_primary_key')) ? $self->_get_primary_key :
($self->can('_get_secondary_key')) ? $self->_get_secondary_key : undef;
my $field_names_str = join q/, /, map { q/"/ . $_ . q/"/ } @field_names;
my (@bind, @values_list);
for (@field_names) {
if (ref $param->{$_} eq 'SCALAR') {
push @values_list, ${ $param->{$_} };
}
else {
push @values_list, '?';
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.pm view on Meta::CPAN
);
$sth->execute(@bind);
if ( $primary_key && defined $self->{$primary_key} ) {
$pkey_val = $self->{$primary_key};
}
else {
$pkey_val =
exists $sth->{mysql_insertid} # mysql only
? $sth->{mysql_insertid}
: $self->dbh->last_insert_id(undef, undef, $table_name, undef);
}
}
if (defined $primary_key && $self->can($primary_key) && $pkey_val) {
#$self->$primary_key($pkey_val);
$self->{$primary_key} = $pkey_val;
}
$self->{isin_database} = 1;
return $pkey_val;
lib/ActiveRecord/Simple.pm view on Meta::CPAN
sub _update {
my ($self, $param) = @_;
return unless $self->dbh && $param;
#my $table_name = $self->_table_name;
my $table_name = _what_is_the_table_name($self);
my @field_names = sort keys %$param;
my $primary_key = ($self->can('_get_primary_key')) ? $self->_get_primary_key :
($self->can('_get_secondary_key')) ? $self->_get_secondary_key : undef;
my (@set_list, @bind);
for (@field_names) {
if (ref $param->{$_} eq 'SCALAR') {
push @set_list, $_ . ' = ' . ${ $param->{$_} };
}
else {
push @set_list, "$_ = ?";
push @bind, $param->{$_};
}
lib/ActiveRecord/Simple.pm view on Meta::CPAN
my $sql_stm = ActiveRecord::Simple::Utils::quote_sql_stmt(
qq{
UPDATE "$table_name" SET $setstring
WHERE
$primary_key = ?
},
$self->dbh->{Driver}{Name}
);
return $self->dbh->do($sql_stm, undef, @bind);
}
sub _mk_rw_accessors {
my ($class, $fields) = @_;
return unless $fields;
return if $class->can('_make_columns_accessors') && $class->_make_columns_accessors == 0;
$class->_mk_accessors($fields, 'rw');
}
lib/ActiveRecord/Simple.pm view on Meta::CPAN
#eval { load $class }; ### TODO: check class has been loaded
#load $class unless is_loaded $class;
#mark_as_loaded $class;
load_module $class;
my $table_name = _what_is_the_table_name($class);
$table_name =~ s/s$// if $what_key eq 'foreign_key';
return ($what_key eq 'foreign_key') ? "$table_name\_id" : undef;
}
sub _delete_keys {
my ($self, $rx) = @_;
map { delete $self->{$_} if $_ =~ $rx } keys %$self;
}
sub _append_relation {
my ($class, $rel_name, $rel_hashref) = @_;
lib/ActiveRecord/Simple.pm view on Meta::CPAN
no strict 'refs';
RELATION_NAME:
for my $relation_name ( keys %{ $relations }) {
my $pkg_method_name = $class . '::' . $relation_name;
next RELATION_NAME if $class->can($pkg_method_name); ### FIXME: orrrr $relation_name???
my $relation = $relations->{$relation_name};
my $full_relation_type = _get_relation_type($class, $relation);
my $related_class = _get_related_class($relation);
### TODO: check for error if returns undef
my $pk = $relation->{params}{pk};
my $fk = $relation->{params}{fk};
my $instance_name = "relation_instance_$relation_name";
if (grep { $full_relation_type eq $_ } qw/one_to_many one_to_one one_to_only/) {
*{$pkg_method_name} = sub {
my ($self, @args) = @_;
if (@args) {
my $object = shift @args;
lib/ActiveRecord/Simple.pm view on Meta::CPAN
my $via_table = $relation->{via_table};
OBJECT:
for my $object (@args) {
next OBJECT if !blessed $object;
my $pk2_name = $object->_get_primary_key;
my $pk2 = $object->{$pk2_name};
my $sql = qq/INSERT INTO "$via_table" ("$fk1", "$fk2") VALUES (?, ?)/;
$self->dbh->do($sql, undef, $pk1, $pk2);
}
}
return $self;
}
# else
if (!$self->{$instance_name}) {
$self->{$instance_name} = $related_class->_find_many_to_many({
root_class => $class,
lib/ActiveRecord/Simple/Connect.pm view on Meta::CPAN
use DBI;
my $self;
sub new {
my ($class, $dsn, $username, $password, $params) = @_;
if (!$self) {
$self = { dbh => undef };
if ($dsn) {
$self->{dsn} = $dsn;
$self->{username} = $username if $username;
$self->{password} = $password if $password;
$self->{connection_parameters} = $params if $params;
}
bless $self, $class;
}
lib/ActiveRecord/Simple/Find.pm view on Meta::CPAN
our $MAXIMUM_LIMIT = 100_000_000_000;
sub new {
my ($self_class, $class, @param) = @_;
#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);
lib/ActiveRecord/Simple/Find.pm view on Meta::CPAN
$fields = qq/"$table_name".*/;
$from = qq/"$table_name"/;
$where = qq/"$table_name"."$pkey" = ?/;
$self->{BIND} = \@param
}
elsif (!ref $param[0] && scalar @param == 0) {
$fields = qq/"$table_name".*/;
$from = qq/"$table_name"/;
$self->{BIND} = undef;
}
elsif (ref $param[0] && ref $param[0] eq 'HASH') {
# find many by params
my ($bind, $condition_pairs) = $self->_parse_hash($param[0]);
my $where_str = join q/ AND /, @$condition_pairs;
$fields = qq/"$table_name".*/;
$from = qq/"$table_name"/;
$where = $where_str;
lib/ActiveRecord/Simple/Find.pm view on Meta::CPAN
$self->{BIND} = $bind;
}
elsif (ref $param[0] && ref $param[0] eq 'ARRAY') {
# find many by primary keys
my $whereinstr = join ', ', @{ $param[0] };
$fields = qq/"$table_name".*/;
$from = qq/"$table_name"/;
$where = qq/"$table_name"."$pkey" IN ($whereinstr)/;
$self->{BIND} = undef;
}
else {
# find many by condition
my $wherestr = shift @param;
$fields = qq/"$table_name".*/;
$from = qq/"$table_name"/;
$where = $wherestr;
$self->{BIND} = \@param;
lib/ActiveRecord/Simple/Find.pm view on Meta::CPAN
scalar @fields > 0 or croak 'Not defined fields for method "only"';
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 {
lib/ActiveRecord/Simple/Find.pm view on Meta::CPAN
$self->{SQL} .= ' LIMIT ' . ($self->{prep_limit} // $MAXIMUM_LIMIT);
$self->{SQL} .= ' OFFSET '. ($self->{prep_offset} // 0);
return $self;
}
sub _parse_hash {
my ($self, $param_hash) = @_;
my $class = $self->{class};
my $table_name = ($self->{class}->can('_get_table_name')) ? $self->{class}->_get_table_name : undef;
my ($bind, $condition_pairs) = ([],[]);
for my $param_name (keys %{ $param_hash }) {
if (ref $param_hash->{$param_name} eq 'ARRAY' and !ref $param_hash->{$param_name}[0]) {
my $instr = join q/, /, map { '?' } @{ $param_hash->{$param_name} };
push @$condition_pairs, qq/"$table_name"."$param_name" IN ($instr)/;
push @$bind, @{ $param_hash->{$param_name} };
}
elsif (ref $param_hash->{$param_name}) {
next if !$class->can('_get_relations');
my $relation = $class->_get_relations->{$param_name} or next;
lib/ActiveRecord/Simple/QueryManager.pm view on Meta::CPAN
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/07-auto_load.t view on Meta::CPAN
use Test::More;
ok my $customer = Customer->new();
eval { $customer->first_name };
ok ! $@, 'loaded accessor `first_name`';
eval { $customer->id };
ok ! $@, 'loaded accessor `id`';
eval { $customer->foo };
ok $@, 'error load undefined accessor';
is(Customer->_get_table_name, 'customer', 'loaded table name');
is(Customer->_get_primary_key, 'id', 'loaded primary key');
done_testing();
t/09-find.t view on Meta::CPAN
eval { Customer->objects->get(1)->fetch };
ok $@, 'fetch after get causes die';
ok my $cnt = Customer->objects->find->count, 'count';
is $cnt, 6;
ok my $exists = Customer->objects->find({ first_name => 'Bob' })->exists, 'exists';
ok(!Customer->objects->find({ first_name => 'Not Found' })->exists);
is(Customer->objects->find({ first_name => 'Not Found' })->exists, undef);
ok my $first = Customer->objects->find->first, 'first';
is_deeply $first, $Bob;
ok my $last = Customer->objects->find->last, 'last';
is $last->id, 6;
ok my $customized = Customer->objects->find({ first_name => 'Bob' })->only('id')->fetch, 'only';
is $customized->id, 1;
ok !$customized->first_name;
t/09-find.t view on Meta::CPAN
ok $first = Customer->objects->find->only('id')->first, 'first->only';
is $first->id, 1;
ok !$first->first_name;
ok $last = Customer->objects->find->last, 'last';
is $last->id, 6;
ok my @list = Customer->objects->find->order_by('id')->desc->fetch, 'order_by, desc';
is $list[4]->id, 2;
undef @list;
ok @list = Customer->objects->find->order_by('id')->asc->fetch, 'order_by, asc';
is $list[4]->id, 5;
ok @list = Customer->objects->find->limit(2)->fetch, 'limit';
is scalar @list, 2;
ok @list = Customer->objects->find->order_by('id')->offset(2)->fetch, 'offset';
is $list[0]->id, 3;
is scalar @list, 4;
undef @list;
$Bill = Customer->objects->find({ first_name => 'Bill' });
ok $Bill->upload;
@list = Customer->objects->find->order_by('first_name')->desc->order_by('id')->asc->fetch;
is $list[0]->first_name, 'Lady', 'order_by does work';
undef @list;
@list = Customer->objects->find->group_by('first_name', 'age')->fetch;
is scalar @list, 5, 'group_by, got 4 objects';
my $count = Customer->objects->find->count;
is $count, 6, 'simple count, got 5';
undef $count;
$count = Customer->objects->find({ first_name => 'Bob' })->count;
is $count, 2, 'count, got 2 Bob\'s';
undef $count;
my @count = Customer->objects->find->group_by('first_name')->count;
is_deeply \@count, [{first_name => '', count => 1}, {first_name => 'Bill', count => 1}, {first_name => 'Bob', count => 2}, {first_name => 'John', count => 1}, {first_name => 'Lady', count => 1}];
@count = Customer->objects->find({ first_name => 'Bob' })->group_by('second_name')->count;
is_deeply \@count, [{second_name => 'Dylan', count => 1}, {second_name => 'Marley', count => 1}], 'count when find by first_name, group by second_name';
$Bill = Customer->objects->find(3)->fetch;
is_deeply $Bill->to_hash, {
first_name => 'Bill',
second_name => 'Clinton',
age => 50,
email => 'mynameisbill@gmail.com',
id => 3,
mixin => undef,
}, 'got undefined mixin in the hash';
$Bill = Customer->objects->find(3)->only('id', 'mixin')->fetch;
is_deeply $Bill->to_hash({ only_defined_fields => 1 }), { id => 3, mixin => 3 }, 'got defined mixin';
done_testing();