| 1 | =head1 名前(NAME) |
|---|
| 2 | |
|---|
| 3 | Catalyst::Manual::ExtendingCatalyst - フレームワークを拡張する |
|---|
| 4 | |
|---|
| 5 | =head1 概要(DESCRIPTION) |
|---|
| 6 | |
|---|
| 7 | This document will provide you with access points, techniques and best |
|---|
| 8 | practices to extend the L<Catalyst> framework, or to find more elegant |
|---|
| 9 | ways to abstract and use your own code. |
|---|
| 10 | |
|---|
| 11 | このドキュメントでは Catalyst を拡張する方法やどうやってエレガントにあなたのコー |
|---|
| 12 | ドを抽象化するかについて論じます。 |
|---|
| 13 | |
|---|
| 14 | The design of Catalyst is such that the framework itself should not |
|---|
| 15 | get in your way. There are many entry points to alter or extend |
|---|
| 16 | Catalyst's behaviour, and this can be confusing. This document is |
|---|
| 17 | written to help you understand the possibilities, current practices |
|---|
| 18 | and their consequences. |
|---|
| 19 | |
|---|
| 20 | Catalyst はいろんな方法で拡張できるので、あなたは混乱しているかもしれません。この |
|---|
| 21 | ドキュメントではそのあたりの詳細を論じます。 |
|---|
| 22 | |
|---|
| 23 | Please read the L<BEST PRACTICES> section before deciding on a design, |
|---|
| 24 | especially if you plan to release your code to CPAN. The Catalyst |
|---|
| 25 | developer and user communities, which B<you are part of>, will benefit |
|---|
| 26 | most if we all work together and coordinate. |
|---|
| 27 | |
|---|
| 28 | あなたのコードを CPAN にうpするまえに、とりあえず L<BEST PRACTICES> の章をよみま |
|---|
| 29 | しょう。 Catalyst の開発者とユーザコミュニティはあなたとともにあります。 |
|---|
| 30 | |
|---|
| 31 | If you are unsure on an implementation or have an idea you would like |
|---|
| 32 | to have RFC'ed, it surely is a good idea to send your questions and |
|---|
| 33 | suggestions to the Catalyst mailing list (See L<Catalyst/SUPPORT>) |
|---|
| 34 | and/or come to the C<#catalyst> channel on the C<irc.perl.org> |
|---|
| 35 | network. You might also want to refer to those places for research to |
|---|
| 36 | see if a module doing what you're trying to implement already |
|---|
| 37 | exists. This might give you a solution to your problem or a basis for |
|---|
| 38 | starting. |
|---|
| 39 | |
|---|
| 40 | あなたがなにか実装にまつわるアイディアをもっているのであれば、Catalyst の ML, |
|---|
| 41 | IRC などで話しかけてください。 |
|---|
| 42 | |
|---|
| 43 | =head1 ベストプラクティス(BEST PRACTICES) |
|---|
| 44 | |
|---|
| 45 | During Catalyst's early days, it was common to write plugins to |
|---|
| 46 | provide functionality application wide. Since then, Catalyst has |
|---|
| 47 | become a lot more flexible and powerful. It soon became a best |
|---|
| 48 | practice to use some other form of abstraction or interface, to keep |
|---|
| 49 | the scope of its influence as close as possible to where it belongs. |
|---|
| 50 | |
|---|
| 51 | Catalyst が若かりしころ、複数アプリにまたがる機能はプラグインにするのがよいとされ |
|---|
| 52 | えいましたが、現在ではもっとフレキシブルでパワフルな方法があります。 |
|---|
| 53 | |
|---|
| 54 | For those in a hurry, here's a quick checklist of some fundamental |
|---|
| 55 | points. If you are going to read the whole thing anyway, you can jump |
|---|
| 56 | forward to L</Namespaces>. |
|---|
| 57 | |
|---|
| 58 | もし君がめっちゃいそいでいるのなら、以下に必須のポイントに関するクイックチェック |
|---|
| 59 | リストを容易したからそれだけでもよんでいきたまえ。もし君がまったりとこの文書を全 |
|---|
| 60 | 部読むつもりなら L</Namespaces> に進む。 |
|---|
| 61 | |
|---|
| 62 | =head2 3分間チェックリスト(Quick Checklist) |
|---|
| 63 | |
|---|
| 64 | =over |
|---|
| 65 | |
|---|
| 66 | =item C<CatalystX::*> というネームスペースをつかいましょう(Use the C<CatalystX::*> namespace if you can!) |
|---|
| 67 | |
|---|
| 68 | If your extension isn't a Model, View, Controller, Plugin, or Engine, |
|---|
| 69 | it's best to leave it out of the C<Catalyst::> namespace. Use |
|---|
| 70 | <CatalystX::> instead. |
|---|
| 71 | |
|---|
| 72 | もしあなたの拡張が Model でも View でも Controller でも Plugin でも Engine でもな |
|---|
| 73 | ければ、それは C<Catalyst::> というネームスペースにおくべきではありません。 |
|---|
| 74 | C<CatalystX::> をかわりにつかってください。 |
|---|
| 75 | |
|---|
| 76 | =item プラグインはつくらない(どうしてもつくらなきゃいけない場合をのぞいて)!(Don't make it a plugin unless you have to!) |
|---|
| 77 | |
|---|
| 78 | A plugin should be careful since it's overriding Catalyst internals. |
|---|
| 79 | If your plugin doesn't really need to muck with the internals, make it a |
|---|
| 80 | base Controller or Model. |
|---|
| 81 | |
|---|
| 82 | プラギンは Catalyst の内部をうわがきしちゃうので、注意してね!もしチミのプラグイン |
|---|
| 83 | が、内部を上書きしたいんでなければ、Controller か Model にしてね! |
|---|
| 84 | |
|---|
| 85 | =item コミュニティがあるから利用しちゃおう!(There's a community. Use it!) |
|---|
| 86 | |
|---|
| 87 | There are many experienced developers in the Catalyst community, |
|---|
| 88 | there's always the IRC channel and the mailing list to discuss things. |
|---|
| 89 | |
|---|
| 90 | Catalyst には IRC も ML もあるから、どんどん利用しちゃおう! |
|---|
| 91 | |
|---|
| 92 | =item テストとドキュメンテーションはしっかりね!(Add tests and documentation!) |
|---|
| 93 | |
|---|
| 94 | This gives a stable basis for contribution, and even more importantly, |
|---|
| 95 | builds trust. The easiest way is a test application. See |
|---|
| 96 | L<Catalyst::Manual::Tutorial::Testing> for more information. |
|---|
| 97 | |
|---|
| 98 | あたりまえのことだけど、テストとドキュメンテーションをしない人はお尻ペンペンだよ! |
|---|
| 99 | |
|---|
| 100 | =back |
|---|
| 101 | |
|---|
| 102 | =head2 ネームスペース(Namespaces) |
|---|
| 103 | |
|---|
| 104 | While some core extensions (engines, plugins, etc.) have to be placed |
|---|
| 105 | in the C<Catalyst::*> namespace, the Catalyst core would like to ask |
|---|
| 106 | developers to use the C<CatalystX::*> namespace if possible. |
|---|
| 107 | |
|---|
| 108 | コアの拡張(engine や plugin とか)は、C<Catalyst::*> におくべきだけど、そういうの |
|---|
| 109 | つくるときは開発者に相談してからにしよう。できることなら C<CatalystX::*> におこう。 |
|---|
| 110 | |
|---|
| 111 | (訳注: CPAN にアップロードする場合、相談してからやらないと mst に痛烈に DIS られます) |
|---|
| 112 | |
|---|
| 113 | When you try to put a base class for a C<Model>, C<View> or |
|---|
| 114 | C<Controller> directly under your C<MyApp> directory as, for example, |
|---|
| 115 | C<MyApp::Controller::Foo>, you will have the problem that Catalyst |
|---|
| 116 | will try to load that base class as a component of your |
|---|
| 117 | application. The solution is simple: Use another namespace. Common |
|---|
| 118 | ones are C<MyApp::Base::Controller::*> or C<MyApp::ControllerBase::*> |
|---|
| 119 | as examples. |
|---|
| 120 | |
|---|
| 121 | =head2 それをシンプルなモジュールにすることはできませんか?(Can it be a simple module?) |
|---|
| 122 | |
|---|
| 123 | Sometimes you want to use functionality in your application that |
|---|
| 124 | doesn't require the framework at all. Remember that Catalyst is just |
|---|
| 125 | Perl and you always can just C<use> a module. If you have application |
|---|
| 126 | specific code that doesn't need the framework, there is no problem in |
|---|
| 127 | putting it in your C<MyApp::*> namespace. Just don't put it in |
|---|
| 128 | C<Model>, C<Controller> or C<View>, because that would make Catalyst |
|---|
| 129 | try to load them as components. |
|---|
| 130 | |
|---|
| 131 | Catalyst はたんなる Perl であることを思いだしてください。C<use> でいつでもモジュー |
|---|
| 132 | ルをつかえるんですよ。フレームワークと連携することが必要ないアプリケーション専用 |
|---|
| 133 | のコードであれば、C<MyApp::*> というネームスペースをつかってください。C<Model>, |
|---|
| 134 | C<Controller>, C<View> というネームスペースにはおかないでください。このネームスペー |
|---|
| 135 | スにおくと Catalyst がコンポーネントとしてロードしちゃいまっせ。 |
|---|
| 136 | |
|---|
| 137 | Writing a generic component that only works with Catalyst is wasteful |
|---|
| 138 | of your time. Try writing a plain perl module, and then a small bit |
|---|
| 139 | of glue that integrates it with Catalyst. See |
|---|
| 140 | L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema> for a |
|---|
| 141 | module that takes the approach. The advantage here is that your |
|---|
| 142 | "Catalyst" DBIC schema works perfectly outside of Catalyst, making |
|---|
| 143 | testing (and command-line scripts) a breeze. The actual Catalyst |
|---|
| 144 | Model is just a few lines of glue that makes working with the schema |
|---|
| 145 | convenient. |
|---|
| 146 | |
|---|
| 147 | Catalyst に依存した一般的なコンポーネントをつくることは、あなたの時間を無駄に浪費 |
|---|
| 148 | させます。素の Perl モジュールを書くことを検討してください。そして、Catalyst とは |
|---|
| 149 | ほんのちょっとの連携コードで連携させてください。このような方法論でつくられたモ |
|---|
| 150 | ジュールの代表例は Catalyst::Model::DBIC::Schema ですので参考にしてください。この |
|---|
| 151 | 方法のよさは、DBIC のスキーマが完全に Catalyst の外でうごいているということです。 |
|---|
| 152 | こうすることによって、テストが楽になり、コマンドラインスクリプトも簡単に書けます。 |
|---|
| 153 | 現実の Catalyst の Model は、ほんとにちょっとの連携コードで便利につかえるようにつ |
|---|
| 154 | なぎこまれてるのだ。 |
|---|
| 155 | |
|---|
| 156 | If you want the thinnest interface possible, take a look at |
|---|
| 157 | L<Catalyst::Model::Adaptor|Catalyst::Model::Adaptor>. |
|---|
| 158 | |
|---|
| 159 | もし、薄いインターフェースで連携できるんなら Catalyst::Model::Adaptor でつなぎこ |
|---|
| 160 | むのがよいです。 |
|---|
| 161 | |
|---|
| 162 | =head2 継承とメソッドのうわがき。(Inheritance and overriding methods) |
|---|
| 163 | |
|---|
| 164 | While Catalyst itself is still based on L<NEXT> (for multiple |
|---|
| 165 | inheritance), extension developers are encouraged to use L<Class::C3>, |
|---|
| 166 | which is what Catalyst will be switching to in some point in the |
|---|
| 167 | future. |
|---|
| 168 | |
|---|
| 169 | When overriding a method, keep in mind that some day additionally |
|---|
| 170 | arguments may be provided to the method, if the last parameter is not |
|---|
| 171 | a flat list. It is thus better to override a method by shifting the |
|---|
| 172 | invocant off of C<@_> and assign the rest of the used arguments, so |
|---|
| 173 | you can pass your complete arguments to the original method via C<@_>: |
|---|
| 174 | |
|---|
| 175 | use Class::C3; ... |
|---|
| 176 | |
|---|
| 177 | sub foo { my $self = shift; |
|---|
| 178 | my ($bar, $baz) = @_; # ... return |
|---|
| 179 | $self->next::method(@_); } |
|---|
| 180 | |
|---|
| 181 | If you would do the common |
|---|
| 182 | |
|---|
| 183 | my ($self, $foo, $bar) = @_; |
|---|
| 184 | |
|---|
| 185 | you'd have to use a much uglier construct to ensure that all arguments |
|---|
| 186 | will be passed along and the method is future proof: |
|---|
| 187 | |
|---|
| 188 | $self->next::method(@_[ 1 .. $#_ ]); |
|---|
| 189 | |
|---|
| 190 | =head2 テストとドキュメンテーション(Tests and documentation) |
|---|
| 191 | |
|---|
| 192 | When you release your module to the CPAN, proper documentation and at |
|---|
| 193 | least a basic test suite (which means more than pod or even just |
|---|
| 194 | C<use_ok>, sorry) gives people a good base to contribute to the |
|---|
| 195 | module. It also shows that you care for your users. If you would like |
|---|
| 196 | your module to become a recommended addition, these things will prove |
|---|
| 197 | invaluable. |
|---|
| 198 | |
|---|
| 199 | CPAN にモジュールをリリースするにあたっては、きちんとしたドキュメンテーションと、 |
|---|
| 200 | テストスィーツ(use_ok してりゃいいってもんじゃないよ)を提供してくださいな。 |
|---|
| 201 | |
|---|
| 202 | If you're just getting started, try using |
|---|
| 203 | L<CatalystX::Starter|CatalystX::Starter> to generate some example |
|---|
| 204 | tests for your module. |
|---|
| 205 | |
|---|
| 206 | もし君が、あたらしくつくりはじめるんなら CatalystX::Starter をつかってみてくださ |
|---|
| 207 | いな。テストの例がはいっているよ。 |
|---|
| 208 | |
|---|
| 209 | =head2 メンテナンス(Maintenance) |
|---|
| 210 | |
|---|
| 211 | In planning to release a module to the community (Catalyst or CPAN and |
|---|
| 212 | Perl), you should consider if you have the resources to keep it up to |
|---|
| 213 | date, including fixing bugs and accepting contributions. |
|---|
| 214 | |
|---|
| 215 | If you're not sure about this, you can always ask in the proper |
|---|
| 216 | Catalyst or Perl channels if someone else might be interested in the |
|---|
| 217 | project, and would jump in as co-maintainer. |
|---|
| 218 | |
|---|
| 219 | A public repository can further ease interaction with the |
|---|
| 220 | community. Even read only access enables people to provide you with |
|---|
| 221 | patches to your current development version. subversion, SVN and SVK, |
|---|
| 222 | are broadly preferred in the Catalyst community. |
|---|
| 223 | |
|---|
| 224 | If you're developing a Catalyst extension, please consider asking the |
|---|
| 225 | core team for space in Catalyst's own subversion repository. You can |
|---|
| 226 | get in touch about this via IRC or the Catalyst developers mailing |
|---|
| 227 | list. |
|---|
| 228 | |
|---|
| 229 | =head2 コンテキストオブジェクト(The context object) |
|---|
| 230 | |
|---|
| 231 | Sometimes you want to get a hold of the context object in a component |
|---|
| 232 | that was created on startup time, where no context existed yet. Often |
|---|
| 233 | this is about the model reading something out of the stash or other |
|---|
| 234 | context information (current language, for example). |
|---|
| 235 | |
|---|
| 236 | If you use the context object in your component you have tied it to an |
|---|
| 237 | existing request. This means that you might get into problems when |
|---|
| 238 | you try to use the component (e.g. the model - the most common case) |
|---|
| 239 | outside of Catalyst, for example in cronjobs. |
|---|
| 240 | |
|---|
| 241 | A stable solution to this problem is to design the Catalyst model |
|---|
| 242 | separately from the underlying model logic. Let's take |
|---|
| 243 | L<Catalyst::Model::DBIC::Schema> as an example. You can create a |
|---|
| 244 | schema outside of Catalyst that knows nothing about the web. This kind |
|---|
| 245 | of design ensures encapsulation and makes development and maintenance |
|---|
| 246 | a whole lot easier. The you use the aforementioned model to tie your |
|---|
| 247 | schema to your application. This gives you a C<MyApp::DBIC> (the name |
|---|
| 248 | is of course just an example) model as well as |
|---|
| 249 | C<MyApp::DBIC::TableName> models to access your result sources |
|---|
| 250 | directly. |
|---|
| 251 | |
|---|
| 252 | By creating such a thin layer between the actual model and the |
|---|
| 253 | Catalyst application, the schema itself is not at all tied to any |
|---|
| 254 | application and the layer in-between can access the model's API using |
|---|
| 255 | information from the context object. |
|---|
| 256 | |
|---|
| 257 | A Catalyst component accesses the context object at request time with |
|---|
| 258 | L<Catalyst::Component/"ACCEPT_CONTEXT($c, @args)">. |
|---|
| 259 | |
|---|
| 260 | =head1 設定(CONFIGURATION) |
|---|
| 261 | |
|---|
| 262 | The application has to interact with the extension with some |
|---|
| 263 | configuration. There is of course again more than one way to do it. |
|---|
| 264 | |
|---|
| 265 | =head2 アトリビュート(Attributes) |
|---|
| 266 | |
|---|
| 267 | You can specify any valid Perl attribute on Catalyst actions you like. |
|---|
| 268 | (See L<attributes/"Syntax of Attribute Lists"> for a description of |
|---|
| 269 | what is valid.) These will be available on the C<Catalyst::Action> |
|---|
| 270 | instance via its C<attributes> accessor. To give an example, this |
|---|
| 271 | action: |
|---|
| 272 | |
|---|
| 273 | sub foo : Local Bar('Baz') { |
|---|
| 274 | my ($self, $c) = @_; |
|---|
| 275 | my $attributes = |
|---|
| 276 | $self->action_for('foo')->attributes; |
|---|
| 277 | $c->res->body($attributes->{Bar}[0] ); |
|---|
| 278 | } |
|---|
| 279 | |
|---|
| 280 | will set the response body to C<Baz>. The values always come in an |
|---|
| 281 | array reference. As you can see, you can use attributes to configure |
|---|
| 282 | your actions. You can specify or alter these attributes via |
|---|
| 283 | L</"Component Configuration">, or even react on them as soon as |
|---|
| 284 | Catalyst encounters them by providing your own L<component base |
|---|
| 285 | class|/"Component Base Classes">. |
|---|
| 286 | |
|---|
| 287 | =head2 カスタムアクセサをつくる(Creating custom accessors) |
|---|
| 288 | |
|---|
| 289 | L<Catalyst::Component> uses L<Class::Accessor::Fast> for accessor |
|---|
| 290 | creation. Please refer to the modules documentation for usage |
|---|
| 291 | information. |
|---|
| 292 | |
|---|
| 293 | L<Catalyst::Component> はアクセサをつくるのに L<Class::Accessor::Fast> をつかって |
|---|
| 294 | います。 |
|---|
| 295 | |
|---|
| 296 | =head2 コンポーネントの設定(Component configuration) |
|---|
| 297 | |
|---|
| 298 | At creation time, the class configuration of your component (the one |
|---|
| 299 | available via C<$self-E<gt>config>) will be merged with possible |
|---|
| 300 | configuration settings from the applications configuration (either |
|---|
| 301 | directly or via config file). This is then stored in the controller |
|---|
| 302 | object's hash reference. So, if you read possible configurations like: |
|---|
| 303 | |
|---|
| 304 | my $model_name = $controller->{model_name}; |
|---|
| 305 | |
|---|
| 306 | you will get the right value. The C<config> accessor always only |
|---|
| 307 | contains the original class configuration and must not be used for |
|---|
| 308 | component configuration. |
|---|
| 309 | |
|---|
| 310 | You are advised to create accessors on your component class for your |
|---|
| 311 | configuration values. This is good practice and makes it easier to |
|---|
| 312 | capture configuration key typos. You can do this with the |
|---|
| 313 | C<mk_ro_accessors> method provided to L<Catalyst::Component> via |
|---|
| 314 | L<Class::Accessor::Fast>: |
|---|
| 315 | |
|---|
| 316 | use base 'Catalyst::Controller'; |
|---|
| 317 | __PACKAGE__->mk_ro_accessors('model_name'); |
|---|
| 318 | ... |
|---|
| 319 | my $model_name = $controller->model_name; |
|---|
| 320 | |
|---|
| 321 | =head1 実装(IMPLEMENTATION) |
|---|
| 322 | |
|---|
| 323 | This part contains the technical details of various implementation |
|---|
| 324 | methods. Please read the L</"BEST PRACTICES"> before you start your |
|---|
| 325 | implementation, if you haven't already. |
|---|
| 326 | |
|---|
| 327 | =head2 Actionクラス(Action classes) |
|---|
| 328 | |
|---|
| 329 | Usually, your action objects are of the class L<Catalyst::Action>. |
|---|
| 330 | You can override this with the C<ActionClass> attribute to influence |
|---|
| 331 | execution and/or dispatching of the action. A widely used example of |
|---|
| 332 | this is L<Catalyst::Action::RenderView>, which is used in every newly |
|---|
| 333 | created Catalyst application in your root controller: |
|---|
| 334 | |
|---|
| 335 | sub end : ActionClass('RenderView') { } |
|---|
| 336 | |
|---|
| 337 | Usually, you want to override the C<execute> and/or the C<match> |
|---|
| 338 | method. The execute method of the action will naturally call the |
|---|
| 339 | methods code. You can surround this by overriding the method in a |
|---|
| 340 | subclass: |
|---|
| 341 | |
|---|
| 342 | package Catalyst::Action::MyFoo; use strict; |
|---|
| 343 | |
|---|
| 344 | use Class::C3; use base 'Catalyst::Action'; |
|---|
| 345 | |
|---|
| 346 | sub execute { |
|---|
| 347 | my $self = shift; |
|---|
| 348 | my ($controller, $c, @args) = @_; |
|---|
| 349 | # put your 'before' code here |
|---|
| 350 | my $r = $self->next::method(@_); |
|---|
| 351 | # put your 'after' code here |
|---|
| 352 | return $r; |
|---|
| 353 | } |
|---|
| 354 | 1; |
|---|
| 355 | |
|---|
| 356 | We are using L<Class::C3> to re-dispatch to the original C<execute> method |
|---|
| 357 | in the L<Catalyst::Action> class. |
|---|
| 358 | |
|---|
| 359 | The Catalyst dispatcher handles an incoming request and, depending |
|---|
| 360 | upon the dispatch type, will call the appropriate target or chain. |
|---|
| 361 | From time to time it asks the actions themselves, or through the |
|---|
| 362 | controller, if they would match the current request. That's what the |
|---|
| 363 | C<match> method does. So by overriding this, you can change on what |
|---|
| 364 | the action will match and add new matching criteria. |
|---|
| 365 | |
|---|
| 366 | For example, the action class below will make the action only match on |
|---|
| 367 | Mondays: |
|---|
| 368 | |
|---|
| 369 | package Catalyst::Action::OnlyMondays; use strict; |
|---|
| 370 | |
|---|
| 371 | use Class::C3; |
|---|
| 372 | use base 'Catalyst::Action'; |
|---|
| 373 | |
|---|
| 374 | sub match { |
|---|
| 375 | my $self = shift; |
|---|
| 376 | return 0 if ( localtime(time) )[6] == 1; |
|---|
| 377 | return $self->next::method(@_); |
|---|
| 378 | } |
|---|
| 379 | 1; |
|---|
| 380 | |
|---|
| 381 | And this is how we'd use it: |
|---|
| 382 | |
|---|
| 383 | sub foo: Local ActionClass('OnlyMondays') { |
|---|
| 384 | my ($self, $c) = @_; |
|---|
| 385 | $c->res->body('I feel motivated!'); |
|---|
| 386 | } |
|---|
| 387 | |
|---|
| 388 | If you are using action classes often or have some specific base |
|---|
| 389 | classes that you want to specify more conveniently, you can implement |
|---|
| 390 | a component base class providing an attribute handler. |
|---|
| 391 | |
|---|
| 392 | For further information on action classes, please refer to |
|---|
| 393 | L<Catalyst::Action> and L<Catalyst::Manual::Actions>. |
|---|
| 394 | |
|---|
| 395 | =head2 コンポーネントなクラス(Component base classes) |
|---|
| 396 | |
|---|
| 397 | Many L<Catalyst::Plugin> that were written in Catalyst's early days |
|---|
| 398 | should really have been just controller base classes. With such a |
|---|
| 399 | class, you could provide functionality scoped to a single controller, |
|---|
| 400 | not polluting the global namespace in the context object. |
|---|
| 401 | |
|---|
| 402 | Catalyst がうまれたての頃に多くの L<Catalyst::Plugin> が書かれました。 |
|---|
| 403 | |
|---|
| 404 | You can provide regular Perl methods in a base class as well as |
|---|
| 405 | actions which will be inherited to the subclass. Please refer to |
|---|
| 406 | L</Controllers> for an example of this. |
|---|
| 407 | |
|---|
| 408 | You can introduce your own attributes by specifying a handler method |
|---|
| 409 | in the controller base. For example, to use a C<FullClass> attribute |
|---|
| 410 | to specify a fully qualified action class name, you could use the |
|---|
| 411 | following implementation. Note, however, that this functionality is |
|---|
| 412 | already provided via the C<+> prefix for action classes. A simple |
|---|
| 413 | |
|---|
| 414 | シンプルな |
|---|
| 415 | |
|---|
| 416 | sub foo : Local ActionClass('+MyApp::Action::Bar') { ... } |
|---|
| 417 | |
|---|
| 418 | will use C<MyApp::Action::Bar> as action class. |
|---|
| 419 | |
|---|
| 420 | は、C<MyApp::Action::Bar> を Action クラスとしてつかいます。 |
|---|
| 421 | |
|---|
| 422 | package MyApp::Base::Controller::FullClass; use strict; use base |
|---|
| 423 | 'Catalyst::Controller'; |
|---|
| 424 | |
|---|
| 425 | sub _parse_FullClass_attr { |
|---|
| 426 | my ($self, $app_class, $action_name, $value, $attrs) = @_; |
|---|
| 427 | return( ActionClass => $value ); |
|---|
| 428 | } |
|---|
| 429 | 1; |
|---|
| 430 | |
|---|
| 431 | Note that the full line of arguments is only provided for completeness |
|---|
| 432 | sake. We could use this attribute in a subclass like any other |
|---|
| 433 | Catalyst attribute: |
|---|
| 434 | |
|---|
| 435 | package MyApp::Controller::Foo; |
|---|
| 436 | use strict; |
|---|
| 437 | use base 'MyApp::Base::Controller::FullClass'; |
|---|
| 438 | |
|---|
| 439 | sub foo : Local FullClass('MyApp::Action::Bar') { ... } |
|---|
| 440 | |
|---|
| 441 | 1; |
|---|
| 442 | |
|---|
| 443 | =head2 コントローラ(Controllers) |
|---|
| 444 | |
|---|
| 445 | Many things can happen in controllers, and it often improves |
|---|
| 446 | maintainability to abstract some of the code out into reusable base |
|---|
| 447 | classes. |
|---|
| 448 | |
|---|
| 449 | You can provide usual Perl methods that will be available via your |
|---|
| 450 | controller object, or you can even define Catalyst actions which will |
|---|
| 451 | be inherited by the subclasses. Consider this controller base class: |
|---|
| 452 | |
|---|
| 453 | package MyApp::Base::Controller::ModelBase; |
|---|
| 454 | use strict; |
|---|
| 455 | use base 'Catalyst::Controller'; |
|---|
| 456 | |
|---|
| 457 | sub list : Chained('base') PathPart('') Args(0) { |
|---|
| 458 | my ($self, $c) = @_; |
|---|
| 459 | my $model = $c->model( $self->{model_name} ); |
|---|
| 460 | my $condition = $self->{model_search_condition} || {}; |
|---|
| 461 | my $attrs = $self->{model_search_attrs} || {}; |
|---|
| 462 | $c->stash(rs => $model->search($condition, $attrs); |
|---|
| 463 | } |
|---|
| 464 | |
|---|
| 465 | sub load : Chained('base') PathPart('') CaptureArgs(1) { |
|---|
| 466 | my ($self, $c, $id) = @_; |
|---|
| 467 | my $model = $c->model( $self->{model_name} ); |
|---|
| 468 | $c->stash(row => $model->find($id)); |
|---|
| 469 | } |
|---|
| 470 | 1; |
|---|
| 471 | |
|---|
| 472 | This example implements two simple actions. The C<list> action chains |
|---|
| 473 | to a (currently non-existent) C<base> action and puts a result-set |
|---|
| 474 | into the stash taking a configured C<model_name> as well as a search |
|---|
| 475 | condition and attributes. This action is a |
|---|
| 476 | L<chained|Catalyst::DispatchType::Chained> endpoint. The other action, |
|---|
| 477 | called C< load > is a chain midpoint that takes one argument. It takes |
|---|
| 478 | the value as an ID and loads the row from the configured model. Please |
|---|
| 479 | not that the above code is simplified for clarity. It misses error |
|---|
| 480 | handling, input validation, and probably other things. |
|---|
| 481 | |
|---|
| 482 | The class above is not very useful on its own, but we can combine it |
|---|
| 483 | with some custom actions by sub-classing it: |
|---|
| 484 | |
|---|
| 485 | package MyApp::Controller::Foo; |
|---|
| 486 | use strict; |
|---|
| 487 | use base 'MyApp::Base::Controller::ModelBase'; |
|---|
| 488 | |
|---|
| 489 | __PACKAGE__->config( model_name => 'DB::Foo', |
|---|
| 490 | model_search_condition=> { is_active => 1 }, |
|---|
| 491 | model_search_attrs => { order_by => 'name' }, |
|---|
| 492 | ); |
|---|
| 493 | |
|---|
| 494 | sub base : Chained PathPart('foo') CaptureArgs(0) { } |
|---|
| 495 | |
|---|
| 496 | sub view : Chained('load') Args(0) { |
|---|
| 497 | my ($self, $c) = @_; |
|---|
| 498 | my $row = $c->stash->{row}; |
|---|
| 499 | $c->res->body(join ': ', $row->name, |
|---|
| 500 | $row->description); } |
|---|
| 501 | 1; |
|---|
| 502 | |
|---|
| 503 | This class uses the formerly created controller as a base |
|---|
| 504 | class. First, we see the configurations that were used in the parent |
|---|
| 505 | class. Next comes the C<base> action, where everything chains off of. |
|---|
| 506 | |
|---|
| 507 | Note that inherited actions act like they were declared in your |
|---|
| 508 | controller itself. You can therefor call them just by their name in |
|---|
| 509 | C<forward>s, C<detaches> and C<Chained(..)> specifications. This is an |
|---|
| 510 | important part of what makes this technique so useful. |
|---|
| 511 | |
|---|
| 512 | The new C<view> action ties itself to the C<load> action specified in |
|---|
| 513 | the base class and outputs the loaded row's C<name> and C<description> |
|---|
| 514 | columns. The controller C<MyApp::Controller::Foo> now has these |
|---|
| 515 | publicly available paths: |
|---|
| 516 | |
|---|
| 517 | =over |
|---|
| 518 | |
|---|
| 519 | =item /foo |
|---|
| 520 | |
|---|
| 521 | Will call the controller's C<base>, then the base classes C<list> |
|---|
| 522 | action. |
|---|
| 523 | |
|---|
| 524 | =item /foo/$id/view |
|---|
| 525 | |
|---|
| 526 | First, the controller's C<base> will be called, then it will C<load> |
|---|
| 527 | the row with the corresponding C<$id>. After that, C<view> will |
|---|
| 528 | display some fields out of the object. |
|---|
| 529 | |
|---|
| 530 | =back |
|---|
| 531 | |
|---|
| 532 | =head2 モデルとビュー(Models and Views) |
|---|
| 533 | |
|---|
| 534 | If the functionality you'd like to add is really a data-set that you |
|---|
| 535 | want to manipulate, for example internal document types, images, |
|---|
| 536 | files, it might be better suited as a model. |
|---|
| 537 | |
|---|
| 538 | The same applies for views. If your code handles representation or |
|---|
| 539 | deals with the applications interface and should be universally |
|---|
| 540 | available, it could be a perfect candidate for a view. |
|---|
| 541 | |
|---|
| 542 | Please implement a C<process> method in your views. This method will |
|---|
| 543 | be called by Catalyst if it is asked to forward to a component without |
|---|
| 544 | a specified action. Note that C<process> is B<not a Catalyst action> |
|---|
| 545 | but a simple Perl method. |
|---|
| 546 | |
|---|
| 547 | You are also encouraged to implement a C<render> method corresponding |
|---|
| 548 | with the one in L<Catalyst::View::TT>. This has proven invaluable, |
|---|
| 549 | because people can use your view for much more fine-grained content |
|---|
| 550 | generation. |
|---|
| 551 | |
|---|
| 552 | Here is some example code for a fictional view: |
|---|
| 553 | |
|---|
| 554 | package CatalystX::View::MyView; |
|---|
| 555 | use strict; |
|---|
| 556 | use base 'Catalyst::View'; |
|---|
| 557 | |
|---|
| 558 | sub process { |
|---|
| 559 | my ($self, $c) = @_; |
|---|
| 560 | my $template = $c->stash->{template}; |
|---|
| 561 | my $content = $self->render($c, $template, $c->stash); |
|---|
| 562 | $c->res->body( $content ); |
|---|
| 563 | } |
|---|
| 564 | |
|---|
| 565 | sub render { |
|---|
| 566 | my ($self, $c, $template, $args) = @_; |
|---|
| 567 | # prepare content here |
|---|
| 568 | return $content; |
|---|
| 569 | } |
|---|
| 570 | 1; |
|---|
| 571 | |
|---|
| 572 | =head2 プラグイン(Plugins) |
|---|
| 573 | |
|---|
| 574 | The first thing to say about plugins is that if you're not sure if |
|---|
| 575 | your module should be a plugin, it probably shouldn't. It once was |
|---|
| 576 | common to add features to Catalyst by writing plugins that provide |
|---|
| 577 | accessors to said functionality. As Catalyst grew more popular, it |
|---|
| 578 | became obvious that this qualifies as bad practice. |
|---|
| 579 | |
|---|
| 580 | まずもって、あなたがプラグインをつくろうとおもっている場合、十中八九プラグインを |
|---|
| 581 | つくるべきではありません。 |
|---|
| 582 | |
|---|
| 583 | By designing your module as a Catalyst plugin, every method you |
|---|
| 584 | implement, import or inherit will be available via your applications |
|---|
| 585 | context object. A plugin pollutes the global namespace, and you |
|---|
| 586 | should be only doing that when you really need to. |
|---|
| 587 | |
|---|
| 588 | あなたのモジュールを Catalyst のプラグインとして実装した場合、あなたが実装したす |
|---|
| 589 | べてのメソッドはあなたのアプリケーションの context object に継承されます。プラグ |
|---|
| 590 | インはグローバルなネームスペースをけがします。あなたはほんとうにそうしたいときに |
|---|
| 591 | だけそうするべきです。 |
|---|
| 592 | |
|---|
| 593 | Often, developers design extensions as plugins because they need to |
|---|
| 594 | get hold of the context object. Either to get at the stash or |
|---|
| 595 | request/response objects are the widely spread reasons. It is, |
|---|
| 596 | however, perfectly possible to implement a regular Catalyst component |
|---|
| 597 | (read: model, view or controller) that receives the current context |
|---|
| 598 | object via L<Catalyst::Component/"ACCEPT_CONTEXT($c, @args)">. |
|---|
| 599 | |
|---|
| 600 | When is a plugin suited to your task? Your code needs to be a |
|---|
| 601 | plugin to act upon or alter specific parts of Catalyst's request |
|---|
| 602 | lifecycle. If your functionality needs to wrap some C<prepare_*> or |
|---|
| 603 | C<finalize_*> stages, you won't get around a plugin. |
|---|
| 604 | |
|---|
| 605 | Another valid target for a plugin architecture are things that |
|---|
| 606 | B<really> have to be globally available, like sessions or |
|---|
| 607 | authentication. |
|---|
| 608 | |
|---|
| 609 | B<Please do not> release Catalyst extensions as plugins only to |
|---|
| 610 | provide some functionality application wide. Design it as a controller |
|---|
| 611 | base class or another suiting technique with a smaller scope, so that |
|---|
| 612 | your code only influences those parts of the application where it is |
|---|
| 613 | needed, and namespace clashes and conflicts are ruled out. |
|---|
| 614 | |
|---|
| 615 | The implementation is pretty easy. Your plugin will be inserted in the |
|---|
| 616 | application's inheritance list, above Catalyst itself. You can by this |
|---|
| 617 | alter Catalyst's request lifecycle behaviour. Every method you |
|---|
| 618 | declare, every import in your package will be available as method on |
|---|
| 619 | the application and the context object. As an example, let's say you |
|---|
| 620 | want Catalyst to warn you every time uri_for returned an undefined |
|---|
| 621 | value, for example because you specified the wrong number of captures |
|---|
| 622 | for the targeted action chain. You could do this with this simple |
|---|
| 623 | implementation (excuse the lame class name, it's just an example): |
|---|
| 624 | |
|---|
| 625 | プラグインの実装は簡単なようにみえます。あなたのプラグインは Catalyst の継承ツリー |
|---|
| 626 | の上にはいります。これによりあなたは、Catalyst のリクエストライフサイクルの挙動を |
|---|
| 627 | 変えることができます。あなたが定義したりあなたのパッケージに import したメソッド |
|---|
| 628 | は、すべてアプリケーションとコンテキストオブジェクトにはえます。例としてあなたが |
|---|
| 629 | Catalyst に uri_for の返り値が undef だったとき警告をだすプラグインをつくったとし |
|---|
| 630 | ましょう。(中略)(アホみたいなクラス名ですいません。これは例なので勘弁してね) |
|---|
| 631 | |
|---|
| 632 | package Catalyst::Plugin::UriforUndefWarning; |
|---|
| 633 | use strict; |
|---|
| 634 | use Class::C3; |
|---|
| 635 | |
|---|
| 636 | sub uri_for { |
|---|
| 637 | my $c = shift; |
|---|
| 638 | my $uri = $c->next::method(@_); |
|---|
| 639 | $c->log->warn( 'uri_for returned undef for:', join(', ', @_), ); |
|---|
| 640 | return $uri; |
|---|
| 641 | } |
|---|
| 642 | |
|---|
| 643 | 1; |
|---|
| 644 | |
|---|
| 645 | This would override Catalyst's C<uri_for> method and emit a C<warn> |
|---|
| 646 | log entry containing the arguments that led to the undefined return |
|---|
| 647 | value. |
|---|
| 648 | |
|---|
| 649 | これは Catalyst の C<uri_for> メソッドをうわがきし、undef がかえってきたら警告を |
|---|
| 650 | ログに吐きだします。 |
|---|
| 651 | |
|---|
| 652 | =head2 COMPONENT() をつかってコンポーネントを生成する(Factory components with COMPONENT()) |
|---|
| 653 | |
|---|
| 654 | Every component inheriting from L<Catalyst::Component> contains a |
|---|
| 655 | C<COMPONENT> method. It is used on application startup by |
|---|
| 656 | C<setup_components> to instantiate the component object for the |
|---|
| 657 | Catalyst application. By default, this will merge the components own |
|---|
| 658 | C<config>uration with the application wide overrides and call the |
|---|
| 659 | class' C<new> method to return the component object. |
|---|
| 660 | |
|---|
| 661 | L<Catalyst::Component> を継承しているすべてのコンポーネントは C<COMPONENT> メソッ |
|---|
| 662 | ドをもっています。 |
|---|
| 663 | |
|---|
| 664 | You can override this method and do and return whatever you want. |
|---|
| 665 | However, you should use L<Class::C3> to forward to the original |
|---|
| 666 | C<COMPONENT> method to merge the configuration of your component. |
|---|
| 667 | |
|---|
| 668 | Here is a stub C<COMPONENT> method: |
|---|
| 669 | |
|---|
| 670 | package CatalystX::Component::Foo; |
|---|
| 671 | use strict; |
|---|
| 672 | use base 'Catalyst::Component'; |
|---|
| 673 | |
|---|
| 674 | use Class::C3; |
|---|
| 675 | |
|---|
| 676 | sub COMPONENT { |
|---|
| 677 | my $class = shift; |
|---|
| 678 | my ($app_class, $config) = @_; |
|---|
| 679 | |
|---|
| 680 | # do things here before instantiation my |
|---|
| 681 | $obj = $self->next::method(@_); |
|---|
| 682 | # do things to object after instantiation |
|---|
| 683 | return $object; |
|---|
| 684 | } |
|---|
| 685 | |
|---|
| 686 | The arguments are the class name of the component, the class name of |
|---|
| 687 | the application instantiating the component, and a hash reference with |
|---|
| 688 | the controller's configuration. |
|---|
| 689 | |
|---|
| 690 | You are free to re-bless the object, instantiate a whole other |
|---|
| 691 | component or really do anything compatible with Catalyst's |
|---|
| 692 | expectations on a component. |
|---|
| 693 | |
|---|
| 694 | For more information, please see L<Catalyst::Component/"COMPONENT($c,$arguments)">. |
|---|
| 695 | |
|---|
| 696 | =head1 SEE ALSO |
|---|
| 697 | |
|---|
| 698 | L<Catalyst>, L<Catalyst::Manual::Actions>, L<Catalyst::Component> |
|---|
| 699 | |
|---|
| 700 | =head1 AUTHOR |
|---|
| 701 | |
|---|
| 702 | Robert Sedlacek C<< <rs@474.at> >> |
|---|
| 703 | |
|---|
| 704 | Jonathan Rockway C<< <jrockway@cpan.org> >> |
|---|
| 705 | |
|---|
| 706 | =head1 LICENSE AND COPYRIGHT |
|---|
| 707 | |
|---|
| 708 | This document is free, you can redistribute it and/or modify it under |
|---|
| 709 | the same terms as Perl itself. |
|---|
| 710 | |
|---|
| 711 | =cut |
|---|