Successfully reported this slideshow.

Customization of DBIC::Schema::Loader

2,667 views

Published on

Published in: Technology, Sports
  • Be the first to comment

  • Be the first to like this

Customization of DBIC::Schema::Loader

  1. 1. Customization DBIC::Schema::Loader d:id:ZIGOROu Toru Yamaguchi <zigorou@cpan.org>
  2. 2. Agenda <ul><li>DBIC::Schema::Loader のおさらい </li></ul><ul><ul><li>make_schema_at() </li></ul></ul><ul><ul><ul><li>生成されたファイル自体に拡張 </li></ul></ul></ul><ul><ul><ul><li>inc パスの追加による拡張 </li></ul></ul></ul><ul><ul><ul><li>really_erase_files の値 </li></ul></ul></ul><ul><ul><ul><li>Schema クラスも拡張対象にする </li></ul></ul></ul><ul><ul><li>DBIC::Schema::Loader を改造 </li></ul></ul><ul><ul><ul><li>DBIC::Schema::Loader の概要 </li></ul></ul></ul><ul><ul><ul><li>名前に制約をつけてリレーション設定 </li></ul></ul></ul><ul><ul><li>まとめ </li></ul></ul>
  3. 3. make_schema_at() <ul><li>DBIC::Schema::Loader のメソッド </li></ul><ul><ul><li>引数 </li></ul></ul><ul><ul><ul><li>1. schema クラス名 </li></ul></ul></ul><ul><ul><ul><li>2. 生成オプション (HASHREF) </li></ul></ul></ul><ul><ul><ul><li>3. connect_info (ARRAYREF) </li></ul></ul></ul><ul><ul><li>基本系 </li></ul></ul><ul><ul><ul><li>#!/usr/bin/perl </li></ul></ul></ul><ul><ul><ul><li>use FindBin; </li></ul></ul></ul><ul><ul><ul><li>use File::Spec; </li></ul></ul></ul><ul><ul><ul><li>use DBIx::Class::Schema::Loader qw(make_schema_at) ; </li></ul></ul></ul><ul><ul><ul><li>make_schema_at( 'MyApp::DBIC::Schema' , { </li></ul></ul></ul><ul><ul><ul><li>dump_directory => File::Spec->catfile( $FindBin::Bin , '..' , 'lib' ), </li></ul></ul></ul><ul><ul><ul><li>relly_erase_files => 1 , }, </li></ul></ul></ul><ul><ul><ul><li>[ 'dbi:mysql:database=dbictest' , 'root' ], </li></ul></ul></ul><ul><ul><ul><li>); </li></ul></ul></ul>
  4. 4. 拡張 (1) ファイル直書き方式 - 1 <ul><li>生成されたファイルに拡張 </li></ul><ul><ul><li>生成された Schema, Table クラスそれぞれの下の方に “ DO NOT MODIFY THIS ” と書かれたコメントがある </li></ul></ul><ul><ul><ul><li>そこから “ You can replace this text ” の領域は拡張領域で自動生成の対象ではない </li></ul></ul></ul><ul><ul><ul><li>生成されたファイルに直接書いて良い </li></ul></ul></ul><ul><ul><ul><ul><li>追加で load_components() したりとか </li></ul></ul></ul></ul><ul><ul><ul><ul><li>あるいは他のプラグインのメソッド叩いたり </li></ul></ul></ul></ul><ul><ul><ul><ul><li>テーブル定義をゴニョゴニョしたりとか </li></ul></ul></ul></ul>
  5. 5. 拡張 (1) ファイル直書き方式 - 2 <ul><li># DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:WFbbTfTFDFr/kewSj3QwAw </li></ul><ul><li>package MyApp::DBIC::Schema; </li></ul><ul><li>__PACKAGE__->load_components( </li></ul><ul><li>qw/+MyApp::DBIC::MyComp/ </li></ul><ul><li>); </li></ul><ul><li>__PACKAGE__->init_mycomp; </li></ul><ul><li># You can replace this text with custom content, and it will be preserved on regeneration 1 ; </li></ul>
  6. 6. 拡張 (2) incにあるテンプレ読み込み方式 - 1 <ul><li>incパスに通したテンプレからinclude </li></ul><ul><ul><li>use libで適当なディレクトリにパスを通すと、そこにあるファイルを対応するモジュールの拡張領域に差し込んでくれる機能 </li></ul></ul><ul><ul><ul><li>ここで注意しないとダメなのはSchemaクラスは対象外 </li></ul></ul></ul><ul><ul><ul><li>Tableクラスのみ拡張可能 </li></ul></ul></ul><ul><ul><ul><li>ちなみに拡張領域の箇所はファイル直書き方式と同じ </li></ul></ul></ul>
  7. 7. 拡張 (2) incにあるテンプレ読み込み方式 - 2 <ul><li>ディレクトリ構成 </li></ul>lib MyApp DBIC Schema User.pm schema MyApp DBIC Schema User.pm 自動生成される領域 Include ( ファイルの下部にくっつく )
  8. 8. make_schema_at() [2] <ul><li>really_erase_files ( 生成オプション ) </li></ul><ul><ul><li>true の時 </li></ul></ul><ul><ul><ul><li>毎回ファイルを消して生成する </li></ul></ul></ul><ul><ul><ul><li>Schema, Table クラス両方消しちゃうので、 ファイル直書き方式は使えない >< </li></ul></ul></ul><ul><ul><ul><ul><li>必然的に include になるが、 Schema に対しては適用出来ない </li></ul></ul></ul></ul><ul><ul><li>false の時 </li></ul></ul><ul><ul><ul><li>毎回ファイルを消さない </li></ul></ul></ul><ul><ul><ul><li>直書き方式が使える </li></ul></ul></ul><ul><ul><ul><ul><li>但し消さないと include で挿入されたブロックが次回の直書きと見なされ、再び include されるので重複コードになるwww </li></ul></ul></ul></ul>
  9. 9. ここまでのまとめ <ul><li>現在の問題点 </li></ul><ul><ul><li>Schema, Table 共に後付的に拡張したい </li></ul></ul><ul><ul><ul><li>Schema も対象にするなら really_erase_files を false にする </li></ul></ul></ul><ul><ul><ul><li>そうすると直書き領域に make_schema_at する度に重複するコードが出来てしまう </li></ul></ul></ul><ul><ul><li>どうすれば良いか </li></ul></ul><ul><ul><ul><li>really_erase_files => 0 </li></ul></ul></ul><ul><ul><ul><li>でも自分で Table クラスを消せば同じ事になる </li></ul></ul></ul><ul><ul><ul><li>拡張は Schema は直書き、 Table は include でやる。 </li></ul></ul></ul>
  10. 10. 改良版Schema生成 <ul><li>改良版 </li></ul><ul><ul><li>#!/usr/bin/perl </li></ul></ul><ul><ul><li>use strict ; </li></ul></ul><ul><ul><li>use warnings ; </li></ul></ul><ul><ul><li>use FindBin; </li></ul></ul><ul><ul><li>use File::Spec; </li></ul></ul><ul><ul><li>use lib ( </li></ul></ul><ul><ul><li>File::Spec->catfile( $FindBin::Bin , qw/.. lib/ ), </li></ul></ul><ul><ul><li>File::Spec->catfile( $FindBin::Bin , qw/.. schema/ ) </li></ul></ul><ul><ul><li>); </li></ul></ul><ul><ul><li>use DBIx::Class::Schema::Loader qw(make_schema_at) ; </li></ul></ul><ul><ul><li>die unless @ARGV ; </li></ul></ul><ul><ul><li>my $schema_class = 'MyClass::DBIC::Schema' ; </li></ul></ul><ul><ul><li># こんな感じで自分で消す </li></ul></ul><ul><ul><li>unlink ( glob ( File::Spec->catdir( $FindBin::Bin , '..' , 'lib' , split ( / :: / , $schema_class ) ) . '/*.pm' ) ); </li></ul></ul><ul><ul><li>make_schema_at( </li></ul></ul><ul><ul><li>$schema_class , </li></ul></ul><ul><ul><li>{ </li></ul></ul><ul><ul><li>components => [ qw/ResultSetManager UTF8Columns InflateColumn::DateTime TimeStamp/ ], </li></ul></ul><ul><ul><li>dump_directory => File::Spec->catfile( $FindBin::Bin , qw/.. lib/ ), </li></ul></ul><ul><ul><li>debug => 0 , </li></ul></ul><ul><ul><li>really_erase_my_files => 0 , </li></ul></ul><ul><ul><li>}, </li></ul></ul><ul><ul><li>@ARGV </li></ul></ul><ul><ul><li>); </li></ul></ul>
  11. 11. さらなる改良 <ul><li>Schema::Loader について </li></ul><ul><ul><li>中で何やってるか理解すればもっとカスタマイズ出来そう </li></ul></ul><ul><ul><ul><li>特にリレーションとか通常の使い方では手出しできない部分を何とかしたい </li></ul></ul></ul><ul><ul><ul><li>まずはクラス図から </li></ul></ul></ul>
  12. 12. Class::Diagram of DBIC::Schema::Loader <ul><li>Class Diagram </li></ul>DBIC::Schema::Loader DBIC::Schema ::Loader::Base DBIC::Schema ::Loader::Base::DBI::mysql DBIC::Schema ::Loader::RelBuilder 各ドライバごとにクラスがある リレーションの構築 Dump 時のオプションの詳細
  13. 13. RelationShip [1] <ul><li>belongs_to とか has_many とか </li></ul><ul><ul><li>belongs_to(“user_id”, ...) </li></ul></ul><ul><ul><ul><li>$rs-> user_id -> user_id </li></ul></ul></ul><ul><ul><ul><li>ダサすぎ </li></ul></ul></ul><ul><ul><ul><li>残念ながら現在はこうなる </li></ul></ul></ul><ul><ul><li>belongs_to(“user”, ...) </li></ul></ul><ul><ul><ul><li>$rs-> user -> user_id </li></ul></ul></ul><ul><ul><ul><li>$rs-> user_id も OK </li></ul></ul></ul><ul><ul><ul><li>自然になる </li></ul></ul></ul><ul><ul><ul><li>こうしたい </li></ul></ul></ul>
  14. 14. RelationShip [2] <ul><li>問題の箇所 </li></ul><ul><ul><li>DBIC::Schema::Loader::Base の _load_relationship() </li></ul></ul><ul><ul><ul><li>foreach my $src_class ( sort keys %$rel_stmts ) { </li></ul></ul></ul><ul><ul><ul><li>my $src_stmts = $rel_stmts ->{ $src_class }; </li></ul></ul></ul><ul><ul><ul><li>foreach my $stmt ( @$src_stmts ) { </li></ul></ul></ul><ul><ul><ul><li>### ここら辺を書き換えちゃえば良い </li></ul></ul></ul><ul><ul><ul><li>$self ->_dbic_stmt( </li></ul></ul></ul><ul><ul><ul><li>$src_class , $stmt ->{method}, ## belongs_to とか </li></ul></ul></ul><ul><ul><ul><li>@{ $stmt ->{args}} ## belongs_to の引数リスト </li></ul></ul></ul><ul><ul><ul><li>); </li></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul>
  15. 15. RelationShip [4] <ul><li>改良版 </li></ul><ul><li>foreach my $src_class ( sort keys %$rel_stmts ) { </li></ul><ul><li>my $src_stmts = $rel_stmts ->{ $src_class }; </li></ul><ul><li>foreach my $stmt ( @$src_stmts ) { </li></ul><ul><li>### belongs_to の時だけ無茶する </li></ul><ul><li>if ( $stmt ->{method} eq 'belongs_to' ) { </li></ul><ul><li>my $table_class_suffix = [ </li></ul><ul><li>split / :: / => $stmt ->{args}->[ 1 ] </li></ul><ul><li>]->[ -1 ]; </li></ul><ul><li>$stmt ->{args}->[ 0 ] = </li></ul><ul><li>String::CamelCase::decamelize( $table_class_suffix ); </li></ul><ul><li>} </li></ul><ul><li>$self ->_dbic_stmt( $src_class , $stmt ->{method}, @{ $stmt ->{args} } ); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  16. 16. まとめ <ul><li>Schema::Loader との付き合いかた </li></ul><ul><ul><li>really_erase_files を false </li></ul></ul><ul><ul><ul><li>でも自分で Table 消す </li></ul></ul></ul><ul><ul><ul><li>Schema は直書き、 Table は include で辻褄合わせる </li></ul></ul></ul><ul><ul><ul><li>belongs_to は問題箇所を redifine する </li></ul></ul></ul><ul><ul><ul><ul><li>名前でコード生成時の制約をつけるのは良さそう。 on_created, on_updated(DATETIME) で NOW() 相当とか </li></ul></ul></ul></ul><ul><ul><ul><li>時間の都合上割愛したけど、それ以上は自分で DBIC 流儀のプラグイン書く ( 割と便利 ) </li></ul></ul></ul><ul><ul><li>そこまでするなら自分で Loader 書くべき?(ぇ </li></ul></ul><ul><ul><li>あるいは DBIC を使わない </li></ul></ul>
  17. 17. おしまい <ul><li>ご清聴ありがとうございました </li></ul>

×