日頃の行い

個人的な日頃の行いをつらつら書いてます\\\\ ٩( 'ω' )و ////

ScalaをItamaeでインストールするだけ

Scalayum installしようとしたら2.9系統が入ったので(下記参考)

arata.hatenadiary.com


楽ちんに最新版をインストールするのめんどいなと思い、
Itamaeに慣れるのも含めてインストール用のレシピ書いてみました。
Itamaeはこちら。wikiのドキュメントが充実しています。github.com

そして、こちらが検証に使ったgithubレポジトリです。

github.com

やることは簡単

  • tarボールを持ってくる
  • 解凍
  • PATHを貼る

準備

下記が必要です。

  • itamaeのインストール
  • vagrant up

こちらを参考にしてください
scala-environment-setup/Makefile at for-blog-20150507 · tarata/scala-environment-setup · GitHub

流れ

レシピ( recipe.rb )
特に何も考えること無くopenjdkインストールしています。

package "java-1.7.0-openjdk"

scala_version = "2.11.6"
execute "Download Scala File" do
  command sprintf("wget http://downloads.typesafe.com/scala/%s/scala-%s.tgz -O /usr/local/src/scala.tgz", scala_version, scala_version)
  cwd "/usr/local/src/"
  not_if "test $(ls /usr/local/src/scala.tgz)"
end

execute "Unzip Tar File" do
  command "tar xzvf scala.tgz"
  cwd "/usr/local/src/"
end

template "/home/vagrant/.bashrc" do
  action :create
  source "templates/bashrc"
  variables(scala_path: sprintf("/usr/local/src/scala-%s", scala_version))
end

PATHを貼るところ
template/bashrc

# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
    . /etc/bashrc
fi

# User specific aliases and functions
export SCALA_HOME=<%= @scala_path %>
export PATH=$PATH:$SCALA_HOME/bin


この状態で下記コマンドをインストールするとScalaJavaがインストールされます。
(参考)

$itamae ssh \
	-h 192.168.33.101 \
	-p 22 \
	-u vagrant \
	-i ~/.vagrant.d/insecure_private_key recipe.rb

テンプレートに対して、変数渡せるので便利ですね。
シェルスクリプトだけだと、sedコマンドで頑張る方法しか私は浮かばないので
シェルスクリプトと比較すると
itamaeのようなツールに頼る方が良いと思える点の一つですね。
Scalaの話というよりItamaeの話でした。

ScalaでDynamoDBに接続するためにAWS SDKを利用してみた時の話

ScalaJavaAWS SDKを使ってDynamoDBを利用した時のメモ
本番ではDynamoDBの本物を利用してましたが、ここではDynamoDB Localを利用します。
Java製のAWS SDKScalaから利用しています。

作業したgithubレポジトリはこちらgithub.com

1. DynamoDBローカルのインストール

下記のページからダウンロードして使います。

docs.aws.amazon.com

DynamoDBローカルの準備や起動についてはREADMEを参考にしてください。

https://github.com/tarata/dynamodb-local-for-blog/blob/master/README.md

2. 適当なテーブルの作成

HashキーやらRangeキーなど設定がありますが、それらについては下記のページを参考にしてください。
今回はHashキーをidにしたテーブル「users」を作成します。

qiita.com

Credentialは $HOME/.aws/credential のkeyを利用しています。

テーブル作成に利用するクラス

これで設定おkです
この後UserモデルだけJavaで書きました
なぜなら、Scala力が足りないからです。

Userモデル

3. テーブルのデータを突っ込む部分と取得する部分

データを突っ込む部分

データを取得する部分

たったこれだけでDynamoDBへのORマッパー的なことが出来ます。
アノテーションで出来て便利ですね!!11
4. ハマったところに続く...


ちなみに、jsonを保存したりしたい場合は以下のサイトが参考になります。
Using Custom Marshallers to Store Complex Objects in Amazon DynamoDB - AWS Developer Blog - Java

4. ハマったところ

com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException: Failed to instantiate new instance of class
	at com.amazonaws.services.dynamodbv2.datamodeling.ConversionSchemas$StandardItemConverter.createObject(ConversionSchemas.java:629)
	at com.amazonaws.services.dynamodbv2.datamodeling.ConversionSchemas$StandardItemConverter.unconvert(ConversionSchemas.java:420)
	at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.privateMarshallIntoObject(DynamoDBMapper.java:690)

Failed to instantiate new instance of class は主に Class.newInstance() を実行した時に出るエラーメッセージみたいですね。
Java力が足りなくてわかりませんでした。
Scala力も足りないしDynamoDB力も足りないので、切り分けが出来なかったのですが、普通にググったら普通にわかってあれがあれでした。

このOR Mapperはデータを取得した際に、今回で言えば、 **User.newInstance()** を実行し、
その後setterでデータを入力しているようです。
なので、引数なしコンストラクタがないと死にます←

IntelliJが「これ使ってないよ!使ってないのに消さないの??」
って言ってきたので消したら動きませんでした。
怖いですね、リフレクションメソッド

5. 最終的にどうしたか

正直Mapperは必要がないので、findAllとsaveといメソッドを持ったRepositoryを定義しました。

UserRepository.scala

UserConversions.scala

こうすればアノテーション地獄にもならずかけます。
そんな複雑なことしないので、ORマッパーを使うよりリポジトリを定義して使ってあげるほうが楽そうです。

ScalaのTestツールScalaTestを使ってみた。

みなさん、こんばんは。
昨日書いたこちらの記事同様Scala書くのが最近とても楽しいので、Scala関連の記事を書こうと思います。

ScalaでTestを書こうと思って、とりあえず出てきたScalaTestというものを試してみました。
SEO高そうですね

使ってみた感想としては、とりあえず全部これでいいんじゃね感です。

|∧∧
|・ω・`) 今回検証に使ったレポジトリも置いておきますね
|o旦o
|―u'
""""""""""""""""'""""""


tarata/scalatest-getting-started · GitHub


とりあえず環境を用意しましょう。

  • Scala 2.11.4
  • sbt 0.13.7

を利用します。
build.sbtに

libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.1" % "test"

と書いてテストツールの準備は終了です。

早いですね。こんな感じです。

https://github.com/tarata/scalatest-getting-started/blob/master/build.sbt

これでsbt testと打てばとりあえずインストールが走り、テストが実行されます。
今はなにもないので、no tests executedとかそれらしきことを言われるでしょう。

次に、以下のことを試してみました。

1. xUnit形式でassertionを行う
2. Property based testingをやってみる

1. テストの形式を選ぶ

ScalaTestではxUnitの形式やBDDっぽくSpecで書く形式などいくつか用意されているようです。
今回はxUnitっぽくやってみました。
xUnit形式の場合、FunSuiteというクラスを継承してTestクラスを作成します。
書いたコードが以下のURLです。

scalatest-getting-started/Calculator.scala at master · tarata/scalatest-getting-started · GitHub

感想としては

  • xUnitっぽく書けました。(当然
  • assert使える(当然
  • ShouldMatchersというtraitを使うと自然言語っぽく書けて( ・∀・)イイ!!

という感じです。
ShouldMatchersは使い勝手そこそこよかったので、使えるかと。
色んな書き方があるようです。
もうちょっと色々試してみようかなぁと思います。

参考:http://www.scalatest.org/user_guide/using_matchers
その他のテスト形式はこちらのブログが綺麗にまとまっていたように感じました。


2. Property based testingをやってみる

そもそもProperty based testingってなんだよ。
って思ったのでぐぐってみて、下記のページを参考にしました。

Abstractivate: Property-based testing: what is it?

タイトルがそのまんま気になってる内容ですねw

Property-based tests make statements about the output of your code based on the input, and these statements are verified for many different possible inputs.

A property-based testing framework runs the same test over and over with generated input

google 翻訳したら以下のようになりました。

プロパティベースのテストは、入力に基づいて、コードの出力についてのステートメントを作成し、これらの記述は、多くの異なる可能な入力に対して検証される。

プロパティベースのテストフレームワークでは、生成された入力に何度も同じテストを実行します

可能な入力を大量に与えて、同一のテストを何度も行うということでしょうか。
HaskellのQuickCheckというものにおける考え方(?)のようです。
そちらの方も後で調べてみようかなと思います。

今回ScalaTestのページに載っていた2種類を試してみました。

Table-driven property checks

前者のTable-driven property checksはDataProvider的な印象を受けました。
自前で用意した入力に対して、テストを行うためです。

書いたテスト
scalatest-getting-started/CalculatorWithTableDrivenPropertyBased.scala at master · tarata/scalatest-getting-started · GitHub

Table関数を利用して、引数として与えたいデータをタプル形式で作り、forAll関数に渡せば良いみたいです。

使った感想

  • データプロバイダーぽい
  • データプロバイダーに慣れてるせいか使いやすかった

Generator-driven property checks

こちらは自前のデータではなく、適当なデータを作成してそのデータセットを入力として、テストを行うということができるようです。

書いたテスト
scalatest-getting-started/CalculatorWithGeneratorDrivenPropertyBased.scala at master · tarata/scalatest-getting-started · GitHub

これを利用する場合、build.sbtに

libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.12.2" % "test"

を追記する必要があります。
もともとScalaCheckの機能のようですね。
HaskellのQuickCheckに対するものがScalaのScalaCheckなのでしょうか。

使った感想

  • 任意と言いつつ、完全なランダムではなさそう。
  • ⇒Test名に入力を含めた際にDuplicateTestNameExceptionが発生したため。
  • ⇒Int型を指定した際には0 , 1, -1, Int型の最大値・最小値とその他ちょっと大きめな値(どういう理由で選ばれたかわからない値)が指定されていたような印象
  • どのタイミングで使うのが良いのだろう。
  • ⇒テストは自分が気になる境界値に対して行うものだと思っているため、任意の入力をするというこのテスト用のデータセットをどのタイミングで使うかがわからない。

という感じでした。

まとめ

  • ScalaでテストするならとりあえずScalaTestを利用するのがよさそう
  • xUnitぽくもBDDぽくも書ける
  • データプロバイダー的なこともできる

やっぱ、sbtでテストできるのは楽ですね。

Scalaのjson変換ツールspray-jsonを試してみた。

Scalaがとても楽しいので、spray-jsonによるScalaコード内でオブジェクトのjson変換を試してみました。

spray/spray-json · GitHub

基本的に上記のレポジトリのコードを元にしました。
ScalaのオブジェクトならDefaultJsonProtocolというトレイトを継承または、利用すれば変換することが出来るようです。
とても楽でした。

準備 /build.sbt

準備はbuild.sbtを以下のようにするだけです。

libraryDependencies += "io.spray" %%  "spray-json" % "1.3.1"

一般的なScalaオブジェクトをjsonに変換する

上記の準備だけで、toJsonを利用して、Scalaのオブジェクトをjson化することが出来ます。
Scalaで用意されたオブジェクト(Listなど)の変換には2通りのやり方があります。

  • 継承するパターン
  • traitを使うパターン

/src/main/com/Sample.scala

継承するパターン

package com

import spray.json._

object Sample extends DefaultJsonProtocol {

  def main(args: Array[String]) {
    val hoge:List[List[String]] = List(
      List("hoge", "fuga"),
      List("piyo", "fuga")
    )

  println(hoge.toJson)

  }
}

traitを使うパターン

package com

import spray.json._

object Sample extends App with DefaultJsonProtocol {

  val hoge:List[List[String]] = List(
    List("hoge", "fuga"),
    List("piyo", "fuga")
  )

  println(hoge.toJson)
}

どちらも出力は以下の様な感じです。

[["hoge","fuga"],["piyo","fuga"]]

エラーが出るとき

[info] Compiling 1 Scala source to /Users/a-tanaka/Documents/scala_workspace/spray-json/target/scala-2.10/classes...
[error] /Users/a-tanaka/Documents/scala_workspace/spray-json/src/main/scala/com/Sample.scala:12: Cannot find JsonWriter or JsonFormat type class for List[List[String]]
[error]   println(hoge.toJson)
[error]                ^
[error] one error found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed 2015/02/11 1:38:59

DefaultJsonProtocolを利用していないために、こんなエラーが出ます。
traitとしてwithでDefaultJsonProtocolを呼ぶか、DefaultJsonProtocolをextendsしてあげるかしてあげましょう。

caseクラスをjson化する

spray/spray-json · GitHub に例としてあがっているものを利用します。

case class Color(name: String, red: Int, green: Int, blue: Int)

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit val colorFormat = jsonFormat4(Color)
}

import MyJsonProtocol._

val json = Color("CadetBlue", 95, 158, 160).toJson
val color = json.convertTo[Color]

case Classを利用している場合は、jsonFormatという関数が用意されているようです。
コンストラクタの引数が4つの場合上記のようにjsonFormat4を利用すれば良いようです。
つまり、case Classのコンストラクタの引数がNの時、jsonFormatNを利用すれば良いらしいです。

caseクラス以外を利用する

implicitオブジェクトを定義する

class Color(val name: String, val red: Int, val green: Int, val blue: Int)

object MyJsonProtocol extends DefaultJsonProtocol {
  implicit object ColorJsonFormat extends RootJsonFormat[Color] {
    def write(c: Color) =
      JsArray(JsString(c.name), JsNumber(c.red), JsNumber(c.green), JsNumber(c.blue))

    def read(value: JsValue) = value match {
      case JsArray(Vector(JsString(name), JsNumber(red), JsNumber(green), JsNumber(blue))) =>
        new Color(name, red.toInt, green.toInt, blue.toInt)
      case _ => deserializationError("Color expected")
    }
  }
}

import MyJsonProtocol._

val json = Color("CadetBlue", 95, 158, 160).toJson
val color = json.convertTo[Color]

writeという関数でjsonに変換する処理を書いて、
readという関数でjsonから元となるクラスへの変換処理を書けば良いみたいです。

もし、List[List[String]]というクラスをjsonに変換したい場合以下の様なコードを書けばよいかと思います。(read関数は適当です。)

package com

import spray.json._

object Sample extends App with DefaultJsonProtocol {
  implicit object NestedListFormat extends RootJsonFormat[List[List[String]]] {
    def write (nestedListFormat:List[List[String]]) =
      JsArray(
        nestedListFormat.map({ list =>
          JsArray(list.map({ str => JsString(str) }).toVector)
        }).toVector
      )

    def read (c:JsValue):List[List[String]] = List(List(""))
  }

  val hoge:List[List[String]] = List(
    List("hoge", "fuga"),
    List("piyo", "fuga")
  )

  println(hoge.toJson)
}

toJsonでjson化されるのはかなり楽でいいですね。