読者です 読者をやめる 読者になる 読者になる

GitBucket 4.12をリリースしました

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

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

GitBucket 4.11をリリースしました

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

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

デプロイキーのサポート

CIツールなどで利用するためのデプロイキーを設定できるようになりました。リポジトリの管理画面で公開鍵を登録することができます。

f:id:takezoe:20170322102545p:plain

これによって専用のユーザを作成することなくJenkinsなどのツールがSSH経由でリポジトリに読み書きできるようになります。

デフォルトのアバター画像の自動生成

ユーザがアバター画像をアップロードしておらず、Gravatar連携も無効になっている場合、ユーザ名の最初の1文字とランダムに選択された色でアバター画像が自動生成されるようになりました。

f:id:takezoe:20170324100207p:plain

プライベートリポジトリをフォークした場合のコラボレータ

プライベートリポジトリをフォークした場合、元リポジトリのコラボレータ設定を引き継ぐようになりました。

f:id:takezoe:20170322102555p:plain

これによって元のリポジトリのオーナーやコラボレーターはデフォルトでフォークされたリポジトリにもアクセスできるようになります。もちろんフォーク後にコラボレータの設定を変更することも可能です。

アバター画像をブラウザでキャッシュ可能に

GitBucketが適切なレスポンスヘッダを出力することにより、アバター画像がブラウザにキャッシュされるようになりました。

また、他のアセットについては以下のようにパスにGitBucketの起動時刻が自動的に付与されるようになりました。

<link href="http://localhost:8080/assets/vendors/bootstrap-3.3.6/css/bootstrap.min.css?20170321132510" rel="stylesheet">
<link href="http://localhost:8080/assets/vendors/octicons-4.2.0/octicons.css?20170321132510" rel="stylesheet">
...

これによってGitBucketのバージョンアップ時などにJavaScriptCSSのキャッシュによる動作の不具合を回避することができるようになりました。

リポジトリイベントをフックするための拡張ポイントを追加

プラグインリポジトリに関するイベントをフックするための拡張ポイントを追加しました。

RepositoryHookを拡張し、Pluginsに登録しておくことでリポジトリに関するイベントの通知を受け取ることができます。RepositoryHookには以下のメソッドが用意されているので処理したいイベントに対応するメソッドをオーバーライドしてください。

  • created
  • deleted
  • renamed
  • transferred
  • forked

今回のバージョンではこの他にも以下のような改善が行われています。

  • Jenkins連携のためのGitHub互換APIの改善
  • いくつかのページでのレイアウト崩れを修正
  • デフォルトで外部サービスへのHTTPアクセスを行わないようにした
  • Java9でもコンパイル可能にした

他にも様々なバグが修正されています。詳細についてはIssueの一覧をご覧ください。