Introduction To Moco
Upcoming SlideShare
Loading in...5
×
 

Introduction To Moco

on

  • 5,453 views

 

Statistics

Views

Total Views
5,453
Views on SlideShare
5,451
Embed Views
2

Actions

Likes
1
Downloads
12
Comments
0

1 Embed 2

http://www.slideshare.net 2

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Introduction To Moco Introduction To Moco Presentation Transcript

  • 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
    • Transparent caching with Cache::* (usually with Cache::Memcached)‏
    • Test fixture
  • Quick start
  • Install
    • "cpan DBIx::MoCo"
      • You may need force install as of now.
  • setup your own classes DBIx::MoCo DBIx::MoCo:: DataBase Bookmark:: MoCo Bookmark:: DataBase Bookmark:: User Bookmark:: Entry uses
  • 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;
  • setup: 1. create DataBase class DBIx::MoCo DBIx::MoCo:: DataBase Bookmark:: DataBase
  • 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; }
  • setup: 2. create base class of your models DBIx::MoCo DBIx::MoCo:: DataBase Bookmark:: MoCo Bookmark:: DataBase uses
  • 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;
  • setup: 3. create model classes DBIx::MoCo DBIx::MoCo:: DataBase Bookmark:: MoCo Bookmark:: DataBase Bookmark:: User Bookmark:: Entry uses
  • 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);
  • SQL operations
  • search()‏ my @entries = moco('Entry') ->search( where => "url like 'http://d.hatena.ne.jp/%'", order => 'entry_id desc', limit => 10, ); say $_->title for @entries;
  • 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;
  • 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;
  • 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;
  • 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;
  • List operations
  • 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;
  • 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
  • List::RubyLike
    • google:github list-rubylike
  • 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');
  • Using your own list class ## $entries is a Blog::Entry::List my $entries = moco('Entry')->search(...); say $entries ->to_json ;
  • Relationships
  • 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', }, );
  • $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;
  • 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' } );
  • $bookmark->entry my $bookmark = moco('Bookmark')->retrieve; say $bookmark ->entry->title; say $bookmark ->owner->name
  • 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 ; }
  • 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 ; }
  • 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/] }, );
  • inflate / deflate
  • 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;
  • 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;
  • 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 } } );
  • Transparent caching
  • Transparent caching ## Just do it my $cache = Cache::Memcached->new({...}); Bookmark::MoCo->cache_object( $cache );
  • 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;
  • NOTE: "session" is needed when you use caching feature or prefetching. Blog::MoCo->start_session; my $entry = moco('Entry')->retrieve(...); Blog::MoCo->end_session;
  • Testing
  • 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/
  • 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, "...";
  • Pros and Cons
  • Pros
    • Simple and easy
    • List operations are very sexy.
    • Transparent caching is "DB に優しい "
    • Test fixture
  • Cons
    • less document
    • some difficulties (especially in session and cache)‏
    • low test coverage
    • some bugs
  • patches are welcome. jkondo at hatena ne jp (primary author)‏
  • We're hiring! google: はてな 求人
  • nice office.
  • nice development environment.
  • ところで ... (By the way)
  • 勤務地は京都です (Our HQ is located at Kyoto.)
  • Thank you!
  • Any Questions?