ushumpei’s blog

生活で気になったことを随時調べて書いていきます。

serverless の設定で簡単なロジックを含める

stage とかに応じて設定を変える方法があったのでいじってみます、${opt:stage} とかよりもっと複雑なことしたくなったときに使えると思います: https://www.serverless.com/framework/docs/providers/aws/guide/variables/#exporting-a-function

なんとなく TypeScript でテンプレート作成しました

sls create -t aws-nodejs-typescript -p serverless-demo
import type { AWS } from '@serverless/typescript';

import hello from '@functions/hello';

const serverlessConfiguration: AWS = {
  ...
  custom: {
    hoge: "${file(./hoge.js)}",
    ...
  },
};

module.exports = serverlessConfiguration;

hoge: "${file(./hoge.js)}"hoge.js に定義した関数を呼び出してくれるらしい

パラメータはどんな感じか見たいのでこんな感じの関数を書いた

module.exports = async (p) => {
  console.log(p)

  return {}
}

serverless print したらこんなの出てきた

{
  options: [Object: null prototype] {
    format: null,
    path: null,
    transform: null,
    region: null,
    'aws-profile': null,
    app: null,
    org: null,
    'use-local-credentials': null,
    config: null,
    stage: null,
    param: null,
    help: null,
    version: null,
    verbose: null,
    debug: null
  },
  resolveConfigurationProperty: [AsyncFunction: resolveConfigurationProperty],
  resolveVariable: [AsyncFunction: resolveVariable]
}

hoge: "${file(./hoge.js)}"hoge: {} として解決されていた

感想

javascript の ArrayBuffer

なんとなく javascript の ArrayBuffer 触っている。データをストリームで処理することが自分は好きなのかもしれないって最近思って、テキストは特にすることがないのでバイトデータ、という感じで。

とりあえず jpeg からサイズ情報が取れる程度のパーサーを実装してみた。GPS は取れるかどうか試していない。サーバでもブラウザでも使えるように tsconfig を調整したりやや勉強になった。(TypeScript で書いている時に import ... from './xxx/xxx.js' みたいに書くの新鮮な違和感だった)

github.com

png, zip とか pdf もちょっとずつ処理できるようにしたいなと思ったりしている。これらがなんかプロダクトにつながってくれればいいけど、今のところあまり考えられていない。

サーバとブラウザでも使えるようにした理由として、サービスワーカー上でも使えて、必要に応じてサーバでも処理できるようなものを書きたいと思ったから (fetch イベントを状況に応じて切り替える感じ)。電波の貧弱な国に少しいたのでブラウザでできることはブラウザでやってしまいたいというのが理由の背景となる体験だと思う。ちょっとファイルを加工するくらいの処理ならブラウザでやってしまった方が費用の面でも安くなるだろうし。

他のデータ形式追加していくのもいいけど、プロダクトに繋がるものがモチベーション保てていいなとも思う。RFB protocol とかも楽しいかもしれないけど、ちょっと昔書いて挫折した記憶がある、状態遷移を上手く扱えなかった感じ (A メッセージを受け取ったら B モードに移行して C メッセージを待つ、みたいな部分が雑だった)。

とりあえずプロトコルとかデータ形式を羅列してみて、面白そうで何か作れそうなやつを考えるのがいいかもしれない。

ブログを放置しすぎてしまっているのが気になって久々に書いてみた。

Deno で簡単な重複行削除スクリプトを書いた

ファイルの重複行削除がしたかったのでスクリプトを書こうと思ったのですが、せっかくだし Deno で書いてみることにしました。

https://github.com/ushumpei/scripts/blob/main/remove_duplicate_lines.ts

import { iter } from "https://deno.land/std@0.93.0/io/util.ts";

const N = Deno.env.get("NEWLINE") || "\n";
export type M = {
  b: boolean;
  d: string[];
  h: { [k: string]: boolean };
};

const i = Deno.args[0];
const o = Deno.args[1];

const f: Deno.File = Deno.openSync(i, { read: true });
const m: M = { d: [], h: {}, b: false };
const t: string = await Deno.makeTempFile();

let r = "";
for await (const ck of iter(f)) {
  const ls = (r + new TextDecoder().decode(ck)).split(N);
  r = ls[ls.length - 1];

  const ot = ls
    .slice(0, -1)
    .reduce((_m: M, l: string) => {
      if (!_m.h[l]) {
        _m.h[l] = true;
        _m.d.push(l);
      }
      return _m;
    }, m)
    .d.join(N);
  if (ot.length === 0) continue;

  const of = new TextEncoder().encode(m.b ? N + ot : ot);
  Deno.writeFileSync(t, of, { append: true });

  m.d = [];
  m.b = true;
}
f.close();

Deno.copyFileSync(t, o);

github にあげていて、こんな感じで URL 指定でも実行できます。

$ deno run --allow-env --allow-read --allow-write \
> https://raw.githubusercontent.com/ushumpei/scripts/main/remove_duplicate_lines.ts \
> 入力ファイル名 \
> 出力ファイル名

感想

  • groovy のようにライブラリのインポート含めてスクリプトが一枚のファイルで完結するので、結構楽でいいです。しかも URL 指定でインポートできるのでより手軽。なので書くときにインポートするものをちゃんと精査しないといけないと思います。 --allow-net などオプションで権限が指定できるので、その辺もしっかり吟味していきたいです。(権限つけ忘れた時のエラー文がわかりやすいのも良い感じ)
  • 書いたスクリプトで宣言している変数名は一文字とかばかりなのですが、なんかテンション上がって極力短くしてみました。楽しかった。
  • 環境変数による改行コードの指定は未テストで、いらないんじゃないかなーと思っています。--allow-env も消せるしそうしたい気がしてきた。
  • メモリに一気に乗せないように書いたつもりだけどどうなんだろう。
    • 既出行を格納しているオブジェクトが巨大になって死ぬとかありそう。
    • あと copyFileSync は中身をみていないけどちょろちょろコピーしてくれるのだろうか?
    • あとこれ TransformStream で描き直したい。
  • Denovscode 拡張入れた後上手く設定できてなくて cannot find name Deno とか言われてたけどコマンドパレットから Deno: Initialize Workspace Configuration 実行したらなんか上手くいった。
  • 無限インポートループとかどうなるんだろうか。
  • https://doc.deno.land/builtin/stable 見て書いた。
  • Deno Deploy はまだちゃんと触っていない、リクエスト処理は fetch イベントのハンドラー書いてやるみたいだけど、ローカルだと発火しなくてちょっと手間取った、なんか公開してくださっているライブラリ入れたら動いた。https://deno.land/x/fetch_event_adapter/listen.ts

WordPress で独自の rss フィードを設定する

意外に簡単だったけど忘れそうなのでメモ。 functions.php に以下を記述

<?php

add_action('init', function() {
    add_feed('custom', function() {
        header('Content-Type: application/rss+xml');
        include_once 'custom_feed.php';
    });
});

デフォルト?だと サイトURL?feed=customcustom_feed.php の中身が返ってくるようになる。でも WordPress から「ちゃんとした xml じゃない!」みたいな怒られ方するので、custom_feed.php を作るのがまた一苦労な感じだと思いました。正しい feed 生成方法とかあるのだろうか。

感想

WordPress いじるときは wp-env と言う WordPress ローカル環境を立ち上げてくれる npm ライブラリを使っていてなかなか快適です。思い出したけど Feedly 整理しなければいけない。

Java でストリームの末尾数行を捨てる

末尾の行を捨てるためには一旦ファイルを全部読まなきゃいけないかと思ったけど、捨てたい行数を先読みしておけば良いという感じでした。

import java.io.*;
import java.util.LinkedList;
import java.util.Queue;

// ファイルの末尾数行捨てる BufferedReader
class TailIgnoringBufferedReader extends Reader {
    private final BufferedReader _reader;
    private final int num;

    Queue<String> queue = new LinkedList<>();

    TailIgnoringBufferedReader(Reader in, int num) {
        this._reader = new BufferedReader(in);
        this.num = num;
    }

    public String readLine() throws IOException {
        while (this.queue.size() <= this.num) {
            String line = this._reader.readLine();
            if (line == null) return null;
            this.queue.add(line);
        }
        return this.queue.poll();
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        return this._reader.read();
    }

    @Override
    public void close() throws IOException {
        this._reader.close();
    }
}

( lines とかは未実装。BufferedReader と名付けていいかは微妙なところかも。)

こんな感じで使えます

InputStream data = new ByteArrayInputStream("1\n2\n3\n4\n5\n6\n7\n8\n9".getBytes());
try (TailIgnoringBufferedReader reader = new TailIgnoringBufferedReader(new InputStreamReader(data), 5)) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

先頭も捨てられるようにすれば任意の切り出し方ができるようになる

感想

InputStream 系のコードは書いていて楽しい

read とかでも対応するにはどうするべきなんだろうか?(先読みは同じで、 currentLine みたいなインスタンス変数持ってそこから返していく、空になったら Queue からロードする感じかな)