[Grails]namespaceの使い方（最終版）

#概要
namespaceという機能を使えば、Grailsで同名のコントローラを使い分けることが出来るようになります。
別の言い方をすれば、コントローラをpackageに分けて整理することが出来るようになります。
例えば、一般ユーザがアクセスするコントローラはfrontendパッケージ内に配置して、管理画面用のコントローラはbackendパッケージに配置して整理するといった使い方が想定されます。

以前、namespaceについて2回記事をQiitaに投稿しました。
[[Grails]namespaceを使って同名コントローラを使い分ける](http://qiita.com/saba1024/items/7694ff2b6597ba2c4526)
[[Grails]namespaceの使い方](http://qiita.com/saba1024/items/5b3a68ee362b153a0407)

うまく動かないな〜と言いながら上記の記事を書きましたが、結局自分の理解が間違っていただけでした。
今回のものが最終版という位置付けです。

#前提条件
アプリケーション名は**namespacetest**としました。
今回は、`admin`と`user`とういパッケージを作成し、そのパッケージにそれぞれ`Profile`というコントローラを作成してみます。

以下のコマンドでコントローラを作成しました。

```
grails> create-controller user.Profile
| Created file grails-app/controllers/user/ProfileController.groovy
| Created file grails-app/views/profile
| Created file test/unit/user/ProfileControllerSpec.groovy
grails> create-controller admin.Profile
| Created file grails-app/controllers/admin/ProfileController.groovy
| Created file grails-app/views/profile
| Created file test/unit/admin/ProfileControllerSpec.groovy
grails> 

```

#実際のコード
特に複雑なものではないので、いきなりソースを見てしまったほうが理解が早いです。

##adminネームスペース

###コントローラ

package名はcreate-controllerの時点で指定したものが自動で付与されています。
単純に`static namespace = "admin"`を追加するのみ。
あとは通常通りコントローラを記述します。

```groovy:namespacetest/grails-app/controllers/admin/ProfileController.groovy
package admin

class ProfileController {

    static namespace = "admin"

    def index() {
    }

    def redirectFromAdminToUser() {
        redirect(namespace:'user', controller: "profile", action: "index")
    }

    def formPrinter(){
        render "This is Admin namespace : ${params.fromUser}"
    }
}
```
###ビュー
**注意:** `create-controller`コマンドではディレクトリ（namespace名）までは生成してくれないので、自分でディレクトリをnamespace名で作成する。

```html:namespacetest/grails-app/views/admin/profile/index.gsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title></title>
</head>

<body>
<h1>This is Administrator profile</h1>
<g:link namespace="user" controller="profile">Go To User Profile</g:link><br />
<g:link namespace="admin" controller="profile" action="redirectFromAdminToUser">Go To User Profile By Redirect</g:link><br />

<g:form namespace="user" controller="profile" action="formPrinter" >
    <g:hiddenField name="fromAdmin" value="Data is from Admin" />
    <g:submitButton name="a" value="Sbumit To User" />
</g:form>
</body>
</html>
```

##userネームスペース
###コントローラ
package名はcreate-controllerの時点で指定したものが自動で付与されています。
単純に`static namespace = "user"`を追加するのみ。
あとは通常通りコントローラを記述します。


```groovy:namespacetest/grails-app/controllers/user/ProfileController.groovy
package user

class ProfileController {

    static namespace = "user"

    def index() {
    }

    def redirectFromUserToAdmin() {
        redirect(namespace:"admin", controller: "profile", action: "index")
    }

    def formPrinter(){
        render "This is User namespace : ${params.fromAdmin}"
    }
}

```
###ビュー
**注意:** `create-controller`コマンドではディレクトリ（namespace名）までは生成してくれないので、自分でディレクトリをnamespace名で作成する。

```html:namespacetest/grails-app/views/user/profile/index.gsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title></title>
</head>

<body>
<h1>This is User Profile</h1>
<g:link namespace="admin" controller="profile">Go To Administrator Profile</g:link><br />
<g:link namespace="user" controller="profile" action="redirectFromUserToAdmin" >Go To Administrator Profile By Redirect</g:link><br />

<g:form namespace="admin" controller="profile" action="formPrinter">
    <g:hiddenField name="fromUser" value="Data is from User" />
    <g:submitButton name="a" value="Submit To Admin" />
</g:form>
</body>
</html>
```

##URLマッピング
元のUrlMappings.groovyに、以下の`/*追記スタート*/`から`/*追記終了*/`の間を追加する。

```groovy:namespacetest/grails-app/conf/UrlMappings.groovy
class UrlMappings {

	static mappings = {

        /*追記スタート*/
        "/profile/$action?/$id?(.$format)?" {
            controller = 'profile'
            namespace = 'user'
        }
        "/admin/profile/$action?/$id?(.$format)?" {
            controller = 'profile'
            namespace = 'admin'
        }
        /*追記終了*/
        "/$controller/$action?/$id?(.$format)?"{
            constraints {
                // apply constraints here
            }
        }

        "/"(view:"/index")
        "500"(view:'/error')
	}
}
```

#Grails自体の再起動と確認
UrlMappings等を含む、Grailsの設定ファイル系を修正した場合は、何も考えずに一旦Grailsアプリの終了させて、インタラクティブモードからも抜ける。
再度Grailsアプリケーションを立ち上げなおして、[http://localhost:8080/namespacetest/profile/index](http://localhost:8080/namespacetest/profile/index)にアクセスすれば、同じProfileControllerというコントローラがちゃんとnamespace毎に分けられて動作することが確認できます。


#おまけ -Scaffolding-
大体のサンプルはこの段階で終わりますが、Grails大好きな自分としてはやはりGORMをScaffoldで扱う場合はどうなるの？というのが気になります。
**結論から言うと問題ありません。**
例えば、ドメイン用のscaffoldは、全て`scaffold`パッケージ内のコントローラで行いたいと想定します。

まず以下のGrailsコマンドでドメインとscaffold用のコントローラを生成します。

```
grails> create-domain-class Profile
| Created file grails-app/domain/namespacetest/Profile.groovy
| Created file test/unit/namespacetest/ProfileSpec.groovy
grails> 
grails> create-controller scaffold.Profile
| Created file grails-app/controllers/scaffold/ProfileController.groovy
| Created file grails-app/views/profile
| Created file test/unit/scaffold/ProfileControllerSpec.groovy
```
**Profile**というドメインと**scaffold**というネームスペースで動作する**ProfileController**を作成しました。

ProfileControllerは以下のように記述します。

```groovy:namespacetest/grails-app/controllers/scaffold/ProfileController.groovy
package scaffold

class ProfileController {

    static namespace = 'scaffold'
    static scaffold = namespacetest.Profile

}
```

そしてUrlMappings.groovyにも同様に情報を追記します。（`/*追記スタート*/`から`/*追記終了*/`の間）

```groovy:namespacetest/grails-app/conf/UrlMappings.groovy
class UrlMappings {

	static mappings = {

        "/profile/$action?/$id?(.$format)?" {
            controller = 'profile'
            namespace = 'user'
        }
        "/admin/profile/$action?/$id?(.$format)?" {
            controller = 'profile'
            namespace = 'admin'
        }
        /*追記スタート*/
        "/scaffold/$controller/$action?/$id?(.$format)?" {
            namespace = 'scaffold'
        }
        /*追記終了*/
        "/$controller/$action?/$id?(.$format)?"{
            constraints {
                // apply constraints here
            }
        }
        "/"(view:"/index")
        "500"(view:'/error')
	}
}
```

これで終了！と言いたいところなのですが、Scaffoldが生成するリンク（編集、登録、削除など）が、デフォルトだと**コントローラ名とドメイン名が同一のもの**かつ**namespaceは考慮していない**、という状態になっています。
今の段階では、URLを手動で指定すればScaffoldの各画面はすでにちゃんと動作する状態ですが、その画面に表示されている編集、登録、削除などのリンクが正常に機能していない状態です。
そこで、Scaffoldが自動で生成するHTMLのテンプレートを修正してあげます。

テンプレートの修正の方法は[[Grails]ドメイン名と異なるコントローラでscaffolding](http://qiita.com/saba1024/items/ab718a0e6e5fd9b9ac32)を参照してください。
ざっくり内容を説明すると、

1. `grails install-templates`でテンプレートを修正できる状態にする。
2. 各GSPの`g:link`と`g:form`にnamespaceを追加する。
3. Controller.groovyのsave()とupdate()のredirectにnamespaceを追加する。

という流れになります。

具体的には、[[Grails]ドメイン名と異なるコントローラでscaffolding](http://qiita.com/saba1024/items/ab718a0e6e5fd9b9ac32)の修正箇所が、
`<g:link class="list" action="index" namespace="scaffold"...省略`
`<g:form url="[action:'save', namespace:'scaffold']" method="POST" ...省略`
`redirect action: 'show', id:${propertyName}.id, namespace:'scaffold'`
といった感じになります。

これで、[http://localhost:8080/namespacetest/scaffold/profile/index](http://localhost:8080/namespacetest/scaffold/profile/index)にアクセスすれば、ちゃんとnamespaceで管理された状態でscaffoldが利用できます。

#所感
何度かチャレンジしてうまく動かない箇所があるな〜と思っていたnamespaceの動作ですが、基本的に自分の書き方（特にUrlMappings.groovy）が悪かったのが原因でした。
今回、aタグでもformタグでもnamespace属性を付与すればちゃんと動作することが確認できました。
Scaffoldもnamespaceをつけてちゃんと動作させることが出来るようになったので、とりあえずnamespaceに関する自分の中の疑問点が消せました。
ドキュメントやユーザベースのナレッジがGrailsの進化に追い付いていないな、という感じで今回もかなり苦労しましたが、噛めば噛むほど味の出るGrails、今後もどんどん調べたことをまとめていきます。

#参考
[http://www.mscharhag.com/2014/05/grails-controller-namespaces.html](http://www.mscharhag.com/2014/05/grails-controller-namespaces.html)

# scaffoldに関する追記 (2015/01/21)

renderEditor.templateの中の`renderOneToMany`というメソッドの中にも`g:link`があるので、その部分にも`namespace`を追加してあげたほうが良いです。

```groovy:src/templates/scaffolding/renderEditor.template
    private renderOneToMany(domainClass, property) {
        def sw = new StringWriter()
        def pw = new PrintWriter(sw)
        pw.println()
        pw.println '<ul class="one-to-many">'
        pw.println "<g:each in=\"\${${domainInstance}?.${property.name}?}\" var=\"${property.name[0]}\">"
        pw.println "    <li><g:link namespace=\"scaffold\" controller=\"${property.referencedDomainClass.propertyName}\" action=\"show\" id=\"\${${property.name[0]}.id}\">\${${property.name[0]}?.encodeAsHTML()}</g:link></li>"
        pw.println '</g:each>'
		pw.println '<li class="add">'
        pw.println "<g:link namespace=\"scaffold\" controller=\"${property.referencedDomainClass.propertyName}\" action=\"create\" params=\"['${domainClass.propertyName}.id': ${domainInstance}?.id]\">\${message(code: 'default.add.label', args: [message(code: '${property.referencedDomainClass.propertyName}.label', default: '${property.referencedDomainClass.shortName}')])}</g:link>"
		pw.println '</li>'
        pw.println '</ul>'
        return sw.toString()
    }
```

#scaffoldに関する追記 (2015/01/23)
当然ページャー使っているところにもnamespaceを指定する必要が有りました。。。

```groovy:src/templates/scaffolding/index.gsp
<g:paginate total="\${${propertyName}Count ?: 0}" namespace="scaffold"/>
```