Grails Quick Start(3)

さてさて、亀のようにすすんでますが、続きです。

Create the Controller

次は、Controllerというやつですね。
クライアントから受け取ったリクエストを適当に捌いてビジネスロジックを起動するコンポーネントです。詳しい説明はここ


ドメインモデル同様、grailsコマンドで雛形を生成できるようです。

> cd firstapp
> grails create-controller

名前を聞いてくるので、チュートリアルにしたがって”book”とします。

init-props:
  [groovy] statements executed successfully

create-controller:
  [input] Enter controller name:
book
  [groovy] statements executed successfully
  [copy] Copying 1 file to ...
  [echo] Created controller: grails-app/controllers/BookController.groovy

internal-create-test-suite:
  [groovy] statements executed successfully
  [echo] Created test suite: grails-tests/BookTests.groovy


以下のようなファイルが作られました。
firstapp\grails-app\controllers\BookController.groovy:

class BookController {

	@Property index = { }
}

これって、URLにマップされるのかしら?
サーバーを動かして...

> cd firstapp
> grails run-app

ブラウザからアクセス → http://localhost:8080/firstapp/book
エラーだ。↓

HTTP ERROR: 404
/WEB-INF/grails-app/views//book/index.jsp
RequestURI=/firstapp/book
Powered by Jetty://

Viewが必要なのかな。そうかそうか、フライングか。


というわけで、チュートリアルに奴隷のように従い、以下のように実装を加えておきます。

class BookController {
   @Property index = {
         redirect(action:list)
   }
   @Property list = {
             [ books : Book.list() ]
   }
}

ここによると、Controllerに属性としてクロージャを持つと、URLとそのクロージャが関連つけられるのだそうです。なるほどー。
どういうことかと言うと、たとえば、上のBookControllerの、

   @Property list = {
             [ books : Book.list() ]
   }

という属性は、名前が”list”なので、http://localhost:8080/firstapp/book/list というURLと関連つけられて、URLにリクエストがあると { } の中のロジックが実行されるのですね。
属性名をindexとすると、index.htmlみたいにデフォルトとして扱われるそうです。ふーん。


ちなみにクロージャというのは、処理のひとかたまり(ブロックとかいうといいのだろうか?)がオブジェクトとして扱える、、みたいなイメージ?正しい定義はご勘弁を。
詳細はこちら
上の例をJava的に書くと、

interface Function { abstract Object exec(); }
....
  public Function list = new Function(){
    Object exec() {
      Map m = new HashMap();
      List books = Book.list();
      m.put("books", books);
      return m;
    };
  };

こんな感じ?


Groovyでは、コレクションの宣言が簡略化されていて、
list = [] で、 List list = new ArrayList();
list = [1,2,3] のように初期化できる模様。
map = [:] で、 Map map = new HashMap();
map = [label1:"value1", label2:"value2"] なんて書ける。
これはすごく便利!
詳細は例によってこちら