狠狠撸

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

More Related Content

Customization of DBIC::Schema::Loader

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