ThinkPadキーボード(の前モデル)こそ至高のキーボード

結構前に会社の人にThinkPadキーボードの前モデル(55Y9024)を譲ってもらって職場のMacBookで使っているのですが、このキーボードが非常に使いやすいです。

レノボ・ジャパン ThinkPad USB トラックポイントキーボード(日本語) 55Y9024

レノボ・ジャパン ThinkPad USB トラックポイントキーボード(日本語) 55Y9024

上記のAmazonの写真は英語配列ですが、自分が使っているのは日本語配列のものです。

現行のThinkPadキーボードも使っていて自宅ではこちらを使っているのですが、以下のような違いがあります。

  • キータッチが良い(X201あたりのタッチを思い出します)
  • ファンクションキーが押しやすい(4つ毎にスペースがあるので間違えにくい)
  • Macでのトラックポイントの動作がスムーズで横スクロールもできる
  • パームレストがある(狭いデスクだと逆に欠点になるかも)

逆に現行モデルのほうが優れていると感じる点もあります。

  • PageUp、PageDownキーの場所(押し間違えやすいので邪魔という話はよく聞くのですが、自分は使用頻度が高いのでカーソルキーの近くにある方が便利と感じます)
  • コンパクトさ(6列+パームレストなしなので狭いデスクでも使いやすいですし、持ち運びもしやすいです)
  • 値段が安い(前モデルはそもそもすでにレノボからは販売されておらず、Amazonヤフオクなどで入手する必要があります)

PageUp、PageDownキーの配置は個人の好みなので除くとしても、現行モデルは価格とコンパクトさくらいしかメリットがありませんし、やはりキーボードは入力デバイスですから入力性が最重要です。今のところこのキーボードが自分にとっては最高の外付けキーボードです。

おそらく今後ThinkPadキーボードがモデルチェンジするとしてもキータッチや配列が以前のように戻ることはもうないと思うので今後に備えて在庫を調達しておいた方がいいのかなぁ…などと思っています。今だとAmazonで入手するか、ヤフオクでも出品している人をチラホラ見かけるのですが、1万5千円くらいするので中々手が出ませんw

先日HHKBも買ってみたのですが、やはりトラックポイントがついてないことでかなりストレスを感じ、結局ThinkPadキーボードをメインで使っています。

takezoe.hatenablog.com

レノボ以外からトラックポイント付きのまともなキーボードが出るといいんですけどねー。つくづくTEX Yodaを買っておかなかったことを後悔しています。

blocking−slickで大体Slick2と同じメソッドが使えるようになりました

先日からblocking-slickというSlick3にSlick2風のブロッキングAPIを追加するライブラリを作っています。

github.com

基本的にはQueryオブジェクトにimplicit conversionで同期実行用のメソッドを生やすという方式で実装していたのですが、更新系のメソッドがSlick3のDBIO生成用のメソッドとの衝突してしまったため、当初衝突を避けるため別のメソッド名にしていました。これによってDBIO生成用のメソッドとブロッキング用のメソッドを利用者側で使い分ける必要があり、間違えてしまう危険がありました。

そこでインポートするAPIを丸ごと別のものにしてしまうことでDBIO生成用のメソッドを無効にし、メソッド名の衝突が起こらないようにしました。また、ブロッキング用のドライバも予め提供するようにしたので自分でブロッキング用のプロファイルをミックスインしたドライバを定義する必要はなくなりました。

import com.github.takezoe.slick.blocking.BlockingH2Driver._
import com.github.takezoe.slick.blocking.BlockingH2Driver.blockingApi._

db.withSession { implicit session =>
  // Insert
  Users.insert(UsersRow(1, "takezoe"))

  // Update
  Users.filter(t => t.id === 1.bind).update(UsersRow(1, "naoki"))

  // Delete
  Users.filter(t => t.id === 1.bind).delete
}

一部Futureをawaitすることで誤魔化している部分はあったりしますが、Plain SQL用のString Interpolationやreturningも含め基本的なメソッドは概ねカバーできています。

これを使ってGitBucketをSlick3に移行してみたところ、以下のように非常に少ない修正で移行することができました。

github.com

インポート文や一部のDSLが変わっていますが、使い勝手はSlick2とほぼ変わりません。これなら最小限のコストでSlick3でのクエリコンパイラの改善のメリットを享受できるのではないのでしょうか。

Slick3用のブロッキングAPIを作ってみました

github.com

なぜ作ったのか?

事の発端はSlickのこのイシューです。

github.com

GitBucketはServletベースということもあり、Slick3のDBIOは非同期実行のメリットが得られないのに複雑さだけが劇的に向上してしまうこと、プラグイン開発者にもモナディックなプログラミングを強いてしまうことなどから当面Slick3へのバージョンアップは行わず、Slick2に留まることにしていました。

しかし、Slickは場合によっては以下のような酷いSQLを生成することがあります。Slick3にはクエリコンパイラが改善されている(今後の改善も期待できる)という利点があるため、可能であるならバージョンアップしたいというのが正直なところでした。

github.com

そこで冒頭のようなイシューが上がっていたため、これはコントリビュートのチャンス、もしSlick3でSlick2互換のブロッキングAPIが利用できるようになればGitBucketをほぼ無傷でSlick3に移行でき、クエリコンパイラの改善というメリットを享受することができるという期待がありました。

というわけでプロトタイプも作って提案してみたのですが、自分のコミュ力不足もあってかあまり理解が得られず、埒が明かなそうだったので独自のライブラリとして提供することにしました。

使い方

build.sbtに以下の依存関係を追加します。

libraryDependencies += "com.github.takezoe" %% "blocking-slick" % "0.0.1"

使っているデータベースに合わせて以下のようなオブジェクトを定義します。

package myapp.slick.driver

import slick.driver.H2Driver
import com.github.takezoe.slick.blocking.SlickBlockingAPI

object BlockingH2Driver extends H2Driver with SlickBlockingAPI

このオブジェクトを以下のようにインポートするとブロッキングAPIが使えるようになります。

import myapp.slick.driver.BlockingH2Driver._
import myapp.slick.driver.BlockingH2Driver.api._

以下はブロッキングAPIの使用例です。

val db = Database.forURL("jdbc:h2:mem:test")

db.withSession { implicit session =>
  // Create tables
  models.Tables.schema.create

  // Insert
  Users.unsafeInsert(UsersRow(1, "takezoe"))

  // Select
  val users: Seq[UserRow] = Users.list

  // Select single record
  val user: UserRow = Users.filter(_.id === "takezoe".bind).first

  // Select single record with Option
  val user: Option[UserRow] = Users.filter(_.id === "takezoe".bind).firstOption

  // Update
  Users.filter(t => t.id === 1.bind).unsafeUpdate(UsersRow(1, "naoki"))

  // Delete
  Users.filter(t => t.id === 1.bind).unsafeDelete

  // Drop tables
  models.Tables.schema.remove
}

トランザクションが必要な場合はwithSessionの代わりにwithTransactionを使用します。

// Transaction
db.withTransaction { implicit session =>
  ...
}

以下にplay-slickと組み合わせてPlay2アプリケーションでこのブロッキングAPIを使った場合のサンプルもあります。

github.com

今後のScalaのORM事情

SlickはTypesafe社のプロジェクトだから使っていたというユーザも多いのではないかと思いますが、Lightbendのスタックから外れてしまった今となっては使い続けるべきなのか微妙なところではあります。クエリコンパイラも3.x系で改善されているとはいえまだ厳しいSQLになってしまうケースもあるみたいですし…。

ただ、今だと代替品に何を使うかというのが悩ましいところで、少し前から注目しているquillはマクロによる実装なので将来的にscalametaにちゃんと移行できるのか?という不安があります。ScalikeJDBCは学習コストが低く、生成されるSQLも素直なのでリスクが少ないと言えますが、国外での知名度という面でやや不安が残るところです。

そもそも海外ではSlickの厳しいSQLが問題になるようなRDBの使い方はレアケースなのか、Scalaに限らずRDB周りは昔から国内と海外の温度差を感じることが多いのですが、実際のところどうなのかというのは個人的には気になるところです。

GitBucketプラグインを作ってみよう

GitBucketはプラグインシステムを備えており、プラグインで様々な機能を拡張することができます。このエントリではプラグイン開発の最初の一歩について説明します。内容的には以前GitBucket Newsで書いた以下の英語のエントリと同じものです。

gitbucket.github.io

プロジェクトの作成

まず初めに以下の内容のbuild.sbtScalaプロジェクトを作成します(gitbucketの依存関係は最新のバージョンにあわせて変更してください)。

name := "gitbucket-helloworld-plugin"

organization := "io.github.gitbucket"

version := "1.0.0"

scalaVersion := "2.11.8"

libraryDependencies ++= Seq(
  "io.github.gitbucket" %% "gitbucket"         % "4.3.0" % "provided",
  "javax.servlet"        % "javax.servlet-api" % "3.1.0" % "provided"
)

プラグインの定義

/src/main/scalaに以下のような内容でPluginクラスを作成します。このクラスではプラグインメタデータと、どのように拡張するかを定義します。ここではcontrollersという拡張ポイントを使用して/helloworldというパスに新たなコントローラを追加しています。

import io.github.gitbucket.solidbase.model.Version
import io.github.gitbucket.helloworld.controller.HelloWorldController

class Plugin extends gitbucket.core.plugin.Plugin {
  override val pluginId: String = "helloworld"
  override val pluginName: String = "HelloWorld Plugin"
  override val description: String = "First example of GitBucket plug-in"
  override val versions: List[Version] = List(new Version("1.0.0"))

  override val controllers = Seq(
    "/helloworld" -> new HelloWorldController()
  )
}

コントローラの実装

Pluginクラスで定義したHelloWorldControllerを実装します。コントローラはgitbucket.core.controller.ControllerBaseを継承し、/src/main/io/github/gitbucket/helloworld/controllerディレクトリに作成します。これは一般的なScalatraのコントローラです。

package io.github.gitbucket.helloworld.controller

import gitbucket.core.controller.ControllerBase

class HelloWorldController extends ControllerBase {

  get("/helloworld"){
    "Hello World!"
  }

}

このサンプルではHelloWorldControllerは文字列をレスポンスとして返却します。もちろんTwirlテンプレートを使用してHTMLを返却したり、JSONを返却したりすることもできます。

プラグインのインストールと実行

sbt packageを実行すると/target/scala-2.11/gitbucket-helloworld-plugin_2.11-1.0.0.jarが生成されるのでこのjarファイルを~/.gitbucket/pluginsディレクトリ(ディレクトリが存在しない場合は手で作成してください)にコピーします。

GitBucketを起動し、http://localhost:8080/helloworldにブラウザでアクセスするとHello World!と表示されるはずです。

次のステップ

このエントリで説明したプロジェクトは以下にありますので、これを雛形にGitBucketプラグインの開発を始めることができます。

github.com

また、gitbucket community pluginsには実用的なgitBucketプラグインのリストがあります。GitBucketプラグインの開発方法の詳細を知りたい場合はこれらのプラグインソースコードを参照してみてください。

GitBucket 4.3をリリースしました

Scalaで実装されたオープンソースのGitサーバ、GitBucket 4.3をリリースしました。

https://github.com/takezoe/gitbucket/releases/tag/4.3

絵文字のサポート

GitBucket 4.3と同時にgitbucket-emoji-pluginがリリースされました。このプラグインは以下の機能を提供します。

  • Markdownやコミットメッセージなどの絵文字記法を画像に変換する
  • Markdownを編集する際のテキストエリアで絵文字記法のサジェストを行う

f:id:takezoe:20160716154453p:plain

ユーザ名のサジェスト

イシューやプルリクエスト作成時やコメント入力欄のテキストエリアでユーザ名のサジェストが可能になりました。@をタイプするとユーザ名の候補がポップアップ表示されます。

f:id:takezoe:20160716154602p:plain

新しいWeb API

GitBucketはGitHub互換のWeb APIのサブセットを提供していますが、今回のバージョンで多くのAPIが追加されました。

また、BASIC認証でのAPIアクセスも可能になりました。

新しい拡張ポイント

プラグインで利用可能な新たな拡張ポイントを3つ追加しました。

  • assetsMapping : プラグインのクラスパス内のリソースをWebアセットとしてアクセスできるようにします
  • suggestionProvider : Markdown編集時のテキストエリアにサジェスト機能を追加します
  • textDecorator : Markdownから変換されたHTMLなどのテキストノードを装飾します(コミットメッセージ等にも適用されます)

これらの拡張ポイントの使用方法についてはgitbucket-emoji-pluginのPluginクラスを参照してください。

この他にも様々な改善やバグフィックスを行っています。詳細についてはIssueの一覧をご覧ください。

第十六回 #渋谷java を開催しました

7月23日(土)に弊社オフィスで第十六回 #渋谷javaを開催しました。

f:id:takezoe:20160724104424j:plain

今回は以下の皆さんに発表していただきました。

  • セッション枠(20分)
    • Shunsuke Tadokoro 「JISの歴史から迫る文字コード入門」
    • ayato_p 「翻訳にまつわるエトセトラ」
  • 通常枠(10分)
    • yy_tank 「Javaで最強のFizzBuzz
    • reona396 「Processingでジャバジャバ稼ぐ」
    • ayato_p 「Tranceducersの話」
    • kazurof 「JUnit5 M1をやってみよう」
    • shimamoto「Quartzでcronを範囲検索したい」

当日の様子や他の皆さんの発表内容については以下のtogetterまとめをご覧いただければと思います。

togetter.com

今回は文字コードの歴史的な話や、ドキュメントの翻訳プロジェクトを成功させるためにはなど、他ではなかなか聞けない貴重なトークだったのではないかと思います。Javaで最強のFizzBuzzも予想の遥か斜め上を行く内容で大変学びが多かったですw

なお、セッション枠でお話いただく予定だったk.hasunumaさんは残念ながら体調不良のためお越しになれませんでしたが、発表予定だった資料を公開していただいています。

www.slideshare.net

今回は以前のように発表枠と観覧枠の募集を同時に開始したのですが、ドタキャンが多く過去にない参加率の低さでした。面白いトークが多かっただけに残念です。開催日時や募集方法など工夫していきたいと思います。

Akka HTTPでJacksonを使ってJSON変換をする

Akka HTTPではspray-jsonを使用するモジュールが提供されており、これを使用することでJSONで受け取ったリクエストをオブジェクトに変換したり、戻り値として返却したオブジェクトをJSONとしてレスポンスすることができます。

takezoe.hatenablog.com

が、spray-jsonJSON変換を行うにはオブジェクトの型に対応したFormatを定義しておかなくてはなりません。これがまあまあ面倒なのでJacksonを使ってリフレクションでズルをするためのMarshallerUnmarshallerを作ってみました。

まずはbuild.sbtに以下の依存関係を追加します。

libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.7.2"

MarshallerUnmarshallerの実装は以下のような感じです。

gist.github.com

以下のようにインポートしておけばOKです。

import com.github.takezoe.akka.http.jackson.JacksonSupport._

個人的にJSONに関してはどうせ一度テキストになるので、いちいちFormat書かずにリフレクションで変換しちゃえばいいんじゃないかなぁと思っているのですが、リフレクションだと型情報を取れないケースもあって結局メタデータ的なアノテーションをつけないといけなかったりするのでどっちもどっちかもしれません。

追記:折角なのでライブラリとして使えるようにしてみました。

github.com