2011-06-19 7 views
5

触媒の中のすべてが文脈オブジェクトを使っている理由を私が本当に理解していないと思っていました。ちょうど約すべてが、我々は触媒モデルでDBICをラップし、なぜCatalystのコンテキストオブジェクトを使用するのですか?その目的は何ですか?

$c->model('DBIC::Table') ... 

または多分私達は

$c->log->warn('foo'); 

を行うが、私は理由を理解していないで終わる

my ($self, $c) = @_; 

で始まっているようです私たちはしないでください

log('warn', 'foo'); # or whatever the API for some log library is. 

なぜコンテキストオブジェクトを使ってすべてを行うのですか?特別なものは何ですか?

答えて

5

私は正しく何が起こっているのか理解していれば(そして、私は触媒を非常に懸命に見ていないので、簡単に可能です)、コンテキスト変数は呼び出しフレームワークです。リクエストが入ると、フレームワークはすべての情報を自分自身に組み込み、自分のクラスのメソッドを呼び出してメソッドがその情報のすべてと他のフレームワークにアクセスできるようにします。 inversion of control(またはIoC)について読んで理解しやすくなります。

また、コンテキスト変数内のすべての機能をラップすることで、名前空間の問題は発生しません。コントローラ、モデルなどのクラスは、ネームスペースで宣言したメソッドしか持たない。

2

Perlや他の言語の共通のイディオムは、名前空間へのインタフェースを効果的に提供する「神」オブジェクトを渡すことです。これにより、すべての関数をネームスペースにインポートするのではなく、メソッドを呼び出すことができます。これらの各ネームスペースは、異なるランタイムデータ(オブジェクトのインスタンス)でカスタマイズすることもできます。

オブジェクトでメソッドを呼び出す構文が嫌いなら、探しているものがJavascriptのwithブロックに似ているように思えます。

use warnings; 
use strict; 
use Carp(); 

sub with ($&) { 
    my ($obj, $code) = @_; 
    my $auto = (caller).'::AUTOLOAD'; 
    no strict 'refs'; 
    local *$auto = sub { 
     my ($name) = $$auto =~ /([^:]+)$/; 
     my $method = $obj->can($name) 
        || $obj->can(lcfirst $name) 
      or Carp::croak "no method '$name' on '$obj' in with block"; 
     unshift @_, $obj; 
     goto &$method 
    }; 
    $code->() 
} 

モックオブジェクトを考える:

{package Obj; 
    sub new {bless []} 
    sub log {shift; say "logging @_"} 
    sub model {shift; say "setting model to @_"} 
} 

は、あなたがそれから書くことができます:

my $c = Obj->new; 

with $c => sub { 
    model('DBIC::Table'); 
    Log('hello world'); # ucfirst 
    &log('hello again'); # or with a & since 'log' is a builtin 
}; 
Perlがこれを行い天然の構造を持っていませんが、それはものを作るためのツールを提供してい

次のうちどれが印刷されますか:

 
setting model to DBIC::Table 
logging hello world 
logging hello again 

楽しんでください。すでに定義されているサブルーチンの名前または名前は、withブロックでオーバーライドされません。名前のucfirstバージョンを使用するか、それらのインスタンスでメソッドを呼び出すことができます。 withブロック内のすべての新しいサブルーチンは、コンパイル時にその名前がわからないため、Log('hello')という括弧で呼び出され、Log 'hello'ではなく、呼び出される必要があります。

+0

私はRakudoの方法を呼び出す方が好きですが、構文に関する質問ではありませんでした... – xenoterracide

+1

@xenoterracide =>なぜあなたは触媒に '$ c-> log-> warn ...) '(' log(warn => ...) 'の代わりに' 'これは構文上の違いです。その理由は、それが触媒の思想家の思想が最も良かったからです。だから、私はあなたに望み通りに書く方法を与えました(この場合、 '' Log() - > warn(...) ''これは触媒の '' log'メソッドの仕組みなので)、答えはあなたが期待していましたか? –

+0

は、コンテキストオブジェクトを通してすべてが行われる理由についてさらに詳しく質問しています。通常は、外部のものにもアクセスされます。構文の正確な理由は、コンテキストオブジェクトを介している理由よりもはっきりしています。言うまでもなく、単にログapiを直接使用します。 – xenoterracide

1

誰かが通常、Catalyst :: Controllerに表示されているものすべてを書きます。これで、URLマッピングを実行するCatalystコントローラが存在することを覚えておく必要があります。もちろん、多くの関数をコントローラにインポートすることは可能ですが、Catalyst自身がlog関数をインポートすると、この関数をURLマッピングにどのように使用しますか?

たとえば、sub log : Local { ... }。まもなくそれは不可能であろうし、そうしなければならないより複雑になるだろう。コントローラには機能がほとんどないため、多くの機能を覚える必要はなく、競合もありません。

Perl自身が特別な変数に特殊文字を持つのと同じ理由です。 $/$_,$]などのように。もちろん、$INPUT_RECORD_SEPARATORまたは$RSをデフォルトとして使用することもできますが、それらを知っている必要があります。特殊変数をすべて知っていないと、コードと競合する可能性があります。

もう1つの理由は、$cで呼び出す追加の機能にいくつかのコンテキストがあることです。たとえば、$c->log->disable('warn', 'error')でログを有効または無効にするか、有効にするだけです。このコンテキストは、より深いコントローラに正しく渡されます。グローバルではないので、別の状態へのリクエストごとに設定することができます。

もう1つの理由は、使用する余分な機能が、設定ファイルなどを読む必要があることがあります。すべてのリクエスト(毎回$cはすべてのリクエストに対して特別です)に渡すオブジェクトを使用し、リクエストごとに変更することができます。これにより、アプリケーションから情報をリクエストしたり、特定のリクエストの状態を処理したりすることができます。

しかし、これをやりたくない場合は、必ず$cを使用する必要はありません。たとえば、Log :: Log4Perlを手動でロードして、特別な設定を使用し、$c->logをまったく使用しないでください。また、多くの機能を自分でインポートすることもできます。しかし、私は名前空間を汚染しないようにデフォルトを設定し、毎回のリクエストに対して特別なことをする可能性を与えることは良いデフォルトだと思います。

少なくとも、$cを使用する必要はありません。たとえば、私は自分でDateTimeを直接使用して新しいオブジェクトを作成し、$c->datetimeを実行できるCatalyst :: Plugin :: DateTimeを使用しません。そして私は最後のことをすることの利益を見ません。 $cを拡張する多くのプラグインが存在するという理由だけで、それらが有用であるとは限りませんし、使用する必要があります。

関連する問題