Play! - Step By Step Tutorial その1 〜導入〜

本家サイトの Tutorial をかいつまんで。
このチュートリアルでは、'Yet Another Blog Engine' 略して yabe というプロジェクトにてブログエンジンを作成していく。

プロジェクトの作成

yabeというプロジェクトを新規作成。アプリケーション名は'Yet Another Blog Engine'とする。

> play new yabe

Eclipseプロジェクトに変換し、eclipseから編集

> play eclipsify yabe


app/views/Application/index.html にある welcome メッセージを編集し以下のようにする。

#{extends 'main.html' /}
#{set title:'Home' /}
 
<h1>A blog will be there</h1>


動作確認

> play run yabe

データベースの設定

Play には HSQLDB が添付されている。最初は HSQLDB をインメモリで使用する。conf/application.conf の Database configuration の項目を編集し、以下の定義を有効にする。

# To quickly set up a development database, use either:
#   - mem : for a transient in memory database (HSQL in memory)
#   - fs  : for a simple file written database (HSQL file stored)
db=mem

再起動は不要で、ブラウザのリロードでDB接続が有効になる。

モデルとなる User クラス作成

Play での OR マッピングには JPA を利用する。app/models/User.java を以下の内容で新規作成。

package models;

import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;

@Entity
public class User extends Model {

    public String email;
    public String password;
    public String fullname;
    public boolean isAdmin;

    public User(String email, String password, String fullname) {
        this.email = email;
        this.password = password;
        this.fullname = fullname;
    }
}

@Entity によるアノテートにより全てのフィールドが自動的に永続化対象となる。@Id が無いが、親クラスで定義されている。

User クラスのテスト作成

/test/BasicTest.java に定義されている既存のテストメソッドを削除し以下に変更する。

@Test
public void createAndRetrieveUser() {
    new User("bob@gmail.com", "secret", "Bob").save();
    User bob = User.find("byEmail", "bob@gmail.com").first();

    assertNotNull(bob);
    assertEquals("Bob", bob.fullname);
}


テストの実行

> play test yabe

http://localhost:9000/@tests でアクセスしてBasicTest.javaを選択→Start!でグリーンを確認。


Userクラスに email password でユーザを認証するメソッド追加。

public static User connect(String email, String password) {
    return find("byEmailAndPassword", email, password).first();
}


BasicTest.java にテストメソッドを追加。

  @Test
  public void tryConnectAsUser() {
    new User("bob@gmail.com", "secret", "Bob").save();

    assertNotNull(User.connect("bob@gmail.com", "secret"));
    assertNull(User.connect("bob@gmail.com", "badpassword"));
    assertNull(User.connect("tom@gmail.com", "secret"));
  }

テスト実施してグリーンを確認。失敗させてもおもしろいです。

Postクラスの作成

ブログへのポスト内容を表す Post クラスを作成。

package models;
 
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
 
@Entity
public class Post extends Model {
 
    public String title;
    public Date postedAt;
    @Lob
    public String content;
    @ManyToOne
    public User author;
    
    public Post(User author, String title, String content) {
        this.author = author;
        this.title = title;
        this.content = content;
        this.postedAt = new Date();
    }
}

@ManyToOne にて先程のUserと多対一の関係を定義。

テストケースの追加

テスト実施前にデータベースの内容を削除するよう事前処理を追加。

public class BasicTest extends UnitTest {

    @Before
    public void setup() {
        Fixtures.deleteAll();
    }
    ・・・

BasicTest に以下のテストメソッド追加。

  @Test
  public void createPost() {
      User bob = new User("bob@gmail.com", "secret", "Bob").save();
      new Post(bob, "My first post", "Hello world").save();
      assertEquals(1, Post.count());

      List<Post> bobPosts = Post.find("byAuthor", bob).fetch();
      assertEquals(1, bobPosts.size());
      Post firstPost = bobPosts.get(0);
      assertNotNull(firstPost);
      assertEquals(bob, firstPost.author);
      assertEquals("My first post", firstPost.title);
      assertEquals("Hello world", firstPost.content);
      assertNotNull(firstPost.postedAt);
  }

最後にコメントクラスの追加

ユーザのポストに対するコメントを表すクラスを追加。

package models;
 
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
 
@Entity
public class Comment extends Model {
 
    public String author;
    public Date postedAt;
    @Lob
    public String content;
    @ManyToOne
    public Post post;
    
    public Comment(Post post, String author, String content) {
        this.post = post;
        this.author = author;
        this.content = content;
        this.postedAt = new Date();
    }
}

Postとの関係は@ManyToOneで定義。

  @Test
  public void postComments() {
      User bob = new User("bob@gmail.com", "secret", "Bob").save();
      Post bobPost = new Post(bob, "My first post", "Hello world").save();

      // Post a first comment
      new Comment(bobPost, "Jeff", "Nice post").save();
      new Comment(bobPost, "Tom", "I knew that !").save();
      List<Comment> bobPostComments = Comment.find("byPost", bobPost).fetch();
      assertEquals(2, bobPostComments.size());
   
      Comment firstComment = bobPostComments.get(0);
      assertNotNull(firstComment);
      assertEquals("Jeff", firstComment.author);
      assertEquals("Nice post", firstComment.content);
      assertNotNull(firstComment.postedAt);
   
      Comment secondComment = bobPostComments.get(1);
      assertNotNull(secondComment);
      assertEquals("Tom", secondComment.author);
      assertEquals("I knew that !", secondComment.content);
      assertNotNull(secondComment.postedAt);
  }

PostクラスとCommentクラスのリレーション

Post から Comment を導けるように Post へ以下のフィールドを追加。

  @OneToMany(mappedBy="post", cascade=CascadeType.ALL)
  public List<Comment> comments = new ArrayList<Comment>();

CascadeType.ALL にてPostが消えたらコメントも削除されるように定義。


また、Post へコメント追加のためのメソッドを追加。

public Post addComment(String author, String content) {
    Comment newComment = new Comment(this, author, content).save();
    this.comments.add(newComment);
    return this;
}

最後にテストケース追加

追加した addComment のテストケースを追加

  @Test
  public void useTheCommentsRelation() {
      User bob = new User("bob@gmail.com", "secret", "Bob").save();
      Post bobPost = new Post(bob, "My first post", "Hello world").save();
   
      // Post a first comment
      bobPost.addComment("Jeff", "Nice post");
      bobPost.addComment("Tom", "I knew that !");
   
      assertEquals(1, User.count());
      assertEquals(1, Post.count());
      assertEquals(2, Comment.count());
   
      bobPost = Post.find("byAuthor", bob).first();
      assertNotNull(bobPost);
      assertEquals(2, bobPost.comments.size());
      assertEquals("Jeff", bobPost.comments.get(0).author);
      
      bobPost.delete();
      assertEquals(1, User.count());
      assertEquals(0, Post.count());
      assertEquals(0, Comment.count());
  }


テスト結果