Showing 6 changed files with 646 additions and 27 deletions
-23
Build.PL
... ...
@@ -1,23 +0,0 @@
1
-use strict;
2
-use warnings;
3
-use Module::Build;
4
-
5
-my $builder = Module::Build->new(
6
-    module_name         => 'DBIx::Custom',
7
-    license             => 'perl',
8
-    dist_author         => 'Yuki Kimoto <kimoto.yuki@gmail.com>',
9
-    dist_version_from   => 'lib/DBIx/Custom.pm',
10
-    build_requires => {
11
-        'Test::More' => 0,
12
-    },
13
-    requires => {
14
-        'Object::Simple' => 3.0201,
15
-        'DBI'            => 1.605,
16
-        'DBD::SQLite'    => 1.25,
17
-        'Time::Piece'    => 1.15
18
-    },
19
-    add_to_cleanup      => [ 'DBIx-Custom-*' ],
20
-    create_makefile_pl => 'traditional',
21
-);
22
-
23
-$builder->create_build_script();
+3
Changes
... ...
@@ -1,3 +1,6 @@
1
+0.1619
2
+  updated document
3
+  added experimental expand method
1 4
 0.1618
2 5
   added helper method
3 6
   added begin_work, commit, and rollback method
+1 -1
README
... ...
@@ -1,6 +1,6 @@
1 1
 DBIx-Custom
2 2
 
3
-Custamizable DBI
3
+DBI interface, having hash parameter binding and filtering system
4 4
 
5 5
 INSTALLATION
6 6
 
+33 -2
lib/DBIx/Custom.pm
... ...
@@ -1,6 +1,6 @@
1 1
 package DBIx::Custom;
2 2
 
3
-our $VERSION = '0.1618';
3
+our $VERSION = '0.1619';
4 4
 
5 5
 use 5.008001;
6 6
 use strict;
... ...
@@ -256,6 +256,21 @@ sub execute{
256 256
     return $affected;
257 257
 }
258 258
 
259
+sub expand {
260
+    my $self = shift;
261
+    my $source = ref $_[0] eq 'HASH' ? $_[0] : {@_};
262
+    my $table = (keys %$source)[0];
263
+    my $param = $source->{$table};
264
+    
265
+    # Expand table name
266
+    my $expand = {};
267
+    foreach my $column (keys %$param) {
268
+        $expand->{"$table.$column"} = $param->{$column};
269
+    }
270
+    
271
+    return %$expand;
272
+}
273
+
259 274
 our %VALID_INSERT_ARGS = map { $_ => 1 } qw/table param append filter/;
260 275
 
261 276
 sub insert {
... ...
@@ -646,7 +661,7 @@ so you have to execute raw SQL in the end.
646 661
 
647 662
 L<DBIx::Custom> is middle area between L<DBI> and O/R mapper.
648 663
 L<DBIx::Custom> provide flexible hash parameter binding and filtering system,
649
-and suger methods, such as C<select()>, C<update()>, C<delete()>, C<select()>
664
+and suger methods, such as C<insert()>, C<update()>, C<delete()>, C<select()>
650 665
 to execute SQL easily.
651 666
 
652 667
 L<DBIx::Custom> respects SQL. SQL is very complex and not beautiful,
... ...
@@ -838,6 +853,22 @@ B<Example:>
838 853
         my $title  = $row->[1];
839 854
     }
840 855
 
856
+=head2 C<(experimental) expand>
857
+
858
+    my %expand = $dbi->expand($source);
859
+
860
+The following hash
861
+
862
+    {books => {title => 'Perl', author => 'Ken'}}
863
+
864
+is expanded to
865
+
866
+    ('books.title' => 'Perl', 'books.author' => 'Ken')
867
+
868
+This is used in C<select()>
869
+
870
+
871
+    
841 872
 =head2 C<delete>
842 873
 
843 874
     $dbi->delete(table  => $table,
+594
lib/DBIx/Custom/Guides/Ja.pod
... ...
@@ -0,0 +1,594 @@
1
+=encoding utf8
2
+
3
+=head1 名前
4
+
5
+DBIx::Custom::Guides::Ja - DBIx::Customの日本語のガイド
6
+
7
+=head1 ガイド
8
+
9
+L<DBIx::Custom>はデータベースへのクエリの発行を簡単に行うための
10
+クラスです。L<DBIx::Class>やL<DBIx::Simple>と同じように
11
+L<DBI>のラッパクラスになっています。
12
+
13
+L<DBIx::Custom>はO/Rマッパーではありません。O/Rマッパーは
14
+便利ですが、O/Rマッパのたくさんの文法を覚える必要があります。
15
+また、O/Rマッパによって生成されたSQLは非効率なことがあり、
16
+生のSQLを発行しなければならないこともあります。
17
+
18
+L<DBIx::Custom>はO/RマッパとL<DBI>の中間に位置するモジュールです。
19
+L<DBIx::Custom>は柔軟なハッシュパラメータバインディングとフィルタリング
20
+のシステムを提供します。またSQLを簡単に実行するための、
21
+C<insert()>, C<update()>, C<delete()>,C<select()>などの
22
+シュガーメソッドも提供します。
23
+
24
+L<DBIx::Custom>はSQLを尊重します。SQLはとても複雑で、美しくはありません。
25
+けれども、SQLはデファクトスタンダードな技術です。
26
+ですので、データベースを学ぶすべての人はSQLを知っています。
27
+あなたがすでにSQLを知っているなら、L<DBIx::Custom>を使って
28
+何かを行うために覚えることはとても少ないです。
29
+
30
+では使い方を解説します。
31
+
32
+=head2 1. データベースへの接続
33
+
34
+L<DBIx::Custom>オブジェクトを生成し、データベースに接続するには
35
+C<connect()>メソッドを使用します。
36
+
37
+    use DBIx::Custom;
38
+    my $dbi = DBIx::Custom->connect(data_source => "dbi:mysql:database=dbname",
39
+                                    user => 'ken', password => '!LFKD%$&');
40
+
41
+データベースがSQLiteであれば、代わりにL<DBIx::Custom::SQLite>を使うと
42
+データベースへの接続が簡単です。
43
+
44
+    use DBIx::Custom::SQLite;
45
+    my $dbi = DBIx::Custom::SQLite->connect(database => 'dbname');
46
+
47
+データベースがMySQLであれば、代わりにL<DBIx::Custom::MySQL>を使うと
48
+データベースへの接続が簡単です。
49
+
50
+    use DBIx::Custom::MySQL;
51
+    my $dbi = DBIx::Custom::MySQL->connect(
52
+        database => 'dbname',
53
+        user     => 'ken',
54
+        password => '!LFKD%$&'
55
+    );
56
+
57
+L<DBIx::Custom>はL<DBI>のラッパです。
58
+L<DBI>オブジェクトはC<dbh>で取得することができます。
59
+
60
+    my $dbh = $dbi->dbh;
61
+
62
+データベースハンドル属性にはデフォルトで次のものが設定されます。
63
+    
64
+    $dbi->dbh->{RaiseError} = 1;
65
+    $dbi->dbh->{PrintError} = 0;
66
+    $dbi->dbh->{AutoCommit} = 1;
67
+
68
+この設定を行っているので、致命的なエラーが起こると、
69
+例外が発生しプログラムは終了します。
70
+またクエリが発行されると自動的にコミットされます。
71
+
72
+=head2 2. シュガーメソッド
73
+
74
+L<DBIx::Custom>は、
75
+C<insert()>、C<update()>、C<delete()>、C<select()>
76
+のようなシュガーメソッドを持っています。
77
+小さなことを行うのであれば、SQL文を
78
+作成する必要はありません。
79
+
80
+=head3 C<insert()>
81
+
82
+C<insert>メソッドです。データベースにデータを挿入します。
83
+
84
+    $dbi->insert(table  => 'books',
85
+                 param  => {title => 'Perl', author => 'Ken'});
86
+
87
+これは次のL<DBI>の操作と同じです。
88
+
89
+    my $sth = $dbh->prepare('insert into (title, author) values (?, ?);');
90
+    $sth->execute('Perl', 'Ken');
91
+
92
+=head3 C<update()>
93
+
94
+C<update>メソッドです。データベースのデータを更新します。
95
+
96
+    $dbi->update(table  => 'books', 
97
+                 param  => {title => 'Perl', author => 'Ken'}, 
98
+                 where  => {id => 5});
99
+
100
+これは次のL<DBI>の操作と同じです。
101
+
102
+    my $sth = $dbh->prepare(
103
+        'update books set title = ?, author = ? where id = ?;');
104
+    $sth->execute('Perl', 'Ken', 5);
105
+
106
+C<update>メソッドは安全のため
107
+where句のないSQLを発行することを許可していません。
108
+もしすべての行を更新したい場合は
109
+C<update_all()>メソッドを使用してください。
110
+
111
+    $dbi->update_all(table  => 'books', 
112
+                     param  => {title => 'Perl', author => 'Ken'});
113
+
114
+=head3 C<delete()>
115
+
116
+C<delete>メソッドです。データベースのデータを削除します。
117
+
118
+    $dbi->delete(table  => 'books',
119
+                 where  => {author => 'Ken'});
120
+
121
+これは次のL<DBI>の操作と同じです。
122
+
123
+    my $sth = $dbh->prepare('delete from books where id = ?;');
124
+    $sth->execute('Ken');
125
+
126
+C<delete>メソッドは安全のため
127
+where句のないSQLを発行することを許可していません。
128
+もしすべての行を削除したい場合は
129
+C<delete_all()>メソッドを使用してください。
130
+
131
+    $dbi->delete_all(table  => 'books');
132
+
133
+=head3 C<select()>
134
+
135
+C<select>メソッドです。テーブル名だけを指定しています。
136
+
137
+    my $result = $dbi->select(table => 'books');
138
+
139
+これは次のL<DBI>の操作と同じです。
140
+
141
+    my $sth = $dbh->prepare('select * from books;);
142
+    $sth->execute;
143
+
144
+C<select()>メソッドの戻り値はL<DBIx::Custom::Result>
145
+オブジェクトです。C<fetch>メソッドを使用して
146
+行をフェッチすることができます。
147
+
148
+    while (my $row = $result->fetch) {
149
+        my $title  = $row->[0];
150
+        my $author = $row->[1];
151
+    }
152
+
153
+次のC<select>は行の名前とwhere句を指定したものです。
154
+
155
+    my $result = $dbi->select(
156
+        table  => 'books',
157
+        column => [qw/author title/],
158
+        where  => {author => 'Ken'}
159
+    );
160
+
161
+次のL<DBI>の操作と同じです。
162
+    
163
+    my $sth = $dbh->prepare(
164
+        'select author, title from books where author = ?;');
165
+    $sht->execute('Ken');
166
+
167
+テーブルをjoinしたい場合はC<relation>を使用します。
168
+
169
+    my $result = $dbi->select(
170
+        table    => ['books', 'rental'],
171
+        column   => ['books.name as book_name']
172
+        relation => {'books.id' => 'rental.book_id'}
173
+    );
174
+
175
+次のL<DBI>の操作と同じです。
176
+
177
+    my $sth = $dbh->prepare(
178
+        'select books.name as book_name from books, rental' .
179
+        'where books.id = rental.book_id;');
180
+    $sth->execute;
181
+
182
+=head3 C<append>オプション
183
+
184
+SQL文の末尾に文字列を追加したい場合は<append>オプションを使用します。
185
+
186
+    my $result = $dbi->select(
187
+        table  => 'books',
188
+        where  => {author => 'Ken'},
189
+        append => 'order by price limit 5',
190
+    );
191
+
192
+次のL<DBI>の操作と同じです。
193
+
194
+    my $sth = $dbh->prepare(
195
+        'select * books where author = ? order by price limit 5;');
196
+    $sth->execute;
197
+
198
+C<append>オプションは、C<insert()>、C<update()>、C<update_all()>
199
+C<delete()>、C<select>メソッドで使用することが
200
+できます。
201
+
202
+=head3 C<filter>オプション
203
+
204
+この後のフィルタリングの解説で詳しく扱いますが、値をフィルタリングしたい
205
+場合はC<filter>オプションを使用することができます。
206
+
207
+    $dbi->insert(table  => 'books',
208
+                 param  => {title => 'Perl', author => 'Ken'});
209
+                 filter => {title  => 'encode_utf8',
210
+                            author => 'encode_utf8'});
211
+
212
+C<filter>オプションは、C<insert()>、C<update()>、C<update_all()>
213
+C<delete()>、C<select>メソッドで使用することが
214
+できます。
215
+
216
+=head2 3. 行のフェッチ
217
+
218
+C<select()>メソッドの戻り値であるL<DBIx::Custom::Result>
219
+には行をフェッチするためのさまざまなメソッドが
220
+用意されています。
221
+(このセクションの解説では「配列」は「配列のリファレンス」を
222
+「ハッシュ」は「ハッシュのリファレンス」を意味しますので
223
+注意してください。)
224
+
225
+=head3 C<fetch>
226
+
227
+一行フェッチして配列に格納します。
228
+
229
+    while (my $row = $result->fetch) {
230
+        my $author = $row->[0];
231
+        my $title  = $row->[1];
232
+    }
233
+
234
+=head3 C<fetch_first>
235
+
236
+一行だけフェッチして配列に格納します。
237
+
238
+    my $row = $result->fetch_first;
239
+
240
+フェッチが終わった後は、ステートメントハンドルからC<finish()>
241
+メソッドが呼び出されてそれ以上フェッチできなくなります。
242
+
243
+=head3 C<fetch_multi>
244
+
245
+複数行をフェッチして配列の配列に格納します。
246
+
247
+    while (my $rows = $result->fetch_multi(5)) {
248
+        my $first_author  = $rows->[0][0];
249
+        my $first_title   = $rows->[0][1];
250
+        my $second_author = $rows->[1][0];
251
+        my $second_value  = $rows->[1][1];
252
+    }
253
+
254
+=head3 C<fetch_all>
255
+
256
+すべての行をフェッチして配列の配列に格納します。
257
+
258
+    my $rows = $result->fetch_all;
259
+
260
+=head3 C<fetch_hash>
261
+
262
+一行フェッチしてハッシュに格納します。
263
+
264
+    while (my $row = $result->fetch_hash) {
265
+        my $title  = $row->{title};
266
+        my $author = $row->{author};
267
+    }
268
+
269
+=head3 C<fetch_hash_first>
270
+
271
+一行だけフェッチしてハッシュに格納します。
272
+
273
+    my $row = $result->fetch_hash_first;
274
+
275
+フェッチが終わった後は、ステートメントハンドルからC<finish()>
276
+メソッドが呼び出されてそれ以上フェッチできなくなります。
277
+
278
+=head3 C<fetch_hash_multi>
279
+
280
+複数行をフェッチしてハッシュの配列に格納します。
281
+
282
+    while (my $rows = $result->fetch_hash_multi(5)) {
283
+        my $first_title   = $rows->[0]{title};
284
+        my $first_author  = $rows->[0]{author};
285
+        my $second_title  = $rows->[1]{title};
286
+        my $second_author = $rows->[1]{author};
287
+    }
288
+
289
+=head3 C<fetch_all>
290
+
291
+すべての行をフェッチしてハッシュの配列に格納します。
292
+
293
+    my $rows = $result->fetch_hash_all;
294
+
295
+=head3 C<sth>
296
+
297
+L<DBI>のステートメントハンドルにアクセスしたい場合は
298
+<sth>を使用します。
299
+
300
+    my $sth = $result->sth;
301
+
302
+=head2 4. Hash parameter binding
303
+
304
+L<DBIx::Custom> provides hash parameter binding.
305
+
306
+At frist, I show normal parameter binding.
307
+
308
+    use DBI;
309
+    my $dbh = DBI->connect(...);
310
+    my $sth = $dbh->prepare(
311
+        "select * from books where author = ? and title like ?;"
312
+    );
313
+    $sth->execute('Ken', '%Perl%');
314
+
315
+This is very good way because database system can enable SQL caching,
316
+and parameter is quoted automatically. this is secure.
317
+
318
+L<DBIx::Custom> hash parameter binding system improve
319
+normal parameter binding to use hash parameter.
320
+
321
+    my $result = $dbi->execute(
322
+        "select * from books where {= author} and {like title};"
323
+        param => {author => 'Ken', title => '%Perl%'}
324
+    );
325
+
326
+This is same as the normal way, execpt that the parameter is hash.
327
+{= author} and {like title} is called C<tag>.
328
+tag is expand to placeholder string internally.
329
+
330
+    select * from books where {= author} and {like title}
331
+      -> select * from books where author = ? and title like ?;
332
+
333
+The following tags is available.
334
+
335
+    [TAG]                       [REPLACED]
336
+    {? NAME}               ->   ?
337
+    {= NAME}               ->   NAME = ?
338
+    {<> NAME}              ->   NAME <> ?
339
+    
340
+    {< NAME}               ->   NAME < ?
341
+    {> NAME}               ->   NAME > ?
342
+    {>= NAME}              ->   NAME >= ?
343
+    {<= NAME}              ->   NAME <= ?
344
+    
345
+    {like NAME}            ->   NAME like ?
346
+    {in NAME COUNT}        ->   NAME in [?, ?, ..]
347
+    
348
+    {insert_param NAME1 NAME2}   ->   (NAME1, NAME2) values (?, ?)
349
+    {update_param NAME1 NAME2}   ->   set NAME1 = ?, NAME2 = ?
350
+
351
+See also L<DBIx::Custom::QueryBuilder>.
352
+
353
+C<{> and C<}> is reserved. If you use these charactors,
354
+you must escape them using '\'. Note that '\' is
355
+already perl escaped charactor, so you must write '\\'. 
356
+
357
+    'select * from books \\{ something statement \\}'
358
+
359
+=head2 5. Filtering
360
+
361
+Usually, Perl string is kept as internal string.
362
+If you want to save the string to database, You must encode the string.
363
+Filtering system help you to convert a data to another data
364
+when you save to the data and get the data form database.
365
+
366
+If you want to register filter, use C<register_filter()> method.
367
+
368
+    $dbi->register_filter(
369
+        to_upper_case => sub {
370
+            my $value = shift;
371
+            return uc $value;
372
+        }
373
+    );
374
+
375
+C<encode_utf8> and C<decode_utf8> filter is registerd by default.
376
+
377
+You can specify these filters to C<filter> argument of C<execute()> method.
378
+
379
+    my $result = $dbi->execute(
380
+        "select * from books where {= author} and {like title};"
381
+        param  => {author => 'Ken', title => '%Perl%'},
382
+        filter => {author => 'to_upper_case, title => 'encode_utf8'}
383
+    );
384
+
385
+C<filter> argument can be specified to suger methods, such as
386
+C<insert()>, C<update()>, C<update_all()>,
387
+C<delete()>, C<delete_all()>, C<select()>.
388
+
389
+    # insert(), having filter argument
390
+    $dbi->insert(table  => 'books',
391
+                 param  => {title => 'Perl', author => 'Ken'},
392
+                 filter => {title => 'encode_utf8'});
393
+    
394
+    # select(), having filter argument
395
+    my $result = $dbi->select(
396
+        table  => 'books',
397
+        column => [qw/author title/],
398
+        where  => {author => 'Ken'},
399
+        append => 'order by id limit 1',
400
+        filter => {title => 'encode_utf8'}
401
+    );
402
+
403
+Filter works each parmeter, but you prepare default filter for all parameters.
404
+
405
+    $dbi->default_bind_filter('encode_utf8');
406
+
407
+C<filter()> argument overwrites this default filter.
408
+    
409
+    $dbi->default_bind_filter('encode_utf8');
410
+    $dbi->insert(
411
+        table  => 'books',
412
+        param  => {title => 'Perl', author => 'Ken', price => 1000},
413
+        filter => {author => 'to_upper_case', price => undef}
414
+    );
415
+
416
+This is same as the following example.
417
+
418
+    $dbi->insert(
419
+        table  => 'books',
420
+        param  => {title => 'Perl', author => 'Ken', price => 1000},
421
+        filter => {title => 'encode_uft8' author => 'to_upper_case'}
422
+    );
423
+
424
+You can also specify filter when the row is fetched. This is reverse of bind filter.
425
+
426
+    my $result = $dbi->select(table => 'books');
427
+    $result->filter({title => 'decode_utf8', author => 'to_upper_case'});
428
+
429
+Filter works each column value, but you prepare a default filter
430
+for all clumn value.
431
+
432
+    $dbi->default_fetch_filter('decode_utf8');
433
+
434
+C<filter()> method of L<DBIx::Custom::Result>
435
+overwrites this default filter.
436
+
437
+    $dbi->default_fetch_filter('decode_utf8');
438
+    my $result = $dbi->select(
439
+        table => 'books',
440
+        columns => ['title', 'author', 'price']
441
+    );
442
+    $result->filter({author => 'to_upper_case', price => undef});
443
+
444
+This is same as the following one.
445
+
446
+    my $result = $dbi->select(
447
+        table => 'books',
448
+        columns => ['title', 'author', 'price']
449
+    );
450
+    $result->filter({title => 'decode_utf8', author => 'to_upper_case'});
451
+
452
+Note that in fetch filter, column names must be lower case
453
+even if the column name conatains upper case charactors.
454
+This is requirment not to depend database systems.
455
+
456
+=head2 6. Get high performance
457
+
458
+=head3 Disable filter checking
459
+
460
+Filter checking is executed by default.
461
+This is done to check right filter name is specified,
462
+but sometimes damage performance.
463
+
464
+If you disable this filter checking,
465
+Set C<filter_check> attribute to 0.
466
+
467
+    $dbi->filter_check(0);
468
+
469
+=head3 Use execute() method instead suger methods
470
+
471
+If you execute insert statement by C<insert()> method,
472
+you sometimes can't get required performance.
473
+
474
+C<insert()> method is a little slow because SQL statement and statement handle
475
+is created every time.
476
+
477
+In that case, you can prepare a query by C<create_query()> method.
478
+    
479
+    my $query = $dbi->create_query(
480
+        "insert into books {insert_param title author};"
481
+    );
482
+
483
+Return value of C<create_query()> is L<DBIx::Custom::Query> object.
484
+This keep the information of SQL and column names.
485
+
486
+    {
487
+        sql     => 'insert into books (title, author) values (?, ?);',
488
+        columns => ['title', 'author']
489
+    }
490
+
491
+Execute query repeatedly.
492
+    
493
+    my $inputs = [
494
+        {title => 'Perl',      author => 'Ken'},
495
+        {title => 'Good days', author => 'Mike'}
496
+    ];
497
+    
498
+    foreach my $input (@$inputs) {
499
+        $dbi->execute($query, $input);
500
+    }
501
+
502
+This is faster than C<insert()> method.
503
+
504
+=head3 caching
505
+
506
+C<execute()> method caches the parsed result of the source of SQL.
507
+Default to 1
508
+
509
+    $dbi->cache(1);
510
+
511
+Caching is on memory, but you can change this by C<cache_method()>.
512
+First argument is L<DBIx::Custom> object.
513
+Second argument is a source of SQL,
514
+such as "select * from books where {= title} and {= author};";
515
+Third argument is parsed result, such as
516
+{sql => "select * from books where title = ? and author = ?",
517
+ columns => ['title', 'author']}, this is hash reference.
518
+If arguments is more than two, this method is called to set cache.
519
+If not, this method is called to get cache.
520
+
521
+    $dbi->cache_method(sub {
522
+        sub {
523
+            my $self = shift;
524
+            
525
+            $self->{_cached} ||= {};
526
+            
527
+            # Set cache
528
+            if (@_ > 1) {
529
+                $self->{_cached}{$_[0]} = $_[1] 
530
+            }
531
+            
532
+            # Get cache
533
+            else {
534
+                return $self->{_cached}{$_[0]}
535
+            }
536
+        }
537
+    });
538
+
539
+=head2 7. More features
540
+
541
+=head3 トランザクション
542
+
543
+
544
+=head3 Change Result class
545
+
546
+You can change Result class if you need.
547
+
548
+    package Your::Result;
549
+    use base 'DBIx::Custom::Result';
550
+    
551
+    sub some_method { ... }
552
+
553
+    1;
554
+    
555
+    package main;
556
+    
557
+    use Your::Result;
558
+    
559
+    my $dbi = DBIx::Custom->connect(...);
560
+    $dbi->result_class('Your::Result');
561
+
562
+=head3 Custamize SQL builder object
563
+
564
+You can custamize SQL builder object
565
+
566
+    my $dbi = DBIx::Custom->connect(...);
567
+    $dbi->query_builder->register_tag_processor(
568
+        name => sub {
569
+           ...
570
+        }
571
+    );
572
+
573
+=head3 Resister helper method
574
+
575
+You can resiter helper method.
576
+
577
+    $dbi->helper(
578
+        update_or_insert => sub {
579
+            my $self = shift;
580
+            # do something
581
+        },
582
+        find_or_create   => sub {
583
+            my $self = shift;
584
+            # do something
585
+        }
586
+    );
587
+
588
+Register helper methods.
589
+These method can be called from L<DBIx::Custom> object directory.
590
+
591
+    $dbi->update_or_insert;
592
+    $dbi->find_or_create;
593
+
594
+=cut
+15 -1
t/dbix-custom-core.t
... ...
@@ -1,4 +1,4 @@
1
-use Test::More 'no_plan';
1
+use Test::More tests => 15;
2 2
 use strict;
3 3
 use warnings;
4 4
 
... ...
@@ -101,3 +101,17 @@ $dbi->register_filter({b => sub {2}});
101 101
 is($dbi->filters->{b}->(), 2, $test);
102 102
 
103 103
 
104
+test 'expand';
105
+{
106
+    $dbi = DBIx::Custom->new;
107
+    my $source = {books => {title => 'Perl', author => 'Ken'}};
108
+    is_deeply({$dbi->expand($source)}, 
109
+              {'books.title' => 'Perl', 'books.author' => 'Ken'});
110
+}
111
+{
112
+    $dbi = DBIx::Custom->new;
113
+    my %source = (books => {title => 'Perl', author => 'Ken'});
114
+    is_deeply({$dbi->expand(%source)}, 
115
+              {'books.title' => 'Perl', 'books.author' => 'Ken'});
116
+}
117
+