君はSDRという資産の単位を知っているか

今日知ったことの日記です。

特別引き出し権、Special Drawing Rightsの頭文字を取ってSDR。

www.imf.org

米ドルとかユーロとかいろんな通貨の加重平均を取って定義されている。 2024年12月31日現在、1SDRは204円らしい。まあ大体それくらい。

元ネタは通貨の請求権の価値単位らしい。

応用としてこれがどこに出てくるかと言うと航空事故の補償金額の定義などに出てくる。元ネタはワルソー条約とかモントリオール条約とかを調べるとよい。

国際運送約款(旅客及び手荷物) - JAL国際線

航空事故となると、関係国が複数になることもあるから補償を条約で決めているのだろうけど、ドルとか単一通貨にすると揉めたりするからこうなっているんでしょうね。あるいは金本位制の名残りかもしれない。全く調べていない妄想ですが。

あと何かもう一つくらいにも使われていてこのエントリを書こうとしたけど何か思い出せない。なんだったかな。

compose.yamlはマージができるし、YAMLのtagでその挙動をコントロールできる

このエントリは、はてなエンジニア - Qiita Advent Calendar 2024 - Qiitaの13日目の記事です。昨日は、 id:maiyama4 さんのSwiftUI の Text がなぜか省略されてしまう問題とそのレイアウトプロセスのデバッグ - maiyama4's blogでした。

続きを読む

OpenFeatureオレオレプロバイダーやってみる

OepnFeatureって何?

OpenFeatureはフィーチャーフラグのベンダーロックインを避けるために策定された標準仕様です。 CNCF incubating projectにも認定されています。

openfeature.dev

アプリケーションからフィーチャーフラグを使うインターフェースはOpenFeatureが提供し、実際にどのような値を返すかを決定するプロバイダーという部分は各ベンダーなどが提供するという仕組みになっています。

つまり、もしフィーチャーフラグを提供するサービスを変えたくなってもアプリケーションの対応はほとんど必要なくなるということです。

詳しくは、OpenFeatureのintroductionを参照してください。

Introduction | OpenFeature

先ほどもあげたように、OpenFeatureにはプロバイダーという概念があります。これは実際にフラグを出し分ける際に参照される具体的な実装と考えればよいです。

どのプロバイダーを使うかに制約はなく自由です。SaaSを使いたいならSaaSベンダーが提供しているProviderを利用すればよいですし、もちろん自分で実装することもできます。

一例として上げると、フィーチャーフラグSaaSの一種である、FlagSmithはOpenFeatureに対応したプロバイダーを提供しています。

OpenFeature | Flagsmith Docs

OpenTelemetryと世界観は似ていますね。

今回は理解のために自分でプロバイダーを作って使ってみようと思います。

Nodejsでプロバイダーを作ってみる

オレオレプロバイダーを作ってみましょう。今回はNodejsを対象としたプロバイダーを作ってみます。

ドキュメントを読むとOpenFeatureが提供するNodeSDKの中にあるProviderインターフェースを満たすクラスを実装すればよいことがわかります。

export class MyFeatureProvider implements Provider {
  metadata: ProviderMetadata = { name: 'MyFeatureFlagProvider' };
  async resolveBooleanEvaluation(
    flagKey: string,
    defaultValue: boolean,
    context: EvaluationContext,
    logger: Logger,
  ): Promise<ResolutionDetails<boolean>> {
     // フラグの評価時などにcontextを渡せる。contextには好きな値を入れられる。
    // ユーザーごとに出し分けるなどしたいときはユーザーIDなどを入れる
    const userId = context['userId'];
    const result = // flagKeyやcontextの値を元にDBや外部APIなどから値を取得する
    logger.debug('result is ', result);
    return {
      value: result ?? defaultValue,
    };
  }
// あとは返り値がstringだったりobjectだったり、オプショナルだったりなので省略

こんな感じです。指定されたインターフェースを満たすようにクラスを作るだけなので難しくはないです。

プロバイダーを使う

まずはどのプロバイダーを使うかを設定する必要があります。とはいっても引数として先ほど作成したプロバイダーを渡すだけ。

OpenFeature.setProvider(new MyFeatureProvider());

これだけ。環境変数を読むなど初期化をする段階で一緒に与えるとよいでしょう。

プロバイダーを登録すれば準備は完了です。

では使ってみましょう。

const featureClient = OpenFeature.getClient();
// フラグの評価時にキーと一緒に使いたい値を渡す
const client = featureClient.setContext({ userId: '1' });

await client.getBooleanValue("someBoolKey", true); // => 何かしらの真偽値。第2引数はデフォルト値
await client.getStringValue("someStringKey", 'hoge'); // => 何かしらの文字列

実際に触ってみるとキーを渡せる第一引数はstring型になっていることやcontextの型の制約が緩いことに気がつきます。またcontextの値を一律で付与したり渡せる値を制限したくなります。

上の理由からkeyに使える値を型で制約したりコンテキストに渡せる値に制約を掛けたくなって実際には以下のようなラッパーを作ることになると思います。

export const boolFeature = async (
  key: 'hoge' | 'fuga',
  defaultValue: boolean,
  context: { userId: string },
): Promise<boolean> => {
  // このようにクライアントを得るときにコンテキストを渡すこともできる
  const client = OpenFeature.getClient(context);
  return await client.getBooleanValue(key, defaultValue);
};

// このようにフラグの値を取得できる
const flagHoge = await boolFeature("hoge", false, { userId: '1' });

まとめ

OpenFeatureに触れるべく、自分でプロバイダーを実装してみました。

実装するまではフィーチャーフラグを提供するSaaS向けかと思っていました。

しかしSaaSを使わずフィーチャーフラグを自前で実装するという場面でも、OpenFeatureプロバイダーを自分で実装することにより実現することができるということが分かりました。

椅子壊れた

背もたれとアームレスト、あとはリクライニングがあればよいと思って安物のゲーミングチェア*1を使っていたのだけど壊れた。

予兆はあってリクライニング機構の調子が1ヶ月くらい前からおかしかった。

最初はリクライニングの操作レバーを操作せずとも背もたれに体重を掛けると背もたれが倒れるようになった。 とはいえもたれる程度では背もたれは動かなかったのであまり支障はなかった。

しかしいろいろ壊れはじめ、今度はレバーを触らずとも背もたれを倒した状態から中立状態になるようになっていた。つまりは背もたれがリクライニング機能を失なった。

倒すときはレバーを操作する必要が無くなり、戻すときは勝手に戻るようになった椅子を騙し騙し使っていたが、あるとき背もたれに体重を掛けた瞬間にバキッと音がした。

嫌な予感がして観察すると背もたれと座面とを固定するネジが折れていた。 左右二点で固定されていて右側だけが固定ネジが折れた状態である。しかも厄介なことに背もたれは残った左側のネジだけでも自立している。触れるとすぐに水平状態になってしまうが。

結果として体重を掛けることのできない、機能不全な背もたれは引き続き椅子に付いている。とても邪魔である。

機能しない背もたれがただあるだけの椅子でしばらく生活してみたが、腰に負荷がかかることがわかった。

いままでは体重を掛けられる背もたれが存在することが当たり前であったので、リラックスしたいときなどは当然背もたれに体を付けようとする*2。しかし背もたれは触れるだけで水平となってしまうので背中はそのまま重力に従って落ちてしまう。結果として腰に負荷がかかってしまっていた。

在宅勤務ということもあって、家にある椅子の上でほとんどの時間を過ごしている。腰を壊すのは怖いので急遽椅子を買うこととした。 同時に自分が快適だと思う椅子を探した。会社にはそれなりに椅子があるのでそのなかから良さそうなのを選んだ。

椅子購入

会社でいろいろ座った結果、オカムラのシルフィーが良さそうであることがわかった。

ちょうどお盆休みの期間に入って到着予定が14日後になってしまったが気長に待つとする。 届くまでは会社に行くこととしようかなぁ。

*1:確か2年くらい前に2万円前後で買った

*2:背もたれであったパーツは左側で座面には固定されているので背中は背もたれに触れている。

手元の便利スクリプトをシェルスクリプトからdeno+daxに置き換えた

みなさん、シェルスクリプト書いていますか?

私は手元環境の更新用にbrew upgraderustup updateなどをまとめたシェルスクリプトを書いて定期的に実行していました。

便利にこのスクリプトを叩いていたのですが、linuxmacで実行するコマンドを変えるなどの込みいったロジックを書こうとするとシェルスクリプトでは力不足を感じます。 また存在しないコマンドは実行しないといった機能や文字をカラフルにしたいなどの機能追加を考えていましたが、これらはあまりシェルスクリプトではやりたくないです。

なので今回はdenoとdenoのライブラリであるdaxを用いてシェルスクリプトを置き換えました。

なぜdenoを使うのか

denoはマルチプラットフォームで動作するJavaScript ランタイムです。ネイティブでTypeScriptをサポートしていることやライブラリを使用するのにあらかじめ別途ライブラリをダウンロードすることが不要などの利点があります。

今回置き換える予定のアップデートスクリプトmaclinuxの両方の環境で実行されてほしく、またなるべく事前準備なく実行されてほしいです。

denoはimport $ from "https://deno.land/x/dax/mod.ts"のようにライブラリの参照元をURLを使って指定することができます。また実行時にローカルに存在していなければ取得をしてくれます。ユーザーはどのライブラリがインストールされているかを気にせずにただ実行すればよいだけです。

dax

daxはdeno上で動くシェルラッパーです。JavaScriptのタグ付きテンプレートを用いて実行することができます。また文字列などはシェルエスケープされて渡されるので安心です。

github.com

例えばechoをしたければ下のように$を付けたテンプレートリテラルを書くことにより実行することができます。またテンプレートリテラルなので変数も持ち込めます。

import $ from "https://deno.land/x/dax/mod.ts";

const sec = 1;
await $`sleep ${sec}`

以下のようにするとdenoとdaxによってhomebrewのアップデートを行うことができます。 便利。

import $ from "https://deno.land/x/dax/mod.ts";

const cmds = [
  ["brew", "update"],
  ["brew", "upgrade"],
];

for (const cmd of cmds) {
  const result = await $`${cmd}`;
}

色を付ける

コマンドの実行結果に色を付けて目立たせたくなったことはありませんか? シェルスクリプトを用いるとANSIコードを使って色付けを有効にしたり無効にしたりと大変です。

シェルスクリプトで行う場合は以下のようにするのが多分良いです。色を無効にしないとずっと色がついて大変。

# ANSIエスケープコードを変数に格納
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'  # No Color

echo "${GREEN}running $cmd_name${NC}"

cliffyというdenoでCLIツールを作成するライブラリの色付け部分を拝借し色を付けました。

TypeScriptで書かれているのでメソッドチェーンされていてもエディタでの補完が効いて簡単。

import $ from "https://deno.land/x/dax/mod.ts";
import * as cliffy from "https://deno.land/x/cliffy/ansi/colors.ts";

await $`echo ${cliffy.colors.green("this message colored green")}`

完成

daxによるシェル呼び出しとcliffyによるログ色付けを組み合わせるとこんな感じ。

import $ from "https://deno.land/x/dax/mod.ts";
import * as cliffy from "https://deno.land/x/cliffy/ansi/colors.ts";

const cmds = [
  ["brew", "upgrade"],
  ["rustup", "update"]
];

for (const cmd of cmds) {
  $.log(cliffy.colors.green(`executing ${cmd}`));
  await $`${cmd}`;
}

このスクリプトdeno runで呼び出せばアップデートコマンドを順次呼び出してくれる。

まとめ

daxを使ってシェルスクリプトを置き換えてみよう!

今回は使わなかったが、daxにはjsonで返ってきた結果をパースして、TypeScriptの世界でオブジェクトとして扱えるようになる機能などもある。 例えばjqを使ってパズルしている処理もArray.prototype.mapで捌くことができる。

皆さんもお手元のスクリプトを置き換えてみてはいかがでしょうか。

Pythonのパッケージ管理ツールRyeのURLのドメインが変更されています

3行まとめ

RyeをダウンロードするスクリプトのURLやドキュメントのURLに使われていたドメインである rye-up.com が使えなくなっている

メンテナらは把握していて代替手段が取られている

新しいドメインは rye.astral.sh となっていて、rye-up.com も復旧次第 rye.astral.sh へとリダイレクトされる

2024年5月29日追記

rye-up.comドメインは無事に復活し、予告通りrye.astral.shへとリダイレクトされるようになりました。 https://github.com/astral-sh/rye/issues/1111#issuecomment-2136490615

追記ここまで

Ryeとは?

Pythonのパッケージの管理や仮想環境の管理などをしてくれるツールです。 このエントリではスコープ外として詳しくは説明しません。 id:nsakki55 さんのこちらの記事がちょっと前の記事ですが詳しいので参考にしてください。

nsakki55.hatenablog.com

本題

Ryeのドキュメントやダウンロードスクリプトを管理していたドメインである、rye-up.comがドメインまわりのトラブルで使用できなくなったようです。

実際にいままでドキュメントやダウンロードスクリプトを提供していたURLにもアクセスできなくなっています。

この問題はURLのドメインを rye-up.com から rye.astral.sh と置き換えることにより解決します。

例えば、旧ダウンロードスクリプトを用いたRyeのセットアップは、以下のコマンドを叩くことにより実現していました。

curl -sSf https://rye-up.com/get | bash

ドメイン移行後は以下のようにURLを変える必要があります。

curl -sSf https://rye.astral.sh/get | bash

従来のドメインが復活したらrye.astral.shは使用できなくなるのではないかという心配は不要そうです。

というのもメンテナが今後はrye.astral.shをメインのドメインとし、rye-up.comへのリクエストをrye.astral.sh へとリダイレクトすることを表明しているからです。

github.com

まとめ

2024年5月26日現在、ドメインをそのままrye.astral.sh に変えることで生活はできます。