Go製 Felica ライブラリ pasori に read without encryption コマンドと write without encryption コマンドを足した

Windows だけだが、拙作の Go製 Felica ライブラリ pasoriread without encryptionwrite without encryption コマンドを足した。

参考にした記事/サイト一覧

FeliCa Lite-S ユーザーズマニュアル

FeliCa - おなかすいたWiki!

[PASMO] FeliCa から情報を吸い出してみる - FeliCaの仕様編 [Android][Kotlin] - Qiita

FeliCa Liteの片側認証 - hiro99ma site

使い方

基本的な使い方は cmd/dump を見てもらえばいい。今のところ、FelicaWriteWithoutEncryption に渡す書き込み値は最初の16バイトをユーザーブロック0に書くのみ。 MAC付読み込み/書き込みは今のところサポートしてない。

package main

import (
    "fmt"

    "github.com/bamchoh/pasori"
)

func dump_buffer(buf []byte) string {
    str := ""
    for _, b := range buf {
        str += fmt.Sprintf("%02X", b)
    }
    return str
}

var (
    VID uint16 = 0x054C // SONY
    PID uint16 = 0x06C3 // RC-S380
)

func main() {
    var err error
    fmt.Println("Please touch FeliCa")
    psr, err := pasori.InitPasori()
    if err != nil {
        panic(err)
    }
    defer psr.Release()

    err = psr.FelicaWriteWithoutEncryption([]byte{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1})
    if err != nil {
        panic(err)
    }

    b, err := psr.FelicaReadWithoutEncryption()
    if err != nil {
        panic(err)
    }
    fmt.Println(b)
}

TODOアプリに react-beautiful-dnd を追加する

f:id:bamch0h:20190224125024g:plain
Drag and Drop している様子

TODO アプリに ドラッグ&ドロップでタスクを動かせるようにした。

苦労した点1 Typescript と react-beautiful-dnd の相性

react-beautiful-dnd では公式には Typescript をサポートしていません。型定義にはflowtypeを使用しています。 ただ、@types/react-beautiful-dnd はあるようなので今回はそちらを使用しました。バージョンは10.0.3 しかし、この定義ファイルにも問題があるようで、そのままで使うと webpack のビルドでエラーが発生しました。 なので、node_modules/@types/react-beautiful-dnd/index.d.ts の中の React.ReactElement の部分を React.ReactElement<any> に書き換え とりあえずビルドを通して使うことにしました。

苦労した点2 firebase realtime database との連携

上記の画像のように入れ替えを行うためには、移動させたい要素を移動させたい位置に挿入することで行うわけですが、realtime database には要素のインデックスは無く挿入用のAPIはありませんので、set()かupdate()で行う必要があります。set() は単体の値を設定するときの関数で、update() 関数は複数の値の書き込みに適しています。今回は update() 関数のほうが有用そうでしたので、そちらを使いました。一番初めに思いついた作戦は、移動させる要素をremove()で削除して、移動先以降の要素も削除して再度push() と set() で要素を追加する方法だったのですが、コストや処理の複雑さからやめました。二番目に思いついたのは移動元と移動先の間にある要素すべてに対して、値だけをスワップさせる方法です。

f:id:bamch0h:20190224132438p:plain
作戦2 イメージ

  onDragEnd(payload:any) {
    const { dst, src, todos } = payload
    const database:any = this.getDatabase();
    const startAt = dst.index
    const endAt = src.index+1
    var todoAry:any = []

    // 元の位置から移動先の位置までの部分配列を作成する
    // 自身より手前に挿入するか、後に挿入するかで作成方法を
    // 変えている。具体的には、手前に挿入する場合は要素の
    // 手前から部分配列の要素として挿入し、後ろに挿入する
    // 場合は後ろから挿入する。
    // こうすることで、一番初めに移動させたい要素がくるので
    // 後の挿入処理が一元化できる。
    if(dst.index < src.index) {
      for(var i = src.index;i >= dst.index;i--) {
        todoAry.push({...todos[i]})
      }
    } else {
      for(var i = src.index;i <= dst.index;i++) {
        todoAry.push({...todos[i]})
      }
    }

    // 挿入処理
    // 初めの要素を取り出してから、次の要素を順次手前に移動させる
    // 移動させ終わったら、最後の要素に初めの要素を挿入する
    // id 要素だけは移動させてはいけないので、元の要素のidを使用する
    var tmp = {...todoAry[0]}
    for(var i:any = 1;i < todoAry.length; i++) {
      todoAry[i-1] = {...todoAry[i], id: todoAry[i-1].id}
    }
    todoAry[todoAry.length-1] = {
      ...tmp,
      id: todoAry[todoAry.length-1].id,
    }

    const todoRef = database.ref(`todos/${this.uid}`)

    // 更新がかかった要素を update() する
    var updates:any = {}
    todoAry.forEach((val:any) => {
      updates[`/${val.id}`] = val
    })

    todoRef.update(updates)
  }

成果物

成果物は以下のリポジトリにある。

GitHub - bamchoh/react-study at b8d395774638edbf3132ea976410a90193496cba

react-beautiful-dnd を触ってみた

TODO アプリのTODOをドラッグ&ドロップで移動させたいなぁ。という欲求が高まってきたので、そういうライブラリがないか探してみたら、Atlassianが提供してる react-beautiful-dnd というライブラリがあるようだったので使ってみた。

GitHub - atlassian/react-beautiful-dnd: Beautiful and accessible drag and drop for lists with React

使い方は動画になっていて、ステップバイステップで教えてくれるので良い。

Beautiful and Accessible Drag and Drop with react-beautiful-dnd from @alexandereardon on @eggheadio

今のところ、第5回まで見て、それの通りに作ってみてドラッグアンドドロップでリストが変えられるようになったので

最小構成のコード片が作れたかなと思って記事にしてみた。

動画の途中で コンポーネントの innerRef 属性に対して設定している箇所があったんだけど、うまく動かなくて色々調べてたら、styled-components v4 から innerRef が削除されて、React の ref 属性をそのまま使うようになったとかで、最新のコードでは innerRef 属性の代わりに ref 属性を使って設定する必要があるみたいだった。

詳しくは、ここらへん に書いてあるので読んでみるといいかも。

使い勝手としては、まぁ今のところは最小構成なのでもうちょっとインタラクティブに動いてほしいなぁと思うところはあれど、期待していたような動作をしてくれるので満足している。スマホからでも動かせるみたいだし、調整次第では使えるものになるかなぁと思っている。

あとは、Material-UI と併用できるのか?というところが気になるので、そのあたりを今後調べていきたい。

成果物はここ に上げた。

(2019/02/21 追記)

material-ui と styled-component を共存させることでドラッグアンドドロップが可能なリストを作成することができた。material-ui のみだと ref が参照できなくて react-beautiful-dnd がエラーを出してしまうようだった。

成果物はここ に上げた。

TODOアプリにfavicon を設定する

TODOアプリに favicon を設定する。ただ、Reactだとbody部分の要素しかいじれないので webpack で設定する。 色々試したけど、favicons-webpack-plugin が最終的にやりたいことにマッチしたので、それにした。 まず、プラグインをインストール。

$ npm i --save favicons-webpack-plugin

次に webpack.config.js を編集

const FaviconsWebpackPlugin = require("favicons-webpack-plugin")

module.exports = {
  plugins: [
    new FaviconsWebpackPlugin('./src/favicon.png'),
  ]
};

./src/favicon.png をもとに Android / iPhone / Web 用の favicon が自動生成され index.html に自動でリンクされる。

ただ、このままだと、webpack-dev-server ではちゃんと表示されなかったので、以下の設定を webpack.config.js に追加した。

module.exports = {
  devServer: {
    contentBase: [path.resolve(__dirname, "public/")],
  },
}

以上!!

webpackでビルドしたjsファイル名にコンテンツハッシュを追加する

ローカルで開発をしていると jsファイルがブラウザでキャッシュされていて「あれ?動かないぞ?」という場面が何度かあった。Webブラウザ上だったら強制更新(Chrome だったら Shift + Ctrl + R) で解決できるんだけど、iPhoneとかのモバイルブラウザだとそういうわけにもいかないので対策が必要だった。

Cache Busting

というわけで、Cache Busting を導入する。Rails のアセットパイプラインにも導入されている方式でこれをwebpackにも導入できるはず。と予想していたが、やはりあった。

Cache Busting とは jsファイルのパス名に適当にクエリ文字列をつけ、別のファイル名として認識させることでキャッシュを別に行うという手法だと認識してる。クエリ文字列をビルド毎に別にしておけばキャッシュも別になるから、いつも最新のファイルが読み込まれるし、プロダクションだとクエリ文字さえ固定であれば同じキャッシュが呼ばれるので、みんな幸せ。

Webpack で Cache Busting

公式のドキュメント には 設定ファイル(webpack.config.js) に以下のように記述すれば解決すると記載されているし、実際その通りにしたらできた。

module.exports = {
  // 一部省略
  output: {
    filename: '[name].[contenthash].js'
  }
};

WebpackのCache Busting はクエリ文字列を付加するのではなくて、ファイル名をビルド毎に違う名前にすることでキャッシュされるのを回避してるようだ。この [contenthash] の部分が実際にはハッシュ文字列に置換されてファイル名を構成する。

Todo アプリを firebase にデプロイしたがテストの書き方がわからなかった

ここ最近はブログの交信がさぼり気味だった。というのも、realtime database のテストの仕方がわからずに試行錯誤していたので、成果をあまりだせなかったというのが大きい。

fetch-mock からの脱却

ローカル環境のデータベースへアクセスしていたときは、データベースへのアクセスをREST API経由で行っていたので、クライアント側はfetch関数を使用していたが、realtime database は専用のクライアントAPIによってアクセスするためfetch関数は使えなくなった。それに伴って、fetch-mockを使用してテストを行っていた部分が壊れてしまったので、realtime database 用のモックが必要となった。調べてみると realtime database シミュレーターなるものがあったわけだが、これはデータベースのルールに関してテストするもので、かつ、まだベータ版とのことで使用は控えたほうがよさそうとの結論になった。

結局は jest のモックを使用して firebase.database を偽装することにした。

jest.fn()

jestはjavascript用のテストフレームワークでfecebookが主に開発を行っている模様。fetch-mock を使っているときは sinon というテストフレームワークを使用していたが、Reactを使う上では jest のほうが親和性が高いかな? と思ったので jest に切り替えた。

jestではモックを作成するのに jest.fn() を使う。内部で呼び出された関数に差し替えることで関数の呼び出しを偽装できる。実際には jest.fn().mockImplementation() によって関数を実装することで偽装する。

firebase.database() を偽装する

firebase.database() は sendToApiServer 内部で呼び出しを行っているので、現状のままでは簡単にモック関数と差し替えることはできない。なので、sendToApiServer 関数をやめて、DatabaseBridgeという名前のクラスを作成し、そいつに処理を移した。そして、getDatabase() という関数を作成し、const database = firebase.database() となっている部分を const database = getDatabse() にしたうえで、getDatabase() 関数の中で return firebase.database() として外向けに公開することでテストでも差し替え可能な関数でありつつ、内部処理の影響を最小限にした。

モック関数をネストする

firebase.database() をモック化することには成功したが、そのあと database の関数を使用している処理もモック化しないと、その部分でコケてしまう。具体的には database.ref() や そのあとの ref.on() や ref.once() でコケる。それらを mockImplementation() を組み合わせて作成する。

const bridge = new DatabaseBridge()
const mock = jest.spyOn(bridge, 'getDatabase'); // spyOn() は 第一引数で渡したインスタンスの中の第二引数のメソッドを監視する

const mockDbRemove = jest.fn() // ref.remove() メソッドのモック
const mockDbRef = jest.fn() // database.ref() メソッドのモック
mockDbRef.mockImplementation(() => { // database.ref() メソッドを偽装する
  return { remove: mockDbRemove } } // ネストさせることで内部の関数もモック化する
})
mock.mockImplementation(() => { // getDatabase() メソッドを偽装する
  return { ref: mockDbRef } // database.ref() のモック関数を埋め込み
})

例えば、実装が以下のようになっている場合、getDatabase() で取得したデータベースオブジェクトはモックにさし変わっているので、その次の database.ref() はモックオブジェクトが持つメソッドが呼ばれることになる。同様にその次の ref.remove() もモックオブジェクトのメソッドが呼ばれる。

class DatabaseBridge {
  // 実際は firebase.database が使用されるが、テストでは mock変数に差し替えられる
  getDatabase() {
    return firebase.database();
  }

  remove() {
    const database:any = getDatabaase() // ここでモック(mock)が返される
    const ref = database.ref(`...`); // ネストしたモック(mockDbRef)が実行される
    ref.remove() // mockDbRemove モックが実行される
  }
}

モックオブジェクトのコール情報を確認する

偽装しただけではテストできないので、その時に渡された引数をチェックするテストを書く必要がある。

  • expect(mock.mock.calls.length).toBe(???)

何回モックがコールされたかをチェックする。

  • expect(mock.mock.calls[0][0]).toEqual(???)

コールされたときの引数の値が期待通りかをチェックする。

今のところ、この二つでまかなえているので、それ以外は今後覚えていく。

jest.fn().mockImplementationOnce()

モック関数のコールに対して一度だけ違う値を返したいみたいな場合は、mockImplementationOnce() で実装できる。

const mockDbOnce = jest
  .fn()
  .mockImplementationOnce(() => {
    return false
  })
  .mockImplementationOnce(() => {
    return true
  })

このモックが呼ばれると、一回目はfalseを返すけど、二回目はtrueを返すようになる。

成果物

GitHub - bamchoh/react-study at 7c28892ea0cac3b23af46d18be7968fbdc86b2db

Firebase 入門(4) ~ Todo アプリをデプロイできるのか? (2)

この数日、Firebase Authentication と格闘してた。一応目処が立って Firebase realtime database を使ってのTodoアプリも完成したんだけど、FIrebase Authentication は 「Todo アプリをデプロイできるのか?」というとこの本質ではなかったなぁと思った。ただ、ちゃんとしたアプリを作りたい場合は必須の知識なので無駄ではなかったと信じたい。

typescript との連携

typescript だからと言って特に何もすることはなかった。

realtime database reference

database.ref(アドレス) で Reference オブジェクトが取得できる。このReference オブジェクトを使って値の取得だったり、値の追加だったりをする。

on() メソッド

on() メソッドは値をサブスクライブするのに使用する。逐一値をポーリングすることが不要になるので便利。

value

第一引数に"value" を指定すると、アドレスで指定した場所以下すべてのデータが返ってくる。一部を変更したとしても全部が返ってくる。

database.ref(`todos/${uid}`).on("value", function(snapshot) => {
  // todos/$uid 以下のデータがすべて送られてくる
  // {
  //    "a": {
  //      "id": "a",
  //      "text": "todo 1",
  //      "completed": false
  //    },
  //    "b": {
  //      "id": "b",
  //      "text": "todo 2",
  //      "completed": false
  //    }
  // }
  // 値は snapshot!.val() で取れる
  console.log(snapshot!.val())
})

child_added

child_added は項目の追加があった場合に追加部分だけが送られてくるので、データ量を少なくしたい場合に便利。

ref.on("child_added", function(snapshot) => {
  // todos/$uid/変更のあったデータ 以下のデータがすべて送られてくる
  // "value" の時とフォーマットが少し異なるので注意が必要
  // {
  //   "id": "a",
  //   "text": "todo 1",
  //   "completed": false
  // }
  // 値は snapshot!.val() で取れる
})

child_removed

child_removed は項目の削除があった場合に削除された項目部分だけが送られてくる コードは child_added と変わらないので省略

child_changed

child_changed は項目に更新があった場合に更新があった箇所を含むブロックが送られてくる。例えば、以下のような構造があったとして

{
  "a": {
    "id": "a",
    "text": "todo 1",
    "completed": false
  },
  "b": {
    "id": "b",
    "text": "todo 2",
    "completed": false
  }
}

"completed" を true にするように変更したとしたら、"b" 以下のデータブロックが送られてくる。child_added, child_removed 以外のなにがしかの交信がここで捕捉され通知されるといった感じだろうか。

ref.on("child_changed", function(snapshot) {
  // true になって送られてくる
  // { "id": "b", "text": "todo 2", "completed": true }
  console.log(snapshot!.val())
})

completed のトグル

先ほどの例のように "completed" を true にする。みたいな場合は以下のようにonceと組み合わせてsetを行う。

const database = firebase.database();

const ref = database.ref(`todos/${uid}/${id}/completed`)
ref.once('value').then((snapshot) => {
  ref.set(!snapshot.val())
})

Action と Action Dispatcher

firebase realtime database が on() メソッドによってデータベースの交信をリアルタイムに通知してくれる関係で、それをトリガにアクションを作成し、dispatcher に投げられるようになった。ただ、Viewからのアクションをaction_dispatcherで捕捉しないとdatabaseへアクセスできなくなるので、View → Action Dispatcher の流れと Firebase → Dispatcher の2種類の流れができてしまう。これはもう仕方ないのかな?と割り切っているが、どうにかできるんだろうか。

View → Action Dispatcher の流れは今までと変わらないので省略。

Firebase → Dispatcher の流れは基本的に Firebase の on() メソッドで捕捉したデータをもとにアクションを作成して dispatcher に渡しているだけだ。Reducer が type によって action と state をマージするという流れは変わらない。

  // Firebase Realtime Database 側
  todosref.on('child_added', function(snapshot) {
    if(snapshot!.val() !== null) {
      dispatch({
        type: 'todos/child_added',
        payload: snapshot!.val()
      })
    }
  }, function(error:any) {
    console.log(error)
  })
  // reducer 側
  case 'todos/child_added':
    if(action.payload === undefined || action.payload === null) {
      return state
    }
    return [
      ...state,
      action.payload
    ]

まとめ

一応、Firebase realtime database と連携させることで Todo アプリをデプロイすることができたが、色々コード的に汚い部分があるので、今後、思いついたタイミングで編集していこうと思う。

成果物

Merge pull request #19 from bamchoh/firebase · bamchoh/react-study@69cbf7b · GitHub