Cycle.jsでやってたらドハマリしまくった話
今の会社は期初が8月なんです。なので今が期末なんですが、色々ありすぎてもう月末になってました。 最近cycle.js を使って趣味プログラムを作ってみているのですが、その中で色々試していたらとてつもなくハマったので、顛末含めて書いておこうかと思います。 ...
今の会社は期初が8月なんです。なので今が期末なんですが、色々ありすぎてもう月末になってました。 最近cycle.js を使って趣味プログラムを作ってみているのですが、その中で色々試していたらとてつもなくハマったので、顛末含めて書いておこうかと思います。 ...
最近は仕事の方が色々とあって、またもや気付いたら6月も後半になっていました。最近時間の流れがマッハすぎて困ります。 今回は、いつもの実験プロジェクトにて、 Solid.jsを触ってみたので、感触とかを書いていこうと思います。 ...
最近は仕事の方が色々とあって、またもや気付いたら6月も後半になっていました。最近時間の流れがマッハすぎて困ります。 今回は、いつもの実験プロジェクトにて、 Solid.jsを触ってみたので、感触とかを書いていこうと思います。 ...
気付いたら10月も終わりです。もう今年も残りは二ヶ月ということで、月日の流れは本当に速い。 さて、先日登場したParcel2.0を個人のプロダクトで試してみたので、それについてライトに書いてみます。 <!–more–> Parcelとは まずparcelとはなにか・・・ということですが、こちらについては公式ページを見てもらうのが一番早いです。あえてまとめるとすると out of the boxで動作するbundler dev server/hot reloadingなどが組み込み済 CSS/XML/JSONなどのbundleも組み込み済 minificationやimage optimizationなども実現 Rustで構築されているため、マルチコア利用かつ高速 という特徴があります。特にRustで構築されているということと、後述するようにtype checkingを省いているため、特にTypeScriptのビルドおよびbundleは特筆すべき速度です。 parcelで動かすようにしてみる https://github.com/derui/simple-planning-poker 犠牲となるのはこのリポジトリになります。元の構成はWebpack5でゼロから構築したものになります。 個人開発では、create-react-appとかNext.jsとかはあんまり使いたくない派閥です https://github.com/derui/simple-planning-poker/tree/parcel で、parcel化したものがこれになります。 parcelは、各種ツール(組み込まれているものに限る)の設定ファイルは自動的に読み込んでくれるので、設定ファイル群はそのまま残っています。 具体的な手順は以下のようになりました。 yarn add -D parcel @parcel/transformer-typescript-tsc tsc postcss.config.jsとかのJavaScriptで記述されていた設定をJSONに移行 TypeScriptと、tsconfig.jsonでpathsを利用していた場合、path設定を書き換える package.jsonのsourceでルートになるファイルを設定する webpackの利用をやめる TypeScriptを利用している場合の注意点 TypeScriptを利用しているとき、importでめっちゃ ../ のような相対パス表記が出るのを防ぐため、 paths に以下のような設定をしている場合があると思います。 { ..., basePath: "./", paths: { "@/*": ["src/ts/*"] } } 実はこの挙動ですが、TypeScriptのオリジナルコンパイラにおける独自拡張であり、Parcelでは動作しません。 parcel2.0のドキュメントではこのあたりの記述が存在しており、基本的には↓のような記述にする必要があります。 { ..., basePath: ".", paths: { "~*": ["./*"] } } こうしないと、bundle時に超大量のエラーが出て涙することになります。 実際JSによる設定レスでいけたのか? 結果としては、若干書き換えは必要でしたが、 JSでの設定レス でビルドできることは確認しました。postcssとかeslintの設定に関しては、デフォルトの挙動で構わないのであれば、本当に設定レスで動かすことができるでしょう。 ...
もう9月も末で、涼しくなってきたなぁと思ったらいきなり暑くなったりと、季節があっちこっちいきますね。これも温暖化の影響でしょうか。 今回も小ネタで、最近やったfirebaseのrealtime databaseでローカルのテストをやる方法をサラッと書いてみます <!–more–> Realtime databaseをローカルで動かす Realtime databaseのテスト手法にはいくつかあると思いますが、基本的にはローカルで動作するemulatorを利用します。 https://firebase.google.com/docs/emulator-suite?authuser=0 β版とはいえ、かなりしっかりできている印象で、少なくともローカルでテストするレベルであれば必要十分です。さすがに実Databaseに比べると、レイテンシーがなさすぎて(そりゃそうだ)、実際にpublishして動かしたときに アレ? ってなる可能性もありますが。 さて、このemulatorですが、firebaseコマンドで初期化するときに選択しているか、configで追加したら、後は以下のコマンドを叩くだけで起動します。 $ npx firebase emulators:start デフォルトでは localhost:4000 で起動し、 localhost:9000 とかでrealtime databaseが起動します。この状態でも、databaseには繋げることができるので、やろうと思えばテストを書けます。 emulators:start の問題 しかし、emulators:startにはいくつか問題があります。 そのなかでも大きな問題は、 backgroundでの起動ができない & テストに同期して落とすとかできない という点です。まぁそりゃそうなんですが。 emulators:exec を使おう そんなときに役立つのが、 emulators:exec というコマンドです。このコマンドは、引数で渡されたコマンドを実行する前後でemulatorのstart/stopをきちんとやってくれる、one-pathなemulatorの起動を提供してくれます。 なので、 $ npx firebase emulators:start npm run test のように書けば、テストが開始する前にemulatorが立ち上がって、テストが終了するとemulatorが終了します。 また、引数に渡したコマンドがwatchとかであれば、watchを C-c とかで落とすまでずっと立ち上がりっぱなしになるので、ずっとテストしてられます。 realtime databaseを使うIntegration test こんな塩梅のボイラープレートを書く必要があります。RulesTestEnvironmentは、 @firebase/rules-unit-testing で提供されている便利ツールです。firebaseと結合したテストを書く場合はこれを使っておくのがよさげかと思います。 let database: any; let testEnv: RulesTestEnvironment; beforeAll(async () => { testEnv = await initializeTestEnvironment({ projectId: "demo-project-1234", database: { host: "localhost", port: 9000, }, }); database = testEnv.authenticatedContext("alice").database(); }); afterAll(async () => { testEnv.cleanup(); }); afterEach(async () => { await testEnv.clearDatabase(); }); 若干の課題 多分私の書いてるソースがどっかおかしいんだと思いますが、実行されるタイミングとか順番によっては、うまくデータが初期化されていなかったりなんだりします。これは原因を探っているところではありますが、基本的にはかなりの速度で動作してくれるので、ある程度の数のテストがあっても問題はなさそうです。 ...
涼しかったのも終わり、相変わらずの厳しい残暑が戻ってきてしまいました。日が出ていないのに36℃とか勘弁して欲しいですほんと。 今回は、ひょんなことからガッと作ったアプリケーションで、Recoil.jsを使ってみたのでその話をしようかと思います。 <!–more–> 作ったもの これです。といってもまだ若干イケていないところがあるので、画面とかはないですが。 https://github.com/derui/simple-planning-poker 複数人で一つのゲームを開催できる 全員がカードを選択したらその平均値を表示できる 表示し終わったら次のゲームを直ぐに開始できる カードの選択、show downなどはリアルタイムで同期されます。 今日時点で9daysなので、大体1週間ちょっと(平日の夜 + 土日の日中)ですね。 なんで作ったん? チームではスクラムっぽいことをやっていて、そのなかで見積もり手法としてプランニングポーカーを利用しています。 で、最初はアナログな手法でやっていたのですが、リモートが長く続くこともあり、 https://planningpokeronline.com/ というサイトを利用するようになっていました。 で、↑のサイトを使っていたときに、チームの中から(自分が言い出したかもしれん)、 これってFirebaseを使えば割と簡単に出来るんじゃない? という話が出ました。 そういえば、Firebaseって使ったことねーなー。 と思ったので、じゃあやってみんべ、と作ってみました。 state管理ライブラリの選定 さて、今回はガッと作ろうと決めていたので、フロントは慣れている React.js + TypeScriptを使うことにしました。react-routerとかも使ってます。が、あんまりまともに使ったことがないのでreact-routerはいつも迷いどころ・・・。 ただ、state管理として Redux を使うかどうか・・・というのはちょっと迷っていました。いくらガッと作ると言っても、趣味プログラミングならばちょっとは冒険してみたいところ。 と調べていたら、 Recoil.js という新しめのstate管理ライブラリを見つけました。 A state management library for React と謳っていることもあり、完全にReact専用です。なんといってもHookを前提にしているので、Reactじゃないとそもそも動作しません。Reduxは別にReactに閉じるものでもないことを考えると、汎用性があるわけではないなーとは思ったんですが、まぁ使ってみるか、とやってみました。 Recoilの基本 基本的なものは、公式サイトを見るのが一番わかりやすいと思うんですが、簡単に紹介します。 Recoilの基本は、 atom と selector の二つの概念です。といっても、 atom = state で、 selector = atomから情報を取得したり加工して情報を返す という区別があります。 const textState = atom({ key: 'textState', // ユニークなID。atom/selector全部を通じてユニークでなければならない default: '', // デフォルトの値 }); const quotedText = selector({ key: 'quotedText', // ユニークなID。atom/selector全部を通じてユニークでなければならない get: ({get}) => { const text = get(textState); return '"' + text '"'; } }); みたいな感じです(例が思いつかなかった)。recoilは、selector/atomの間にデータフローを作成し、atomが更新された場合はそのatomを利用しているselectorを更新する、という形になっているようです。 ...
GWでも変わらずこもってますが、よく考えたら常日頃こもってるので、いつもと変わらんじゃないか、とも思ってきました。 さて、最近Emacsの設定や開発環境を見直したりしていますが、その中で久しく動いていなかったmigemoを動くようにしてみましたので、それについて書きます。 <!–more–> なんで動いていなかったのか そもそもなんで動いていなかったのか、ですが、cmigemo自体は自分でbuildしていたものがあったので、動かすことは出来ました。ただ、 cmigemoが依存しているnkfが、gentooでinstallできない(ので、cmigemoもinstallできない) nkfがpython2.7のみに依存しているのですが、私の環境は脱python2.7しているので、installできなかった。。。 という一点に於いて、cmigemoをこのまま使うべきかどうか、中々判断しづらかった、というのがあります。 cmigemoの代替は・・・ cmigemoでなければどうするか、ということですが、実は選択肢はほとんど無く、originalのmigemoくらいしかありません。 http://0xcc.net/migemo/ ただ、cmigemoはC言語で実装されているので、速度がダンチです。また、私の環境ではRubyが入っていないも同然なので、migemoのためだけに入れるのも・・・と感じていました。そんな時、ふと以前OCamlで実装し直したmigemocamlというのがあるのを思い出しました。 これにEmacs用の実装を入れればいいんじゃないか? という天啓(?)を受けて、実装することにしました。 こぼれ話:なんでOCamlでmigemoを実装していたのか 趣味で作っているOCamlアプリケーションで、migemoを使った検索をしたかったんです。ただ、Windows/Linux両対応する、というときに、C Libraryを使うのは色々とめんどくさいですし、stubの実装が馬鹿になりません。 pure OCamlで作ってしまえば、Windows/Linux両対応部分が大幅に減るので、色々楽じゃん、ということで再実装しました。 cmigemoとほぼ遜色のない速度を出せているので、個人的には満足です。 Emacsの正規表現をだせるようにする migemocamlで出力している正規表現は、PCREを前提とした、最小限の特殊文字だけ利用しています。ただ、Emacsの正規表現は、歴史的な事情から、特殊なエスケープが必要になっています。 https://flex.phys.tohoku.ac.jp/texi/eljman/eljman_218.html 詳しくは上掲のサイトに載っていますが、今回対応した分だと、次のような違いがあります。 (と) は、 \(と\) にしないといけない | は、 \| にしないといけない ただ、OCaml上での実装は、こういった違いをmoduleで表現して差し替えられるようにしたくらいで、あんまりいじってはいません。 https://github.com/derui/migemocaml/pull/1 ocamlformatをかけたのでめっちゃ差分が出てるけど・・・ Emacsに設定する 無事に実装できたので、Emacsで設定してみます。なお、Emacsの設定では全面的に leaf.el + straight.el になっております。 (leaf migemo :straight t :commands migemo-init :custom (migemo-command . "~/.opam/4.09.1/bin/migemocaml") (migemo-options . '("-q" "--emacs")) (migemo-dictionary . "/usr/local/share/migemo/utf-8") (migemo-user-dictionary . nil) (migemo-regex-dictionary . nil) (migemo-coding-system 'utf-8-unix) (migemo-use-pattern-alist . t) (migemo-use-frequent-pattern-alist . t) (migemo-pattern-alist-length . 1024) :config (migemo-init)) migemo-dictionary の設定がcmigemoと異なりますが、それ以外はcmigemoと同じ設定でいけるようになっています。あとは、 avy-migemo とか helm とか、migemoをsupportしているpackageを使えば動作します。 ...
いろいろコロナの影響が出てきましたが、いかがお過ごしでしょうか。会社でも東京勤務は基本的に在宅となりました。 今まで、ReactとかのIntegration testにはenzymeを使っていましたが、Preactに切り替えた際に課題が多発したため、なんとかして解決してみました。 <!–more–> Preact + Enzymeの課題 Enzymeは元々jsdomとセットで利用していたのですが、 React -> Preact に切り替えたとき、仕様の違いによってテストが動かなかったです。 主な違いとしては、 Preactではshallow renderingが出来ない Portalで色々問題がある これはReactでも色々あるみたいなので、Preactに限ったことではないですが React/Preactでeventの扱いが全く異なる といったあたりです。特に既存のtestでshallow renderingを多用しており、大抵は動くのですが、 React.createPortal を使っているところが全滅でした。 testing-library React.createPortal および Preactの createPortal ですが、いずれも libraryの管理範囲外のDOMにrenderingする というcomponentを構成します。 React.jsの場合、EnzymeのAdapter側でかなり色々やって対応しているようですが、Preactの場合、 Componentを完全にrenderingする というそもそもの仕様から、対応が無理っぽいです。ちゃんと見ていませんが、third partyがlibraryの内部実装に依存している、というのはあまりよろしくないように感じます。 また、よく考えると、jsdomとはいえ実DOMにrenderingするということは、もう それはIntegration Testじゃないのか? という意見を見て、 ( ゚д゚)ハッ! となりました。そんな折に見つけたのが、testing-libraryです。 公式によると、testing-libraryは次のようなソリューションを提供するlibraryです。 The Solution The Testing Library family of libraries is a very light-weight solution for testing without all the implementation details. The main utilities it provides involve querying for nodes similarly to how users would find them. In this way, testing-library helps ensure your tests give you confidence in your UI code. ...
超暖冬だったりコロナウイルス騒ぎだったりと、なんと言うか全く落ち着かないこの頃、いかがお過ごしでしょうか。私は投資信託の金額が乱高下してなんとも言えない気持ちになっているのと、リーマンの再来とか言われてビクッとしている今日この頃です。 そういう世間の流れを一旦見ないことにして、最近やっと動作の確認が取れた、ProtocolBuffer + WebSocketによるRPCの方法を書こうかと思います。 <!–more–> さきにまとめ ここを見たら後の文は見なくていいんじゃないか疑惑が。 gRPCを利用しない場合、ブラウザと双方向でやり取りできるのはWebSocketくらい WebSocketではHTTP/2のようなpathは使えない JSON-RPCのような形式を使うとよい command的なもののEnumはデフォルト値をエラーとして使うといい 以降ではこれについての詳細を。 gRPCの利点と課題 ProtocolBufferを利用したRPCというと、gRPCがまっさきに出てきますし、ProtocolBufferを使う方の半分くらい(要出典)はこれが目的でしょう。ただし、それはgRPCを利用できる環境があるから、という前提が当然あります。 ではgRPCを利用できる条件とはどのようなものでしょうか。その大前提として、 HTTP/2 の存在があります。元々HTTP/2のベースであるSPDY自体、Googleが開発していたためでしょう。HTTP/2とHTTP/1.1の違いは次のようなものがあります。 HTTP/2はバイナリベース multiplexy Serverからクライアントに対してpushすることが出来る headerの圧縮が必須 gRPCは、これらのHTTP/2が持つ利点を活かして、高パフォーマンスかつ低遅延なRPCを実現しています。 間違いなく次世代のネットワークはHTTP/2ベースになると思いますが、OCamlではHTTP/2を利用する難易度がかなり高いです。また、ブラウザからJavaScriptを利用してHTTP/2接続を利用することも今は出来ません。 いま個人的に作っているツールでは、serverからのpushを必要としているので、HTTP/2が使えない場合、WebSocketを使うしかありません。しかし、WebSocketではgRPCを使うことは出来ません。 (gRPCは、HTTPのmethodやpathを利用して色々行っているため) WebSocketでRPCを実現しよう 改めて、WebSocketは以下のような特徴を持つprotocolです。 HTTPからのハンドシェイクをもって切り替える 一本のconnectionだけで通信を行う 双方向の通信が可能 完全に非同期 あるメッセージを待つ、というようなことは出来ない 参考:WebSocket Protocol仕様の日本語訳 しかし、JavaScriptでWebSocketを扱うプログラムを書いたことのある方はわかると思いますが、WebSocketは message という塊のやり取りしか出来ません。HTTP/1.1のようなpath/methodというようなものを使うことは出来ません。 RPCを実装する上では、そのメッセージがどのcommandに対するresponseなのか?を判別する必要があります。 ではどうするか?となりますが、ここで参考になるのが JSON-RPC です。 JSON-RPC自体、非常にLightな仕様ですが、大事なのが requestとresponseが完全に非同期である ということを前提としていることです。この特徴から、WebSocket上でも特に問題なく動作します。ということは、 JSON-RPCっぽいのをProtocolBufferで実装すればいいんではないか? という考えが浮かびます。 ProtocolBufferにJSON-RPCっぽいのを実装する では早速protoファイルを作ってみます。 syntax = "proto3"; enum Command { UNKNOWN_COMMAND= 0; FILER_INITIALIZE = 1; FILER_RELOAD_ALL= 2; FILER_MOVE_LOCATION= 3; FILER_UPDATED= 4; FILER_COPY_INTERACTION= 5; FILER_MOVE_INTERACTION= 6; FILER_DELETE_INTERACTION = 7; KEYMAP_ADD_KEY_BINDING= 8; KEYMAP_REMOVE_KEY_BINDING= 9; KEYMAP_GET = 10; KEYMAP_UPDATED = 11; } // common request message Request { string id = 1; Command command = 2; bytes payload = 3; } enum Status { UNKNOWN = 0; SUCCESS = 1; INVALID_REQUEST_PAYLOAD = 2; COMMAND_FAILED = 3; } message Error { int32 status = 1; string error_message = 2; } // common response. Field `id' must same value of the request. message Response { string id = 1; Status status = 2; bytes payload = 3; Error error = 4; } message SomeProcedureRequest { string fooBar = 0; } message SomeProcedureResponse { int32 count = 0; } service SampleService { rpc someProcedure(Request) returns (Response); } ポイントはいくつかありますが、特に大事だと感じたのは次の点です。 ...
閏年の閏日ということなので(?)、記事を書いておきます。特別な日にでも書いておかないとアウトプットがないので・・・。 今回は、最近使い始めて結構いい感じになってきた、hygen.ioについてです。 <!–more–> hygen.ioとは hygen.ioは、公式で以下のように紹介されています。 The scalable code generator that saves you time. 簡単に書くと、MavenとかGradleとかで初期構成を自動生成したり、create-react-appとかで生成したりといった、code generatorの一つです。 特徴としては 速度 と シンプルである ことで、複雑なDSLを覚える必要は特になく、簡単に使い始められます。また、後述する inject という機能のおかげで、自動生成しつつ、その情報を別ファイルに埋め込む、みたいなことが割と簡単です。 どんなprojectで使われてる? ここを見ると大体わかりそうです。JavaScript界隈での有名企業が入っていたりと、それなりに広く使われているようです。 なお、gulpとかnpm scriptとかMakefileでも出来るんちゃう?という気もしますし、実際出来ると思いますが、code generatorとして特化した機能を提供しているhygenを利用する方が、設定のごった煮になる可能性が低いかな・・・という気がします。 boilerplateを自動生成してみる 今個人で作業しているリポジトリでは、Reduxをmoduleという形で利用するとともに、多数のcommandというmoduleを生成する必要があります。ほとんどinterfaceだけは決まっているので、新しいcommandやmoduleを追加する度、同じようなファイルを生成したり、構造に気を使ったり・・・という作業が必要になります。 流石にこれはめんどくさい・・・となってきたので、hygenを利用していろいろ自動生成してみました。 hygen自体の使い方は公式サイトを見てもらったほうが良いと思いますので、リンクだけ貼っておきます。今回作ったgeneratorの構造はこんな感じです。 実際に使っているのはもうちょっと色々追加されています。 --+ _template |-+ module |-- help |-+ init | |-- actions-test.ejs.t | |-- actions.ejs.t | |-- index.ejs.t | |-- inject_reducer.ejs.t | |-- inject_import-module.ejs.t | |-- inject_action-type.ejs.t | |-- reducer-test.ejs.t | |-- reducer.ejs.t | |-- types.ejs.t |-- new-action これを使うと、こんな感じで新しいmoduleを追加したり、追加したmoduleに対して新しいactionを追加したり出来ます。 ...