Introduction To Moco

55
©2008 株株株株株株株 株株株株 株株株株株株株株株株株 株株株株株株株 Introduction to DBIx::MoCo Naoya Ito http://www.hatena.ne.jp/

Transcript of Introduction To Moco

Page 1: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Introduction to DBIx::MoCo

Naoya Itohttp://www.hatena.ne.jp/

Page 2: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

What is DBIx::MoCo?

“Light and Fast Model Component”

O/R Mapper for MySQL and SQLite

Page 3: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Features

Easy SQL operations like CDBI / ActiveRecord (Rails)

Ruby-like list operations Transparent caching with

Cache::* (usually with Cache::Memcached)

Test fixture

Page 4: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Quick start

Page 5: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Install

"cpan DBIx::MoCo" You may need force install as of

now.

Page 6: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

setup your own classes

DBIx::MoCoDBIx::MoCo::

DataBase

Bookmark::MoCo

Bookmark::DataBase

Bookmark::User

Bookmark::Entry

uses

Page 7: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

setup: 1. create DataBase classpackage Bookmark::DataBase;use base qw/DBIx::MoCo::DataBase/;

__PACKAGE__->dsn('dbi:mysql:dbname=bookmark');__PACKAGE__->username(‘foo');__PACKAGE__->password(‘bar');

1;

Page 8: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

setup: 1. create DataBase class

DBIx::MoCoDBIx::MoCo::

DataBase

Bookmark::DataBase

Page 9: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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;}

Page 10: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

setup: 2. create base class of your models

DBIx::MoCoDBIx::MoCo::

DataBase

Bookmark::MoCo

Bookmark::DataBase

uses

Page 11: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

setup: 3. create model classespackage 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;

Page 12: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

setup: 3. create model classes

DBIx::MoCoDBIx::MoCo::

DataBase

Bookmark::MoCo

Bookmark::DataBase

Bookmark::User

Bookmark::Entry

uses

Page 13: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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);

Page 14: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

SQL operations

Page 15: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

search()

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

say $_->title for @entries;

Page 16: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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;

Page 17: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

search() : named placeholdersmy @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;

Page 18: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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;

Page 19: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Create, Update, Delete

## create new recordmy $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;

Page 20: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

List operations

Page 21: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Ruby-like list operations

## Scalar contextmy $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;

Page 22: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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

Page 23: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

List::RubyLike

google:github list-rubylike

Page 24: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Using your own list class

## create your own list class of Blog::Entrypackage Blog::Entry::List;use base qw/DBIx::MoCo::List/;

sub to_json { ...}

## setup list_class()package Blog::Entry;...__PACKAGE__->list_class('Blog::Entry::List');

Page 25: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Using your own list class

## $entries is a Blog::Entry::Listmy $entries = moco('Entry')->search(...);

say $entries->to_json;

Page 26: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Relationships

Page 27: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

An entry has many bookmarkspackage 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', },);

Page 28: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

$entry->bookmarks

my $entry = moco('Entry')->retrieve_by_url(...);

## bookmarks() returns bookmarks of the entrymy @bookmarks = $entry->bookmarks;

## offset and limit (offset 10, limit 50)@bookmarks = $entry->bookmarks(10, 50);

## Ruby-like list operations in scalar contextprint $entry->bookmarks(10, 50)->grep(...)->size;

Page 29: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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' });

Page 30: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

$bookmark->entry

my $bookmark = moco('Bookmark')->retrieve;

say $bookmark->entry->title;say $bookmark->owner->name

Page 31: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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;}

Page 32: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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;}

Page 33: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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/] },);

Page 34: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

inflate / deflate

Page 35: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

inflate / deflate (explicitly)

my $entry = moco('Entry')->retrieve(1);

## plain stringsay $entry->timestamp;

## timestamp column as DateTime objectsay $entry->timestamp_as_DateTime->hms;

## url column as URI objectsay $entry->url_as_URI->host;

Page 36: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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;

Page 37: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

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 } });

Page 38: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Transparent caching

Page 39: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Transparent caching

## Just do itmy $cache = Cache::Memcached->new({...});Bookmark::MoCo->cache_object( $cache );

Page 40: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Transparent caching

## The entry object will be cachedmy $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;

Page 41: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

NOTE: "session" is needed when you use caching feature or prefetching.

Blog::MoCo->start_session;

my $entry = moco('Entry')->retrieve(...);

Blog::MoCo->end_session;

Page 42: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Testing

Page 43: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Fixtures: building records for testing from YAML

## fixtures/entries.ymlmodel: Bookmark::Entryrecords: first: id: 1 title: Hatena Bookmark url: http://b.hatena.ne.jp/

second: id: 2 title: Yahoo! Japan url: http://www.yahoo.co.jp/

Page 44: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Writing tests with fixtures

## t/entry.tuse 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, "...";

Page 45: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Pros and Cons

Page 46: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Pros

Simple and easy List operations are very sexy. Transparent caching is "DB に優し

い " Test fixture

Page 47: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Cons

less document some difficulties (especially in

session and cache) low test coverage some bugs

Page 48: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

patches are welcome.

jkondo at hatena ne jp (primary author)

Page 49: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

We're hiring!

google: はてな 求人

Page 50: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

nice office.

Page 51: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

nice development environment.

Page 52: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

ところで ... (By the way)

Page 53: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

勤務地は京都です (Our HQ is located at Kyoto.)

Page 54: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Thank you!

Page 55: Introduction To Moco

©2008 株式会社はてな  本資料の一部または全部の無断複製・転載を禁じます

Any Questions?