ラベル Play の投稿を表示しています。 すべての投稿を表示
ラベル Play の投稿を表示しています。 すべての投稿を表示

2013年2月26日火曜日

fakeApplicationを使ってテストの初期値を作ろう

Selenium2でテストを続けるうちに出てきた問題.
DBの初期値やらリセットやらどうしよう…

前にjUnitでテストの段階わけのようなことができたので
「じゃぁ@Beforeでデータ入れて@Afterで消せばいっかなー(・з・)」
なんて考えてたら…

fakeApplication()で本体のメソッドを使う

当然本体ではDBにアクセス→参照やら挿入やらやってるもんだから, このメソッドもらえないかなーなんて考えてとりあえずテストの中で呼ぶ

→ There is no started application

…('A`)なんだこれ
どうやらテストは jUnit, 本体は Playの領分らしく, そのままではまたいで呼び出すことはできないらしい. あらめんどくさい. 呼び出せるアプリが起動していないから There is no started application(開始しているアプリはありません) ってことなのか?
そこで見つけたのが fakeApplication()など play.test.Helpersシリーズ. どうやら本体を仮に動かして中のメソッドをいじれるようにしてくれるっぽいという印象だけの理解('A`)
これ使えばわざわざテスト用に起動しなくていいのかも…
import static play.test.Helpers.fakeApplication;
import static play.test.Helpers.running;

running(fakeApplication(), new Runnable() {

      @Override
      public void run() {
         // 初期値作るメソッドやらなんやら
      }
    });
こんな感じ.

ただし今回は, confファイルで使うテーブルを指定しており, これをテスト用のテーブルを使う, と一緒に宣言を変えたいという追加設定.
最初は「別の confを宣言するのか?」なんて考えてまた迷走.
結局たどり着いたのが,
Map<String String> map = new HashMap<String String>();
map.put("confの中で使うテーブルを宣言している項目", "テスト用テーブルの設定");

running(fakeApplication(map), new Runnable() {
// 以下略
Mapを使ってアプリ起動時の application.confを一部書き換えて読み込む方法.
例えば db.default.driverなんていう, 使用する DBの Driverの宣言. これをテスト時には別のものに書き換えたい!なんてときは
map.put("db.default.driver", "別のドライバ")
としてやれば, fakeApplication()で呼ばれるアプリ中では "別のドライバ" を使ってくれる. まぁテスト時だけドライバ変えるのも珍しすぎる気はするけど… いい例えが思いつかなかった…
なお複数 putしていっぱい書き換えることも可能.
そして, 元になるのは通常起動時に読まれる application.confのはず.


そして git嫌い('A`)

2013年2月22日金曜日

Play起動時の覚書

シンプルに
play "run 9001"
と Playを runすると, デフォルトの9000ではなく指定したポートでPlayを走らせられる.(この場合 9001番ポート)

[run 指定ポート番号]を "" で囲むのがポイントらしい




それだけ(・ω・`)

参考

2013年2月20日水曜日

PlayのアプリをSelenium2でテスト

やられた…
PlayとSeleniumって仲ええんやん…
最初からPlayの中にSeleniumいてるんやん…orz
しかしいざ本体のプロジェクトの中で以前と同じようにテストを作る→jUnit Test

…なんか真っ赤になってるー

妙なエラー吐いてテストしてくれなくなりましたorz

Playのアプリ本体にテストを入れ込む

A. エラーの原因


要するに「テストプロジェクト」を別に立てずに一括で管理したいなー(チラッ という要望から入り込んだ今回の泥沼.. 冒頭の通りただ本体に放り込んだら…
java.lang.NoSuchMethodError: org.apache.http.conn.scheme.Scheme.<init>(Ljava/lang/String;ILorg/apache/http/conn/scheme/SchemeSocketFactory;)V
 at org.openqa.selenium.remote.internal.HttpClientFactory.getClientConnectionManager(HttpClientFactory.java:59)
 at org.openqa.selenium.remote.internal.HttpClientFactory.<init>(HttpClientFactory.java:48)
 at org.openqa.selenium.remote.HttpCommandExecutor.<init>(HttpCommandExecutor.java:117)
 at org.openqa.selenium.remote.service.DriverCommandExecutor.<init>(DriverCommandExecutor.java:46)
 at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:165)
 at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:107)
 at SampleTest.setUp(SampleTest.java:21)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
 at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
 at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
 at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

長い・・・
正直1行目しか読んでない.
「そんなメソッド知らねーよ( ・A・)=Cく ´Д`;)」とか言われたので何か足りないのかと考えて探し回った挙句たどり着いた答えがここの一番下の書き込み.
なんかダブって競合してんじゃね?
Playのバージョンに依るのかはわからないけど, Playプロジェクト作成時, なにやらファイルをいくつかダブらせてパスをお通しになるらしい.(尚この時点での使用verは2.1-RC1)
なのでビルドパスから外してやりましょう. ※1
外すのは
commocs-codec.jarの1.3
htmlclient.jarの4.0.1
httpcore.jarの4.0.1
もちろん他との兼ね合いも考慮すべきだろうけど, 今回はこの3つにverのこだわりは無いので古いほうをカット.

これでOK.

B-1. 実験(適当なPlayプロジェクトの作成)

実際に試してみた Playも最新2.1.0にアップデート
まずは適当にPlayプロジェクト作成→eclipseプロジェクトにする.
プロジェクトを作りタイところでcmdを開き
play new SeleniumTest
ちょっと長いけど前にまとめて?いるので今回はさらっと
SeleniumTestなる適当なプロジェクトを作成. cdコマンドかなんかで作ったプロジェクトに入ってから
play eclipse
eclipseプロジェクトとして認識してもらえるようにする.
ついでに
play run
して走らせておく. でないとテストできない(・ω・`)
これで localhost:9000 にPlayのチュートリアルを表示し続けるアプリが起動しているはず.
走らせたら今度は eclipseでプロジェクトを importする.

B-2. 実験(テスト作成)

importに成功したらまずやるのはビルドパスの修正. 上に述べたとおり余計なパスを削除する.
プロジェクトのビルドパスを開き,
commocs-codec.jarの1.3
httpclient.jarの4.0.1
httpcore.jarの4.0.1
の3つを探して Removeする. 数が多くて目が痛くなる…('A`)

削除したらテストコードの作成
testディレクトリなんて折角作ってくれているしここに入れましょう. (play testコマンドに反応するので注意)

テストコードはシンプルに
public class SampleTest {

  WebDriver driver;
  String url = "localhost:9000";

  @Before
  public void setUp() throws Exception {
    driver = new FirefoxDriver();
    driver.get(url);
  }

  @After
  public void tearDown() throws Exception {
  }

  @Test
  public void test() {
    assertTrue(driver.getPageSource().contains("Welcome"));
  }
}

ただ localhost:9000をFirefoxで開いて 「Welcome」の文字列が表示されているかのテスト.

B-3. 実験(実行)

コードができたら 実行→jUnit test.

ようやっと走ってくれました.







無駄に遠回りしたのにまとめると実は3行でおkレベルだったときの疲労感…('A`)疲れた




※1 : 02/26追記 : だからといって全部が全部「ダブってんじゃね?」とか疑うのはやめましょう. まずはほんとに無いのか確かめましょう. 地獄を見ます('A`)

2012年12月25日火曜日

Play文字化け対策

WindowsのcmdでPlayをコンパイルなどしていじる際発生する"文字化け".
こいつを回避するには先に

set _JAVA_OPTIONS="-Dfile.encoding=SJIS"

と打っておいてやるといいらしい.







ぶっちゃけよくわからないけど('A`)

参考 

2012年12月18日火曜日

Play! ~チュートリアルをやってみた後編

前回からのさらに続き. いい加減終わりたい(;ω;`)

7. Taskフォームの作成

今回はTaskクラスなのでTaskフォーム. UserクラスならUserフォーム. まんまですね(・ω・`)
イメージは中身のクラスを運ぶコンテナ的な何か.
準備としてはTaskクラスの方に
import play.data.*
が必要とのこと. しかし例ではいきなり必要な部分だけimportしてるし('A`)
準備ができたら今度はコントローラの方に,
 Form<Task> taskForm = form(Task.class);
を宣言. これにより, ビューとコントローラの間でのデータのやり取りができるほか, 制約を簡単に加えることができる.
制約に関しては, 例えばTaskでは必須入力項目としてタスクのラベルに制約をかけたいなら, Taskクラスの方で
@Required
public String label;
としてやるだけで, やれデータが入っているかー, なんてif文とか書かなくて済むのである.

8. トップページ描画

Taskフォームを用いて実際にページを見てみる. Application.java のtasks()の戻り値を,
return ok(views.html.index.render(Task.all(), taskForm));
Task.all()はstaticな関数, Taskのリストを返す. これとFormを投げて準備完了.
これでここまできちんとコピペしてきていたら, 適当にブラウザを立ち上げ 「localhost:9000」で見られるはず. (ちゃんと play run していれば)
ただしこの時点ではTaskのCreate機能を積んでいないので, 次の項目で作る.

9. Createボタンの実装

もう訳とかどうでもよくnこの項目でタスクの新規作成処理を積み込む.
Application.javaのnewTask(), この内容を作成する.
public static Result newTask() {
  Form<Task> filledForm = taskForm.bindFromRequest();
  if(filledForm.hasErrors()) {
    return badRequest(views.html.index.render(Task.all(), filledForm));
  } else {
    Task.create(filledForm.get());
    return redirect(routes.Application.task());
  }
}
さらっと書かれていてコピペしてスルーしてしまいそうなところだが, 実はイロイロ大事な部分が含まれているという罠. チュートリアル汚い.

2行目 bindFromRequest() これはリクエストからFormを取り出すメソッド. これをしないといつまでもこのクラスの最初に作成した空のtaskFormを参照してしまい, なにも表示されないことに折れるまで首を傾げるハメになる. もちろんリクエストが無いときは取れません.

3行目 filledForm.hasErrors() 2行目で取得したものにエラーが無いかチェックするメソッド. エラーがあったらtrueが返り, 4行目のようにバッドリクエストを返すようにする大事な処理.

6行目 filledForm.get() これでFormの中に含まれるTaskを取り出す. Formのまま投げないように. この6行目でTaskクラスから新規タスクを作成する.


10. データベースによるTaskの保存

正直わかんないorz なんでいきなりH2DBが絡んでくるのよ…('A`) 前に使ってH2DBの準備してたからいいけど, これ準備無かったらどうなるんだろう…
超簡単に.
conf/application.conf, ここまでずっと横をスルーされてきたこいつをいじる. 中身はいろいろ必要なことが書かれているので下手にいじると爆死します. "#" がその行のコメントアウト. チュートリアルではaddと言っているが, 既に書かれた上でコメントアウトされているので, 混乱を避けるためにも#を外して対処しましょう. ちなみにその下にパスワード設定なんかもありますがいじって無いので省略.  このちょっと下に次の設定もコメントアウトされているのでこちらも対処.
その後Taskクラスをチュートリアルのように書き換えると, 見事タスクが増えるようになりました.
うん, よくわからんorz.

11. タスクの削除

これ逆に章分ける必要あるのか?
DBへの接続の設定は済んでいるので, 削除部分をApplication.javaへ書き込む.
以上.


デプロイはやっていないので今回は省略. なのでここで終了.
こっちを見るとより詳しく見ることができる.





最後グダグダだぁ…('A`)



2012年12月17日月曜日

Play! ~チュートリアルをやってみた中編②

はたしてここで終われるのか…終われなかった…orz
前回からの続き.


3. 開発ワークフロー

直訳でも気にしない. 要は app/controll/Application.java をいじるとこうなるよ! ってお話. 前回の1. でやっちゃった気がしなくも無い(・з・)
1. では他のViewを呼び出していたけど, Stringを直に放り込むことも可能. この場合Content-Typeは text/plain となりなんとも味気ないページがでてきます.
また, コードを変えたときにコンパイルとサーバ再起動を自動でやってくれるのはPlayの利点の一つ. 特にエラーが起きたときや構文エラーがあったときは, ブラウザ上にその箇所を表示してくれる親切設計. ただそのことを,
「As you can see, errors are beautifully displayed directly in your browser.(ご覧の通り, エラーはあなたのブラウザ上に直接美しく表示されます)」
美しくって…. しかもエラーによっては文字化けするし('A`)

4. アプリケーションの準備

直訳でも(ry. conf/routesを編集することで, アクションの振り分けができるよー, ってお話.
また最終行の,
POST    /tasks/:id/delete       controllers.Application.deleteTask(id: Long)
ここで, アクションに引数が持たせられる他,  URL部分に「:id」と入れると, 引数に使ったidをURLに埋め込むことができるよ!(・∀・)といったことも.
当然このconf/routesだけをいじってもアクションのほうを書かないとエラー吐かれるよ, ってことをわざわざ3. で出たブラウザ表示を用いて例示している. 当然ですね('A`)
なのでapp/controller/Application.javaにアクションを書いていきましょう. この時, まだ内容が決まっていないなら,
return TODO
としておくことで, とりあえずコンパイルは通るようになるよ, ということらしい.
ちなみにこのまま走らせると 「501 Not Implemented(未実装)」 が返ります. 便利.
最後はリダイレクトのお話. Viewを通さず, 別のアクションを呼びたいときは,
return redirect(routes.Application.tasks());
と書くことで, 直接conf/routesに飛び, アクションの振り分けから再開できるよ!(・∀・)ってこと.
このときのステータスはちゃんと 「303 See Other(他参照)」 でリダイレクトされる. これまた便利.

5. Taskモデルの準備

直(ry. ようやっとModelの登場. しかしデフォルトではディレクトリは作ってくれないので, まずは app/models ディレクトリを作る必要がある.
ディレクトリを作ったら中に Task.javaを作成. それだけ. ( ゚д゚ )
このあたりで今回では終わらないことを悟り 中編②とか妙なタイトルに変更. どうしてこうなったorz.


6. アプリケーションテンプレート

逆に訳すに訳せない('A`) Todoリストを表示するためのHTMLを作成する…のだが.
なんかやたらすっきりしてる. 2. でもちょっと書いたが, @~というのはPlay!フレームワークが関数のように扱ってくれて, 自動でHTML文を生成してくれる便利なツールとの事. ただし使うためには,
@import helper._
を宣言しておく必要がある.
中でも, @form は重要. この宣言だけで<form>タグを生成してくれる. マジ便利. もちろん例のように引数を放り込むことも可能.
また, 他にもテキストボックスなど<input>タグシリーズを生成する@input等もある. これもまた便利.
チュートリアルにある最後の数行,
@form(routes.Application.newTask()){
  @inputText(taskForm("label"))
<input type="submit" value="Create">
}

この宣言で,
<form action="/tasks" method="POST">
  <dl class=" " id="label_field">
    <dt><label for="label">label</label></dt>
    <dd><input type="text" id="label" name="label" value=""></dd>
  </dl><input type="submit" value="Create">
</form>
とこれくらい作ってくれる. idなんかを追加で指定してやることも可能.
ちなみに生成するHTMLをカスタムすることもできるらしい.
@inputは他にも@inputPasswordなんてのもあるが, ボタンに関してはまだ見つかっていない.
無いのかな…(・ω・`)






次だ…次で終わらせる…('A`)


2012年12月13日木曜日

Play! ~チュートリアルをやってみた中編

前回に引き続きPlay!チュートリアル.
ついにコードをいじり始める


Playちゅーとりある後編


0. 準備いろいろ

とりあえず前回の3. でサーバが run している状態から続き.
run していなければとりあえず run.
そしてもうひとつ必要なことが, MVCモデルの理解.
理解といっても詳細に説明できるほど, なんてことはなく, 
「まぁ役割に応じてそれぞれ管理するところを分けてんだへぇー('A`)」
くらいの認識があれば十分.(なはず(・ω・`)大丈夫かな)
詳しくはグーグル先生あたりに聞くといろいろ教えてくれるはず.
まぁMVCが
「M(モデル), V(ビュー), C(コントローラ)」
を表現してることだけ知ってれば自分はどうにかなってる('A`)たぶん

1. アクションとコントローラ

まずはアクションとそれをまとめるコントローラのお話らしい. もう少し構造について話してからでも遅くない気がするんだけどなぁ…(・д・`)
conf/routesファイルをテキストエディタあたりで開くとチュートリアルにあるような文が見られるはず.
GET /       controllers.Application.index()
いきなりなんじゃいorz特に右のお前.
これだけだとらちが明かないのでapp/controllers/Application.javaも隣に開いてみる.
すると中にはjavaコード. よく見てみるとなにやらメソッドが入ってる.
  public static Result index() {
    return ok(index.render("Your new application is ready."));
  }
このindex(), これがちょうど先ほどroutesファイルに入ってた右のあいつにあたる.
なのでApplication.javaではなく, Hoge.javaのindex()を呼びたいなら,
GET /       controllers.Hoge.index()
になるはず.
そして左はリクエストの情報. なのでチュートリアルのほうで和訳すると,
「GETメソッドで localhost:9000 でアクセスしてきた時, controller.Application.javaのindex()を呼び出すよ!(・∀・)」
という設定を書いてることになる.

そしてApplication.index(), こちらが返すResult型がresponseとして扱われる, との事.
return しているものも簡単に和訳すると,
「index に"Your new application is ready."というStringを与えて, ステータス200 OK で返すよヽ(・ω・)/」
って事らしい, ok の部分を badrequest などに変えるとステータスを変えてreturnできます, index.render()についてはもうちょっと後に出てきます.

簡単にまとめると,
 conf/routes -> リクエストに対するアクションの振り分けをします.
  app/controllers/ -> アクションを内包するコントローラクラスを入れます.
ってことらしい.
ちなみに今更だけど, ここではアクションとはindex()なんかのメソッドにあたります. たぶん(・з・)

2. ビュー

別にチュートリアルでは分かれて無いけど. だいぶ冗長なので.
こんどはapp/views/index.scala.htmlをテキストエディタで開いてみましょう. ファイル名気持ちわる('A`)
ちなみにこいつが1. ででてきた index.render() のindexにあたる.
なので main.render() にするとすぐしたの main.scala.html を指定することになる.
さてindex.scala.html の中身はというと…
@(message: String)

@main("Welcome to Play 2.0") {
    
    @play20.welcome(message)
    
}
これだけ.
これまた @ だらけで良くわからん.
少しずつ見ていくと, まず最初の
@(message: String)
いきなりわけがわからないが, なにやら変数の宣言にも見えなくは無いか.
しかしこれはどちらかというと引数の宣言に当たる.
すなわち, 1. のApplication.index() で与えられたString「"Your new application is ready."」はここで message に格納される.
ここで引き受けたら後は5行目のように, コード内で使えるようになる.
そして
@main("Welcome to Play 2.0") {
    
    @play20.welcome(message)
    
}
mainはちょうどmain.scala.htmlにあたる, 後ろの2つは引数. 
 @play20.welcome(message)
こいつは良くわからない('A`)が働きとしては message を引数にデフォルトのページのhtmlを返すメソッドとして働く.
ということは@main以下は,
「main.scala.htmlに, String型 と Html型 2つの引数を投げてここに貼るよ!(・з・)」といったところ.
なのでmain.scala.htmlも開いて確認してみる.
@(title: String)(content: Html)

<!DOCTYPE html>

<html>
    <head>
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
        <script src="@routes.Assets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
    </head>
    <body>
        @content
    </body>
</html>

はい, 一番上にStringとhtmlを受け取る準備ができていました.

これが大体の流れ. チュートリアルの内容にだいぶいろいろ足したけど('A`)
おかげで終わらなかったorz

続きはまた次回にでも.







次で…終わらない気がするーヽ(;´Д`)ノ

2012年12月12日水曜日

Play! ~チュートリアルをやってみた前編

最近Unity, Playをいじり始めました.
おまけにいじり始めたBlenderに泣かされてます.
インタフェースが実に変態的です. でもすごい.


いろいろごちゃごちゃになってきたのでPlayから少しメモメモφ(・ω・ )


Playちゅーとりある


Playとは要するにWebアプリを簡単に作るためのフレームワークらしい. 実にわかっていない感想.
とりあえずここのチュートリアルをやってみる.


1. 下準備

とりあえずDownloadからPlayを貰ってくる. 現時点ではplay-2.0.4.zip. 地味に時間がかかる…(・ω・`)
もらえたら展開しつつ適当な場所に丸ごと移動. 自分はC:\の下にPlayディレクトリを作ってそこに.
さらに展開した中に入っているplay.bat. これを使うためにこのディレクトリにパスを通しておく.

ここまできたら下準備は完了.

2. アプリケーションの作成

C:\あたりでコマンドプロンプトを開いて, とりあえず

play help

とでも打ってパスが通っていることを確認. 確認できたらAndroidでいうプロジェクトを作成する.
コマンドは
play new アプリケーション名
名前はお好きに. これでコマンドプロンプトを開いた場所にディレクトリを作成する準備が整う. なので別の場所で作りたいならそこでコマンドプロンプトを開きましょう.

上記コマンドではアプリケーション作成の準備が整っただけ. コマンドプロンプトはこんな感じかと
AAには若干無理を感じる(・ω・`)
とりあえずアプリケーション名, そしてテンプレートにどの言語を用いるかを聞いてくる.
言語はscala or java



…scalaわからないからjavaで作る('A`)
これで最低限必要なものは一通りそろった!

3. デフォルトページを見る

とりあえずデフォルトはどんなものか見に行きましょう. できたディレクトリの中には結構イロイロ入ってます.
一通り眺めたら今度はブラウザで表示するやり方をデフォルトページで練習.
上で開きっぱなしのコマンドプロンプトで
cd 作ったディレクトリ名
で移動. 移動できたらとりあえず
play
と入力
するとまたAAのようなplayの文字が. 無事出現したら最後のコマンド
run
これでPlayがサーバの役割も果たしてくれる(らしい)

落ち着いたら今度は適当なブラウザを起動. アドレスに「localhost:9000」と打ってみると…

でました.+(・ω・0)*
このデフォルトページはそのままチュートリアルっぽい内容にもなってます.

4. サーバを切る

終わったからっていきなりコマンドプロンプトを消すのはちょっと危険. サーバを止めるときは
ctrl + d
これで止まったら今度はplayを終了する.
exit
これでPlayも終了できる.
なお, 3. においてplayと打ったところを
play run
と打つと直接サーバがrunします. サーバを止めるときも一緒にPlayも止まります.
消し忘れ防止にいいかも?






これでチュートリアル前半終了.
次からコードを見ていく…いつになるかわからないけどヽ(;´Д`)ノ






画像に変なもの写ってないよな…('A`)

AWS CDKで立てたEC2インスタンスのTimeZoneとかいじりたかった話

EC2を立てることはできたけど、立てたインスタンスは UTCのままだし設定ファイルとかいちいちscpしてくるのはダルい。 当初UserDataでなんとかしようとしたものの、「書く量がヤバいしメンテしにくい」と悩んでいたところ見かけたのが  AWS::CloudFormation:...