Life is Really Short, Have Your Life!!

ござ先輩の主に技術的なメモ

Pythonで小数点の計算をする時、strでキャストすべし...

まじかよ...

dev.classmethod.jp

decimalモジュールに記載がある通り、float型ではなくstr型にすることで回避できます。 ですので、実装時にはstr型にキャストしたほうが良いと思います。

>>> from decimal import Decimal, ROUND_HALF_UP
>>> (Decimal(1750) * Decimal(0.43)).quantize(Decimal("0"), rounding=ROUND_HALF_UP)
Decimal(752)

四捨五入出来てない・・・

>>> from decimal import Decimal, ROUND_HALF_UP
>>> (Decimal(1750) * Decimal(str(0.43))).quantize(Decimal("0"), rounding=ROUND_HALF_UP)
Decimal(753)

YES YES YES!

Dartも同じようなことがあった気がする。Python以外にもfloat to Decimalじゃなくて、str to Decimalにしろってやつ。752.4999999999999999 とかになっちゃうんだよね、前者だと。

Jotai めっちゃ良いじゃん...

nulab.com

  • WeakMapを使っておりコンポーネントツリーがdisposeされるタイミングでGCが走る
  • Mockを作るのが簡単
  • 非同期処理に対応している
  • atomの中でatomを参照でき、リスナーが作れる

WeakMapの話はこの辺に詳しい。フロントエンドあるあるだね。ARCを採用しているiOSのweak/unownedの脈略を感じる。

qiita.com

なんで RecoilはWeakMapを利用しなかったんだろう。そこは気になる。

WordPressの検索機能のカスタマイズ

post_typeを追加したいという要件だった。これで瞬殺。

<?php
function custom_search_query($query) {
    if (!is_admin() && $query->is_search) {
        $query->set('s', $query->get('s'));
        $query->set('post_type',
 ["post", "page", "YOUR_POST_TYPE"]);
    }
}
add_filter('pre_get_posts', 'custom_search_query');

WP_Queryで指定できるものは何でも指定できるようなので、meta_dataやカスタムフィールドなんかも全然行けそー

FlutterのFirebase MessaingにおけるonTokenRefreshがよくわからない

このAPIすげー微妙な気がする。使い所がわからない。毎回 getToken でデバイストークンをぶん投げてええんちゃうって思う。

端末が変わる、アプリの再インストールでは自動的に新しいデバイストークンが採択されるから、onTokenRefreshが呼び出されることがない。このメソッド Stream を購読するものだから、Firebase側からのPUSHがないと動かない。

考えられるユースケースは、デバイストークンをアプリの操作で削除した場合。deleteToken したら Stream で新しいトークンが渡されて、それをサービスに投げるまでがワンセットみたいな仕様以外に、使い所が全然ないと思う。

起動時にgetTokenした値をバックエンドにぶん投げて、ユーザーと紐づけているトークンリストに該当していないならappendするべき。で、デバイストークンでPUSH通知を送ると失敗したトークンのリストがもらえるのでDELETEして、生きてるトークンだけ保持すれば良い。

これでいいのだ。

FlaskのBluePrintで404を指定することは出来ない

Flaskの仕様で、404のエラーハンドラーを blueprint 毎に定義することが出来ず、ちょっとハマった。

以下の公式にあるように、Flaskのアプリケーションそのものが、404になるURLがリクエストされた時にどのblueprintのエラーハンドラーを呼び出していいかわからんって書いてある。つまり、404と405はグローバルに定義しろってことらしい。

This is because the blueprint does not “own” a certain URL space, so the application instance has no way of knowing which blueprint error handler it should run if given an invalid URL.

Modular Applications with Blueprints — Flask Documentation (2.3.x)

@app.errorhandler(404)
@app.errorhandler(405)
def _handle_api_error(ex):
    if request.path.startswith('/api/'):
        return jsonify(error=str(ex)), ex.code
    else:
        return ex

初めてFlask触ったのが、2013年とかで、当時のバージョンは0.10だった。今は2.3か。それでも全然コアのアーキテクチャは変わらない。Flaskに学ぶことは多かった。

Flutterだけやってるのが怖くなってきた話

2年半ぐらいFlutterをやっているし、今後もやっていくのですけれど、初学の頃に比べると刺激がなくなってきた。 「やらなければできない」ことが減っていることが怖い。

UIの実装ができれば、悩みどころは状態管理・Widgetの初期化と後処理・デザインシステムの3つ。RiverpodとHooksの使い方が固まってきたので、悩むこともなくなった。デザインシステムはThemeExtensionを使うのが簡単で良さそうだ。

Flutter SDKの開発者体験のスタンダードなレベルが高すぎるので、Dartが3になってFlutterも4や5になるんでしょうけど、革新的なコードベースの変化というか、今までの書き味がガラッと変わるような変化が想像できない。1.0の頃とWidgetの設計は全然変わってない。更に簡便な書き味が扱いやすいAPIWidgetがリリースされる可能性はあるけど、Vue2 -> 3ほどの変化はないと思う。

コードの書き方についても、Lintで潰せる部分を除けば、Widgetの分割と初期化方法ぐらいじゃないですかね。RiverpodとHooks使っていたら、必然的にパターンが決まる。Riverpod前提だと、Hooksを使うケースってuseEffect / useStateぐらい。9割がuseEffectかな。ローカルなデータとグローバルなデータを共存して、Widgetをまたいでデータが管理できるRiverpodに抱かれるのが楽。

Widget単体のテストを通せるようにクラスを分けて(ヘルパーメソッドをなるべく使わない)、RiverpodでデータとUIが分離できていれば、捨てやすくなる。捨てやすいコードがきれいなコード。自分の書いたコードを捨てて再構築する過程を見て、ますますそう感じた。

Flutterを第一言語にするのは今年まで、来年以降はTypeScriptを第一言語にして、React/Vue3 を使ったフロントエンドの経験値を上げていくぞ。

ビジネスロジックのAPIをオーケストレーションするDSLをどう作るのか

早くこれになりたい。本当にそう思う。

ビジネスロジックAPI群とそれを駆動するDSLを駆使したプラグインの塊というのは、こういう感じのYAMLが作られて、DSLで呼び出し順やパラメーターを指定すると、ビジネスロジック自体が生成され、あとはそれを適宜順番に流すみたいなやつをイメージしている。

qiita.com

func: print
args:
    # argsは配列として記述
    - Hello, World!
    - Goodbye!
import yaml

# DSLから呼び出し可能な関数を列挙
func_dict = {'print': print}

with open('hello1.yml') as yaml_file:
    dsl = yaml.load(yaml_file)
    if 'func' in dsl and 'args' in dsl:
        func_name = dsl['func']
        if func_name in func_dict:
            func = func_dict[func_name]
            func(*dsl['args'])

見積を登録するという処理があったとすると、単純なDBへのUPSERT(明細型UIの場合、変更時に変更前の明細行が消えることがあるので、DELETE→INSERT)だけが関数として定義されているのがサーバーサイドの基本。ここで内部で色んなテーブルのUPDATEが走ってしまうとキツイので、イミュータブルなデータモデルにDB設計を可能な限り行う。状態の変更はテーブルのINSERTとDELETEで賄う。DB書き込みは単純なのが一番だ。在庫だけはどうしようもない。引当と出荷の変更が入ったら変えざるを得ない。

データを登録してから、色んな要望が出てくることは予想される。Slackに飛ばしたい、ワークフローを回したい、PDFを出力したい、送料を算出したい、Cubic Meterを元に総重量を出したい、任意のExcelで出力して委託倉庫にデータ投げたい、KintoneのようなPaaSのAPIを叩きたい等。これら一つ一つが 関数として定義されていて、多分それがビジネスロジックAPI群のこと。モノの輸出入をする場合、運賃のベースレートとなるCBMが必要になることが多い。タンカーで運ぶ賃料が変わるので。

そのAPIを叩くタスクランナーとしてのDSLがあって、beforeUpsert afterUpSert みたいなタスクを定義して実行できると強そう。このタスクランナーを作るAPIが別途あって、それがFirestoreあたりにしれっと保存されている感じ。顧客サイドは管理画面でチェックをポチポチ、行を追加するだけでこの辺がカスタマイズできるとベストだな。

version: '3'

tasks:
  beforeUpsert:
    calc_shipping_charge:
      - {{.ZIPCODE}}
      - {{.WEIGHT}}
  afterUpsert:
    webhook:
      - {{.WEBHOOK_URL}}

全テナントに共通でもつデータと、顧客属性に応じてカスタマイズして持つデータがある。設計としては以下のようになる。共通属性と可変属性、可変属性は属性のキーとタイプ、中身は属性情報に持つ。WordPressでよく見た構成だな。弱点としては、属性情報を保存するテーブルのカラムは文字列にしかできない点かな。1:1のJOIN。

マルチテナント・アーキテクチャ - @IT https://atmarkit.itmedia.co.jp/fdotnet/bookpreview/azureoverview_0301/azureoverview_0301_04.gif

TypeScriptであれば、必須情報と属性情報を型演算で和集合してそいつをPrismaに食わせることができるとコードはスッキリするが。

まぁ初回としてはこんなもんかな。1年かかるなこれ作り上げるのに、今の仕事を抱えながらだと...