@@ -25,12 +25,16 @@ my %args = (
name => 'Teng',
module_name => 'Teng',
- allow_pure_perl => 0,
+ allow_pureperl => 0,
script_files => [glob('script/*'), glob('bin/*')],
+ c_source => [qw()],
+ PL_files => {},
test_files => ((-d '.git' || $ENV{RELEASE_TESTING}) && -d 'xt') ? 't/ xt/' : 't/',
recursive_test_files => 1,
+
+
);
if (-d 'share') {
$args{share_dir} = 'share';
@@ -1,5 +1,14 @@
Revision history for Perl extension Teng
+0.22 2014-04-04T05:22:48Z
+ - FIXED: Allow calling `row_class` before `name` for Teng::Schema::Declare (thanks GeJ)
+ - MODIFY: Allow specify `txn_manager_class` parameter for Teng (thanks GeJ)
+
+0.21 2014-02-14T02:59:46Z
+ - ADD:: Implemented new_row_from_hash which just creates new Teng::Row instance with column data. (thanks karupanerura)
+ - MODIFY: Fixed around multiple primary key. (thanks Songmu)
+
+
0.20 2013-08-21T07:39:25Z
- Re-release for override UNAUTHORIZED flag (same as 0.19)
@@ -23,6 +23,7 @@ lib/Teng/Schema/Declare.pm
lib/Teng/Schema/Dumper.pm
lib/Teng/Schema/Loader.pm
lib/Teng/Schema/Table.pm
+minil.toml
t/001_basic/001_compile.t
t/001_basic/002_schema.t
t/001_basic/003_schema_loader.t
@@ -46,6 +47,7 @@ t/001_basic/030_reconnect_ping.t
t/001_basic/032_reconnect_no_ping.t
t/001_basic/033_load_plugin_with_args.t
t/001_basic/034_execute.t
+t/001_basic/035_new_row_from_hash.t
t/002_common/000_new.t
t/002_common/001_insert.t
t/002_common/002_update.t
@@ -106,6 +108,7 @@ xt/mysql/004_schema_dumper.t
xt/mysql/bulk_insert.t
xt/mysql/common.t
xt/mysql/fork.t
+xt/mysql/multi_pk_autoincr.t
xt/mysql/pager_mysql_found_rows.t
xt/mysql/transaction.t
xt/perlcritic.t
@@ -4,8 +4,10 @@
"Atsushi Kobayashi C<< <nekokak __at__ gmail.com> >>"
],
"dynamic_config" : 0,
- "generated_by" : "Minilla/v0.4.5",
- "license" : "perl_5",
+ "generated_by" : "Minilla/v0.11.0",
+ "license" : [
+ "perl_5"
+ ],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : "2"
@@ -19,7 +21,8 @@
"share",
"eg",
"examples",
- "author"
+ "author",
+ "builder"
]
},
"prereqs" : {
@@ -65,7 +68,7 @@
"provides" : {
"Teng" : {
"file" : "lib/Teng.pm",
- "version" : "0.20"
+ "version" : "0.22"
},
"Teng::Iterator" : {
"file" : "lib/Teng/Iterator.pm"
@@ -130,7 +133,8 @@
"web" : "https://github.com/nekokak/p5-Teng"
}
},
- "version" : "0.20",
+ "version" : "0.22",
+ "x_authority" : "cpan:NEKOKAK",
"x_contributors" : [
"Yuki Ibe <yibe@yibe.org>",
"libkenta <libkenta@gmail.com>",
@@ -145,14 +149,17 @@
"Atsushi Kato <ktat@cpan.org>",
"ktat <ktat.is@gmail.com>",
"Kazuhiro Osawa <yappo@shibuya.pl>",
- "karupanerura <karupa@cpan.org>",
- "Kenta Sato <k-sato@mfac.jp>",
"Masahiro Chiba <chiba@everqueue.com>",
- "Masayuki Matsuki <y.songmu@gmail.com>",
"Atsushi Kobayashi <nekokak@gmail.com>",
"Jun Kuriyama <kuriyama@s2factory.co.jp>",
"Masahiro Nagano <kazeburo@gmail.com>",
- "cho45 <cho45@lowreal.net>",
- "Tokuhiro Matsuno <tokuhirom@gmail.com>"
+ "Tokuhiro Matsuno <tokuhirom@gmail.com>",
+ "k-sato <k-sato@mfac.jp>",
+ "Songmu <y.songmu@gmail.com>",
+ "ITO Nobuaki <daydream.trippers@gmail.com>",
+ "karupanerura <karupa@cpan.org>",
+ "Kenichi Ishigaki <ishigaki@cpan.org>",
+ "Geraud CONTINSOUZAS <geraud@hyaku.nijuu.nana>",
+ "cho45 <cho45@lowreal.net>"
]
}
@@ -13,7 +13,7 @@ configure_requires:
CPAN::Meta::Prereqs: 0
Module::Build: 0.38
dynamic_config: 0
-generated_by: 'Minilla/v0.4.5, CPAN::Meta::Converter version 2.131490'
+generated_by: 'Minilla/v0.11.0, CPAN::Meta::Converter version 2.120921'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -28,44 +28,62 @@ no_index:
- eg
- examples
- author
+ - builder
provides:
Teng:
file: lib/Teng.pm
- version: 0.20
+ version: 0.22
Teng::Iterator:
file: lib/Teng/Iterator.pm
+ version: 0
Teng::Plugin::BulkInsert:
file: lib/Teng/Plugin/BulkInsert.pm
+ version: 0
Teng::Plugin::Count:
file: lib/Teng/Plugin/Count.pm
+ version: 0
Teng::Plugin::FindOrCreate:
file: lib/Teng/Plugin/FindOrCreate.pm
+ version: 0
Teng::Plugin::Lookup:
file: lib/Teng/Plugin/Lookup.pm
+ version: 0
Teng::Plugin::Pager:
file: lib/Teng/Plugin/Pager.pm
+ version: 0
Teng::Plugin::Pager::MySQLFoundRows:
file: lib/Teng/Plugin/Pager/MySQLFoundRows.pm
+ version: 0
Teng::Plugin::Replace:
file: lib/Teng/Plugin/Replace.pm
+ version: 0
Teng::Plugin::SQLPager:
file: lib/Teng/Plugin/SQLPager.pm
+ version: 0
Teng::Plugin::SingleBySQL:
file: lib/Teng/Plugin/SingleBySQL.pm
+ version: 0
Teng::QueryBuilder:
file: lib/Teng/QueryBuilder.pm
+ version: 0
Teng::Row:
file: lib/Teng/Row.pm
+ version: 0
Teng::Schema:
file: lib/Teng/Schema.pm
+ version: 0
Teng::Schema::Declare:
file: lib/Teng/Schema/Declare.pm
+ version: 0
Teng::Schema::Dumper:
file: lib/Teng/Schema/Dumper.pm
+ version: 0
Teng::Schema::Loader:
file: lib/Teng/Schema/Loader.pm
+ version: 0
Teng::Schema::Table:
file: lib/Teng/Schema/Table.pm
+ version: 0
requires:
Carp: 0
Class::Accessor::Lite: 0.05
@@ -81,7 +99,8 @@ resources:
bugtracker: https://github.com/nekokak/p5-Teng/issues
homepage: https://github.com/nekokak/p5-Teng
repository: git://github.com/nekokak/p5-Teng.git
-version: 0.20
+version: 0.22
+x_authority: cpan:NEKOKAK
x_contributors:
- 'Yuki Ibe <yibe@yibe.org>'
- 'libkenta <libkenta@gmail.com>'
@@ -96,12 +115,15 @@ x_contributors:
- 'Atsushi Kato <ktat@cpan.org>'
- 'ktat <ktat.is@gmail.com>'
- 'Kazuhiro Osawa <yappo@shibuya.pl>'
- - 'karupanerura <karupa@cpan.org>'
- - 'Kenta Sato <k-sato@mfac.jp>'
- 'Masahiro Chiba <chiba@everqueue.com>'
- - 'Masayuki Matsuki <y.songmu@gmail.com>'
- 'Atsushi Kobayashi <nekokak@gmail.com>'
- 'Jun Kuriyama <kuriyama@s2factory.co.jp>'
- 'Masahiro Nagano <kazeburo@gmail.com>'
- - 'cho45 <cho45@lowreal.net>'
- 'Tokuhiro Matsuno <tokuhirom@gmail.com>'
+ - 'k-sato <k-sato@mfac.jp>'
+ - 'Songmu <y.songmu@gmail.com>'
+ - 'ITO Nobuaki <daydream.trippers@gmail.com>'
+ - 'karupanerura <karupa@cpan.org>'
+ - 'Kenichi Ishigaki <ishigaki@cpan.org>'
+ - 'Geraud CONTINSOUZAS <geraud@hyaku.nijuu.nana>'
+ - 'cho45 <cho45@lowreal.net>'
@@ -1,3 +1,7 @@
+# NAME
+
+Teng - very simple DBI wrapper/ORMapper
+
# SYNOPSIS
my $db = MyDB->new({ connect_info => [ 'dbi:SQLite:' ] });
@@ -36,7 +40,6 @@ in your script.
use Your::Model;
-
my $teng = Your::Model->new(\%args);
# insert new record.
my $row = $teng->insert('user',
@@ -145,6 +148,11 @@ Teng provides a number of methods to all your classes,
Specifies the schema class to use.
By default {YOUR\_MODEL\_CLASS}::Schema is used.
+ - `txn_manager_class`
+
+ Specifies the transaction manager class.
+ By default DBIx::TransactionManager is used.
+
- `suppress_row_objects`
Specifies the row object creation mode. By default this value is `false`.
@@ -258,7 +266,7 @@ Teng provides a number of methods to all your classes,
simple search method.
search method get Teng::Iterator's instance object.
- see [Teng::Iterator](http://search.cpan.org/perldoc?Teng::Iterator)
+ see [Teng::Iterator](https://metacpan.org/pod/Teng::Iterator)
get iterator:
@@ -275,6 +283,14 @@ Teng provides a number of methods to all your classes,
my $row = $teng->single('user',{id =>1});
+- `$row = $teng->new_row_from_hash($table_name, \%row_data, [$sql])`
+
+ create row object from data. (not fetch from db.)
+ It's useful in such as testing.
+
+ my $row = $teng->new_row_from_hash('user', { id => 1, foo => "bar" });
+ say $row->foo; # say bar
+
- `$itr = $teng->search_named($sql, [\%bind_values, [$table_name]])`
execute named query
@@ -349,15 +365,15 @@ Teng provides a number of methods to all your classes,
If an exception occurs, or the guard object otherwise leaves the scope
before `$txn->commit` is called, the transaction will be rolled
- back by an explicit ["txn\_rollback"](#txn\_rollback) call. In essence this is akin to
- using a ["txn\_begin"](#txn\_begin)/["txn\_commit"](#txn\_commit) pair, without having to worry
- about calling ["txn\_rollback"](#txn\_rollback) at the right places. Note that since there
+ back by an explicit ["txn\_rollback"](#txn_rollback) call. In essence this is akin to
+ using a ["txn\_begin"](#txn_begin)/["txn\_commit"](#txn_commit) pair, without having to worry
+ about calling ["txn\_rollback"](#txn_rollback) at the right places. Note that since there
is no defined code closure, there will be no retries and other magic upon
database disconnection.
- `$txn_manager = $teng->txn_manager`
- Get the DBIx::TransactionManager instance.
+ Create the transaction manager instance with specified `txn_manager_class`.
- `$teng->txn_begin`
@@ -377,7 +393,7 @@ Teng provides a number of methods to all your classes,
- `$teng->do($sql, [\%option, @bind_values])`
- Execute the query specified by `$sql`, using `%option` and `@bind_values` as necessary. This pretty much a wrapper around [http://search.cpan.org/dist/DBI/DBI.pm\#do](http://search.cpan.org/dist/DBI/DBI.pm\#do)
+ Execute the query specified by `$sql`, using `%option` and `@bind_values` as necessary. This pretty much a wrapper around [http://search.cpan.org/dist/DBI/DBI.pm#do](http://search.cpan.org/dist/DBI/DBI.pm#do)
- `$teng->dbh`
@@ -421,17 +437,17 @@ Teng provides a number of methods to all your classes,
- How do you use display the profiling result?
- use [Devel::KYTProf](http://search.cpan.org/perldoc?Devel::KYTProf).
+ use [Devel::KYTProf](https://metacpan.org/pod/Devel::KYTProf).
# TRIGGERS
-Teng does not support triggers (NOTE: do not confuse it with SQL triggers - we're talking about Perl level triggers). If you really want to hook into the various methods, use something like [Moose](http://search.cpan.org/perldoc?Moose), [Mouse](http://search.cpan.org/perldoc?Mouse), and [Class::Method::Modifiers](http://search.cpan.org/perldoc?Class::Method::Modifiers).
+Teng does not support triggers (NOTE: do not confuse it with SQL triggers - we're talking about Perl level triggers). If you really want to hook into the various methods, use something like [Moose](https://metacpan.org/pod/Moose), [Mouse](https://metacpan.org/pod/Mouse), and [Class::Method::Modifiers](https://metacpan.org/pod/Class::Method::Modifiers).
# SEE ALSO
## Fork
-This module was forked from [DBIx::Skinny](http://search.cpan.org/perldoc?DBIx::Skinny), around version 0.0732.
+This module was forked from [DBIx::Skinny](https://metacpan.org/pod/DBIx::Skinny), around version 0.0732.
many incompatible changes have been made.
# BUGS AND LIMITATIONS
@@ -458,7 +474,7 @@ Daisuke Maki `<daisuke@endeworks.jp>`
# LICENCE AND COPYRIGHT
-Copyright (c) 2010, the Teng ["AUTHOR"](#AUTHOR). All rights reserved.
+Copyright (c) 2010, the Teng ["AUTHOR"](#author). All rights reserved.
This module is free software; you can redistribute it and/or
-modify it under the same terms as Perl itself. See [perlartistic](http://search.cpan.org/perldoc?perlartistic).
+modify it under the same terms as Perl itself. See [perlartistic](https://metacpan.org/pod/perlartistic).
@@ -91,7 +91,7 @@ sub table(&) {
no warnings 'once';
local *{"$dest_class\::name"} = sub ($) {
$table_name = shift;
- $row_class = row_namespace($table_name);
+ $row_class ||= row_namespace($table_name);
};
local *{"$dest_class\::pk"} = sub (@) { @table_pk = @_ };
local *{"$dest_class\::columns"} = sub (@) { @table_columns = @_ };
@@ -139,21 +139,13 @@ create new Teng::Schema::Table's object.
get column SQL type.
-=item $table->get_deflator
+=item $table->add_deflator($column_rule, $code)
-get deflate code reference.
+add deflate code reference.
-=item $table->get_inflator
+=item $table->add_inflator($column_rule, $code)
-get inflate code reference.
-
-=item $table->set_deflator
-
-set deflate code reference.
-
-=item $table->set_inflator
-
-set inflate code reference.
+add inflate code reference.
=item $table->call_deflate
@@ -24,7 +24,7 @@ use Class::Accessor::Lite
)]
;
-our $VERSION = '0.20';
+our $VERSION = '0.22';
sub load_plugin {
my ($class, $pkg, $opt) = @_;
@@ -341,14 +341,27 @@ sub insert {
my $table = $self->schema->get_table($table_name);
my $pk = $table->primary_keys();
- if (scalar(@$pk) == 1 && not defined $args->{$pk->[0]}) {
- $args->{$pk->[0]} = $self->_last_insert_id($table_name);
+
+ my @missing_primary_keys = grep { not defined $args->{$_} } @$pk;
+ if (@missing_primary_keys == 1) {
+ $args->{$missing_primary_keys[0]} = $self->_last_insert_id($table_name);
}
return $args if $self->suppress_row_objects;
- if (scalar(@$pk) == 1) {
- return $self->single($table_name, {$pk->[0] => $args->{$pk->[0]}});
+ my %where;
+ my $refetch = 1;
+ for my $key (@$pk) {
+ if (ref $args->{$key}) {
+ # care references. eg. \'NOW()'
+ $refetch = undef;
+ last;
+ }
+ $where{$key} = $args->{$key};
+ }
+ if (%where && $refetch) {
+ # refetch the row for cleanup scalar refs and fill default values
+ return $self->single($table_name, \%where);
}
$table->row_class->new(
@@ -440,7 +453,9 @@ sub delete {
sub txn_manager {
my $self = shift;
$self->_verify_pid;
- $self->{txn_manager} ||= DBIx::TransactionManager->new($self->dbh);
+ $self->{txn_manager} ||= ($self->{txn_manager_class})
+ ? $self->{txn_manager_class}->new($self->dbh)
+ : DBIx::TransactionManager->new($self->dbh);
}
sub in_transaction_check {
@@ -607,6 +622,32 @@ sub single_by_sql {
);
}
+sub new_row_from_hash {
+ my ($self, $table_name, $data, $sql) = @_;
+
+ my $table = $self->{schema}->get_table( $table_name );
+ Carp::croak("No such table $table_name") unless $table;
+
+ return $data if $self->{suppress_row_objects};
+
+ $table->{row_class}->new(
+ {
+ sql => $sql || do {
+ my @caller = caller(0);
+ my $level = 0;
+ while ($caller[0] eq __PACKAGE__ || $caller[0] eq ref $self) {
+ @caller = caller(++$level);
+ }
+ sprintf '/* DUMMY QUERY %s->new_row_from_hash created from %s line %d */', ref $self, $caller[1], $caller[2];
+ },
+ row_data => $data,
+ teng => $self,
+ table => $table,
+ table_name => $table_name,
+ }
+ );
+}
+
sub single_named {
my ($self, $sql, $args, $table_name) = @_;
@@ -801,6 +842,11 @@ instantiated for you.
Specifies the schema class to use.
By default {YOUR_MODEL_CLASS}::Schema is used.
+=item * C<txn_manager_class>
+
+Specifies the transaction manager class.
+By default DBIx::TransactionManager is used.
+
=item * C<suppress_row_objects>
Specifies the row object creation mode. By default this value is C<false>.
@@ -933,6 +979,14 @@ give back one case of the beginning when it is acquired plural records by single
my $row = $teng->single('user',{id =>1});
+=item C<$row = $teng-E<gt>new_row_from_hash($table_name, \%row_data, [$sql])>
+
+create row object from data. (not fetch from db.)
+It's useful in such as testing.
+
+ my $row = $teng->new_row_from_hash('user', { id => 1, foo => "bar" });
+ say $row->foo; # say bar
+
=item C<$itr = $teng-E<gt>search_named($sql, [\%bind_values, [$table_name]])>
execute named query
@@ -1015,7 +1069,7 @@ database disconnection.
=item C<$txn_manager = $teng-E<gt>txn_manager>
-Get the DBIx::TransactionManager instance.
+Create the transaction manager instance with specified C<txn_manager_class>.
=item C<$teng-E<gt>txn_begin>
@@ -0,0 +1,2 @@
+authority = "cpan:NEKOKAK"
+
@@ -38,6 +38,20 @@ use Test::More;
row_class 'Mock::BasicRow::FooRow';
};
+ table {
+ row_class 'Mock::BasicRow::BarRow';
+ name 'mock_basic_row_bar';
+ pk 'id';
+ columns qw/
+ id
+ name
+ /;
+ };
+ package Mock::BasicRow::BarRow;
+ use strict;
+ use warnings;
+ use base 'Teng::Row';
+
package Mock::BasicRow::FooRow;
use strict;
use warnings;
@@ -85,6 +99,7 @@ subtest 'your row class' => sub {
subtest 'row_class specific Schema.pm' => sub {
is +$db_basic_row->schema->get_row_class('mock_basic_row_foo'), 'Mock::BasicRow::FooRow';
+ is +$db_basic_row->schema->get_row_class('mock_basic_row_bar'), 'Mock::BasicRow::BarRow';
};
subtest 'handle' => sub {
@@ -3,6 +3,8 @@ use Test::More;
use Mock::Basic;
use Test::SharedFork;
+plan skip_all => 'not for Win32' if $^O eq 'MSWin32';
+
my $dbh = t::Utils->setup_dbh;
my $db = Mock::Basic->new({dbh => $dbh});
$db->setup_test_db;
@@ -3,6 +3,8 @@ use Test::More;
use Mock::Basic;
use Test::SharedFork;
+plan skip_all => 'not for Win32' if $^O eq 'MSWin32';
+
unlink './t/main.db' if -f './t/main.db';
my $db = Mock::Basic->new(
{
@@ -3,6 +3,8 @@ use Test::More;
use Mock::Basic;
use Test::SharedFork;
+plan skip_all => 'not for Win32' if $^O eq 'MSWin32';
+
my $dbh = t::Utils->setup_dbh('./t/main.db');
my $db = Mock::Basic->new({dbh => $dbh});
$db->setup_test_db;
@@ -0,0 +1,39 @@
+use t::Utils;
+use Mock::Basic;
+use Test::More;
+
+my $dbh = t::Utils->setup_dbh;
+my $db_basic = Mock::Basic->new({dbh => $dbh});
+$db_basic->setup_test_db;
+
+subtest 'new_row_from_hash method' => sub {
+ my $raw_data = [
+ {
+ id => 1,
+ name => 'perl',
+ },
+ {
+ id => 2,
+ name => 'ruby',
+ },
+ {
+ id => 3,
+ name => 'python',
+ },
+ ];
+
+ my $rows = [ map { $db_basic->new_row_from_hash(mock_basic => $_) } @$raw_data ];
+ is $rows->[0]->{sql}, sprintf('/* DUMMY QUERY Mock::Basic->new_row_from_hash created from %s line %d */', __FILE__, __LINE__ - 1);
+ isa_ok $_, 'Teng::Row' for @$rows;
+ is $rows->[0]->id, 1;
+ is $rows->[1]->id, 2;
+ is $rows->[2]->id, 3;
+
+ subtest 'with sql' => sub {
+ my $sql = 'SELECT * FROM mock_basic WHERE id = 1';
+ is $db_basic->new_row_from_hash(mock_basic => $raw_data->[0], $sql)->{sql}, $sql;
+ };
+};
+
+done_testing;
+
@@ -133,7 +133,7 @@ my $guard = MyGuard->new(sub { unlink $db_file });
$row = $teng->insert( 'a_multi_pk_table', { id_a => 3, id_b => 40 } );
- is_deeply( $row->get_columns, { id_a => 3, id_b => 40 } );
+ is_deeply( $row->get_columns, { id_a => 3, id_b => 40, memo => 'foobar' } );
@rows = $teng->search( 'a_multi_pk_table', { id_a => 3 } );
is( 0+@rows, 4 );
@@ -214,7 +214,7 @@ my $guard = MyGuard->new(sub { unlink $db_file });
$row = $teng->insert( 'c_multi_pk_table', { id_c => 3, id_d => 40 } );
- is_deeply( $row->get_columns, { id_c => 3, id_d => 40 } );
+ is_deeply( $row->get_columns, { id_c => 3, id_d => 40, memo => 'foobar' } );
@rows = $teng->search( 'c_multi_pk_table', { id_c => 3 } );
is( 0+@rows, 4 );
@@ -12,6 +12,7 @@ subtest 'scalar data bug case' => sub {
my $row = $db->insert('mock_inflate',{
id => 1,
+ bar => 'baz',
name => 'azumakuniyuki',
});
@@ -0,0 +1,53 @@
+use xt::Utils::mysql;
+use Test::More;
+
+{
+ package Mock::MultiPK;
+ use parent 'Teng';
+
+ sub setup_test_db {
+ my $self = shift;
+
+ for my $table ( qw( multi_pk_table ) ) {
+ $self->do(qq{
+ DROP TABLE IF EXISTS $table
+ });
+ }
+
+ {
+ $self->do(q{
+ CREATE TABLE multi_pk_table (
+ id INTEGER AUTO_INCREMENT,
+ created_at DATETIME,
+ memo VARCHAR(255) NOT NULL DEFAULT 'foobar',
+ PRIMARY KEY( id, created_at )
+ )
+ });
+ }
+ }
+
+ package Mock::MultiPK::Schema;
+ use utf8;
+ use Teng::Schema::Declare;
+
+ table {
+ name 'multi_pk_table';
+ pk qw( id created_at );
+ columns qw( id created_at memo );
+ };
+}
+
+my $dbh = t::Utils->setup_dbh;
+my $db = Mock::MultiPK->new({dbh => $dbh});
+$db->setup_test_db;
+
+my $row = $db->insert('multi_pk_table', {
+ created_at => '2000-11-11',
+ memo => 'blah',
+});
+
+isa_ok $row, 'Mock::MultiPK::Row::MultiPkTable';
+ok $row->id;
+is $row->memo, 'blah';
+
+done_testing;