AndroidのCIはlintOptions.textReportを有効にしておくと捗る

ThreeTenABPの差分(v1.0.3...v1.0.4)を見ていて気づいたんですが、Android Lintの結果をテキスト形式で標準出力に出すことができるんですね。マニュアル にも書いてありますが、今日初めて知りました。

lintOptions {
  textReport true
  textOutput 'stdout'
}

これをするとコマンドラインのビルドでlintのwarningやerrorが表示されるので、たとえばCIがlint errorで落ちたときに手元でlintを走らせることなく原因がわかります。

日頃有効にしておくとwarningでも対処しようというプレッシャーになるので、やって損はなさそうです。

Orma v3.0.0をリリースしました

Orma v3.0.0をリリースしました*1

実にv2.6.0から100コミット以上、v2.0.0から8ヶ月後のリリースとなります。

このバージョンは、内部的には大きく変えたものの、インターフェイス的にはそれほどv2.6.0と変わりありません。v2.6.0で警告なしに動いていたものはそのまま動くと思います。

transctionAsync() まわりで古いメソッドを消したので、修正が必要なこともあるかもしれません。たとえば、DroidKaigi2016では次のような修正が必要でした。

Upgrade Orma to v3.0.0-rc2 by gfx · Pull Request #419 · konifar/droidkaigi2016 · GitHub

ひとつ注意点として、Android Gradle Plugin (正確のはその一部であるDataBinding library)との相性問題があります。CHANGELOGにも書きましたが、以下のような組み合わせでないと動きません。

  • Android Gradle Plugin 2.1以下 + Orma v2.x
  • Android Gradle Plugin 2.2以上 + Orma v3.x

これは、Android Gradle PluginとOrmaがfull qualified nameが全く同じクラスをもち実装が微妙に異なるANTLR (TunnelVisionLabs ANTLR vs Original ANTLR) に依存しているためです。

android bug ticket: Issue 200925 - android - Databinding plugin uses non-standard Antlr dependency. - Android Open Source Project - Issue Tracker - Google Project Hosting

このため、Android Gradle PluginとOrmaを同時にアップデートする必要があります。お手数をおかけしますがよろしくおねがいします。

*1:Orma入門もv3.0.0に対応済みです: Android Orma入門 - Islands in the byte stream

Orma v3.0.0-rc2 と内部設計の見直し

Orma v3.0.0-rc2をリリースしました。CHANGESはこちら:

Android-Orma/CHANGELOG.md at master · gfx/Android-Orma · GitHub

三行まとめ

  • あるモデルが複数の同じ型のモデルをdirect associationで持てるようになりました
  • 内部的に非常に大きな変更を行ったので新しいバージョンを検証してみてください
  • v3.0.0はもうしばらく寝かせてから来週あたりにリリース予定です

解説

このバージョンは、3.0.0-rc1と比較してただ1つのバグフィクスを含みます。しかし、この修正のために内部の設計の見直しが必要だったため、変更量が非常に大きいものになっています。

そもそもdirect associationとはなにか、このリリースで解決したかったことはなにかを説明します。

Direct Associations

OrmaモデルAが別のOrmaモデルBを直接カラムとして保持することを、Ormaでは "direct association" と言っています。

たとえば以下のようなモデルがあるとき、BlogEntryはauthor:Userのdirect associationを持つということです。

@Table
public class User {
  @PrimaryKey
  long id;

  @Column
  public String name;
}

public class BlogEntry {
  @PrimaryKey
  public long id;

  @Column
  User author;

  @Column
  String title;

  // ...
}

これをSELECTでデータを検索してモデルを構築する際は、JOINを使って関連するモデルのデータもすべてロードする、いわゆるeager loadを行います。Ormaはモデルに制約を課していないので、direct associationの際はeager loadする他ないのです ((lazy loadを行うにはSingleAssociation<T> を使ったindirect associationにすればできます。しかし、モデルのインターフェイスが変わってしまいます。)) 。まあ、eager loadはそれが起きると分かっていれば、N+1クエリも発生しないしクエリの結果も予想しやすいので便利なものではあります。

JOINs

さてOrma v2の時点では、BlogEntryを生成するためクエリは次のようなものでした。

SELECT BlogEntry.id, BlogEntry.author, BlogEntry.title,  User.id, User.name FROM BlogEntry
  LEFT OUTER JOIN User ON
    BlogEntry.author = User.id
    -- ...

fully-qualified nameで参照しているので、仮にBlogEntryとUserに共通するフィールド(たとえばid)があったとしても問題なくクエリを発行できます。

ところが、このやり方だとBlogEntryがさらに別のUserをdirect associationで保持したとたんうまく行かなくなります。たとえば、BlogEntryに User u2 というカラムに追加したとすると、SELECTの再は次のようなクエリが生成されます。 SELECT句のUser.id /* for author */User.id /* for u2 */ を区別する方法がないため、SQLiteに渡すとエラーになります。

SELECT BlogEntry.id, BlogEntry.author, BlogEntry.title, User.id /* for author */, User.name, User.id /* for u2 */, User.name FROM BlogEntry
  LEFT OUTER JOIN User ON
    BlogEntry.author = User.id
  LEFT OUTER JOIN User ON
    BlogEntry.u2 = User.id
    -- ...

Table Name Aliases

この問題を解決するには、それぞれのテーブルに対して関連するテーブル群においてユニークな別名(alias)を付ける必要があります。またそのことにより、カラム名を静的に生成できなくなります。たとえばUserがどのモデルから参照されているかによって、別名が変わる可能性があるからです。

この「関連するテーブル群においてユニークな別名を付ける」というのが Aliases というクラスで、たとえば "BlogEntry.author:User" のようなカラムに至るパスを与えると "u10" (User型のカラムなのでu)といった別名を割り当てます。これは同じパスに対しては常に同じ値を返すので、これを信頼してJOIN句やWHERE句のカラム名を構築していくことになります。

The Schema Classes (v3)

というわけでOrma v3 (rc2) におけるスキーマクラスはこんな感じになりました。

Item_Schema.java

v2での同じクラスは次のようになります。

Item_Schema.java

クラス変数として定義しているものがほとんどなくなり、多くがインスタンス変数になっているのが特徴です。 特に、カラム定義(ColumnDef)がstatic fieldではなくinstance fieldになったので、これらを使っているアプリケーションは変更が必要です。

また getSelectFromTableClause() が単一の文字列ではなくなったので、少し見通しも悪くなりました。しかしこれはやむを得ない変更です。

全体として、動的に構築するSQLが増えた分パフォーマンスが低下している可能性はあります。とはいえ内蔵のベンチマークだとほとんど差が見られないので、許容範囲だと思います。

さて以上のように、v3は内部的に大幅に変更を加えました。既存のテストはすべてパスしているので大丈夫だとは思いますが、不安定になっている可能性もあるので検証してみてください。v3.0.0正規版も10月半ばから下旬くらいにはリリースする予定です。

npm scripts 内では zshの**が使えないのでmochaの場合はそのまま渡すこと

npmのpackage.jsonに以下のように書いていたら、明らかに実行されるファイルが少なくてどうしたものかと思っていました。

"scripts": {
  "test": "mocha --opts spec/client/support/default.opts spec/client/**/*.spec.js"
}

理由は詳細には調べてませんが、おそらくこのスクリプトがzshではなくbashで実行されるためでしょう。

npm/npm#10481 によると、mochaは "**" をzsh的な意味で展開するのでそのまま渡せばいいということでした。

はたして以下のように書き換えるとちゃんと動きました。

"scripts": {
  "test": "mocha --opts spec/client/support/default.opts 'spec/client/**/*.spec.js'"
}

Orma v3.0.0-rc1 for Android Studio v2.2 / Android Gradle Plugin v2.2

Orma v2.x は Android Gradle Plugin v2.2 と同時に使えないという問題がありまして、このたびこれを修正した v3.0.0-rc1 を出しました。RCとはいうものの、中身はdeprecated methodsを消したくらいであまり変えていないので、安定性は v2.6.0 と大差ないはずです。

github.com

Orma v3.x は逆に Android Gradle Plugin v2.2 より古いものとは同時に使えないのでご注意ください。

また、Orma v3.0.0-rc1 は RxJava v1.2.0 の、晴れて @Beta となった Completable を使っています。したがって、RxJavaのアップグレードも必要です。

v3.0.0 正規版は #189 を修正したら出すつもりでいます。これがわりと内部的に大規模な変更が必要なので、RCを更にいくつかリリースすることになるかもしれません。

#write_code_every_day の試みから1年たってどうだったか

そういえば write_code_every_day 活動をやろうと思い立ったのがちょうど1年くらい前だったなと思いだしたので振り返りです。

この話をしたのは Rebuild: 120: Swiftonomics (gfx) だったかな?

で、GitHubの草の現状です(ログアウトしてpublic contributionsのみ計測):

https://github.com/gfx

f:id:gfx:20160917220559p:plain

はい、5月くらいまではそれなりに継続して、そのあとしばらく空白の多い日々で、最近また少し復活しているという感じでしょうか。

もともとこの活動は日々のアウトプットを増やすのが目的で、GitHubの草は所詮モチベーションを上げるための小道具としての位置づけです。なので、草を継続するためのハック、たとえば日付変更の前後にわけてコミットして草を稼ぐなどはしないと決めていました。こだわりすぎてストレスを感じたり家族との関係が荒れたりするのは本末転倒ですからね。

実際この活動のおかげでOrmaの開発やブラッシュアップがかなり進んだので、やってよかったなと思いますし、これからも継続的にやっていこうという思いを新たにしました。逆に、デメリットは特に感じませんでした。わりとゆるくやっていたからかもしれませんが、このくらいでいいのかなと思っています。

そういえば、GitHubのアップデートで過去の草も見れるようになったので見てみたのですが、学生最後の年の2010年のコントリビューション数が4000超えていて驚きました。学生時代はそれなりに忙しかったと思っていましたが、やはり社会人とくらべると余暇はあったのだなあと。

f:id:gfx:20160917221734p:plain

最近はどういうaltJsが使われているのか

Twitterでアンケートとりました。偏りはあると思いますが、採用判断の参考にどうぞ。

おおむね6割がJavaScript (or Babel)、2割がTypeScript、1割がCoffeeScript、Flowはだいぶ少ない、という感じでしょうか。

TypeScriptが意外と使われてますね、1割くらいかと思っていました。

dts-gen(1) でJavaScripファイルからdtsを生成する

2016-09-12のJS: Polymer 2.0 Preview、Service Worker入門、JSから型定義ファイルの生成 - JSer.infogen-flow-filesdts-gen を知ったのでちょっとだけ使ってみました。

gen-flow-files (as of flow v0.32.0)

Release v0.32.0 · facebook/flow · GitHub

We're starting work on flow gen-flow-files, which consumes Flow code and outputs .flow files containing only the types exported. It's alpha-level and we're still iterating on it, so use at your own peril!

flow(1)のサブコマンドで、flow type annotation付きのJSファイルから型定義ファイルを抽出するものです。処理対象のコードは、すべて型アノテーション付きでなければいけません。

ふつうのJSファイルには使えないので、何に使うのかよくわかりませんでした。npm packageを作る際、pure JSと型定義ファイルに分割する用でしょうか。TypeScriptはそうしているので。

dts-gen (as of v0.4.16)

GitHub - Microsoft/dts-gen: dts-gen creates starter TypeScript definition files for any module or library.

こちらは独立したコマンドで、素のJSから TypeScript definition files (dts) を生成するコマンドです。

いまのところ入力はglobalにインストールされたモジュールかロードされたシンボルのみなので使い勝手がイマイチですが、説明にある通り手書きでdtsを書く際のスターターとして使い、そのままだとanyが多いので型を書き足していくような使い方を想定していそうです。

たとえばnodejs標準ライブラリのchild_processはこんな感じです。

dts-gen -s -m child_process
export class ChildProcess {
    constructor();
    kill(sig: any): any;
    ref(): void;
    spawn(options: any): any;
    unref(): void;
}
export function exec(command: any, ...args: any[]): any;
export function execFile(file: any, ...args: any[]): any;
export function execFileSync(...args: any[]): any;
export function execSync(command: any, ...args: any[]): any;
export function fork(modulePath: any, ...args: any[]): any;
export function spawn(...args: any[]): any;
export function spawnSync(...args: any[]): any;

class ChildProcess のメソッドはそこそこ揃ってますが、プロパティは全滅ですね。 ChildProcess#disconnect() など見当たらないメソッドもあります。

コードを読んでみると、入力を純粋にJSのオブジェクトとして評価してそこから情報を取り出しているだけの非常に単純なものなのでした。将来的にはフロー解析して型推論してくれるとよさそうです。なお、tscの --allowJs との差などは調べてません。

2016年9月現在だとReactアプリのTypeScript採用は時期尚早(※追記あり)

追記(2019/04/16): 2017年半ばにここで触れているプロジェクトはTSに移行しました。今となってはTS+Reactの組み合わせは全く問題がなく、むしろ非常に相性のよい組み合わせであるとすらいえます。

TypeScript化の調査 2016年9月現在(React v15.3.1, TypeScript 2.0-rc)の話です。

いま開発しているウェブアプリのフロントエンドをTypeScript化しようと思ってちょっと調べてみたんですが、今やるのはいくらTypeScript推進派でもちょっと厳しいなと。

  • TypeScriptでimportできるライブラリは、TypeScriptのコード(.ts)またはdts: TypeScript definition files (.d.ts) のみ
  • Reactは素のJS + 一部FlowType なのでdtsの公式提供は期待できない
  • DefinitelyTypedのdtsは手動でメンテナンスされているよう
    • https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/react
    • これが現在 v0.14 ベースで古い
    • v15.3ベースに修正するにしても作業量が多すぎてつらい
    • 仮に今がんばってv15.3ベースにしたとしても将来にわたって最新版に追従し続けるのがつらい
    • つまりこの規模の大型ライブラリのdtsを手作業でメンテナンスし続けるのはつらい

将来的には、 ReactコードのほぼすべてにFlowTypeアノテーションがついてdtsがそこから自動生成されるというようにはできるだろうと思っているのですが、現状そうではないので、繰り返しになりますが現時点での導入はかなり厳しいと思います。

次のように、v15.3のランタイムとv0.14のdtsを組み合わせてなんとなく動かすことはできるんですが、たぶん茨の道だろうと思いますね…:


追記:

--allowJs でとりあえず導入みたいなのはありかもしれないですね。TypeScriptの恩恵を受けられないので素のJSよりどのくらい楽になるかはちょっとやってみないとわかりませんが。

CircleCIの"Auto-cancel redundant builds"を有効にするとPRごとの冗長なCIをキャンセルできる

2016/7/27 にこんなアップデートがあったようです。

Project SettingsAdvanced Settings に設定項目があります。

f:id:gfx:20160904155004p:plain

With the exception of your default branch, we will automatically cancel any queued or running builds on a branch when a newer build is triggered on that same branch. This feature will only apply to builds triggered by pushes to GitHub.

説明にあるとおり、あるブランチの最新のビルドがトリガーされたとき、そのトリガーされたもの以外の実行中ないしキューにあるビルドをキャンセルする機能です。ただし、デフォルトブランチ(=開発ブランチ)ではこの自動キャンセルを行いません。

pull-requestの更新が多くてビルドキューが増えてしまうようであれば有効にしてみてはいかがでしょうか。というかデフォルト有効でもいいくらいですね。