【シーシャ】Purjfam PODにアップグレードパーツを取り付ける

同居人がかっこいいシーシャ台を購入した。PurjfamのPODという機種らしい。

PurjFam | Stemless Portable POD

かっこいい。

このシーシャ台、どうも吸い心地を向上させたいとかで、アップグレードパーツが同梱されていたので、付け替えてみることにする。

同梱のアップグレードパーツ

分解

分解するとこんな感じ。かっこいい。

中央のパーツのパッキンで仕切られた部分に煙が流れる仕組みになっているようだ。空気の流れをたどってみると、それぞれ赤い部分がステム、青い部分が吸い出し、緑色の部分が吹き返しに対応するっぽい。

交換対象となる底面のパーツを外す。ネジは付属の星型ドライバーで外せる。ドライバーを紛失しても、精密ドライバーセット的なのを買えばなんとかなりそう。

パーツを並べてみた。ロゴがある方がアップグレード版。結構違いがわかる。

組み立て

組み立てていく。内部には逆止弁(金属の一枚板)が置かれているので、これが落ちないように慎重に。

こんな感じに両端のパーツを置いて…

真ん中のパーツをぐっと押し込むと、ピッタリはまった。あとはひっくり返してネジ止めすればOK。

できた

少し隙間がある…気もするが、今のところ実用上問題はない。

実際に吸ってみた。

自分はそんなにシーシャを吸わないが、このパーツを付けると少し吸いが重くなるというか、ボコボコしてくれるのはわかる。 羽のない扇風機だったのが、羽のある静かな扇風機になった、みたいな感じだと思う。

作業ログを書く最適なタイミングを発見した #working_out_loud

弊社には日報を書く文化がある。日々の業務を振り返り、何をやったかを逐一記録していくわけだが、なんか「効いている感」がなかった。

ついつい後回しにして、結局「3行しか書けなかった…」という日もあるし、細かく書きすぎて「メモ帳と勘違いしてるのかな俺は」と反省する日もあった。試行錯誤を繰り返す中で、どうすれば効果的に記録できるか、模索していたところ…

「切り替えログ」で、集中を妨げずに記録する

最近、あるマイルールを意識するようになってから、この課題が劇的に改善された。それは、「コンテキストスイッチが起こったら、その都度書く」というもの。これを私は「切り替えログ」と呼んでいる。(嘘です。キャッチーな呼び名をGeminiに考えてもらいました)

「記録をつける」という行為は、作業を中断しなければ実行できない。集中が阻害されるのは当たり前のこと。特に自分のような、脳内に両手いっぱいの情報しか留めておけない、落語の「平林」にガチで共感するようなワーキングメモリ四畳半神話大系人間にはなおさら。

だからこそ、集中が途切れて次のタスクへ意識が向かう「切り替えの瞬間」に書く。

例えば、

  • AのタスクからBのタスクへ移る時
  • コーディング作業中に、急ぎの連絡が入った時
  • 定例ミーティングが始まる前

など、「今やっていることから、別のことに意識が切り替わった時」に、その直前までやっていたことをサッと記録する。一つのタスクに集中している間は無理に中断せず、区切りとなるタイミングで記録する、これが「切り替えログ」の肝だ。

「切り替えログ」で得られた良いことリスト

マルチタスクが激減した

「後でまとめて書けばいいや」と思うと、複数のタスクを並行してこなそうとしがち。しかし、都度記録することで自分のマルチタスク具合が可視化でき、「今はこのタスクに集中しよう」という意識が強くなり、結果的にマルチタスクが減った。ハイパースレッディングみたいに、マルチタスクはせず、無駄なくタスクを切り替えることで日々の仕事がやれている(と思う)。

ワーキングメモリに余裕が生まれた

「あれもこれも覚えておかないと」という脳の負担が減り、本来の業務に集中できるようになった。忘れて困るようなことは、日報に書き残しておいて、次に着手した時にロードすればよい、といったマインドでいる。

時間を地味に食っている細かいタスクの存在に気づける

記録を都度残すことで、自分がどれだけ多くの細かい作業に時間を取られているのかが可視化される。日報の行数が長い=一つのことに集中できていない という明確なインジケータが生まれ、働き方を良くしていく道標にできている。

働いている実感、達成感が得られる

特に細かいタスクをこまぎれに進めている時は、午前中にやったタスクが記憶から飛んでいたりして、「あれ?今日何もやってなくね?」的な不安感に襲われがち。そういう時に日報を見返すと「今日はこれとこれをやったんだ」と思えるのは心強い。

「認識のズレ」を減らすヒントにも

自分が「これくらいしかやってない」と思っていても、逐一記録することで、実はたくさんの小さなタスクをこなしていたことに気づける。逆に、「こんなにやったぞ」と思っていても、書き出してみると、非本質的なタスクを5つくらい同時に抱えて死にかけている状態だと気づけたりもする。

これは自己評価を適正化する上でも役立つし、チームメンバーとの 「何をやっているか」という認識のズレ を減らすヒントにもなりそう。

チーム開発において、「期待されている水準ぴったりの行動をする」のは難しいと言われますが、日々の作業ログを丁寧に記録することは、そのギャップを埋める一助になるのではないでしょうか。

自分で言うのはあまりにも恥ずかしいので、Geminiに言わせた。自分的には「Working Out Loud の本来の精神に立ち返った記録ができているかも」と思っている。

「日報はコンテキストスイッチが起きるたびに書く」。ワーキングメモリ弱者の兵法。いかがでしょうか。

wranglerでreact-router v7 + Drizzle ORM + Cloudflare D1の開発環境を整える

react-router v7のCloudflare D1用テンプレートで開発を始めた。ORMはDrizzle。 pnpm run devであっという間にローカル環境のDBが構築できるのは便利なのだが、デプロイ周りはちょっとだけ作業が必要なので、ここにまとめておく。

プロジェクトを作る

ディレクトリ名を適当に決めて、Readmeに示されたコマンドを打つ。

npx create-react-router@latest --template remix-run/react-router-templates/cloudflare-d1

対話型UIが起動するので、良い感じに回答する。

Where should we create your new project? → ./my-application
Initialize a new git repository? → Yes
Install dependencies with npm? → Yes
cd my-application

なんか色々生えているのがわかる。(tree --gitignoreした)

.
├── README.md
├── app
│   ├── app.css
│   ├── entry.server.tsx
│   ├── root.tsx
│   ├── routes
│   │   └── home.tsx
│   ├── routes.ts
│   └── welcome
│       ├── logo-dark.svg
│       ├── logo-light.svg
│       └── welcome.tsx
├── database
│   └── schema.ts
├── drizzle
│   ├── 0000_outstanding_trauma.sql
│   └── meta
│       ├── 0000_snapshot.json
│       └── _journal.json
├── drizzle.config.ts
├── load-context.ts
├── package-lock.json
├── package.json
├── public
│   └── favicon.ico
├── react-router.config.ts
├── tsconfig.cloudflare.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
├── worker-configuration.d.ts
├── workers
│   └── app.ts
└── wrangler.toml

今回気にするのはwrangler.tomldrizzle.tsだけだ。ここさえ設定すれば動くようにできている。

wranglerの設定

Cloudflare Workersのデプロイを担当するCLIがwranglerだ。

まずwranglerにログインする。アカウントは作っておこう。

npx wrangler login

ここでnpm run deployをすると、エラーが出てしまう。どうやら、wrangler.tomlの設定で正しいDBが設定されていないのが原因のようだ。というわけで、対象になるD1インスタンスを作成・設定していく。

npm run deploy
✘ [ERROR] You must use a real database in the database_id configuration. You can find your databases using 'wrangler d1 list', or read how to develop locally with D1 here: https://developers.cloudflare.com/d1/configuration/local-development

DBを作る

以下のコマンドでDBを作成する。これを控えておいて、wrangler.tomlを書き換える。DB名はうまいこと決めよう。

npx wrangler d1 create my-application-db   

すると↓みたいな出力が出てくる。このdatabase_idを控えておく。

[[d1_databases]]
binding = "DB"
database_name = "my-application-db"
database_id = "****-****-****-****"

これをwrangler.tomlに書き込んでおく。これでwrangler側の設定は完了だ。

[[d1_databases]]
binding = "DB"
- database_name = "your-database-name"
+ database_name = "my-application-db"
- database_id = "your-database-id"
+ database_id = "****-****-****-****"
migrations_dir = "drizzle"

マイグレーション

ORMのDrizzleはdatabase/schema.tsでスキーマを管理している。

import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";

export const guestBook = sqliteTable("guestBook", {
  id: integer().primaryKey({ autoIncrement: true }),
  name: text().notNull(),
  email: text().notNull().unique(),
});

まだこのスキーマがDBのテーブル構造に反映されていないので、このままデプロイするとエラーが出てしまう。 マイグレーションしたいが、その前にDrizzleにCloudflareの認証情報を覚えさせてやる必要がある。(愚直にやると↓のエラーが出る)

 Error  Please provide required params for D1 HTTP driver: 
    [x] accountId: undefined
    [✓] databaseId: 'your-database-id'
    [x] token: undefined

3つの認証情報が必要らしい。 database-id

accountIdtokenの入手方法はDrizzleのドキュメントに丁寧に書かれていた。 - accountId: サイドバーの「Workers」→「Workers&Pages」に遷移して、右側の「アカウント」欄からコピー - token: アカウントの下にある「API トークンの管理」→「トークンを作成する」→「カスタムトークン」で作成画面へ。識別しやすい名前をつけ、「権限」を「アカウント」「D1」「編集」に設定。あとは適当でも大丈夫(後から編集できる)。

これらは環境変数として渡すので、.envファイルを作成する。tokenを扱うので、.envを忘れずにgitignoreしよう!

CLOUDFLARE_ACCOUNT_ID=(アカウントID)
CLOUDFLARE_TOKEN=(さっき発行したtoken)

databaseIdwrangler.tomlに書いたのと同じものをdrizzle.config.tsに書き込んでおく。

  dbCredentials: {
-   databaseId: "your-database-id",
+   databaseId: "****-****-****-****",
    accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
    token: process.env.CLOUDFLARE_TOKEN!,
  },

これでOK。あとはmigrationしよう!

npm run db:migrate-production
$ npm run db:migrate-production

> db:migrate-production
> dotenv -- drizzle-kit migrate

No config path provided, using default 'drizzle.config.ts'
Reading config file '/Users/...'

ついでにlocal環境のmigrationもやっておく。

npm run db:migrate

なんか聞かれるのでyを押す。 これでnpm run devからlocal環境が立ち上がるようになった。

デプロイ

最後にアプリケーションをデプロイする。

npm run deploy
Deployed my-worker triggers (0.43 sec)
  https://my-worker.***.workers.dev

はい完了!実際にコマンドラインに表示されたURLを見にいくと、動く形でアプリケーションが動作しているのがわかる。DBへの接続もできる。便利〜。

おわり

めちゃくちゃ簡単にDB + アプリケーションが生えた。 簡単さと安価さとパフォーマンスを全て兼ね備えた良い感じの構成なので、ぜひ触ってみよう。

大きな目標への道のりは、「帰り道」をイメージする

趣味でよく散歩をするのだが、月一くらいで散歩欲が爆発して5キロくらい歩きたくなることがある。 そういうときは、単純に自宅からゴール地点まで歩くのではなくて、まずゴール地点に電車なりで移動してしまって、そこから自宅まで帰る、というふうにすると楽しく歩けることに気づいた。

これで私は三軒茶屋から経堂まで約4キロを歩いて帰ることに成功しました(参加者の声)。

で、この考え方を応用することで趣味や仕事で長期的な目標を達成できたことが何回かあったので、将来の自分のためにその時考えていたことを記録しておく。

目標を超具体的にイメージする

自宅のイメージが曖昧なやつはいない。だから、帰路が家に近づけば近づくほど安心感が高まるし、「ここまできたらもう大丈夫」という高揚感が背中を押してくれる。 目標を達成したらこんな状態になっていて、そしたらこうしよう、みたいな流れが完璧にイメージできていたほうが、達成することへのモチベーションが高まるのは必然だと思う。イメトレ的な側面でもそうだし、達成した人がその後どうなっているか?を調査することもその後押しになるだろう。

達成した後のことを(一旦)考えない

自宅から遠くまで歩いたとして、「知らない街で、そこからどうする?」を考えるのはストレスになる。いっぽう自宅なら「帰ったらシャワー浴びて寝よう」となるから、帰ることに全エネルギーを注ぎ込むことができる。 目標達成でも同じで、目標に向かっているフェーズではひとまずその達成にだけ心血を注ぎたい。それ以降のことは終わってから考えよう!くらいの覚悟で臨んだ方が、むしろ安定感がある気がする。

目標を動かさない。ダメそうなら手段とスコープを動かす

「家まで帰るのしんどいから、家を動かすか…」とはならない。なるわけない!目標を帰り道だと思えば、それを達成しないという選択肢など存在しない。 もしどうしても帰るのがしんどければ、最寄りの駅まで歩いて電車を使ったり、しぶしぶタクシーを使ったりすることはあるだろうが、家に帰るという目標は変わらない。 目標達成もそんな感じで、できるだけ手段とスコープの変更で達成することを諦めないようにしたい。期限を延ばすことと、達成しないことをなんとしても防ぎたい

近づいていることを実感できる仕組みを作る

帰宅散歩(そう呼んでいます)の醍醐味は、歩けば歩くほど土地勘が増してきて、確実に家に近づいていることが実感できるところにある。逆に、ぼんやり歩いていたら全然違う方面に無駄に歩いていた時のメンタルへのダメージはヤバい。 目標達成の文脈で言い換えれば、定期的に自分の現在位置を計測して成長を実感したりする仕組みがモチベーション維持のためにどうしても必要だということ。それがむずかしければ、「これだけ頑張ったから近づいているはず」と言う自己暗示でも究極良いと思う。「自分は無駄な努力をしているかもしれない」という不安が足を竦ませる。

こんな感じか。ちなみに達成した目標というのは「大喜利大会での優勝」と「エンジニアとしての副業で謎解き業界に関わらせてもらう」の2つ。大喜利大会で優勝したい方は試してみるといいのかも。

パン

ふだん行くパン屋の店員さんは、①お会計する→②パンを袋に入れる→③「お手拭きいくつ入れますか?」→④「レシートがここから出てきますので…」 という流れで動いている。

が、今日の昼にミルクフランスを買いに行ったときの店員さんは、①お会計する→②パンを袋に入れる→③お手拭きを1つ袋に入れる→④「おしぼり…あっ」→⑤(爆笑) という流れだった。その言い間違えそんなに面白いか?と思ったのだが、笑い終わったあと「レシートここから出てきますので」と言いなおしていたので謎が解けた。

おそらく、「お手拭きここから出てきますので」と言おうとしたのを急停止したのだろう。あのおばさんの脳内にはセルフレジの機械からにゅっとお手拭きが出てくる絵面が浮かんだことになる。それは確かに面白いわ。人の脳内を覗いたような不思議な気持ちになった。

太陽の列車、埼京線

人生で最も愛した街・経堂を離れ、板橋に引っ越した。同棲が始まる。先月末のことだった。オフィス出社のパートナーの都合だから、こればっかりは仕方ない。

 

リモートワークの昼食事情はいっさいの懸念が杞憂だった。スーパーも近いし、なにより板橋はご飯が安い。落とし穴は別のところにあった。

 

埼京線の方面をめちゃくちゃ間違える。

 

新宿に行きたいのに、間違えて赤羽方面の電車に乗ってしまったことが何度あったか。この勘違いを加速させるのが、板橋は1路線駅で、ホームも1つしかないということ。。方向感覚がゼロの自分にとって、左右対称のホームで正しい向きの電車に乗るのはめちゃくちゃ大変なことだ。

 

しかも越してきたばかりで、両側の景色の見分けがあんまりついてない。いざ電車が来ても、小田急みたいに「新宿行き OR 小田原行き」みたいな世界観でもないから、行き先表示だけ見ても上りか下りかわかんなかったりする。新木場ってどっち?

 

そんな終わっている自分の電車事情のなかで、解決策を思いついた。埼京線はほぼ南北に走っているから、新宿方面は真南に向かう。つまり、「新宿にいきたければ、太陽のある方に走っていく電車に乗ればいい」

 

これ。これに気づいてから、板橋から赤羽方面に間違って乗ってしまうことが全くなくなった。アパホテルのある側がこっちで…とか考えなくても、太陽の方に電車が向かっていくイメージなら不思議と間違わなかった。

 

埼京線、太陽の列車tren del sol。板橋に光あれ。

 

 

【今日の知見】react-simple-typewriterで複数行にまたがる文字送りエフェクトを簡単に作る

急にタイプライター風エフェクト(1文字ずつ表示されるやつ)が作りたくなったので、react-simple-typewriterを導入した。

const MyComponent = () => {
  return (
    <div style={{ margin: "1rem" }}>
      <h3>夏目漱石『草枕』より</h3>
      <Typewriter
        words={[
          "智に働けば角が立つ。情に棹させば流される。意地を通せば窮屈だ。とかくに人の世は住みにくい。",
        ]}
      />
    </div>
  );
};

こういうコードを書くだけで、かんたんにゲーム風?通信風?の表示ができる。

カーソルの表示/非表示や表示速度のコントロールも簡単でめちゃくちゃ助かるのだが、改行付きテキストを入れても半角スペースに置換されて複数行表示ができないという問題があってかなり困っていた。しょうがないから自作するか…と思っていたが、ググったらすぐ解決した。

解決策は簡単で、react-simple-typewriterは親要素からスタイルを受け継ぐ。親要素側で改行コードを半角スペースに置換しないスタイルwhite-space:pre-lineを設定してやればよいのだった。 white-space - CSS: カスケーディングスタイルシート | MDN

const MyComponent = () => {
  return (
    <div style={{ margin: "1rem", }}>
      <h3>夏目漱石『草枕』より</h3>
      <Typewriter
        words={[
          "智に働けば角が立つ。情に棹させば流される。\n意地を通せば窮屈だ。とかくに人の世は住みにくい。",
        ]}
      />
    </div>
  );
};

react-simple-typewriter、RPGっぽい挙動をさせたり、ChatGPTっぽい挙動をさせるのにも向いている。簡単に使えて色々設定できる(すべて表示されたときに特定の関数を実行したりできる)ので、存在を知っておくと困った時の助けになってくれそうだ。