Takuji->find;

株式会社はてなでアプリケーションエンジニアやってます、技術的な記事を書いているつもり

一気に書いた新規ファイルを一部git addしたい時にはgit add -Nが便利

新機能作ったりする時、コード書くのに集中しすぎるとコミットする前にめっちゃ色々混ざったファイルができてしまったりして、最悪な感じになることが多い。

自分は割と細かい単位でコミットするようにしている(あとで戻せるように)ので、こうなってしまった時今まではチマチマと手動で消してaddして、commitして、undoしてみたいなことをやっていたが、ミスったりすると数時間分の作業が消える。

そういう時にはgit add -N (file path) すると、ファイルの存在だけindexに乗せることができるので、あとはgit add -pでパッチモードにしてがんばる。

そもそもちゃんと細かくコミットしろよって話でもある。

エンジニア立ち居振舞い : 常に問題意識を持つ

お題「エンジニア立ち居振舞い」

エンジニアに限ったことではなさそう。 サービスとかアプリが運用フェーズに入るとつい思考停止して日々のタスクをこなして生きるだけの機械になりそうになるが、現状が例え良さそうに見えても常に問題はあると考えるようにしている。 そうすることでサービスやアプリの改善に繋がるかもしれないし、業務やチーム、組織の改善につながるきっかけが生まれるのではと思っている。

常に思考し続けている結果、夜にも頭が冴えていることが多くてなかなか寝付けないのが最近の悩み。

MySQLの文字列型

雑なメモ

8月からWebのお仕事やってて、5千年ぶりくらいにまともにMySQL触ることが最近増えてきた。

MySQLのテーブルへのカラム追加のレビューをしていたときにわかったことを書いておく。

.*TEXTな型は TINYTEXT TEXT MEDIUMTEXT LONGTEXT があるが、 最大長は TINYTEXT < TEXT < MEDIUMTEXT < LONGTEXTの順らしい。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 11.1.3 文字列型の概要

MEDIUMなんて言うからてっきり無印と同じかと思ってたら全然そんなことなかった。

他にもつい VARCHAR(255) とか雑に書いてしまうけど、 VARCHAR は最大65535までいけるのね、たぶんCHARの最大長が255だからそれのノリで(学生時代の頃から)書いていたんだと思う。

以外と色々知らずに思考停止気味に使っていたことに気付いた昼下がりでした。

TODOコメントにGitのbranch名を入れたい

めっちゃ雑なメモ。

今いるチームではTODOは"必ずやらなければならないこと"にだけ付けるというルールがあって、だいたいshibayuさんのブログの通りに運用してる。

最近コード中のTODOコメントの書き方を工夫している - $shibayu36->blog;

自分の場合割とGitのbranch名に機能名とかをprefixとしてつけいるので、TODO(feature-phase)的なTODOを延々と量産してはやったら消すし、やってないなら残したままmergeして適切なタイミング(リリース前とか次のbranchとか)で消すようにしている。 残っているTODOを確認するのも TODO(feature をgit grepなりagなりで検索すればよい。

割と量産するので、TODO(branch-name)を毎回打つの面倒だし、コピペするのも行ごとってわけじゃなくてやはり面倒なので、勝手にbranch名取ってきて挿入してくれるneosnippetのスニペットを書いた。

snippet     todo
options     head
    # TODO(`substitute(system('git rev-parse --abbrev-ref HEAD'), '\n\+$', '', '')`) : ${0}

これでtodoを展開すると# TODO(feature-phase) : みたいなのができあがって便利。

git管理下以外では動かないけど、そもそもそういうところで使わないので気にしていない。

Support Library 24.2.0で追加されたDiffUtilを試してみた

こんばんは、最近はAndroid JavaではなくPerlとTypeScriptを書いているid:takuji31です。

この記事は本日開催の関西モバイルアプリ研究会 #17の発表を元に作成しています。

今日はSupport Library 24.2.0でrecyclerview-v7に追加された DiffUtil を試してみたので紹介します。

DiffUtilとは

2つの List の差分を計算するユーティリティー。

List の要素ごとの変化を計算する DiffUtil.Callback を引数に取り、 DiffUtil.DiffResult を受け取る。 デフォルトでは追加と削除と更新のみ受け取れるが、オプション指定することで移動も計算できる。ただし、計算コストが上がる。

DiffUtil.Callback

それぞれの要素の変化を DiffUtil に伝えるCallback。5つのメソッドが用意されていて、4つがabstractになっている。

getNewListSize() / getOldListSize()

名前通り新しい/古い List のサイズを返す。

areItemsTheSame(int, int)

2つのアイテムが同じものであるかどうかを判定する。

例えば2つの List にユーザーのエンティティーが入っていると仮定して、それぞれのユーザーが同一のものかをこのメソッドで判定してやる。

areContentsTheSame(int, int)

areItemsTheSametrue だった時に呼ばれる。

areItemsTheSame はアイテムそのものが同じものであるかどうかの判定だったが、 areContentsTheSame はアイテムの内容が同じかどうかを判定する。

見た目上なんらかの変化があれば false を返す。

getChangePayload(int, int)

areContentsTheSamefalse だった時に呼ばれる。

古いアイテムと新しいアイテムで、どういった変更があったか通知するオブジェクトを生成する。

通知するオブジェクトの型は自由なので、変更通知用のクラスを作ってそのオブジェクトを渡すイメージ。

このメソッドだけabstractになっていないので、実装しなくてもよい。その場合は null が渡される。

DiffUtil.DiffResult

DiffUtil.DiffResult は計算したdiffの結果を保持している。直接結果を読むことはできず、 ListUpdateCallback あるいは RecyclerView.Adapter を経由して取得する。 RecyclerView.Adapter に新しいデーターを渡した後に DiffResult.dispatchUpdatesTo(RecyclerView.Adapter adapter) を呼ぶことで、 RecyclerView の変更通知に使える。

使ってみる

※いつも通りKotlinです

DiffUtil はrecyclerview-v7の24.2.0以降に入っているので、入れておく。

雑にModelを作る

enum class Status {
    INTERESTED, LIKE, LOVE;
}

data class Artist(val name: String, val status: Status) {
    companion object {
        val list: List<Artist> = listOf(
                Artist(name = "小倉唯", status = LOVE),
                Artist(name = "雨宮天", status = LOVE),
                Artist(name = "水瀬いのり", status = LIKE),
                Artist(name = "Trysail", status = LIKE),
                Artist(name = "Minami", status = LIKE),
                Artist(name = "佐倉綾音", status = LOVE),
                Artist(name = "田村ゆかり", status = INTERESTED),
                Artist(name = "ワルキューレ", status = INTERESTED),
                Artist(name = "水樹奈々", status = INTERESTED)
        )
    }
}

DiffUtil.Callback の実装を作る、匿名クラスでも問題ないが、ちゃんとクラス化しておくと使い回せるのでよさそう。

class DiffCallback(val oldList: List<Artist>, val newList: List<Artist>) : DiffUtil.Callback() {
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition].name == newList[newItemPosition].name
    }

    override fun getOldListSize(): Int {
        return oldList.size
    }

    override fun getNewListSize(): Int {
        return newList.size
    }

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition] == newList[newItemPosition]
    }

    override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Pair<Status, Status> {
        return Pair(oldList[oldItemPosition].status, newList[newItemPosition].status)
    }
}

このケースでは name をIDととらえて、 name が同じなら同じアイテムで、かつ他のプロパティー(といっても status だけだが)が一致した場合に同じコンテンツとみなしている。

あとは適当に RecyclerView.Adapter やら Activity やら必要なものを実装してやって、リストの更新をするときに以下のように実行してやる。

    fun updateItems(items : List<Artist>) {
        val oldItems = adapter.items
        val diffResult = DiffUtil.calculateDiff(DiffCallback(oldList = oldItems, newList = items), true)
        adapter.items = items
        diffResult.dispatchUpdatesTo(adapter)
    }

するといい感じにリストの更新がアニメーションされる。

RecylerView 以外と組み合わせる、Change payloadの中身を見て何かしたい、などといった場合は ListUpdateCallback と組み合わせて使うこと。

制限事項

  • 古い List と新しい List の中身を比較するので、 List の要素が変わる、あるいは要素そのものの値が自動更新されるような仕組み(Realmとか)とは一緒に使えなさそう
  • List の要素数は2^26(=67108864)まで

最後に

  • DiffUtil を使うことで RecylerView への詳細な更新通知が楽になる
  • 手で更新通知投げるの地味に面倒だったが、これだと DiffResult.dispatchUpdatesTo(RecyclerView.Adapter adapter) 呼ぶだけでよい
  • パフォーマンスが気になるが、全部同期的に実行されるので、Rxなんかを使ってバックグラウンドで実行してやるとよさそう
  • RecylerView 以外との組み合わせなど凝ったことをやりたい時は ListUpdateCallback を使う
  • Realmなど自動更新されるようなものとの組み合わせは悪そうだが、ViewModelを挟んでやるとよいのではないか

サンプルコードはGithubにあげてます。

github.com

Kansai.kt #1 開催のお知らせ #kansaikt

こんにちは、id:takuji31です。

以前告知していた関西Kotlin勉強会 #3改め、Kansai.kt #1を7/9(土)に開催します!

↓↓↓↓↓↓申し込みはこちら↓↓↓↓↓↓

kansai-kt.connpass.com

トーク枠とLT枠もまだ若干空きがありますので、お気軽にご応募ください!

参加おまちしております。