日向夏特殊応援部隊

俺様向けメモ

DBIx::Class SourceCode Reading

を予備知識としてDBIx::Classを読み解きます。
もうなんか疲れて来た><

mk_classdata(), mk_classaccessor()

sub mk_classdata { 
  shift->mk_classaccessor(@_);
}

sub mk_classaccessor {
  my $self = shift;
  $self->mk_group_accessors('inherited', $_[0]); 
  $self->set_inherited(@_) if @_ > 1;
}

ちなみにこれらは共にクラスメソッドインスタンスメソッドのいずれでも呼び出し可能ではありますが、名前からしてクラスメソッドとして呼ぶべきでしょう。*1
まぁこれってClass::Data::Inheritableみたいな用途でmk_classdataを呼び出す事を想定しているんだけど、大きな違いはd:id:ZIGOROu:20080324:1206351293で説明したように、DBIx::ClassMROがC3ですから、探索順がdepth-firstではありません。*2

C3な探索順で継承可能なクラス変数的な値を宣言するのに使ってるみたいですね。

component_base_class()

これは意図的に呼ぶ物ではなくてClass::C3::Componentised用にあるメソッド*3

sub component_base_class { 'DBIx::Class' }

load_component()を呼び出した時にsuffixとしてこのメソッドの戻り値が採用されます。

MODIFY_CODE_ATTRIBUTES(), _attr_cache()

sub MODIFY_CODE_ATTRIBUTES {
  my ($class,$code,@attrs) = @_;
  $class->mk_classdata('__attr_cache' => {})
    unless $class->can('__attr_cache');
  $class->__attr_cache->{$code} = [@attrs];
  return ();
}

sub _attr_cache {
  my $self = shift;
  my $cache = $self->can('__attr_cache') ? $self->__attr_cache : {};
  my $rest = eval { $self->next::method };
  return $@ ? $cache : { %$cache, %$rest };
}

これはCatalystな人にはおなじみのコードですね。
前者はメソッドにつけたattributeをそのメソッドのCODEREFをキーにしてARRAYREFとして__attr_cacheに保存しておきます。

後者は_attr_cacheが継承ツリーで存在する限りnextでグルグル叩きまくって全てのCODE対attrのHASHREFを取って来ます。

#!/usr/bin/perl;

package Foo;

use base qw(DBIx::Class);
use Class::C3;

sub foo: Hoge Fuga { __PACKAGE__ }

package main;

use Data::Dump qw(dump);
print dump(Foo->_attr_cache);

は例えば、

{ "CODE(0x1808b08)" => ["Hoge", "Fuga"] }

感じになります。

まとめ

DBIx::Classを継承すると以下のような事が出来るようになります。

  • Class::C3::Componentised由来のload_*_components()による継承ツリーへの動的inject
    • load_componentsはDBIx::Class::*なモジュールをprefixを略してinject
    • load_own_componentsは今のpackage名をprefixにしたモジュールをprefixを略してinject
    • load_optional_componentsはload_componentsと大体同じ。存在確認付きなので無ければ読まない。
  • メソッドに付けたattributesを_attr_cacheに保存します。
    • 取り出す際はキーがCODEREFなのでClass::Inspector/シンボルテーブルとかでゴニョらないとダメだと思う
  • Class::Accessor::Groupedのメソッドが全て使えます
    • 必要ならばmk_group_accessors()にて自前のアクセサをバックエンドのオブジェクトを隠蔽して定義出来る
  • 継承可能なクラス変数的な物はmk_classdata()を使う
    • __PACKAGE__->mk_classdata("foo" => { id => 1, name => "zigorou" }) とかとか

概ねこんなのがDBIx::Classのベースになってるみたいです。