「リクエストラムダ」の編集履歴(バックアップ)一覧に戻る

リクエストラムダ - (2008/05/01 (木) 18:04:18) のソース

== リクエストラムダについて詳しく ==

Update: 用語のちょっとしたこと:ブロックへのリクエストをマッピングするアイデアをよりよく反映させるために、この特徴を「リクエストラムダ」と呼んでいます。

毎日(または週末の数時間)Waves のさまざまな側面にほんの少し詳しく説明するために、ちょっとずつ試しては書いています。最初にされるもっとも共通する質問の1つは 「Waves はフレームワークXとどんな違いがあるのですか?」です。 何がWavesを特徴付るかフロントページにリンクしているけれど、あなたは今のところ簡単な案内を受けられるだけです。詳細については何か異なったことを通して本当に理解できるだけです。 (?)

(As an aside, I think Ruby is a lot like this. I see attempts all the time where people try to explain to the resident skeptic why Ruby is so cool. There is always someone there who lists off a bunch of features, and the skeptic always says, “well, my language has those, too.” And they’re right. In the end, it isn’t about the features, it’s about how they’re implemented. I mean, C++ has iterators and a fantastic collection library. But it still ain’t Enumerable.)

I got into a bit of discussion about the concept of request mappings on the mailing list, so I thought I’d recap that here as my first attempt to look at the nuts and bolts of Waves. Request mappings are akin to routes in Rails or Merb. But there are some profound differences. Request mappings are exactly that – they map a request to a block of code.

It’s called a request mapping because we are talking here about the entire request, not just the path. You can match against the URL, the path, the HTTP method, the accept header, etc. The entire request can be matched against, not just the path. Further, the block of code you are mapping into can do anything you want. For example, in Waves, the “hello world” application is just the one line in the mapping file, no controller or view.

 path('/') { 'Hello World!' }

On the other hand, you can also do things like this:

 # add / update the given resource for the given model
 path %r{^/#{model}/#{name}/?$}, :method => :post do |  model, name |
   use( model ) | controller { update( name ) }; redirect( url )
 end


We’re matching a binding regular expression here and constraining it to match only an HTTP POST. We pass the regular expression bindings into the block as paremeters. The methods or variables model and name have been defined elsewhere in the mapping module to help make the rule more readable. Within the block, we set the resource to be used based on the URL and then call the appropriate controller’s update method. Finally, we redirect when we’re done with the update to the GET version of the same URL.

Railsのようなフレームワークのルートと重要な違いは2つ:

* mapping はリクエストと controller または view メソッドによって明確に作られています。 どんな controller メソッドもそれとなく提供されています。 URLにより不適切に呼び出されるコントローラメソッドを追加することを心配する必要はありません。 (または、もし呼び出されたら、 あなたが実行されるようにマッピングを作成したからです。) 

*コントローラには責任がそれほどありません、これはマッピングにたくさん責任があることを意味します。 Waves ではコントローラはビューを決定しませんが、他のフレームワークではコントローラに責任がたくさんあるかもしれません。(?)そしてコントローラが気にしない大部分はマッピング自身の責任になります。

This is a departure from classic MVC, in that it completely decouples the controller and view. In reality, most Web application frameworks already partially decouple them anyway; Waves just finishes the job, as it were.

All a controller is supposed to do in Waves is process a request in terms of a model. Models don’t know about requests. They don’t know about request parameters or 404s. And well they shouldn’t. So controllers act as a sort of receptionist for models, keeping them insulated from the request context. The request mappings become a sort of meta-controller.

Along the same lines, filters are handled within the request mapping, not the controller. This makes it much easier to apply filters consistently across a variety of controllers. For example, suppose we want to ensure that anyone accessing an admin screen first logs in. We might create a request filter that looks like this:

 before %r{^/admin/} do | model, name |
  redirect('/login') unless session[:user]
 end

Now, there is no way to accidentally expose a secure controller method, because any path that starts with /admin/ is going to require a log in anyway. (Well, I shouldn’t say no way … but you can’t do it just by adding a new method to a controller. You’d have to have your mappings messed up.) And everything is in one place in terms of how a request is actually processed.

Of course, you can (and probably should) refactor your mappings into modular components. Waves provides an example of how to do this in the Waves::Mapping::PrettyUrls module. This packages up common mapping pattern for use with “pretty URLs” (resources accessed via name instead of id). So now you can add a whole variety of powerful mappings to your application just by using include. This is the way the default application is set up:

 module Blog
  module Configurations
    module Mapping
      extend Waves::Mapping
      # your custom rules go here
      include Waves::Mapping::PrettyUrls::RestRules
      include Waves::Mapping::PrettyUrls::GetRules
    end
  end
 end

As you develop common mapping patterns, you can simply put them in a module like this and then include them. Waves does not dictate to you what these patterns are (although I expect to introduce a number of common ones in addition to PrettyUrls.) And this is also a great example of the Waves philosophy. Ruby already provides the ability to define DSLs and package reusable code up into modules. There isn’t really a need to for a special method to define particular sets of URLs that only Waves “gurus” understand: we just use what Ruby already provides.

So that’s a hopefully helpful glimpse into what makes Waves unique and powerful. In my next post, I’ll start to get into the intracacies of the MVC wiring within the blocks themselves. Until then, don’t hesitate to comment or post questions.
記事メニュー
目安箱バナー