Scala ScalatraをIntelliJを使って開発&デバッグする Windows10

さてさて、前々回前回とsbtのScalaプロジェクトをIntelliJで開発するためにてこずっていたワタクシですが、やっと真打ち登場!です。

もともとやりたかったことは、Scalatraで作られたプロジェクトをIntelliJでデバッグとかしながらオシャレに開発していきたかったのです。

ScalatraはScalaのフレームワークです。今回は、バージョン2.6.5です。

環境は、Windows10 + IntelliJ コミュニティバージョン2020.1 です。

Scala IntelliJ sbtでビルドするプロジェクトをインポートする

で説明したように、プロジェクトをインポートします。

この後、sbt tool windowとかsbt shellのウィンドウがなければ、sbtでビルドすいるプロジェクトだと認識されていませんので、やりなおしましょう…。

Buildに失敗したというメッセージが出ます。

Extracting structure failed
(Global / dumpStructureTo) java.io.FileNotFoundException: C:\Users

気にしたほうがいいのかもしれませんが、支障は今のところないので、気にせず続行します。

デフォルトではsbtでビルドできるようになってないので、下記でも紹介しているように、

File→Setting→Build, Excution…→Build Tools→sbtの画面で

Use sbt shellとあるところのチェックボックスを二つチェックします。

sbt shellのウィンドウで、

jetty:start

と打ち込みます。いろいろ、バーっと表示が出ますが、

2020-07-31 16:55:08.612:INFO:oejs.Server:main: Started @2375ms

と出て、Jetty Webサーバーがちゃんと動作したっぽいです!!
ブラウザなどで localhost:8080 に接続すると、ちゃんと応答が帰ってきます。やったぁ。

さて、次の課題はデバッグです。

デフォルトでは、デバッグの緑の虫アイコンさえ、灰色になってます。

虫さん…死んでる…。

以下、デバッグできるようにする手順です。

Run → Edit configurations 

として、Remoteというのを選んで

Attach to remote JVM

というのが選択されているのを確認して、OKをクリックします。

ちなみに、ここでJVMに下記のオプションをつけて実行することになっています。
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

おまじないみたいですが、この内容が知りたければ、下記サイトに書いてあります。

https://docs.oracle.com/javase/1.5.0/docs/guide/jpda/conninv.html

虫のアイコンが緑に光り輝きますが、まだここでは押さないでください。


build.sbt に次のオプションを足します。

javaOptions ++= Seq(
"-Xdebug",
"-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"
}


これで、もう一度ビルドします。jetty:start ってやって、緑の虫さんのデバッグアイコンをクリックします。

注意事項ですが、 jetty:start ってやって から、デバッグしないとできません。

5005番のポートを使う、JVMのプロセスにアタッチしてデバッグするからからなので、JVMが立ち上がってないとデバッグできないのです。

Debug ウィンドウに

Connected to the target VM, address: 'localhost:5005', transport: 'socket

って表示されたら、とりあえずデバッグが接続できています。

localhost:8080 にWebブラウザで接続してみます。

すると、設定してあったブレークポイントで止まりました!やったね⊂(^-^)⊃


Scala IntelliJ IDEAでsbtのプロジェクトを作る Windows10

現在私は、ScalaのScalatraというフレームワークで書かれた開発プロジェクトをわけあって引き継いでおります。
前に作っていた人は、現在うちの会社で別の仕事をしているのですが、その人は開発環境とかあんまり気にしないタイプで、テキストエディタ的にVisual Studio Codeを使っていたということです。

ただ、私は開発環境でデバッグとかしながら開発したい派です。Android Studioとかも使っていて親しみがあるので、IntelliJ IDEAでやってやろうと思いました。

しかし、いきなりIntelliJにScalatraのコードをブチこんでも全然うまくいかなかったんですよ。Scalatraのバージョンは2.6.5です。

というわけで、まずは初心にかえり、超簡単なプロジェクトをIntelliJで作ってみます。

環境は、Windows10 + IntelliJ IDEAコミュニティバージョン2020.1です。

ここで、とっても大事な話をします。

sbtで動かすScalaプロジェクトと、sbtを使わないScalaプロジェクトは、IntelliJに最初の導入をする部分で違います。

ここ間違うと、私みたいにはまります。(ノω・、) ウゥ・・・

最初、下記のページを見てやってたんですよ。

Intellij で sbt を使って Scala プロジェクトをビルドする
https://docs.scala-lang.org/ja/getting-started/intellij-track/building-a-scala-project-with-intellij-and-sbt.html

File→New→Projectと行くと、下記のような画面になりますが、

↑ここでScalaを選択してはダメです。

↓下記のように、左がScalaで、右がsbtを選択しましょう。


今回は、サンプルなので、気を付ける場所はここだけです。

Nextを押して、プロジェクトの保存場所を選択して、OKをクリックします。

プロジェクトを作ると、sbtの構成を、IntelliJが作ってくれるので、多少時間がかかります。

サンプルとして、さっきも紹介した下記のサイトにあるサンプルコードを書いておきます。

https://docs.scala-lang.org/ja/getting-started/intellij-track/building-a-scala-project-with-intellij-and-sbt.html

object Main extends App {
    val ages = Seq(42, 75, 29, 64)
    println(s"The oldest person is ${ages.max}")
}

画面の一番下に、sbt shellというタブがあります。

これで、

「ん~ 動くのかな?」

ということで、run と打ち込んでも、何も起こりません。

「動け、動け、動け、動いてよ~!!」

と泣き叫びたくなりますが、落ち着きましょう(´ω`)。

File→Setting→Build, Excution…→Build Tools→sbtの画面で

Use sbt shellとあるところのチェックボックスを二つチェックします。

OKをクリックして、sbt shellのところでもう一度、run って入力します。

すると、ちゃんと動作して、プログラムの実行結果がsbt shellに表示されました!

The oldest person is 75

ってやつが実行結果ですね。


Win10 Hyper-V Dockerインストール

Win10のHyper-Vを使ってDocker Desktop for Windowsを動かすことを目指す回。

事前準備

Hyper-Vを有効化する必要があるが、下記の公式ドキュメントを参照されたい。

Windows 10 での Hyper-V の有効化 | Microsoft Docs

インストール

下記の公式ページよりインストーラーをダウンロードした。

Docker Desktop for Mac and Windows | Docker

ボタンの表記が「Download for Windows (stable) 」となっているので、そのまま押下。

Docker Desktop Installer.exeが取得できたので、実行してインストール。

デスクトップのショートカットはいらないかも……
インストールが完了した後、PCの再起動

さっくりとインストール完了。ちょろすぎる。

動作確認

おもむろにWindows PowerShellを起動。

バージョン確認

2020/07/28の最新?

> docker version
Client: Docker Engine - Community
 Version:           19.03.12
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        48a66213fe
 Built:             Mon Jun 22 15:43:18 2020
 OS/Arch:           windows/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.12
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.10
  Git commit:       48a66213fe
  Built:            Mon Jun 22 15:49:27 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

Hello world

Dockerが自動でイメージの取得まで済ませてくれるらしい。便利。

> docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:451ce787d12369c5df2a32c85e5a03d52cbcef6eb3586dd03075f3034f10adcd
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

チュートリアル?

Docker Desktopを起動してみると、チュートリアルが始まった。

右上のセッティグとかも見てみたけど、まだ良くわからなかった

画面のボタンをぽちぽち押すと画面右側のコンソールで処理が走るというチュートリアルだったが、正直何が起きているのかよくわからないままに進んでいく感はあった(この時点で細かく説明するよりは全体の流れをさっと見せる方が良いと思うので、悪い点ではない)。


途中、ファイアウォールの警告が出たので[OK]を押下
イメージの保存とシェアをするステップ
アカウントを持っていなかったので新規登録
チュートリアル画面のUI
イメージの保存まで完了すると
[View in Browser]ボタンの押下を促される
ブラウザでこのようなページが開かれる
先程のボタンポチポチよりも重めのイントロダクションとチュートリアルだ
Docker Desktopを見ると、コンテナリストが表示されている
(hello-worldをやった形跡がある)

以上の流れで、Docker Desktop for Windowsをインストールしてチュートリアルに到達するところまでができた。

ひとまずここまで。

Vue.js コンポーネントの中のテンプレートで配列のオブジェクトのリンクを表示する

以前、Vue.jsを使って、下記のようなことをしましたが、「早いもんだぜ」でもう1年が経ちました。

私ってめちゃくちゃ忘れっぽいんですよ。健康だってことですかね?つまり、Vueのことについてカケラも覚えていませんでした…。

特に時間がかかったのが、
「リンクを作る」
ところです。

「エッツ そんなのちょちょいとできないの?」

と思われると思いますが、できなかったんですよ。

前置き長いですが、本題に入ります。ちなみに、この度のケースでは、Nuxtとかで使っているわけではなく、Vueをパーツで使っています。Routerとかを使うわけではありません。

今回はVue.jsでリンク、つまり<a>タグの”href”属性を作ることをやります。

下記は、まだリンクがありません。

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <script src="https://unpkg.com/vue@2.5.17"></script>
</head>

<body>

<div id="fruits-component">
  <ol>
    <fruits-component v-for="eachFruit in fruitsItems" :key="eachFruit.name" :fantastic-fruit="eachFruit">     	
    </fruits-component>
  </ol>
</div>

<script>

let fruitsItems= [
    {name:'りんご', id:'101'}, {name:'バナナ',id:'102'}, {name:'なし',id:'103'}
  ];


let fruitsComponent = Vue.component('fruitsComponent',{
		props: {
			  'fantasticFruit': { 
			      type: Object, 
			      required: true 
			    }
			  },

		template: ' <li>{{fantasticFruit.name}}</li>',
});

new Vue({
	
  el: '#fruits-component',
  data: { fruitsItems  },
  components: {
	    'fruitsComponent': fruitsComponent,
	  }
  
})

</script>
</body>

上記の解説です。

①データは [{name:’りんご’, id:’101′}, {name:’バナナ’,id:’102′}, {name:’なし’,id:’103′}] となっています。3つのオブジェクトが一つの配列に入っています。

②コンポーネントという仕組みを使っています。
HTMLタグでは<fruits-component> </fruits-component>で表現されるところがコンポーネントの中身になります。
わかりにくい部分なんですが

<fruits-component v-for="eachFruit in fruitsItems" :key="eachFruit.name" :fantastic-fruit="eachFruit">         
</fruits-component>

ってなっている、:fantastic-fruit=”eachFruit” が、v-forで回しているeachFruitを fruits-component コンポーネントのfantastic-fruitに結びつけています。データバインディングのディレクティブというやつです。
:fantastic-fruit

v-bind:fantastic-fruit
の略です。詳しくは、本家サイトをご覧ください。

https://v1-jp.vuejs.org/guide/syntax.html

③ fruits-component コンポーネントのfantastic-fruitに結び付いたデータを、コンポーネント内のテンプレート構文で出力します。
template: ‘ {{fantasticFruit.name}}’ の部分ですね。

正直なところ、同じデータの参照を、いろいろな名前で呼んでいるので、わかりにく…って思います。(もっとスッキリするやり方があれば教えてください。)

さてさて、本題の<a>リンクの出力に行きます。

コンポーネントのテンプレート部分だけ変更します。

let fruitsComponent = Vue.component('fruitsComponent',{
		props: {
			  'fantasticFruit': { 
			      type: Object, 
			      required: true 
			    }
			  },

		template: '<li><a href="{{fantasticFruit.id}}.html">{{fantasticFruit.name}}</a></li>',
});

これで<a>リンクができたように見えますが、実はできてません。出力されたHTMLを見ると、下記のようになっています。

<a href="{{fantasticFruit.id}}.html">りんご</a>

ふぇぇ~なんで~(>_<)

となりますが、XSSを防ぐために、このようになっているようです。(すみません、Vue関連のどっかのサイトで見た気がするのですが、リファレンスがわからなくなってしまいました…。)

一つの手段は、computed という機能を使うことです。fruitsComponentの中身だけ、次のように書き換えます。

let fruitsComponent = Vue.component('fruitsComponent',{
		props: {
			  'fantasticFruit': { 
			      type: Object, 
			      required: true 
			    }
			  },

		template: '<li><a :href="fruitURL">{{fantasticFruit.name}}</a></li>',
		computed:    {
			  fruitURL: function () {
			      return this.fantasticFruit.id + ".html";
	    	  }
		}
});

:href=”fruitURL” とやっているところで、hrefという属性に、fruitURLを紐づけています。前述もしました、データバインディングです。

computedの中から、popsにあるデータにアクセスするときは

this.fantasticFruit.id

.html の部分が必要なければ単純に、テンプレートの中身を

template:'{{fantasticFruit.name}}',

としてもできます。が、この中にjavascriptの文字列が書けるようなので、+”html” とかで行けそうですが、何かと面倒そうだったのでやめました…。

DBUnit 使ってみた

最近、テストコードを書いているのですが、DB 周りのテストに大変苦戦しておりました。PHPUnit を使って書いているのですが、ただの Insert 文をテストするのにもかなり遠回りしなくちゃいけないことがあるんですよね。。。
今回は、そんな PHPUnit の拡張機能である DBUnit を使ってみます。

参考サイト:
https://phpunit-tenkoma-working.readthedocs.io/ja/latest/database/
https://www.ritolab.com/entry/168
https://qiita.com/takatama/items/63c7c82108af48b7bbdb

何ができるか

以下が DBUnit の特徴です。

  • DB の初期状態を別ファイルで定義して、テスト用の初期環境を作る。
  • 上記の別ファイルデータを、データセットやデータテーブルと呼ぶ。
  • アサーションで比較に使う値もデータセットで定義可能。
  • データセットは、XML、YAML、CSV、PHP の配列等々をサポート。
  • テーブルの行数カウントや、テーブルの情報自体を比較可能。
  • テスト実行後は、(特定のメソッドを定義すれば)自動で DB の変更を消去!

偉い感じしますね!

インストール方法

今回は、Composer を使ってインストールします。
アプリケーションのディレクトリで、下記コマンドを実行します。

composer require --dev phpunit/dbunit

同ディレクトリの、composer.json に下記記述が入っているはずです。

"require-dev": {
    "phpunit/dbunit": "^4.0"
}

これで DBUnit のインストールは完了です。

DBUnit 使い方

phpunit.xml に下記の記述をします。
ここでは、テスト用 DB に接続するための定数を定義しています。

<phpunit bootstrap="..\vendor\autoload.php">
  <php>
    <ini name="display_errors" value="on"/>
    <var name="DB_DSN" value="mysql:dbname=local_test;host=localhost" />
    <var name="DB_USER" value="root" />
    <var name="DB_PASSWD" value="" />
    <var name="DB_DBNAME" value="local_test" />
  </php>
</phpunit>

次に、DB 接続するメソッドを記述した、抽象クラスを定義します。
各テストコードは、このクラスを継承することになります。

<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\DbUnit\TestCaseTrait;

abstract class Generic_Tests_DatabaseTestCase extends TestCase{
    use TestCaseTrait;

    // PDO のインスタンス生成は、クリーンアップおよびフィクスチャ読み込みのときに一度だけ
    static protected $pdo = null;

    // PHPUnit\Dbunit\Database\Connection のインスタンス生成は、テストごとに一度だけ
    private $connection = null;

    // DB接続
    final public function getConnection() {
        // TODO: Implement getConnection() method.
        if ($this->connection === null) {
            if (self::$pdo == null) {
                self::$pdo= new PDO($GLOBALS['DB_DSN'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']);
            }
            $this->connection = $this->createDefaultDBConnection(self::$pdo, $GLOBALS['DB_DBNAME']);
        }

        return $this->connection;
    }


    // DB のクリーンアップ
    public function getTearDownOperation() {
        return \PHPUnit\DbUnit\Operation\Factory::TRUNCATE();

    }

}

フィクスチャ用のデータセットを作ります。
今回は YAML ファイルを使います。

food:
  -
    id: 1
    name: karaage
    price: 450

それでは、テストコードを書きます。

<?php
require_once('Generic_Tests_DatabaseTestCase.php');
require_once('../../class/food.class.php');
use PHPUnit\DbUnit\DataSet\YamlDataSet;


class TestSample extends Generic_Tests_DatabaseTestCase {


    /**
     * フィクスチャ設定
     * @return \PHPUnit\DbUnit\DataSet\IDataSet|YamlDataSet
     */
    protected function getDataSet() {
        // TODO: Implement getDataSet() method.
        return new YamlDataSet(dirname(__FILE__).'\fixture_sample\sample.yml');
    }



    /**
    * テストコード
    */
    public function testGetDriverID() {


        // 初期状態の行数アサーション
        $this->assertEquals(1, $this->getConnection()->getRowCount('food'));

        // YAML ファイルで定義してあるデータから、アサーション用の比較データ取得。
        // 本来はフィクスチャ用のファイルと別に作るが、今回は Insert, Update, Delete を行わないため同じものを使用。
        $expectedTable = $this->getDataSet()->getTable('food')->getRow(0);


        // テストしたいメソッドの実行。
        $actual = Food::getById($expectedTable['id'], self::$pdo);


        // アサーション用のキー配列。created はテストしない。
        $check_keys_array = [
            'id', 'name', 'price'
        ];


        // 値が一致しているかのアサーション
        foreach ($check_keys_array as $key) {
            $this->assertEquals($expectedTable[$key], $actual[$key]);
        }

    }

}

このテストの実行はコマンドライン上で下記コマンドを打ってください。

phpunit -c phpunit.xml foodTest.php

以上でテスト完了です。
テストでテーブルに変更があっても変更したところを消去してくれるんで、かなーり便利だと感じました。

一応、注意しなければならないのが、PHPUnit としては DBUnit の開発を既に終えているそうです。
ですが、foak されたプロジェクト( https://github.com/kornrunner/dbunit )があるので、検討する価値はあるかなという感じです。