Click here to load reader

Introduction To Moco

  • View
    3.835

  • Download
    1

Embed Size (px)

Text of Introduction To Moco

  • 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 DBIx::MoCo:: DataBase Bookmark:: MoCo Bookmark:: DataBase Bookmark:: User Bookmark:: Entry uses 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 DBIx::MoCo:: DataBase 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 [email protected]; $model; } 10. setup: 2. create base class of your models DBIx::MoCo DBIx::MoCo:: DataBase 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 DBIx::MoCo:: DataBase Bookmark:: MoCo Bookmark:: DataBase Bookmark:: User Bookmark:: Entry uses 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:urland 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(" "); 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 bookmarkswith 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 useDBIx::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. 51. nice development environment. 52. ... (By the way) 53. (Our HQ is located at Kyoto.) 54. Thank you! 55. Any Questions?

Search related