1 / 55

Introduction to DBIx::MoCo

Introduction to DBIx::MoCo. Naoya Ito http://www.hatena.ne.jp/. What is DBIx::MoCo?. “ Light and Fast Model Component” O/R Mapper for MySQL and SQLite. Features. Easy SQL operations like CDBI / ActiveRecord (Rails) ‏ Ruby-like list operations

fynn
Télécharger la présentation

Introduction to DBIx::MoCo

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Introduction to DBIx::MoCo Naoya Ito http://www.hatena.ne.jp/

  2. What is DBIx::MoCo? • “Light and Fast Model Component” • O/R Mapper for MySQL and SQLite

  3. Features • Easy SQL operations like CDBI / ActiveRecord (Rails)‏ • Ruby-like list operations • Transparent caching with Cache::* (usually with Cache::Memcached)‏ • Test fixture

  4. Quick start

  5. Install • "cpan DBIx::MoCo" • You may need force install as of now.

  6. setup your own classes DBIx::MoCo:: DataBase DBIx::MoCo Bookmark:: MoCo Bookmark:: DataBase uses Bookmark:: User Bookmark:: Entry

  7. setup: 1. create DataBase class package Bookmark::DataBase; use base qw/DBIx::MoCo::DataBase/; __PACKAGE__->dsn('dbi:mysql:dbname=bookmark'); __PACKAGE__->username(‘foo'); __PACKAGE__->password(‘bar'); 1;

  8. setup: 1. create DataBase class DBIx::MoCo:: DataBase DBIx::MoCo Bookmark:: DataBase

  9. setup: 2. create base class of your models package Bookmark::MoCo; use base qw/DBIx::MoCo/; use UNIVERSAL::require; use Expoter::Lite; our @EXPORT = qw/moco/; __PACKAGE__->db_object('Bookmark::DataBase'); ## moco('User') returns "Bookmark::MoCo::User" sub moco (@) { my $model = shift; return __PACKAGE__ unless $model; $model = join '::', 'Bookmark::MoCo', $model; $model->require or die $@; $model; }

  10. setup: 2. create base class of your models DBIx::MoCo:: DataBase DBIx::MoCo Bookmark:: MoCo Bookmark:: DataBase uses

  11. setup: 3. create model classes package Bookmark::MoCo::Entry; use base qw/Bookmark::MoCo/; __PACKAGE__->table('entry'); __PACKAGE__->primary_keys(qw/entry_id/); __PACKAGE__->unique_keys(qw/url/); __PACKAGE__->utf8_columns(qw/title/); 1;

  12. setup: 3. create model classes DBIx::MoCo:: DataBase DBIx::MoCo Bookmark:: MoCo Bookmark:: DataBase uses Bookmark:: User Bookmark:: Entry

  13. retrieve()‏ my $entry = moco('Entry')>retrieve(url => $url); say $entry->entry_id; say $entry->title; say $entry->url; ## retrieve_by_foo(...) equals retrieve(foo => ...)‏ $entry = moco('Entry')->retrieve_by_url($url); $entry = moco('Entry')->retrieve_by_entry_id($id);

  14. SQL operations

  15. search()‏ my @entries = moco('Entry')->search( where => "url like 'http://d.hatena.ne.jp/%'", order => 'entry_id desc', limit => 10, ); say $_->title for @entries;

  16. search() : placeholders my @entries = moco('Entry')->search( where => [ "url like ?", 'http://d.hatena.ne.jp/%' ], order => 'entry_id desc', limit => 10, ); say $_->title for @entries;

  17. search() : named placeholders my @entries = moco('Entry')->search( where => [ "url like :url and is_public = :is_public", url => 'http://d.hatena.ne.jp/%', is_public => 1 ], order => 'entry_id desc', limit => 10, ); say $_->title for @entries;

  18. search() : where ... in (...)‏ ## SELECT * from entry where entry_id in (1, 2, 5, 6, 10)‏ my @entries = moco('Entry')->search( where => [ "entry_id in (:entry_id)", entry_id => [1, 2, 5, 6, 10], ], order => 'entry_id desc', ); say $_->title for @entries;

  19. Create, Update, Delete ## create new record my $entry = moco('Entry')->create( url => 'http://www.yahoo.co.jp/', title => 'Yahoo!'; ); ## update a column $entry->title('Yahoo! Japan'); ## save (in session)‏ ## If it is not in session, updates are automatically saved. $entry->save; ## delete the record $entry->delete;

  20. List operations

  21. Ruby-like list operations ## Scalar context my $entries = moco('Entry')->search(...); say$entries->size; say$entries ->collect(sub { $_->title })‏ ->join("\n"); say$entries ->grep(sub { $_->is_public })‏ ->collect(sub { $_->num_of_bookmarks }} ->sum;

  22. Ruby-like methods • push, pop, shift, unshift, add, append, prepend • size • first, last • slice, zip • map, collect, each • grep • compact • flatten • delete, delete_if, delete_at • inject • find • join • reduce • sum • uniq • dup • dump

  23. List::RubyLike • google:github list-rubylike

  24. Using your own list class ## create your own list class of Blog::Entry package Blog::Entry::List; use base qw/DBIx::MoCo::List/; sub to_json { ... } ## setup list_class()‏ package Blog::Entry; ... __PACKAGE__->list_class('Blog::Entry::List');

  25. Using your own list class ## $entries is a Blog::Entry::List my $entries = moco('Entry')->search(...); say $entries->to_json;

  26. Relationships

  27. An entry has many bookmarks package Bookmark::MoCo::Entry; use Bookmark::MoCo; use base qw/Bookmark::MoCo/; __PACKAGE__->table('entry'); ... __PACKAGE__->has_many( bookmarks => moco('Bookmark'), { key => 'entry_id', order => 'timestamp desc', }, );

  28. $entry->bookmarks my $entry = moco('Entry')->retrieve_by_url(...); ## bookmarks() returns bookmarks of the entry my @bookmarks =$entry->bookmarks; ## offset and limit (offset 10, limit 50)‏ @bookmarks = $entry->bookmarks(10, 50); ## Ruby-like list operations in scalar context print$entry->bookmarks(10, 50)->grep(...)->size;

  29. bookmarks has an entry and an owner package Bookmark::MoCo::Bookmark; use Bookmark::MoCo; use base qw/Bookmark::MoCo/; __PACKAGE__->table('bookmark'); ... __PACKAGE__->has_a( entry => moco('Entry'), { key => 'entry_id' } ); __PACKAGE__->has_a( owner => moco('User'), { key => 'user_id' } );

  30. $bookmark->entry my $bookmark = moco('Bookmark')->retrieve; say $bookmark->entry->title; say $bookmark->owner->name

  31. BTW: SQL is executed too many times ... my $entry = moco('Entry')->retrieve(...); say $entry->bookmarks->size; ## 1,000 ## oops, SQL is executed in 1,000 times. for ($entry->bookmarks) { say $_->owner->name; }

  32. A entry has many bookmarks with owner (prefetching)‏ my $entry = moco('Entry')->retrieve(...); say $entry->bookmarks->size; ## 1,000 ## bookmarks and owners will be retrieved at the same time. ## (SQL stetements are executed only 2 times.)‏ for ($entry->bookmarks(0, 0, {with => [qw/owner/]})) { say $_->owner->name; }

  33. Implicit prefetching package Bookmark::MoCo::Entry; use Bookmark::MoCo; use base qw/Bookmark::MoCo/; ... __PACKAGE__->has_many( bookmarks => moco('Bookmark'), { key => 'entry_id', order => 'timestamp desc', with => [qw/owner/] }, );

  34. inflate / deflate

  35. inflate / deflate (explicitly) my $entry = moco('Entry')->retrieve(1); ## plain string say $entry->timestamp; ## timestamp column as DateTime object say $entry->timestamp_as_DateTime->hms; ## url column as URI object say $entry->url_as_URI->host;

  36. inflate / deflate (implicitly) package Bookmark::MoCo::Entry; ... ## plain string __PACKAGE__->inflate_column( url => 'URI', timestamp => 'DateTime, ); package main; say moco('Entry')->retrieve(1)->url->host;

  37. inflate_column() without builtin classes package Bookmark::MoCo::Entry; ... ## plain string __PACKAGE__->inflate_column( title => { inflate => sub { My::String->new(shift) } deflate => sub { shift->as_string } } );

  38. Transparent caching

  39. Transparent caching ## Just do it my $cache = Cache::Memcached->new({...}); Bookmark::MoCo->cache_object( $cache );

  40. Transparent caching ## The entry object will be cached my $entry = moco('Entry')->retrieve(1); ## Cached object will be retrieved from memcached $entry = moco('Entry')->retrieve(1); ## both cache and database record will be updated $entry->title('foobar'); $entry->save;

  41. NOTE: "session" is needed when you use caching feature or prefetching. Blog::MoCo->start_session; my $entry = moco('Entry')->retrieve(...); Blog::MoCo->end_session;

  42. Testing

  43. Fixtures: building records for testing from YAML ## fixtures/entries.yml model: Bookmark::Entry records: first: id: 1 title: Hatena Bookmark url: http://b.hatena.ne.jp/ second: id: 2 title: Yahoo! Japan url: http://www.yahoo.co.jp/

  44. Writing tests with fixtures ## t/entry.t use DBIx::MoCo::Fixture; use Bookmark::Entry; use Test::More tests => 2; ## loading records from entries.yml, ## then returns them as objects. my $f = fixtures(qw/entries/); my $entry = $f->{entry}->{first}; is $entry->title, "..."; is $entry->url, "...";

  45. Pros and Cons

  46. Pros • Simple and easy • List operations are very sexy. • Transparent caching is "DBに優しい" • Test fixture

  47. Cons • less document • some difficulties (especially in session and cache)‏ • low test coverage • some bugs

  48. patches are welcome.jkondo at hatena ne jp (primary author)‏

  49. We're hiring!google:はてな 求人

  50. nice office.

More Related