Closed25

自作TODOアプリの技術をルーレットで決めて作る

uta8auta8a

https://levtech.jp/media/article/column/detail_473/

この記事いいな〜と思ったが、試したい技術がたくさんあったのでルーレットで決めることにした。
試しに次のように技術をジャンル問わず列挙して、ルーレットを回して3つの技術を決めて、それを使ってTODOアプリを作る。

- React
- Solid.js
- Vue
- Nuxt
- Next.js
- Android
- iOS
- Fastly Compute
- AWS lambda
- GCP Cloud Functions
- Cloudflare Workers
- Deno Deploy
- Rust
- Go
- Deno
- Node.js
- Ruby
- Python
- PHP
- Java
- Kotlin
- Swift
- TypeScript
- Dart
- C#
- C++
- D
- F#
- Haskell
- Julia
- Lua
- Perl
- Postgresql
- MySQL
- MariaDB
- SQLite
- MongoDB
- Redis
- Elasticsearch
- Tailwind CSS
- Bootstrap
- Material UI
- Chakra UI
- OCaml
uta8auta8a

1つ目: Deno
2つ目: GCP Cloud Functions
3つ目: Perl

やっていき

uta8auta8a

行き詰まったらルーレットをまた回して要素を追加することにする

uta8auta8a

アーキテクチャ雑に書いた

疑問

  • Cloud FunctionsでPerl使えるのか?
  • Stateをどこに持てばいいんだ?
  • Cloud Functionsで無限に叩かれると困るので認証入れたい

uta8auta8a

Cloud FunctionsでPerl使えるのか?

Cloud Functions 実行環境  |  Cloud Functions Documentation  |  Google Cloud

Node, Python, Go, Java, .NET, Ruby, PHPのランタイムがある。Perlはない。

アプローチが2つ

  • システムの中にPerlがありそうなので、perlのファイルを直にCloud Functionsに持っていてnodeのランタイムで exec(...) を叩く
  • Perlをwasmに変換して、nodeランタイムからロードする
uta8auta8a

Stateをどこに持てばいいんだ?

Google Cloud データベース | Google Cloud
https://cloud.google.com/products/databases?hl=ja

AlloyDB for PostgresSQL, Cloud SQL, Spanner, BigQuery, Bigtable, Firestore, Firebase Realtime Database, Memorystore

いっぱいあるけどFirestoreでいってみよう

uta8auta8a

決定

  • 手元に立てるView: Deno
  • Add等の処理: Cloud Functionsのnodeランタイムでperlコマンドをexecで叩く
  • DB: Firestore
uta8auta8a

GCP久々に使うからプロジェクト新しく切って課金設定して予算とアラート設定した

uta8auta8a

念の為firestoreを消しておいた

uta8auta8a

hello world的なCloud functionsを書いて、opentofuのコードも書いたのでデプロイ

...APIの有効化をしてないので色々怒られる。

  • Cloud Functions API
  • Artifact Registry API
  • Cloud Build API

を有効化した

uta8auta8a

以下のようなコードでPerlを実行した時のstdoutの値を返せた。めちゃくちゃ素朴にPerlを実行してる
同じディレクトリにget.plを含めてzipに固めている

const { exec } = require('node:child_process');

exports.execute = async (req, res) => {
    const out = exec('perl get.pl', (err, stdout, stderr) => {
        if (err) {
            console.error(err);
            return res.status(500
            ).send(err);
        }
        console.log(stdout);
        res.status(200).send(stdout);
    });
    if (!out.killed) {
        return;
    }
    res.status(200).send('hello world.');
}

uta8auta8a

firestoreにperlから読み書きするのは無理だ...

perl要素が薄くなるけど、入力値をperlに渡してデータ整形したらnodeに戻してfirestoreに突っ込む形にしよう。

uta8auta8a

方針

  • GoでCloud Functionsを書く
  • Perlを使える場所を探す
  • Goからexecでperlを呼ぶ
uta8auta8a

どうせならデータ整形ではなくPerlの正規表現を使ってValidationを書いたら面白いかもということで、ValidationをPerlに任せる

uta8auta8a

Perlの正規表現が難しい。

正規表現で日本語と半角英数字の間にスペースを挿入する - make world
https://littlebuddha.hatenadiary.org/entry/20090101/1230781134

こちらの記事を参考にしてtextlintのサブセットみたいなことをしてみようと思ったのだけど、日本語にマッチして数字にマッチしないみたいな正規表現を書くことができない...

uta8auta8a

ユニコードとPerlの情報が古いものが多すぎて何も分からん...
諦めてnumberとnon-numberの間にspaceを挟むやつにした

uta8auta8a

Perlにシェル経由で引数渡すの脆弱すぎて笑う
今回はCloud Functions側に認証をかけているので目を瞑って実装

uta8auta8a

バックエンドの実装が終わった。

ここまででやったことまとめ

  • OpenTofuを用いてCloud Functionsをgoランタイムで立ち上げた code
    • 手動でやっているところ: Cloud RunのExecution時の権限にFirestoreの権限をアタッチ
    • (正直Google Cloudの権限の用語周りまだ分かってないので勉強したい)
  • Cloud Functionsの中身を書いた code
    • Goでメイン処理を書いた。get/post/put/deleteに対応した。
    • Perlで数字と非数字の間に半角スペースを入れるようにした。これはShell escapeして渡しているが多分変なの渡すと壊れる。Cloud Functions側で認証をかけているので目を瞑った。
    • Cloud Buildでgoのビルドをする際にPerlのスクリプトが消えるので困っていたが、Go embedを使ってビルド成果物にPerlのファイルを埋め込んで、関数実行時に毎回ファイルをwriteして実行することにした。ここは非効率だけど目を瞑っている。
uta8auta8a

達成できた

  • Cloud Functions
  • Perl
  • Todoアプリのバックエンド

これからやる

  • DenoでCLI軽く書く
  • バックエンドであるCloud Functionsとの接続
uta8auta8a

TUIチャレンジしてみようかと思ったけど、もう3日もかけていて消耗してきてたので妥協することに。

$ deno run --allow-all main.ts -- post "宿題やる"

$ deno run --allow-all main.ts -- put 2900821b-ac99-457f-8afc-a7491dd34356 true "ご飯食べる"

$ deno run --allow-all main.ts -- done be8430a4-f628-4113-a047-fc3cb2e36db1

$ deno run --allow-all main.ts -- del c83a3ea4-030b-4a7f-9a49-334b2afbc033

$ deno run --allow-all main.ts -- show
⬜ コード書く (id: 112a16a4-afe1-4b21-8cf7-6bac54187a91)
✅ ご飯食べる (id: 2900821b-ac99-457f-8afc-a7491dd34356)
✅ 宿題やる (id: be8430a4-f628-4113-a047-fc3cb2e36db1)

こんなノリのCLIをさっくり書いて終わりとした。

$ deno run --allow-all main.ts -- post "卵を3つ買ってくる"
$ deno run --allow-all main.ts -- show                    
⬜ コード書く (id: 112a16a4-afe1-4b21-8cf7-6bac54187a91)
✅ ご飯食べる (id: 2900821b-ac99-457f-8afc-a7491dd34356)
✅ 宿題やる (id: be8430a4-f628-4113-a047-fc3cb2e36db1)
⬜ 卵を 3 つ買ってくる (id: ff2de2f0-7a5f-48c0-bb45-d44d2d611ae0)

ちゃんとPerlで数字と非数字の間にスペース挟むやつも動いている。

uta8auta8a

学んだ

  • 学習の叩き台としてのTodoアプリは良い。
    • 結構何を作るかを考えがち(新規性のあるもの・課題解決をするもの・長期に渡り使われるもの...)だけど、開発コストをかけて"捨てる"ことが大事なのだろうと理解した。どうしても時間をかけてコストをかけて作って長く使おうという思考に個人的にハマってしまいがちだったので、お題をTodoアプリに固定して思考コストをゼロにするのはいいなと思った。
  • 手を動かさないと意外と知識は古くなっている。
    • Cloud Functions、gen2は裏側Cloud Runになっているの知らなかった。
    • Perl、思ったより情報源の探し方から分からずつらかった
    • 手を動かして初めて「Cloud Functionsじゃなくて今はCloud Runを使うべきっぽいな?」とかわかる
  • Firestoreのローカルエミュレータえらい
  • 微妙なことをするのは良くない
    • 元記事でmattnさんは興味をメモしてTODOアプリで試すみたいなことを書いていて、こうあるべきという感じがした。今回でいうとルーレットで決めたのでPerlをCloud Functionsで使うところとか非本質で詰まっていた。
  • Denoに慣れているんだなと感じた
    • DenoでCLI書くパートは一瞬で終わったのでやっぱ慣れって強いなと思った。他の言語にも慣れていきたい。
  • 改善フェーズに入るまでを課題に感じているとTodoアプリ作成は良さそう
    • スクラッチで書くのが苦手で、考えているうちに時間が経っちゃうタイプだったのでやばいコードであってもとりあえず動くものを作って、そこから改善していけばいいというフェーズに持っていくのはいいなと思った
      • テストもCI/CDもないし、コードも褒められたものではないけど、とにかく手を動かすという点においては達成できた
このスクラップは2024/07/15にクローズされました