ActorでDispatchを使うときHttp.configureは使えなかった

Actorでdispatchを使ってRSSを解析しようとしたら。

dispatchの通常の使い方ではエラーとなったので、残しておく。

dispatchを使う時、dispatch.Http を作成して利用するのですが。

Actor内では、Http.configure を使って dispatch.Http を作ろうとすると上手くいきませんでした。

val executorService = Executors.newFixedThreadPool(10)
val httpClient = Http.configure {
    _.setExecutorService(executorService)
        .setIOThreadMultiplier(1)
        .setAllowPoolingConnection(true)
        .setAllowSslConnectionPool(true)
        .setConnectionTimeoutInMs(3000)
        .setRequestTimeoutInMs(10000)
        .setFollowRedirects(true)
        .setMaximumConnectionsPerHost(30)
}

Http.configure 頼らず、Javaの書き方で対応する。

val executorService = Executors.newFixedThreadPool(10)

val asyncHttpClient = new AsyncHttpClient(
    new AsyncHttpClientConfig.Builder()
        .setExecutorService(executorService)
        .setIOThreadMultiplier(1)
        .setAllowPoolingConnections(true)
        .setAllowPoolingSslConnections(true)
        .setConnectTimeout(3000)
        .setRequestTimeout(10000)
        .setCompressionEnforced(true)
        .setFollowRedirect(true).build)
val httpClient = new Http(asyncHttpClient)

httpClientが作成できれば、こっちのものなのでいつも通りのdispatchを使い方でおk

val req: dispatch.Req = dispatch.url(url)
val r = httpClient(req > (x => x))
val res = r()

Actorのコード全体

class RssActor extends Actor with ActorLogging {

    private val executorService = Executors.newFixedThreadPool(10)

    private val asyncHttpClient = new AsyncHttpClient(
        new AsyncHttpClientConfig.Builder()
            .setExecutorService(executorService)
            .setIOThreadMultiplier(1)
            .setAllowPoolingConnections(true)
            .setAllowPoolingSslConnections(true)
            .setConnectTimeout(3000)
            .setRequestTimeout(10000)
            .setCompressionEnforced(true)
            .setFollowRedirect(true).build)
    private val httpClient = new Http(asyncHttpClient)

    //        private val httpClient = Http.configure {
    //            _.setExecutorService(executorService)
    //                .setIOThreadMultiplier(1)
    //                .setAllowPoolingConnection(true)
    //                .setAllowSslConnectionPool(true)
    //                .setConnectionTimeoutInMs(3000)
    //                .setRequestTimeoutInMs(10000)
    //                .setFollowRedirects(true)
    //                .setMaximumConnectionsPerHost(30)
    //        }

    def receive = {
        case msg: String =>
            log.info("start actor")
            ....
    }

    override def postStop() = {
        log.info("stopped actor")
    }

}

slimでrubyの変数によってクラスを変えるやり方

slimでrubyの変数によってクラスを変えたい時のやり方です。

よくやるやり方が - if複数行に渡って書くやり方がある。

- if status == 0
  tr.success
- else
  tr.danger

ただ、これだとネストするしこのロジックの後に 同じhtmlを生成しようとすると面倒になる

- if status == 0
  tr.success
    td = hoge
    td = fuga
    ...
- else
  tr.danger
    td = hoge
    td = fuga
    ...

これは、嫌だ!!

そこで、1行で綺麗に書く方法が以下の通り。

tr class=(status == 0 ? 'success' : 'danger' )

楽で簡単!!

三項演算子みたいなものです。

docker-machineをupdateした時に出たエラーの対処

docker-machineでdockerを使ってたら、以下のエラーが出たのでその対応!

$ docker ps
Error response from daemon: client is newer than server (client API version: 1.21, server API version: 1.20)

Error response from daemon: client is newer than server with Docker 1.9 RC3 · Issue #2147 · docker/machine · GitHub

docker-machineで作ったサーバをアップグレードすることにしました

docker-machine upgrade [server-name]

$ docker-machine upgrade dev
Detecting the provisioner...
Upgrading docker...
Downloading latest boot2docker iso...
Latest release for github.com/boot2docker/boot2docker is v1.9.1
Downloading https://github.com/boot2docker/boot2docker/releases/download/v1.9.1/boot2docker.iso to /Users/oomagaritomohisa/.docker/machine/cache/boot2docker.iso...
0%....10%....20%....30%....40%....50%....60%....70%....80%....90%....100%
Stopping machine to do the upgrade...
(dev) Stopping VM...
Upgrading machine "dev"...
Starting machine back up...
(dev) Starting VM...
Restarting docker...

docker-machine envをzshrcでやっていたので新たにtmuxでセッションを開いたら、以下の内容が出ました。

Error checking TLS connection: Error checking and/or regenerating the certs: There was an error validating certificates for host "192.168.99.100:2376": dial tcp 192.168.99.100:2376: i/o timeout
You can attempt to regenerate them using 'docker-machine regenerate-certs [name]'.
Be advised that this will trigger a Docker daemon restart which will stop running containers.

docker-machine regenerate-certs [server-name] を実行した

$  docker-machine regenerate-certs dev
Regenerate TLS machine certs?  Warning: this is irreversible. (y/n): y
Regenerating TLS certificates
Detecting the provisioner...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...

うまく、動きました。

バックアップファイルを圧縮してs3に保存するメモ

MySQLのバックアップ

mysqldump -uroot --single-transaction wordpress | gzip | aws s3 cp - s3://backup/wordpress/`date +'%Y-%m-%d'`.gz

個人で利用している分なので mysqldump で十分な感じです。
mysqldumpしたものを圧縮して s3 に保存するワンライナー

--single-transactionは、ダンプ処理をtransactionで行ってくれます。

画像系のバックアップ

$ tar zcfP - /home/user/backup/ |  aws s3 cp - s3://backup/tweet_image/`date +'%Y-%m-%d'`.tar.gz
tar: Removing leading `/' from member names

画像系があるフォルダを圧縮してs3に送ります。
tar: Removing leading '/' from member namesは、絶対パスをしていると出る警告みたいです。
オプションの-Pで消せます。

tar: Removing leading `../' from member names - Ask Ubuntu

- でただひたすら繋げるだけですね。

簡単!!

Playでテスト用の設定ファイルを読み込む(Scala)

毎回、忘れるのでメモとして残しとく

ScalaのPlayでは、以下のように -Dconfig.file=conf/test.conf で設定しても
デフォルトの application.conf しか読み込まれません。

activator -Dconfig.file=conf/test.conf test

テスト用の設定を読み込ませたい場合、build.sbtに設定を追加しなければいけない。

in Test はテストコマンドを実行しているときのみに反映するという設定方法。

javaOptions in Test += "-Dconfig.file=../conf/test.conf"

忘れないためのメモ。

CircleCIでScalaのマルチプロジェクトをテストし、カバレッジを出力する

CircleCIでScalaのプロジェクトのテストを回した時の内容です。

circle.ymlは、以下の通りです。

machine:
  timezone:
    Asia/Tokyo
  java:
    version: oraclejdk8
dependencies:
  cache_directories:
    - "~/.sbt/boot"
  override:
    - sbt update
test:
  override:
    - sbt clean coverage test coverageReport coverageAggregate
  post:
    - cp -R ~/hoge/target/scala-2.10/scoverage-report/ $CIRCLE_ARTIFACTS

基本設定

タイムゾーンjavaのバージョンは、お好きにどうぞ

machine:
  timezone:
    Asia/Tokyo
  java:
    version: oraclejdk8

sbtのバージョンに関して

初めはdependenciesにsbtをダウンロードする内容を記入していたのですが。

dependencies:
  pre:
    - wget -q https://dl.bintray.com/sbt/debian/sbt-0.13.8.deb
    - sudo dpkg -i sbt-0.13.8.deb

リポジトリの内のproject/build.propertiesを見て、
書いてあるsbtのバージョンをインストールしてくれるみたいです。

キャッシュに関して

sbt updateを行う際に利用しているライブラリを毎回ダウンロードするのは、
効率が悪いのでライブラリ関連をキャッシュします。
その設定が以下の部分です。

キャッシュには、sbtのプラグイン系(plugins.sbt)とsbtでダウンロードしたライブラリ(build.sbt)の2種類が必要です。
sbtプラグイン系のキャッシュは ~/.ivy2 の以下にあります。
これは、CircleCI側でデフォルトでキャッシュされるので問題ないです。

もう一つの、sbtでダウンロードしたライブラリのキャッシュは ~/.sbt/boot にあります。 これは、デフォルトでキャッシュしてくれないので以下のように設定します。

dependencies:
  cache_directories:
    - "~/.sbt/boot"

これで、毎回ライブラリ系をダウンロードしなくなるので、大丈夫です。

マルチプロジェクトでのカバレッジの出し方に関して

最後に、カバレッジの出し方です。

scalaカバレッジを出すプラグインは、sbt-scoverageを利用しています。

マルチプロジェクトの場合、最後にcoverageAggregateのコマンドを実行することで 各プロジェクトで出力したレポートをマージしてくれます。

test:
  override:
    - sbt clean coverage test coverageReport coverageAggregate

CircleCIは $CIRCLE_ARTIFACTSという環境変数のパスに
テスト結果のカバレッジのレポートを移動させるといい感じに見れるようにしてくれます。

出力したレポートを$CIRCLE_ARTIFACTSへコピーすれば完了です。

  post:
    - cp -R ~/hoge/target/scala-2.10/scoverage-report/ $CIRCLE_ARTIFACTS

Continuous Integration and Deployment

いい感じにテストを回せるようになってよかった。

Slick 3.1.0でのCodeGenerator(joda-timeサポート付き)

自分が前に開発したプロジェクトでslickのバージョンが2系を 利用していたのでslick 3系にアップロードした際に CodeGeneratorの書き方が変わっていたので、そのメモです。

slick 2の場合

import com.typesafe.config.{ Config, ConfigFactory }
import scala.slick.{ model => m }
import scala.slick.codegen.SourceCodeGenerator
import scala.slick.driver.JdbcProfile

class CustomSourceCodeGenerator(model: m.Model) extends SourceCodeGenerator(model) {

    override def code = "import com.github.tototoshi.slick.MySQLJodaSupport._\n" + "import org.joda.time.DateTime\n" + super.code

    override def Table = new Table(_) {
      override def Column = new Column(_) {
            override def rawType = model.tpe match {
                case "java.sql.Timestamp" => "DateTime"
                case _ => {
                    super.rawType
                }
            }
        }
    }
}

object CodeGen {

    def main(args: Array[String]) {

        val config: Config = ConfigFactory.load()
        val slickDriver = "scala.slick.driver.MySQLDriver"
        val jdbcDriver = config.getString("db.default.driver")
        val url = config.getString("db.default.url")
        val user = config.getString("db.default.user")
        val outputFolder = "lib/src/main/scala/"
        val pkg = "dilaton.lib"

        val driver: JdbcProfile = scala.slick.driver.MySQLDriver

        val db = {
            driver.simple.Database.forURL(url, driver = jdbcDriver, user = user)
        }

        db.withSession { implicit session =>
            new CustomSourceCodeGenerator(driver.createModel()).writeToFile(slickDriver, outputFolder, pkg)
        }
    }
}

slick 3の場合

import slick.codegen.SourceCodeGenerator
import slick.driver.JdbcProfile
import scala.concurrent.ExecutionContext.Implicits.global

object CodeGenMySQL {

    def main(args: Array[String]) {
        val driver: JdbcProfile = slick.driver.MySQLDriver

        val db = InfrastructureDao.MySQLClient().db

        val modelAction = driver.createModel(Some(driver.defaultTables))
        val modelFuture = db.run(modelAction)
        val codeGenFuture = modelFuture.map(model => new SourceCodeGenerator(model) {
            override def code = "import com.github.tototoshi.slick.PostgresJodaSupport._\n" + "import org.joda.time.DateTime\n" + super.code

            override def Table = new Table(_) {
                override def Column = new Column(_) {
                    override def rawType = model.tpe match {
                        case "java.sql.Timestamp" => "DateTime" // kill j.s.Timestamp
                        case _ => {
                            super.rawType
                        }
                    }
                }
            }
        })

        codeGenFuture.onSuccess {
            case codeGen =>
                codeGen.writeToFile(
                    "slick.driver.MySQLDriver", "lib/src/main/scala/", "test.lib", "MySQLTables", "MySQLTables.scala"
                )
        }
        codeGenFuture.onFailure {
            case e =>
                println("error")
                e.printStackTrace()
        }
    }
}