flowtypeのmagic typeは直接使うものじゃないよ

実践投入してわかったflowtypeのメリデメ // Speaker Deck で「Magic typeがドキュメントされてない(のに便利)」という説明がありましたが、それはmagic typeを直接使う想定ではないプライベートAPIからドキュメントに載せてないというだけだと思われます。

ドキュメントに書いていないということはそれは内部実装の詳細であって、将来にわたって使える保証はありません。

flowtypeのmagic typeについて - Qiita のためになんだかmagic typeという内部実装の詳細が広まってしまった感がありますが、このエントリでも述べているように、magic typeはそれに対応する構文があります。たとえば、$Either<A, B>A | B というunion typeを表現するための内部型です。他のmagic typeも対応する構文があるはずです。

magic type自体は面白いものだとは思いますし、内部でこういうものがあるということは知っておいていいかもしれませんが、コード上に現れるものではありません。flowtypeのドキュメントをよく読み、flowtypeの構文を使いましょう。

OrmaのRxJava2対応プラン

三行まとめ

  • Orma v3.x で RxJava2 対応をいれます。RxJava 1.xのメソッドはこれまでどおり使えて、後方互換製は保ちます
  • Orma v4.x で RxJava2 対応を主とします。RxJava 1.x対応は続けますが、"asObservable1" とリネームさされ、またそれらメソッドはdeprecatedとなります
  • Orma v5.x で RxJava2 対応のみとし、RxJava 1.x 関係のメソッドは削除します。

詳細

Migration to RxJava 2.0 · Issue #324 · gfx/Android-Orma · GitHub

ほかにも2.x用にブランチおよび成果物を分けるという方法があるのですが、Ormaは1人でメンテしているのでRxJava 1.x系ブランチとRxJava 2.x系用ブランチを分けるのはコスト的に現実的ではありません。よって、ブランチは分けずに、なるべく互換性を維持しつつメジャーバージョンアップのタイミングでRxJava2推しにしていく方法をとることにしました。

DroidKaigi 2017のトーク募集は11/1まで!

droidkaigi.github.io

私もひとつ「Android ORMの選び方」というトークを応募しました。ここ1年ほどOrmaの開発をしているので、その知見を活かしてなるべく公平なORMの比較検討をしたいと思っています。

他に個人的に興味があるのはこんな感じの話題です。誰か話してくれるといいな!

  • Web API clientの話
    • SwaggerとかJSON Schemaの実践の話を聞きたい
  • CIやテストの話
    • 個人的には得意分野ではあるものの、CIのパフォーマンスについてはまだ改善できそうな気がしている
    • Robolectric / JVM unit testingまわりの実践を知りたい
  • iOSとのアセットの共有の話
    • vector assetsが一般的になった今、どうやってアセットの共有をしているのかみたいな話
    • dpiごとにアセット用意するのはもうオワコンなのかな?とか
  • HTTP/2 x Androidの話
    • 画像を沢山動的にロードするケースでは有効だと思われるが…

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割くらいかと思っていました。