Sierraでのキーボードの設定

SierraでKarabinar & Seilが使えなくなってしまうという致命的な問題によりキーボードの設定について研究が必要となりました。特に自分の場合、

  • Mac本体のキーボードはUS配列
  • ThinkPadキーボードはJIS配列

となっており、US配列とJIS配列の両方をカバーする設定が必要になります。やりたいことは単純で、スペースキーの両サイドのキーで英数/日本語の入力切替を行いたいということで、あとはこれを成立させるための設定や、より便利にするための細かい設定という感じです。

結論から言うと、Karabinar Elements英かなというツールを併用することで以前と同じキーマッピングを実現することができました。

Mac本体での設定

デフォルトでは日本語入力の切替がcontrol + spaceで行われるようになっているのですが、IDEでの入力補完やEmacsでの範囲選択などと被るので無効化しました。

f:id:takezoe:20170501202804p:plain

caps lockcontrolに入れ替えるのはOSの設定でもできるのですが、接続するキーボード毎に設定しなくてはいけないので後述するKarabinar Elementsで行っています。

また、トラックパッドの設定で、外部マウスが接続された場合にトラックパッドを無効化するようにしています。

f:id:takezoe:20170501202819p:plain

英かなの設定

日本語入力を左右のコマンドキーで行うように設定します。

f:id:takezoe:20170501201850p:plain

Karabinar Elementsでも左右のcommandキーを英数/日本語の切替に変更することができるのですが、そうするとcommandキーが使えなくなってしまうので、commandキーと組み合わせるショートカットが使用できなくなってしまいます。

Karabinarではcommandキーを単独で押した場合は入力切替、他のキーと組み合わせて押した場合はコマンドキーとして動作するという設定ができたのですが、Karabinar Elementsでは今のところできないようです。ただ、以下のプルリクエストが取り込まれればできるようになるみたいなので将来的にはKarabinar Elements単体で済むようになるかもしれません。

github.com

Karabinar Elementsの設定

JIS配列のキーボードの変換、無変換キーで入力切替を行うように設定します。また、caps lockcontrolに置き換える設定もKarabinar Elementsで行なっています。

f:id:takezoe:20170501202029p:plain

あとはファンクションキーを押した場合に通常のファンクションキーとして動作するようにするのと、外付けキーボード接続時は本体のキーボードを無効化する設定を行なっています。

f:id:takezoe:20170501202855p:plain

f:id:takezoe:20170501202905p:plain

残課題

これで一応以前と同じ動きをするように設定できたのですが、外付けのJISキーボードがUS配列に認識されたり、一度JISキーボードを接続すると本体のキーボードもJIS配列と認識されてしまったりすることがあります。OSの設定でもう一度キーボードの配列を指定すれば直るのですが、ちょっと面倒です。

面倒といえばこれは以前からなのですが、ThinkPadキーボードのトラックポイントのスクロール方向と、本体のトラックパッドの設定が連動しており、本体のトラックパッドと外付けのトラックポイントを切り替える際に毎回設定を変更しないといけません。外付けマウスの場合のみスクロール方向を逆転させるツールもあって以前試してみたのですが、うまくスクロールができなくなってしまことがあるなど問題があり、常用は断念しました。

まとめ

  • US配列なら英かなだけでよさそう(ただし外付けキーボードのファンクションキーをデフォルトでF1〜F12として使うにはKarabinar Elementsが必要かもしれない)
  • JIS配列ならKarabiner Elementsだけでよさそう
  • 使用するキーボードはUS配列ならUS配列、JIS配列ならJIS配列に統一するべき

GitBucket 4.12をリリースしました

4.12にはリダイレクト時の処理に問題があり、これを修正した4.12.1をリリース済みです。こちらをご利用ください。 https://github.com/gitbucket/gitbucket/releases/tag/4.12.1

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

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

Gist埋め込み用のJavaScript

GistプラグインがGitBucket 4.12にあわせてバージョンアップし、スニペット埋め込み用のJavaScriptを提供するようになりました。サイドバーからJavaScriptを取得することができます。

f:id:takezoe:20170428124144p:plain

このJavaScriptをHTML中にペーストするとスニペットの内容が以下のように表示されます。

f:id:takezoe:20170428124152p:plain

ブランチ比較時のドロップダウンにフィルタを追加

ブランチ比較時にリポジトリやブランチを選択するドロップダウンメニューにフィルタリング用のボックスを追加しました。フォークしたリポジトリやブランチの数が多い場合でも目的のブランチを簡単に選択できるようになります。

f:id:takezoe:20170428124317p:plain

組み込みH2データベース利用時に警告

GitBucketはデフォルトでは組み込みモードのH2データベースを使用します。しかし、組み込みモードのH2はGitBucketと同じVM内で動作するため、GitBucketがクラッシュした場合にH2のデータが破損してしまう可能性があります。このためGitBucketを重要な用途に使用する場合はMySQLもしくはPostgreSQLの使用を推奨しています。

今回のバージョンから、GitBucketが組み込みH2で動作している場合は以下のように管理画面に警告メッセージを表示するようにしました。データが消えたら困るという場合はリンク先のインストラクションに従ってMySQLもしくはPostgreSQLへの移行を行っていただければと思います。

f:id:takezoe:20170428124820p:plain

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

Mirage 1.3.0をリリースしました

少し前にMirageのヘビーユーザーだというAdrianがコミッタに加わってくれたのですが、彼の会社でカスタマイズした部分をOSS化するなどいろいろやっていきたいとのことだったので、自分があまりメンテできていないこともあり、organizationに移して開発を行っていくことにしました。

github.com

で、早速organization移行後の初リリースということで1.3.0をリリースしてもらいました。このバージョンではアーティファクトのグループIDが変わっただけでパッケージ名含むソースコードには変更ありません。なので現状のMirageをご利用の場合は依存関係を以下のように書き換えるだけでバージョンアップできるはずです。

<dependency>
    <groupId>com.miragesql</groupId>
    <artifactId>miragesql</artifactId>
    <version>1.3.0</version>
</dependency>

今後Mirageは2.0系でパッケージ名の変更、モジュールの分割などを行っていく予定ですが、既存コードにバグなどがあった場合にはこの1.3系をメンテナンスしていくとのことです。mirage-scalaもこの1.3.0に対応したバージョンを近日中にリリースする予定です。

ユーザーもそんなにいないと思いますし、自分としては積極的に開発していくつもりはないのですが、アクティブなコミッタが増えたので今後はそれなりに動いていくのではと思います。2way SQLの評価器として使えるのでS2DaoS2JDBCで作ったSQLファイルが大量にある場合は脱Seasar2の1ステップとしてご検討いただくのはありかもしれません(というかもともとそのために作った)。

JJUG CCC 2017 Springにプラチサスポンサーとして参加します

f:id:takezoe:20170422160532p:plain

5月20日(土)に西新宿で開催されるJJUG CCC 2017 Spring、これまでもスポンサーとしてサポートさせていただいていましたが、今回はプラチナスポンサーとして参加させていただくことになりました。

セッションでは弊社の若者が以下の2つのセッションでお話させていただきます。

Scala機械学習基盤PredictionIOとSparkによるレコメンドシステム @haginot

SparkやMLlib、HDFS、Elasticsearchなど、注目を集めるオープンソースをベースとした機械学習サーバApache PredictionIOの概論と、同システムを使ったレコメンドシステム開発で得られた知見を共有するセッションです。Apache PredictionIOは様々な機械学習の手法をテンプレートに記述するだけで、Sparkをベースに学習タスクの分散処理が可能になります。それだけでなく、学習モデルから予測値を返したり、新たなイベントデータをリアルタイムに受けつけるAPIサーバまでを統合的に提供するプラットフォーム技術です。

本セッションでは、機械学習のインフラデザインとしても参考になるPredictionIOのアーキテクチャや、日本最大級の求人検索エンジンのログデータから、ユーザに最適な求人を推薦するレコメンドシステムの開発を通じて、学習ロジックのつくり方、学習モデルの評価と改善、Spark MLlibのチューニングやハマりどころなど実践導入のなかでのノウハウや苦労をお話します。Webシステムに機械学習を導入する際にPredictionIOを使うメリットをお伝えできればと思いますので、ぜひご参加ください。

Scalaによるサービス開発現場での継続的リファクタリングの実践 @iwamatsu0430

昨年から新規サービスとして開発に着手したプロジェクトですが、短いサイクルの連続で改善を繰り返してきたため開発開始から1年以上が経過した現在、技術的負債が開発効率に及ぼす影響も無視できないものになってきました。チーム内でリファクタリングの必要性は認識されていたものの、機能開発を止めるわけにはいかない状況の中、私たちは開発と並行して継続的にリファクタリングを行う道を選択しました。

このセッションでは実際にサービス開発の現場でどのようにリファクタリングを推進したのか、またプロダクト・コード上の問題点や解決方法の整理、チーム内での合意形成、リファクタリングのスコープ設定や進行方法など具体的なノウハウについてお話しさせていただきます。


PredictionIOはApache Software FoundationでIncubation中ですが、最近自分のチームでも取り組んでいるものです。うまくいけば当日にはよい成果を発表できるかもしれません。また、リファクタリングのセッションもScalaと銘打ってはいますが、自社サービスの開発現場でどのようにリファクタリングを行っているのかをお話しさせていただくとのことですのでScalaをご存じなくてもお楽しみいただけると思います。

Scalaのセッションでは@aa7thさんの「JavaエンジニアのためのScala入門」もありますし、他のJVM言語ではFrege(Haskell on JVM)やClojureのセッションもあるようです。もちろんSpring BootやJVM関連のセッションも目白押しです。JJUG CCCは参加無料ですので当日お時間のある方は是非ご参加いただければと思います。

Scala用のタイプセーフなSQLビルダを作ってみた

Scalaには様々なデータベースアクセスライブラリがありますが、Scalaの型安全性を活かしたクエリビルダはコンパイル時にクエリの間違いを検出することができるため非常に有難いものです。しかし、私見ですが、タイプセーフなクエリビルダは歴史あるトピックにも関わらずScalaにおいてはこれだというデファクトスタンダードが存在しません。

SlickやQuillは実行時 or マクロかという違いはあるもののASTからSQLへの変換を行っており、特にSlickに関しては変換後のSQLがかなり冗長なものになってしまうケースが多いです。また、変換処理も複雑で簡単に最適化できるようなものでもありません。一方国内を中心に人気の高いScalikeJDBCのQueryDSLは生成されるSQLが予想しやすいものの型付けの面で弱い部分があり、プログラマが意識しないと実行時エラーを引き起こす可能性のあるコードを書くことが割と簡単にできてしまいます。

Slickほどの柔軟性はなくてもいいので同じくらい型安全で、なおかつScalikeJDBCのように予測可能なSQLが生成されるSQLビルダを作ることはできないだろうか?という実験のためシンプルなSQLビルダを作ってみました。

github.com

まずはテーブルの定義とマッピングするケースクラスを以下のような形で用意しておきます。自動生成ツールなどはまだないので手で書かなければなりません。ケースクラスへのマッピングのあたりなどはもっとエレガントな仕組みにできそうな気もしますが、そこを頑張るのが目的ではないのでいったん考えないことにします。

import com.github.takezoe.tranquil._

case class User(userId: String, userName: String, companyId: Option[Int])

class Users(val alias: Option[String]) extends TableDef[User] {
  val tableName = "USERS"
  val userId    = new Column[String](alias, "USER_ID")
  val userName  = new Column[String](alias, "USER_NAME")
  val companyId = new NullableColumn[Int](alias, "COMPANY_ID")
  val columns = Seq(userId, userName, companyId)

  override def toModel(rs: ResultSet): User = {
    User(userId.get(rs), userName.get(rs), companyId.getOpt(rs))
  }
}

object Users {
  def apply() = {
    val users = new Users(None)
    new SingleTableAction[Users](users)
  }
  def apply(alias: String) = {
    val users = new Users(Some(alias))
    new Query[Users, Users, User](users, users, users.toModel _)
  }
}

case class Company(companyId: Int, companyName: String)

class Companies(val alias: Option[String]) extends TableDef[Company] {
  val tableName = "COMPANIES"
  val companyId   = new Column[Int](alias, "COMPANY_ID")
  val companyName = new Column[String](alias, "COMPANY_NAME")
  val columns = Seq(companyId, companyName)

  override def toModel(rs: ResultSet): Company = {
    Company(companyId.get(rs), companyName.get(rs))
  }
}

object Companies {
  def apply() = {
    val companies = new Companies(None)
    new SingleTableAction[Companies](companies)
  }
  def apply(alias: String) = {
    val companies = new Companies(alias)
    new Query[Companies, Companies, Company](companies, companies, companies.toModel _)
  }
}

すると以下のようにSlickっぽい感じでタイプセーフにクエリを組み立てることができます。leftJoinしたテーブルは自動的にOptionマッピングされます。

val conn: java.sql.Connection = ...

// SELECT
val users: Seq[(User, Option[Company])] =
  Users("u")
    .leftJoin(Companies("c")){ case u ~ c => u.companyId eq c.companyId }
    .filter { case u ~ c => (u.userId eq "takezoe") || (u.userId eq "takezoen") }
    .sortBy { case u ~ c => u.userId asc }
    .list(conn)

出力されるSQLはこんな感じです。leftJoinって書いたらちゃんとLEFT JOINになります。こんなに素晴らしいことはない…。

SELECT
  u.USER_ID      AS u_USER_ID,
  u.USER_NAME    AS u_USER_NAME,
  u.COMPANY_ID   AS u_COMPANY_ID,
  c.COMPANY_ID   AS c_COMPANY_ID,
  c.COMPANY_NAME AS c_COMPANY_NAME
FROM USERS u
LEFT JOIN COMPANIES c ON u.COMPANY_ID = c.COMPANY_ID
WHERE (u.USER_ID = ? OR u.USER_ID = ?)
ORDER BY u.USER_ID ASC

一応INSERTUPDATEDELETEもこんな感じでできるようにしてみました。このへんももう少し工夫の余地はありそうですが、まあ今回はおまけみたいなものです。

// INSERT
Users()
  .insert { u => 
    (u.userId -> "takezoe") ~ 
    (u.userName -> "Naoki Takezoe")
  }
  .execute(conn)

// UPDATE
Users()
  .update(_.userName -> "N. Takezoe")
  .filter(_.userId eq "takezoe")
  .execute(conn)

// DELETE
Users()
  .delete()
  .filter(_.userId eq "takezoe")
  .execute(conn)

今のところ以下のようなことはできません。めんどくさいのでやってないだけというものもありますが、集約や関数呼び出しが必要な場合は生SQLを書くと割り切ってもよいかもと思っています。

  • SQLでの関数呼び出し
  • 特定のカラムだけSELECTする
  • GROUP BY、HAVING
  • ページング(LIMIT、OFFSET的なやつ)

DB毎の方言を吸収する仕組みもないし、パラメータの型も文字列と数値くらいしか対応してない(型クラスで拡張可能)ので、とても実用的とは言えないものですが、ScalaでまともなSQLが生成できるタイプセーフなクエリビルダを作るのはそんなに難しくなさそうということはわかりました。

SlickもDBIOはともかくクエリビルダに関しては変に抽象化せずにこういうものでよかったのではと思うのですが、まあ今更言っても詮のないことです。

Play2ハンズオンがScalikeJDBCにも対応しました

これまでPlay2 + Slickの組み合わせで提供していたPlay2ハンズオンですが、社内でもScalikeJDBCの利用が増えてきたこともあり、Play2 + ScalikeJDBCの組み合わせにも対応しました。

github.com

また、あわせてこれまでGitHubリポジトリMarkdownファイルを格納していただけだったものをhugoを使ってWebサイトを生成してGitHub Pagesで運用するようにしました。

http://bizreach.github.io/play2-hands-on/

f:id:takezoe:20170413025517p:plain

見た目やサイドバーの追加などによって単純に読みやすくなっていると思いますし、目次やナビゲーションなどはJSONファイルとして用意したデータから自動生成するようにしたのでメンテナンスもだいぶ楽になるのではないかと思います。

Scalaの入門用コンテンツのひとつとして、今後もご活用いただければと思います。

IntelliJのScalaプラグインでケースクラスのパラメータのインデントが深すぎるのを回避する設定

IntelliJScalaを書いている時にいつもイラッとするのがケースクラスのパラメータのインデントです。こういうコードが…

case class Person (
  name: String,
  age: Int
)

自動的にこのようになってしまいます。

case class Person (
                    name: String,
                    age: Int
                    )

手動で調整しても何かの拍子にコードフォーマットが走ったり、新しいパラメータを追加すると上記のようにインデントされてしまい、非常にイライラします。 

ググってみたところStack Overflowで同じ質問をしている方がいました。

stackoverflow.com

File -> Settings… -> Code Style -> Scalaで以下の設定をすると回避できるようです。

  • “Use normal indent for parameters"をチェック
  • “Align when multiline"のチェックを外す

f:id:takezoe:20170402162747p:plain

これで普通にインデントされるようになりました。