Diary

@ssig33

ボケンミホドボが販売するコデマクロシのカヒシオ

https://gyazo.com/3006a791633c020be9a854757c25d9ea/raw

僕は Amazon で安いロボット掃除機を適当に購入するという趣味を持っています。 Amazon には怪しい中華商品が大量に散乱しており、そのなかから良い商品を見抜くために以下のようなことに注意せよとよく言われています

  • 商品名の先頭がブランド名になっていないものを避ける
  • 商品一枚目の写真に余計なエフェクトがのっているものを避ける
    • 上記 2 項目は規約違反なので
  • レビューが不自然な商品を避ける
    • 不自然に 5 が多いとか同じ時期に大量投稿されてるとか、その商品しかレビューしてない人がたくさんいるとか

しかし少なくとも格安ロボット掃除機というカテゴリーにおいては、これらのノウハウはほぼ役に立たないものだと思います。理由としはいくつかあります。まず格安ロボット掃除機ではほぼすべてレビューは捏造されており、商品名や商品写真は規約を守っていません(というよりこれらの規約はほぼ有名無実化しています)。

ではこの分野の商品が全部ゴミなのかというとそうではなくてそこそこ使えるものもたまに転がっています。その「使える」商品をどうやって探すのかというと、それはもう返品の繰り返しです。買って、 2-3 回使ってみて、ダメであれば返品します。体感ですが 1 万円未満のゴミロボットの使える率は 10% 無いぐらいです。

この時、他人の言うことを信用してはいけません。それは以下のような理由からです。

  • 家の中のコンディションが人によって全然違う
    • 築 30 年で家の中に畳や段差がたくさんある家に住んでいる人と、新築でバリアフリーで段差がまったくない家に住んでいる人ではまたく事情が違ってきます。段差を乗り越えたりする性能や落下防止センサーの性能の重要性は人によって違うわけです。
  • ロボット掃除機に求めるものは人によって違います
    • ロボット掃除機だけで掃除を済ませたい人とたまに手動で掃除をしてロボット掃除機はある程度掃除してくれればいいみたいな人がいます

このため、自分の環境にあった商品を見つけるためにはとにかく手当たり次第に買ってみて使えるもの以外返品するというのがもっとも近道となります。幸い Amazon の返品はそんなに大変ではないので、ある程度これを繰り返していると安いけど使えるロボット掃除機を見つけることができるでしょう。

ボケンミホドボが販売するコデマクロシのカヒシオは少なくとも僕の環境ではかなりよく使えました。吸引力は弱いですが毎日走らせてればそんなに問題ありません。

https://s.ssig33.com/file/380a2879a9374219a95e7d0141145fec

じゃあこれをこれを読んでいるあなたにもオススメするかというとオススメしませんが、、、

27 May 2020 Wed 10:10

Recoil

どういうライブラリなのはか他所をみてくれ。簡単にさわってみた。設計の要点としては、 atom の key がグローバルユニークなんで、

  • src/states/session.ts
  • src/states/posts.ts
  • src/states/garbage.ts

みたいな感じで Recoil の states はディレクトリ上で一箇所にまとめるのがよいと思う。各ファイルは ducks のモジュール + α のようなイメージ。具体的には以下のような感じ(型はめんどいのでつけてない)。

garbage.ts

import { useSession } from './session';
import axios from 'axios';
import { atom, useRecoilState } from "recoil";

const garbagesState = atom({
  key: "garbages",
  default: [],
});

export const useGarbages = () => {
  const [garbages, setGarbages] = useRecoilState(garbagesState);
  const { session } = useSession();

  const fetchGarbages = async () => {
    const res = await axios.get(
      "https://example.com/api/v3/gomi_no_yama",
      { headers: { Authorization: `Bearer ${session.accessToken}` } }
    );
    setGarbages(res.data.garbages);
  };

  const updateGarbage = async (garbage) => {
    const res = await axios.post(
      `https://example.com/api/v3/gomi_no_yama/${garbage.id}, 
      garbage,
       { headers: { Authorization: `Bearer ${session.accessToken}` } }
    );
    const newArray = garbages.map(g => g.id === garbage.id ? res.data.garbage : g);
    setGarbages(newArray);     
  };

  return { garbages, fetchGarbages, updateGarbage };
};

状態の管理と API アクセスを redux で無理するよりもはるかに綺麗に React Hooks の形でカプセル化することができる。これは非常に革命的だと思う。

16 May 2020 Sat 11:16

リモートデスクトップ

Web 開発するために Linux につなぐ前提

  • VNC
    • xrdp が簡単にまともに動く時代なので特に選択する理由はないと思う
  • xrdp + なんらかの VPN
    • とても安定している
    • Debian 10 とかだと何も考えずに apt-get install xrdp で動く
    • VPN の世話は面倒
      • AWS Lightsail に Pritunl を入れているが、 AWS からインターネットに出ていくことになった結果ちょいちょいアクセスできないサイトとか出てくる
    • Windows/Mac/Linux のクライアントで修飾キーがちゃんと動く
    • Chrome OS で動かない
    • iPad 用の公式 RDP クライアントで物理キーボードの修飾キーがちゃんと動く
  • Chrome Remote Desktop
    • たまに刺さるがそこそこには安定している
    • セットアップはわりかしめんどう
    • VPN がいらいない、クライアントはブラウザさえあればよい
    • 修飾キーがちゃんと動かない
      • キーマッピングなどを駆使してごまかす必要あり
    • Chrome OS で動くし、 Chrome OS だと修飾キーがだいたいまともに動く
    • iOS クライアントは正直そんなに出来よくない

VPN + RDP を受け入れるのがよい感じはある、もうちょっと考えていきたい

14 May 2020 Thu 21:50

かに食いてえ

14 May 2020 Thu 21:41

とりあえず https://diary.app.ssig33.com/404 を表示できるようにしつつ SSG できるようになったが 最悪のワークアラウンドの集合体という感じだし、 next.js 使うときはこのあたり慎重に URL 設計したほうがいいし、既存のものに next.js 充てるのも考え物であるかもしれない。

ただまあ SSR + CDN でキャッシュ戦略ならなんも困りはしないのでそれはそれでって感じやね。

13 May 2020 Wed 17:40

URL が 404 だと next.js の SSG ちゃんと動かない 問題があって、対処法を探している。

13 May 2020 Wed 15:57

SSG + SSR + Fastly でキャッシュ みたいな構成に移行しようとしているが、なんとも迂遠な感じがすごい

13 May 2020 Wed 15:37

Cypress で Firebase つかったログインをテストする

結論: jest-puppeteer で我慢できるならそっちのほうがいい

https://github.com/cypress-io/cypress/issues/408

を見てくださいで終わってしまう話なのだが、 Cypress はサードパーティーの cookie をクリアすることが出来ない。これは典型的にどういうときに問題になるかというと、 Firebase のような外部の認証プロバイダーを用いてアプリにログインしているとき、テストの実行事にログインセッションがクリアされない。

これが大きな問題であることは明らかで、↑のとおり Cypress の創業者もこれを改善する意思を示しているのだが、実際これを実装する難しさは issue で説明されてる通りで 3 年間放置されている。

https://github.com/cypress-io/cypress/issues/408#issuecomment-555394035

結局このコメントのとおりに回避をしていくしかない。

I found a workaround which is to have a logout call before each test.

である。

じゃあこういう処理を Cypress でどのように実装すべきかというと、

https://docs.cypress.io/guides/core-concepts/conditional-testing.html#Element-existence

が参考になるだろう。テストがどのようなシチュエーションで実行されるかはわからない(CI かもしれないし開発者のデスクトップかもしれない)ので、「ログインしてるかどうかを確認して、ログインしていればログアウトする」という処理を beforeEach 内で実行するとよい。

cy.get('body').then(($body) => {
  $body.find(selector)
});

で DOM の有無を観測できるので、ログインしてるときにだけ発生する DOM があるかを確認して、ログインしているのであれば、ログアウトボタンかなにかをクリックする処理を書けばよい。

Cypress はあんまり難しくないことをしている限りは jest-puppeteer よりもはるかに書きやすくて気持ちがいいのだが、こういう風にちょっと複雑なことをしようとするとすぐに内部の気持ちを察したことをさせられる。また、ドキュメントの雰囲気からも察せられることと思うが案外 jQuery 的な価値観で全てが作られていてともするとレガシーさを感じることになる。

そのあたりも含めて SPA の E2E を書くなら jest-puppeteer より Cypress のほうが優れているとは思うが、よく注意して使ったほうがいい。

15 Apr 2020 Wed 21:58

PDF を Gyazo に展開して Scrapbox の記事にして全文検索する という試みについてです。

まず PDF を Gyazo に展開して Scrapbox の記事にするということですが、これについてブラウザ上で簡単に動くツールを実装しました。

https://ssig33.github.io/pdftoscrapbox/

img

おそろしく素朴な見た目ですがとりあえず動きます。Chrome や Edge に Tamper Monkey (試してないけど Firefox と Greasemonkey でも動くんじゃないかな)を入れて、 input に Scrapbox のプロジェクト名を入れて user.js をインストールした上で赤いところに PDFをドラッグ&ドロップすると、 PDF.js で PDF でレンダリングした上で全てのページを Gyazo にアップロードして Scrapbox のページを作成します。

何故 user.js を使っているかというと、 CORS 制限を突破する目的です。

これで実際どういう記事が出来るかということなのですが、

img

こういう感じです。

ページごとに画像になっているので、特定のページへのリンクを作成できますし、また Scrapbox の機能を用いていろいろとメモを書いていくことも可能です。

ではこうやって PDF を Scrapbox に展開できたとして検索が出来なければあまり使いやすいとはいえません。ですが、 Gyazo には強力な OCR 機能があり、画像内の文字列をかなり正確に検索することができます。

img

この結果を用いて Scrapbox を検索することができると便利です。というわけでそれも作りました。こちらの user.js をインストールすると Gyazo の OCR 結果を使って Scrapbox を検索することができます。

img

こんな感じで新しいボタンが出るようになるので、これをクリックすると

img

こう。ちなみにこの機能をまともに使うには Gyazo Pro に課金する必要がありますが、画像を強力に OCR して検索できるツールが月額たったの $5 と思えば大変に安いものです、是非契約しましょう。

PDF がいろいろ集まってくるけど読めない、管理できない、読んだメモを残せない、という人は結構多いと思うのですが、個人用の Scrapbox とこれらのスクリプトを用いることで非常によい検索性とメモ環境を得られると思います。

今回つくったツールのソースコードは https://github.com/ssig33/pdftoscrapbox にあります。ソースを見れば分かる通りですが今回作られたツールはすべてブラウザ上で動作し、僕が管理するサーバーをデータを通過することがないため安全に使うことができます(ぼくがこのツールの運用に支払うコストがゼロであることも意味します)。

18 Mar 2020 Wed 15:17

コアラ

10 Mar 2020 Tue 15:07

はーだるい

10 Mar 2020 Tue 15:07

GraphQL を CDN にキャッシュさせるのってどうするのがいいの

25 Feb 2020 Tue 14:39

ゴアラについて考えている

25 Feb 2020 Tue 14:29

ゴリラについて考えている

25 Feb 2020 Tue 14:23

とてもだるい

25 Feb 2020 Tue 14:22

消費者庁のサイトが speechSynthesis を使って読み上げ機能を提供しているが壊れている

現代のブラウザには speechSynthesis という API があってそれなりの性能の読み上げをすぐ実装できて便利なのだが、あまり活用例はないという状態だった。

ところが昨日偶然消費者庁がやっている特商法の解説サイトがこれを活用しているのを発見した。しかし盛大に壊れている。どう壊れているかは以下をご確認頂きたい。

で、なんでこんなことになっているのかとソースコードを見てみた。ぶっ壊れの理由は以下のような実装になっているからである(一部省略して核心部だけ示している)。

var voiceNum = 0;       
if(navigator.userAgent.toLowerCase().indexOf('firefox') != -1) {
    voiceNum = 0;
    console.log('win firefox');
}else if(navigator.appVersion.indexOf('Edge') != -1) {
    voiceNum = 0;
    console.log('win Edge');
}else if(navigator.appVersion.indexOf('Chrome') != -1) {
    voiceNum = 11;
    voiceRate = 1.1;
    console.log('win Chrome');
}else{
    voiceNum = 0;
    console.log('win');
}

var utterance = new SpeechSynthesisUtterance();
utterance.lang = 'ja-JP';
utterance.rate = voiceRate;
utterance.voice = speechSynthesis.getVoices()[voiceNum];

このコードは、 speechSynthesis.getVoices() の値が常に一定であることに依存している。しかし MDNの解説を見る限りそれは保証されていないようだし(そもそも This is an experimental technology とある)、実際僕の環境は実装者の意図とは違う値がかえってきているようだ。

ちょっと確認した限りでは、

  • Windows 10 の Chrome と Edge ではダメ
  • Mac の Chrome は大丈夫
  • Mac の Firefox もダメ

というかんじであった。

ではどうすればよかったのか?というと以下のようにすべきである

speechSynthesis.getVoices().find(v => v.lang === 'ja-JP')

で、 Windows 10 の Chrome でこれに置き換えてみたら以下のようになった。

感想

  • experimental と書かれている機能を使う場合、継続的にちゃんと動いているかの確認が必要と思う
  • speechSynthesis は(ちゃんと使えば)想像以上によく動く。積極的に導入していく価値がある
06 Feb 2020 Thu 12:06

Fastly のログイン画面をどうにかしてほしいという話

Image from Gyazo

複数の Fastly アカウントにまたがって構築されいるシステムのメンテナンスという作業をしていて、気付いたらかなり体調が悪化してて、激しい頭痛と吐き気の襲われた。というのも、ログアウト => ログインを何度も何度も繰り返すことになるから↑の画面を何度も何度も何度も何度もみていたわけだ。

これを 30 インチのディスプレイに全画面でドカーンと出していたので、これはつまりポケモンショック同様の症状ということだと思う。

自分がそういう状態になっていると気付いてからは、以下の user.css を書いて回避した(user.css の適用にはこの拡張を使っている)。しかしまあ一度崩れた体調は今に至るまで回復していない。

section.authentication{
  background-color:darkred;
}

Image from Gyazo

Fastly は実際驚異的な CDN で、これなしに自分の仕事、生活はもはやなりたたないほどで本当に感謝しているのだが、さすがにこのログイン画面はしんどすぎる。どうにかしてほしい、、、

04 Feb 2020 Tue 15:21

かに

01 Feb 2020 Sat 22:48

next.js で作った動的なサイトを CDN で全力でキャッシュさせる

というようなことをしたくなることもあると思います。その上で動的なサイトの場合 CDN のキャッシュをリアルタイムで飛ばしたいことも多いかと思いますので、 CDN の選択肢は事実上 fastly 一つということになります。

この時、 next.js なアプリをどこにどうやってデプロイするかが問題になってきます。

1. ZEIT

next.js をつくってるところのホスティングサービスで、すごく簡単に使えていいのですが、キャッシュさせようと思うとすごくめんどくさくなります。ZEIT 標準の CDN とキャッシュ機構もありますが使いやすくないですし、 fastly でキャッシュさせるためにカスタムヘッダーを吐かせようとしても now.json とかにゴチャゴチャ書くことになってあまりよろしくない。

ZEIT 自体よく出来てる気はするのだがあと一歩という感じがする。

2. firebase

next.js のデプロイ先として定番だと思うのですが、今回はダメでした。 firebase の CDN はなぜか Cloud CDN でなく fastly であることがよく知られています。このためさらに前に fastly を置こうとすると firebase の fastly に Surrogate-Key ヘッダーを握り潰されてしまいます。

3. Netlify

これも定番なんだろうけど触ったことないので知らん

4. で、どうしたか

Docker で固めて Cloud Run において fastly => Cloud Run という構成にしました。カスタムサーバーは以下のようになっています。

const express = require("express");
const next = require("next");

const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });

const handle = app.getRequestHandler();

app.prepare().then(() => {
  const server = express();

  server.get("*", (req, res) => {
    res.setHeader("Surrogate-Key", "SOME_KEY");
    handle(req, res);
  });

  server.listen(port, err => {
    if (err) throw err;
    console.log(`> Ready on http://localhost:${port}`);
  });
});

URL ごとにキャッシュのルール変えたいときとかはここで Surrogate-Key 出し分ければよさそうですね。しかしまあ ZEIT にサクっと上げて超全力でキャッシュしてくれるとかそういうのできればそれが楽なのでどうにかできないのか、、、

01 Feb 2020 Sat 16:46

はーめんどくせ

01 Feb 2020 Sat 16:37

firebase の前に fastly おくと fastly => fastly になってしまってあんまりよくないっぽいな、、、

01 Feb 2020 Sat 16:25

フロントエンド firebase においたが 暖まってないと Cloud Run より起動遅いな

01 Feb 2020 Sat 15:22

ページ遷移わりと最速になった

31 Jan 2020 Fri 11:43

CDN おじさんになったところで 、潰しがきかないんだろうと思っていて、あまり深入りしないようにはしている

30 Jan 2020 Thu 23:53

Cloudflare がいらんことしていた

30 Jan 2020 Thu 23:52

test

30 Jan 2020 Thu 23:48

キャッシュできてるけど思ったとおりに飛んでねえな

30 Jan 2020 Thu 23:37

もうちょっといじって

Cloudflare(HTTPS化) => Fastly(キャッシュ) => Cloud Run(ここで next.js 動かす) => Cloudflare => Fastly => Cloud Run(Go で書かれた API)

という構成にした。

30 Jan 2020 Thu 23:35

CDN 沢山つかってるとキャッシュとばすのまじでめんどう

30 Jan 2020 Thu 22:58

キャッシュ

30 Jan 2020 Thu 22:34

Next