cakephpのデバッグについて

タイトルはcakephpとなっていますが、cakephpとか関係なく単純にphpでのデバッグです。

・配列をvar_dumpで出力する
saveAllでデータが保存されなくて困ったんですが、
やっぱりarrayが原因でした。
調べ方は単純にvar_dump。
親切なエラーメッセージでは無かったので気づきづらいですが。。

今回あまり深くcakephpを触ることは無かったので、
これ以上はしていませんし必要もありませんでした。
経験豊富なエンジニアの方いわく、デバッグはそもそも難しいので一気に書かないで少しずつ動かしながらやるとかですかね。と言われました。

確かに!!
気づいたら書きすぎていること多いので、それがそもそも間違いなんですね。
もっと最小単位での実装心がけます!!

できるエンジニアさんのお言葉
デバッグは経験が大きいし不確定要素大きいので、そもそもデバッグする羽目にならないようにするのがコツ」

cakephpのオフィシャルのデバッグページ
デバッグ — CakePHP Cookbook 2.x ドキュメント

コメント機能

前回は投稿とユーザーの紐付けを行ったので、今回は投稿に対して一般のユーザーがコメントをできる機能を作っていきます。

流れとしてはこんな感じです。

・commentテーブルの作成&外部キーの追加
・entityとschemaの作成
・全ての投稿一覧が見れるページを作成
・各投稿が閲覧でき、かつコメントするページ
・routesファイルの設定
・controllersファイルの設定

では早速作っていきましょう。

Post Entity

case class Post(
  @Column("post_id")
  id: Long,
  @Column("title")
  title: String,
  @Column("content")
  content: String,
  @Column("user_id")
  userId: Int ) extends KeyedEntity[Long] {
  }

User Entity

case class User(
  @Column("user_id")
  id: Long,
  @Column("user_name")
  userName: Option[String],
  @Column("mail_address")
  mailAddress: Option[String] ) extends KeyedEntity[Long] {
  }

・commentテーブルの作成&外部キーの追加

ここではuser_idはuserテーブルのuser_idに、post_idはpostテーブルのpost_idにリレーションさせています。

create table comment (
  comment_id integer primary key,
  comment text,
  post_id integer REFERENCES post(post_id)
);

・entityとschemaの作成

//entityの作成
case class Comment(
  @Column("comment_id")
  id: Int,
  @Column("comment")
  comment: Option[String],
  @Column("post_id")
  postId: Int ) extends KeyedEntity[Int] {
    }


object AppDB extends Schema {
  ...省略...
  //schemaの作成
  val commentTable = table[Comment]("comment")
}

・全ての投稿一覧が見れるページを作成

まずはviewから

//allposts.scala.html
@(posts: List[(db.Post, db.User)])

<ul>
    @for( (post, user) <- posts ){
    <li>title: @post.title
        <br/>content: @post.content
        <br/>author: @user.userName
        <p>
          <a href="@routes.Application.entry(post.id)">view comments</a>
        </p>
    </li>
    }
</ul>

・各投稿が閲覧でき、かつコメントするページ

今回コメントは各投稿閲覧時のページからできるようにします。

@(post: db.Post, comments: List[db.Comment], form: play.api.data.Form[db.Comment])


<h2>@post.title</h2>
<p>@post.content</p>

<ul>
    <li>
    @for( comment <- comments){
    <p>Viewer's Comment: @comment.comment</p>
    </li>
    }
</ul>

@helper.form(action = routes.Application.addComment) {

<input type="hidden" name="post_id" value="@post.id"/>
@helper.inputText(form("comment"))
<input type="submit"/>

}

・routesファイルの設定

今度はroutesの設定を行っていきます。
ここでも今までと比べてあまり変わったことはしていないので、細かい説明は省きます。

//全ての投稿を表示
GET   /                          @controllers.Application.index

//各投稿を表示
GET   /entry/:id                        @controllers.Application.entry(id: Long)

//commentフォームのデータの送信先として
POST  /addComment                       @controllers.Application.addComment

・controllersファイルの設定

前回は投稿とユーザーの紐付けを行ったので、今回は投稿に対して一般のユーザーがコメントをできる機能を作っていきます。

流れとしてはこんな感じです。

・commentテーブルの作成&外部キーの追加
・entityとschemaの作成
・全ての投稿一覧が見れるページを作成
・各投稿が閲覧でき、かつコメントするページ
・routesファイルの設定
・controllersファイルの設定

では早速作っていきましょう。

Post Entity
>|scala|
case class Post(
  @Column("post_id")
  id: Long,
  @Column("title")
  title: String,
  @Column("content")
  content: String,
  @Column("user_id")
  userId: Int ) extends KeyedEntity[Long] {
  }

User Entity

case class User(
  @Column("user_id")
  id: Long,
  @Column("user_name")
  userName: Option[String],
  @Column("mail_address")
  mailAddress: Option[String] ) extends KeyedEntity[Long] {
  }

・commentテーブルの作成&外部キーの追加

ここではuser_idはuserテーブルのuser_idに、post_idはpostテーブルのpost_idにリレーションさせています。

create table comment (
  comment_id integer primary key,
  comment text,
  post_id integer REFERENCES post(post_id)
);

・entityとschemaの作成

//entityの作成
case class Comment(
  @Column("comment_id")
  id: Int,
  @Column("comment")
  comment: Option[String],
  @Column("post_id")
  postId: Int ) extends KeyedEntity[Int] {
    }


object AppDB extends Schema {
  ...省略...
  //schemaの作成
  val commentTable = table[Comment]("comment")
}

・全ての投稿一覧が見れるページを作成

まずはviewから

//allposts.scala.html
@(posts: List[(db.Post, db.User)])

<ul>
    @for( (post, user) <- posts ){
    <li>title: @post.title
        <br/>content: @post.content
        <br/>author: @user.userName
        <p>
          <a href="@routes.Application.entry(post.id)">view comments</a>
        </p>
    </li>
    }
</ul>

・各投稿が閲覧でき、かつコメントするページ

今回コメントは各投稿閲覧時のページからできるようにします。

@(post: db.Post, comments: List[db.Comment], form: play.api.data.Form[db.Comment])


<h2>@post.title</h2>
<p>@post.content</p>

<ul>
    <li>
    @for( comment <- comments){
    <p>Viewer's Comment: @comment.comment</p>
    </li>
    }
</ul>

@helper.form(action = routes.Application.addComment) {

<input type="hidden" name="post_id" value="@post.id"/>
@helper.inputText(form("comment"))
<input type="submit"/>

}

・routesファイルの設定

今度はroutesの設定を行っていきます。
ここでも今までと比べてあまり変わったことはしていないので、細かい説明は省きます。

//全ての投稿を表示
GET   /                          @controllers.Application.index

//各投稿を表示
GET   /entry/:id                        @controllers.Application.entry(id: Long)

//commentフォームのデータの送信先として
POST  /addComment                       @controllers.Application.addComment

・controllersファイルの設定

package controllers

import models.UserData
import play.api.mvc._
import play.api.data._
import org.squeryl.PrimitiveTypeMode._
import db._
import play.api.data.Forms._
import securesocial.core._


class Application(override implicit val env: RuntimeEnvironment[UserData]) extends Controller with securesocial.core.SecureSocial[UserData] {

  //コメントのフォームの定義
  val commentForm = Form(
    mapping(
      "comment_id" -> ignored(0),
      "comment" -> optional(text),
      "post_id" -> number
    )(Comment.apply)(Comment.unapply)
  )

  def index = Action { implicit request =>
    transaction {
      val posts = from(AppDB.postTable, AppDB.userTable)((p, u) =>
        select(p, u)).toList
      Ok(views.html.allpost(posts))
    }
  }

  def entry(id: Long) = Action { implicit request =>
    transaction {
      val comments = from(AppDB.commentTable)((c) =>
        where(c.postId === id)
        select(c)).toList

      from(AppDB.postTable)((p) =>
      where(p.id === id)
      select(p)).headOption.map { postContent =>
        Ok(views.html.entry(postContent, comments, commentForm))
      }.getOrElse {
        NotFound("404 Page not found")
      }
    }
  }

  def addComment = Action { implicit  request =>
    transaction {
      commentForm.bindFromRequest.fold(
        errors => {
          BadRequest
        },
        comment => {
          inTransaction(AppDB.commentTable.insert(comment))
          Redirect(routes.Application.entry(comment.postId))
        }
      )
    }
  }

}

簡単に各Actionでどのようなことをしているかを説明していきます。

indexでは、ユーザー関わらず全ての投稿を表示させています。

entryでは該当のpost_idがない場合、404ページを表示するしています。
今回は簡易的に「404 Page not found」の文字のみ表示させています。

addCommentではフォームの内容が正しく受け取れた場合、DBにその内容を反映させた後該当の投稿ページにリダイレクトさせています。

これでcompileしてerrorが表示されなければ、runコマンドのあとページが正しく表示されることを確認しましょう。
viewで正しい表示が確認できたら、次はコメントフォームからコメントしてcommentテーブルに入力したデータが正しく反映されていることを確認しましょう。

これでコメント機能の完成です。
次回は全投稿閲覧ページにページング機能を作成していきたいと思います。

OS X Mavericks 10.9.5にアップデートしたらHomebrewのパッケージがunlinkされた件

自分と同じ境遇の人の参考になればと思い、記事アップしておきます。
今回久々にOSのマイナーアップデート(OX X Mavericks 10.9.5)をした時の話です。





scalaとplayFrameworkで開発をしているんですが、OSをアップデートしたら突然activatorコマンドが使えなくなりました。

$ activator 
-bash: activator: command not found

なのでなにが原因かをぐぐったら、Homebrewあたりがあやしい。。
Mountain Lion から Mavericks にアップデートしたら Homebrew の諸々の Link が消えた - ravelllの日記

ってことで

brew unlink typesafe-activator
brew link typesafe-activator

無事activatorコマンド使えるようになりました。
ふーーー!こんなこともあるんですね。。。



hashCodeとは?

Java系言語におけるハッシュコードとは?

データを元に作り出した整数値です。
各オブジェクトに割り振った一種の識別番号で、ハッシュコードを用いることでオブジェクトの同一性の判定が高速化され、オブジェクトの検索などの処理が効率良く行えます。
http://www.kab-studio.biz/Programing/JavaA2Z/Word/00000045.html

scala> case class Iphone(color:String, price:Int)
defined class Iphone

scala> val x = Iphone("green", 900)
x: Iphone = Iphone(green,900)

scala> a.hashCode
res1: Int = 1732122428

scala> x == a
res2: Boolean = false

scala> val y = Iphone("green", 900)
y: Iphone = Iphone(green,900)

scala> x == y
res3: Boolean = true

同一の内容を持つオブジェクトは必ず同一のハッシュコードを持つように定義されますが、同一のハッシュコードをもつオブジェクトが必ずしも同じ内容とは限りません。

今回の例ではケースクラス作成時にhashCodeも作成されるので、定義する必要はありませんでした。
しかしequalsメソッドとhashCodeメソッドを定義する際には、下のように決まり事があるようです。

→equalsメソッドとhashCodeメソッドは、「2つのオブジェクトがequalsメソッドで等しいと判定された場合、両者のハッシュコードは等しくなければならない。」という規定があります。なのでequalsメソッドをオーバーライドした時には、hashCodeメソッドもオーバーライドするようにする必要があります。
http://xerial.org/scala-cookbook/recipes/2012/07/05/equality/

ユーザーと投稿の紐付け

前回でユーザー登録できるようになりましたので、今回はユーザーと投稿の紐付けを行っていきます。
流れとしてはこんな感じで作っていきます。

・投稿画面の作成(post.scala.htmlファイルの作成)
・routesの設定
・controllersで投稿ページのpostアクションとデータの送信先としてのaddPostを作成
・ログインユーザーの自身の投稿のみを閲覧できるページ作成

ちなみに現在のpostのentityはこうなっています。

    case class Post(
      @Column("post_id")
      id: Long,
      @Column("title")
      title: String,
      @Column("content")
      content: String,
      @Column("user_id")
      userId: Int ) extends KeyedEntity[Long] {
    }

schema設定は前回のentity定義の際に行いましたので、変更点はありません。

val postTable = table[Post]("post")

・投稿画面の作成(post.scala.htmlファイルの作成)

では最初に投稿画面の作成(post.scala.html)をしていきたいと思います。
ユーザーIDはユーザーが直接変更するものではないため、type="hidden"で渡しています。

@(form: play.api.data.Form[db.Post], userData: UserData)

@helper.form(action = routes.AdminController.addPost) {
//hiddenに対応したhelperはないためHTML形式で
<input type="hidden" name="user_id" value="@userData.id" />
@helper.inputText(form("title"))
@helper.inputText(form("content"))
<input type="submit"/>
}

・routesの設定

次にroutesファイルの設定ですが、今回は投稿画面とユーザーが入力したデータの送信先が必要です。なので下記のように記述します。

//投稿画面用
GET     /admin/post                      @controllers.AdminController.post

入力データの送信先
POST    /admin/addPost                   @controllers.AdminController.addPost

・controllersの設定(投稿ページのpostとデータの送信先としてaddPostを作成)

controllersではpostとaddPostの作成を行っていきます。

  //postページでは、user_idを参照できるようにユーザー情報を渡します
  def post = SecuredAction { implicit request =>
    Ok(views.html.post(postForm, request.user))
  }
  //投稿はログインユーザーのみ可能なためSecuredActionつける
  def addPost = SecuredAction { implicit request =>
    postForm.bindFromRequest.fold(
      errors => {
          BadRequest
      },
      //postページで受け取った情報をDBに反映する
      post => {
        inTransaction(if(request.user.id == post.userId) AppDB.postTable.insert(post))
        Redirect(routes.AdminController.index())
      }
    )
  }

これで投稿時にuser_idとpost_idが紐付けられるので、特定のユーザーの投稿のみの表示ができるようになりました。

・ログインユーザーの自身の投稿のみを閲覧できるページ作成

仕上げにログインユーザーが自分の投稿のみ閲覧できるページを作成します。

controllersの設定

  def index = SecuredAction { implicit request =>
    transaction {
      //ページを閲覧しているユーザーのidをwhereで指定
      val posts = from(AppDB.postTable)(p => where(p.userId === request.user.id) select (p)).toList
      Ok(views.html.index(posts, request.user))
    }
  }

ユーザー自身の投稿を閲覧するためのページ
ここはあまり変更点ないため説明は飛ばします。

@(posts: List[db.Post], userData: UserData)

<p>@userData.userName</p>
■<a href="@routes.AdminController.post">Post</a>
<ul>
    @for( post <- posts ){
    <li>@post.title, @post.content
        <p>
            ■<a href="@routes.AdminController.deletePost(post.id)">Delete</a>
            ■<a href="@routes.AdminController.update(post.id)">Update</a>
        </p>
    </li>
    }
</ul>

routesファイルでも変更点はありません。
これでユーザーと投稿の紐付け、特定のユーザーと紐付けられた投稿の表示までできました。
今回作ったものはよくあるwebサービスのマイページのユーザー自身の投稿ページと思ってもらえたらイメージしやすいかと。
次回は一般のユーザーが投稿を閲覧できて、かつコメントできるページを作成していきます。

securesocialを使ってPlay2.3でユーザー登録機能をつくってみた

今回はsecuresocialとplay2.3を使ってユーザー登録機能を作ったので、それを忘れないうちにブログに書いておきます。

今回はFacebookをつないでユーザー登録ができるようにします。
なのでFacebookの開発用アプリは事前に作成しておいてください。
こちらでは今回は説明しませんのでとばします。

まず今回使うのはこのsecuresocialです。
SecureSocial - Authentication for Play Framework Applications

↓のブログの通り安定版ではPlay2.3に対応していませんので、sampleコードなどを参考に実装していきます。
Play2.3でSecureSocialを使う(2014年6月時点) - らびたろちゃんにっき

securesocialのレポジトリ
https://github.com/jaliss/securesocial/tree/master/samples/scala/demo


今回やることとしてはこんな感じです。
流れとしてはこのリンクのページ通りすすめていきますが、一部play2.3で変更した部分があるのでそれに関してはレポジトリの最新のsampleを見て対応していきます。
http://securesocial.ws/guide/getting-started.html

まずはsbtのインストールです。
では早速実装をしていきましょう。

まずはsecuresocialを使えるようにするために、built.sbtに取り込みます。
securesocialとresolversの部分が今回追加した記述です。

libraryDependencies ++= Seq(
  jdbc,
  "org.squeryl" %% "squeryl" % "0.9.5-7",
  "postgresql" % "postgresql" % "9.1-901.jdbc4",
  "io.backchat.jerkson" % "jerkson_2.9.2" % "0.7.0",
  "ws.securesocial" %% "securesocial" % "master-SNAPSHOT"
)

resolvers += Resolver.sonatypeRepo("snapshots")

application.confに↓の記述を追加

include "securesocial.conf"

routesも↓のたったこれだけの記述でOKです。

->      /auth   securesocial.Routes

securesocial.confファイルを作成し、

securesocial {
	#
	# Where to redirect the user if SecureSocial can't figure that out from
	# the request that was received before authenticating the user
	#
	onLoginGoTo=/

	#
	# Where to redirect the user when he logs out. If not set SecureSocial will redirect to the login page
	#
	onLogoutGoTo=/login

	#
	# Enable SSL 
	#
	ssl=false	

	#
	# The controller class for assets. This is optional, only required
	# when you use a custom class for Assets.
	#
	assetsController=controllers.ReverseMyCustomAssetsController

	 cookie {
            #name=id
            #path=/
            #domain=some_domain
            #httpOnly=true
            #idleTimeoutInMinutes=30
            #absoluteTimeoutInMinutes=720
    }
 
  userpass {
    withUserNameSupport=false
    sendWelcomeEmail=true
    enableGravatarSupport=true
    signupSkipLogin=true
    tokenDuration=60
    tokenDeleteInterval=5
    minimumPasswordLength=8
    enableTokenJob=true
    hasher=bcrypt
  }
//今回はfacebookのみ対応するので↓の記述だけでOK
  facebook {
    authorizationUrl="https://graph.facebook.com/oauth/authorize"
    accessTokenUrl="https://graph.facebook.com/oauth/access_token"
    clientId=input_your_app_id
    clientSecret=input_your_secret_key
    # this scope is the minimum SecureSocial requires.  You can add more if required by your app.
    scope=email
  }

}

ここまできたら一旦securesocialのマニュアルから離れて、DAOを作成します。
今回はapp/dao/FacebookDao.scalaファイルを作成して、
・ユーザーを検索するメソッド
・ユーザー情報をアップデートするメソッド
・該当ユーザーがいない場合、ユーザーを作成するメソッド
を作っていきます。

import models.UserData

import org.squeryl.PrimitiveTypeMode._
import org.squeryl.{ SessionFactory, Session }
import db._

object FacebookDao {
  //ユーザーを作成するメソッド
  def createUser(userData: UserData) = {
    transaction {
      val (user, facebookUser) = createUserFacebookUserByUserData(userData)
      AppDB.facebookUserTable.insert(facebookUser)
      AppDB.userTable.insert(user)
    }
  }
  //ユーザー情報をアップデートするメソッド
  def updateUser(userData: UserData) = {
    transaction {
      val (user, facebookUser) = createUserFacebookUserByUserData(userData)
      AppDB.facebookUserTable.update(facebookUser)
      AppDB.userTable.update(user)
    }
  }
  //facebook idを引数にユーザーを探すメソッド
  def findUser(facebookUserId: Long): Option[UserData] = {
    transaction {
      from(AppDB.userTable, AppDB.facebookUserTable)((u, fu) =>
        where(u.facebookUserId === facebookUserId) select(u, fu)).headOption.map {
        t => UserData(t._1.id, t._1.userName, t._1.mailAddress, t._1.facebookUserId, t._2.accessToken)
      }
    }
  }
  //ユーザーデータ型を引数にとり、User型とFacebookUser型が入ったタプルを返すメソッド
  def createUserFacebookUserByUserData(userData: UserData): (User, FacebookUser) = {
    val user = User(userData.id, userData.userName, userData.mailAddress, userData.facebookUserId)
    val facebookUser = FacebookUser(userData.facebookUserId, userData.accessToken)
    (user, facebookUser)
  }

}

上で記述されていたUserData型が何かというと、userテーブルとfacebook_userテーブルから必要なentityのフィールドだけを取ってきてモデル化したもの。

今回はapp/models/UserData.scalaファイルを作成し、そこの下記のように記述しました。

package models

case class UserData(
  id: Long,
  userName: Option[String],
  mailAddress: Option[String],
  facebookUserId: Long,
  accessToken: String) {
}

UserDataはそれ自身がテーブルを持つわけではないので、schemaの定義をする必要はありません。

今度は↓の部分をつくっていくのですが、play2.3ではこのページ通りに作成しても上手く動作しません。
http://securesocial.ws/guide/user-service.html

なので最新のレポジトリを参考にMyUserServiceを作っていきます。
https://github.com/jaliss/securesocial/blob/master/samples/scala/demo/app/service/InMemoryUserService.scala

ちなみに今回実装していないfindByEmailAndProviderメソッドとdeleteTokenメソッドはNotImplementedErrorを返すようにしています。

ここではさっき使ったDaoを利用しながらsecuresocialが求めるフォーマットで引数を渡して、返り値を返すというメソッドを作成していきます。
入り口と出口のフォーマットは決まっているので、それに合わせて処理の内容を記述していきます。
それでは例を見ていきましょう。

import models.UserData

import scala.concurrent.Future
import play.api.{Logger, Application}
import securesocial.core.{AuthenticationMethod, PasswordInfo, BasicProfile}
import securesocial.core.providers.MailToken
import securesocial.core.services.{SaveMode, UserService}
import FacebookDao._

class MyUserService extends UserService[UserData] {
  /**
   * Finds a user that maches the specified id
   *
   * @param providerId
   * @param facebookUserId
   * @return
   */
  def find(providerId: String, facebookUserId: String): Future[Option[BasicProfile]] = {
    val facebookId: Long = facebookUserId.toLong
    val s:Option[BasicProfile] = findUser(facebookId).map{
      result => BasicProfile(providerId, result.id.toString, None, None, result.userName, result.mailAddress, None, AuthenticationMethod.OAuth2)
        //BasicProfile(val providerId : scala.Predef.String, val userId : scala.Predef.String, val firstName : scala.Option[scala.Predef.String], val lastName : scala.Option[scala.Predef.String], val fullName : scala.Option[scala.Predef.String], val email : scala.Option[scala.Predef.String], val avatarUrl : scala.Option[scala.Predef.String], val authMethod : securesocial.core.AuthenticationMethod, val oAuth1Info : scala.Option[securesocial.core.OAuth1Info] = { /* compiled code */ }, val oAuth2Info : scala.Option[securesocial.core.OAuth2Info] = { /* compiled code */ }, val passwordInfo : scala.Option[securesocial.core.PasswordInfo] = { /* compiled code */ }) extends scala.AnyRef with securesocial.core.GenericProfile with scala.Product with scala.Serializable
    }
    Future.successful(s)
  }

  /**
   * Finds a user by email and provider id.
   *
   * Note: If you do not plan to use the UsernamePassword provider just provide en empty
   * implementation.
   *
   * @param email - the user email
   * @param providerId - the provider id
   * @return
   */
  def findByEmailAndProvider(email: String, providerId: String): Future[Option[BasicProfile]] =
  {
    Future.failed(new NotImplementedError())
  }

  /**
   * Saves the user.  This method gets called when a user logs in.
   * This is your chance to save the user information in your backing store.
   * @param prof
   */
  def save(prof: BasicProfile, mode: SaveMode): Future[UserData] = {
    //...
    findUser(prof.userId.toLong) match {
      case Some(x) => {
        val user: UserData
          = UserData(x.id, prof.fullName, prof.email, prof.userId.toLong, prof.oAuth2Info.get.accessToken)
        updateUser(user)
        Future.successful(user)
      }
      case None => {
        val user: UserData
          = UserData(0, prof.fullName, prof.email, prof.userId.toLong, prof.oAuth2Info.get.accessToken)
        createUser(user)
        Future.successful(user)
      }
    }

  }

  /**
   * Links the current user Identity to another
   *
   * @param currentUser The Identity of the current user
   * @param to The Identity that needs to be linked to the current user
   */
  def link(currentUser: UserData, to: BasicProfile): Future[UserData] = {
    Future.failed(new UnsupportedOperationException())
  }

  /**
   * Saves a token.  This is needed for users that
   * are creating an account in the system instead of using one in a 3rd party system.
   *
   * Note: If you do not plan to use the UsernamePassword provider just provide en empty
   * implementation
   *
   * @param token The token to save
   */
  def saveToken(token: MailToken): Future[MailToken] = {
    Future.failed(new UnsupportedOperationException())
  }

  /**
   * Finds a token
   *
   * Note: If you do not plan to use the UsernamePassword provider just provide en empty
   * implementation
   *
   * @param token the token id
   * @return
   */
  def findToken(token: String): Future[Option[MailToken]] = {
    Future.failed(new UnsupportedOperationException())
  }

  /**
   * Deletes a token
   *
   * Note: If you do not plan to use the UsernamePassword provider just provide en empty
   * implementation
   *
   * @param uuid the token id
   */
  def deleteToken(uuid: String): Future[Option[MailToken]] = {
    Future.failed(new NotImplementedError())
  }

  /**
   * Deletes all expired tokens
   *
   * Note: If you do not plan to use the UsernamePassword provider just provide en empty
   * implementation
   *
   */
  def deleteExpiredTokens() {
    throw new UnsupportedOperationException()
  }

  def passwordInfoFor(user: UserData): Future[Option[PasswordInfo]] = {
    Future.failed(new UnsupportedOperationException())
  }

  def updatePasswordInfo(user: UserData, info: PasswordInfo): Future[Option[BasicProfile]] = {
    Future.failed(new UnsupportedOperationException())
  }
}

Global.scalaに↓の記述を追加

object Global extends GlobalSettings {
  object MyRuntimeEnvironment extends RuntimeEnvironment.Default[UserData] {
    override lazy val userService: MyUserService = new MyUserService()
    override lazy val providers = ListMap(
      // oauth 2 client providers
      include(new FacebookProvider(routes, cacheService,
oauth2ClientFor(FacebookProvider.Facebook)))
    )
  }

  override def getControllerInstance[A](controllerClass: Class[A]): A = {
    val instance = controllerClass.getConstructors.find { c =>
      val params = c.getParameterTypes
      params.length == 1 && params(0) == classOf[RuntimeEnvironment[UserData]]
    }.map {
      _.asInstanceOf[Constructor[A]].newInstance(MyRuntimeEnvironment)
    }
    instance.getOrElse(super.getControllerInstance(controllerClass))
  }
}

ここまできてようやくcontrollersの設定を行っていきます。
これも↓のページを参考に
https://github.com/jaliss/securesocial/blob/master/samples/scala/demo/app/controllers/Application.scala

//クラスはこのように宣言
class AdminController(override implicit val env: RuntimeEnvironment[UserData]) extends Controller with securesocial.core.SecureSocial[UserData] {

ログインしているユーザー以外には見せないページは↓のようにActionをSecuredActionに変更したりしていきます。

  def index = SecuredAction { implicit request =>
    transaction {
      val posts = from(AppDB.postTable)(p => where(p.userId === request.user.id) select (p)).toList
      Ok(views.html.index(posts, request.user))
    }
  }

ここでactivatorを立ちあげrunします。
そして早速さっきSecuredActionを付与したページにアクセスします。
そこでこのように画面が表示されていたらOKです。f:id:hikonori07:20140930003333p:plain

これで関連DBをみて適切に値が入っていたらひとまずsecuresocialが正常に動いているでしょう。

だいぶ長くなりましたが、securesocialの導入方法でした。
今後はユーザーのコメント機能やユーザーと投稿の紐付けの部分を実装して記事に書いていきたいと思います。

参考ブログ
http://h-yamaguchi.hatenablog.com/entry/2014/09/22/160629