読者です 読者をやめる 読者になる 読者になる

プログラミング 美徳の不幸

Ruby, Rails, JavaScriptなどのプログラミングまとめ、解説、備忘録。

RailwayJS ハマりポイント① filterの使い方

RailwayJSを3週間近くいじって、少しはまりやすいところについて解説したいと思います。

まず、filterというのはcontrollerのアクションの前後で動作する処理のことで、具体的にはログイン済みであることを要求するページにアクセスする際にログイン済みかを判定し、ログインしていたらそのまま処理を続行、していなかったらログインページにリダイレクトさせる、などの処理をfilterとして書くと、同じくログインを要する別のアクションからもそのfilterを使うことでコードの重複を防ぐことができます。

通常、ログインを要求する、のようなfilterはapplication全体で共有したいので、application_controllerに書くわけです。
◯application_controller.coffee

before 'set current user', ->
  User.find req.cookies.user_id ,(err,user)=>
    if err || !user?
      @current_user = null
      console.log "user not found"
    else
      @current_user = user
      console.log "user found"
    next()

こうすると、すべてのアクションでこのfilterが呼ばれます。やっている処理は、cookieからuser_idを取得、findして存在していたら@current_userに値をセット、存在しないならnullを入れておくという処理です。これですべてのアクションから@current_userを使用することができます。

ここで、RoRと違う点として、最後にnext()を書く必要があるのが重要です。next()は処理をもとのアクションに戻すために使います。redirectするか、next()しない限り次の処理には永遠に戻らないので注意してください。(試してないけどたぶんrender()でもいいと思う)

このように@current_userをセットするだけならすべてのactionに対して実行していいわけですが、「ログインしていなかったらログインページにリダイレクトする」というような処理はactionごとに指定しないといけません。このような場合はapplication_controllerにfilterを用意して、それをそれぞれのcontrollerから呼び出す必要があります。

◯application_controller.coffee

requireAuthenticate = ()-> ・・・・(1)
  if @current_user == null
    flash 'error', 'You must login for continue'
    redirect path_to.new_session
  else 
    next()
publish('requireAuthenticate', requireAuthenticate)

◯hoge_controller.coffee

load 'application'
before(use 'requireAuthenticate'), {only: ['index','....] }

action 'index', .....

ここで新しくpublishとuseというのがでてきました。通常、あるcontroller(基本的にはapplication_controllerになるだろう)を取り込む場合は、load関数を使用します。
load 'application'
とした時点で、application_controller自体でbefore定義されているもの(上の例だと'set current user')はフィルタとして機能します。一方で、関数として定義されているけど、filterとして登録されていないもの(上の例だと'requireAuthenticate')はloadした上で明示的にbefore指定する必要があります。

ただ、controllerをまたぐ場合はpublish関数によって公開しないとアクセスができません。だからこのように
publish(' 登録名', 関数)
としてapplication_controllerで指定した上で、それぞれのcontrollerから
load 'application' #=> application_controllerをloadする
before(use 'requireAuthenticate') #=> requireAuthenticate(publish時の登録名)を取得し、before登録する。
という処理が必要なんですね。

このへん(publish,use)はRailsにはない考えです。
なぜならRailsではそれぞれのcontrollerがすべてapplication_controllerのサブクラスで、application_controller中で定義されたものはスーパークラスのメソッドとしてアクセスしますが、RailwayJSではapplication_controllerは親クラスではないから、どちらかというとモジュールです。

RailwayJSでは基本的にcontrollerは優秀ですね。使い方に一度慣れてしまえば、さほどRailsと使用感は違わないのではないのでしょうか。

※1...ここが->でなく=>となっているのはthisのコンテクストを変更しているため。これが->だともとのアクションとはthisが異なって、インスタンス変数にアクセスできなくなる。