VS Codeでパネルの最大化をするショートカット

前提

macOS

$ code -v
1.51.0

VS Codeのターミナルを表示するまではいいんだけど、エディタのことは忘れて広い画面でターミナルを操作したい、というときのショートカット。

workbench.action.toggleMaximizedPanel がそれ。既存だとバインドされていないので別途設定が必要。

自分は統合ターミナルの表示を Ctrl Shift @ に割り当てているので、隣の Ctrl Shift [ に割り当ててみた。ドラッグして大きさを調整せずに済むのでそこそこ楽。

Swarmのチェックインをふりかえりやすくするためにやってること

ikaruga.org鳥人雑技団なんて地元にはない に月1で日常の出来事を書いてるんだけど、意外とこれに依存しているという気付きが合ったので記録する。大したことではなく、IFTTTのアプレットで楽しているという話。

チェックインをカレンダーに記録する

4sq check-ins (now Swarm) to Google Calendar - IFTTT を使っている。どこにいた、という記録がカレンダーの予定と一致するので便利。

チェックインをスプレッドシートに記録する

If Any new check-in on Foursquare, then Add row to Google Drive spreadsheet - IFTTT を使っている。今年Swarmのチェックインで何回「はい」といったか雑なスクレイピングで数えて2019年をふりかえる。 - Re: 醤油の一升瓶じゃあ戦えない で去年集計したのを楽にしたい思いがあったので。チェックインしたときにすでにスプレッドシートに記録されていればいい話。

近況を書くときは前月のカレンダーを見て予定とチェックインを確認する、休みのtwilogをみる、出来事を見出しにして適当に書き連ねる、といった感じで書いている。そうやってできた今月の近況はこちらです。

ikaruga.org

1stシーズンと2ndシーズンのリザルト

1stシーズンのリザルト、という下書きが残ってた。多分ペパボで1年仕事してたことについてのふりかえりを書こうとしてたのだろう。実は既にに2ndシーズンも過ぎていたので、やったこととかギャッと並べてみる。

1st

やった

  • フィヨルドブートキャンプでRailsの修行した
  • 情報安全確保支援士の登録した
  • builderscon 2018 tokyoに参加した
  • RubyKaigi 2019に参加した
  • Rails Developpers Meetup 2019 に参加した
  • 仕事では決済のAPIを使ってあれこれしたり、一括処理について思いを馳せていた
  • 社のイベントやブログの写真を撮るなどしてた

  • キーボード2台作った

  • キーボード作ってわかったことを未来大で話した
  • M5Stack触って作ったもの解説を書いたら同人誌になった

わかった

  • モデリングは異様にむずかしい
  • すべてを理解する必要はない
  • ふりかえり続けるとつかれるので、ある程度は捨てて前を向くほうがよい

2nd

やった

  • 税率に思いを馳せていたり、1からなにか作るということをしていた
    • 平成Ruby会議で税率について少し話した
    • Rails+Vue+TypeScriptのモノレポリポジトリの扱い方
    • 1から環境を作る、ということはしていないけどそれに乗っかってAPI、フロントエンドを書いたり
  • 社のイベントやブログの写真を撮るなどしてた
  • 仕事がフルリモートになった
  • 新卒の研修をみてすげーじゃん!とか言ったりした
  • 面倒見ているサービスの運用
    • herokuで動いているサービスの面倒見たり

わかった

  • TypeScriptは書くこと増えるけど、自分の間違いには気づきやすくなるので楽…
  • OpenAPIGeneratorでTypeScriptのAPIクライアントが生成できるのがかなり便利
  • 価格は外税で保存すると税率変更対応時に楽
  • 仕様を決めるのは大変
  • minikubeを使ったk8sの環境の作り方
  • Rackのアプリケーション構造
  • Zeitwerkでファイル読み込み方法がどう変わったか
  • リモート楽だけど、たまには出社して他職種のひとと話したくなる
    • 偶発的な会話を求めているのかもしれない
  • 人を撮るに当たり、光とレタッチを学びたくなった
  • 運動しないと体重が落ちる
  • まだ必殺する力が足りていない
    • 一時的な修正でなく、機能として落とし込む力

つぎやること

  • TypeScriptを身につくくらいには書きたい
    • テストをさっと書けるようになるといいなあ
  • ハードは一旦やらない
  • GCPAWSかどっちかで盆栽を作る

やったことわかったこと並べれば少なくとも何か学んではいる、ということなのでポジティブにいこう。

Request Idについて調べた

Request id について

herokuで動いているRailsアプリケーションのログを眺めていたときに出てくる、uuidって何?となったのでメモる。わからないところがわかったら、あとで加筆する。

つまり

  • RailsアプリケーションにはX-Request-Id というHTTPヘッダーを見てログに出す仕組みがある。
  • herokuのWebサーバーが X-Request-Id を付与してRailsアプリケーションにリクエストを流している
  • X-Request-Id の仕組みはherokuに限らず、Webサーバー全体で使われてそう。
sequenceDiagram
    participant ブラウザ
    participant heroku/router
    participant app/web(Rails)
    
    ブラウザ->>heroku/router: APIリクエスト
    heroku/router->>heroku/router: X-Request-IDヘッダーをログ出力
    heroku/router->>app/web(Rails): X-Request-IDヘッダーを付加してリクエスト
    activate app/web(Rails)
    app/web(Rails)->>app/web(Rails): 処理
    app/web(Rails)->>app/web(Rails): X-Request-IDの内容を含めてアプリケーショのログ出力
    deactivate app/web(Rails)
    app/web(Rails)->>heroku/router: レスポンスを返す
    heroku/router->>ブラウザ: レスポンスを返す

Railsのロギング

先にRailsアプリケーションがどうやってログ出しているの?というところから。

Railsアプリケーションでログ出力の設定としてRails::Application::Configuration クラスの log_tags プロパティが存在する。 Rails アプリケーションを設定する - Railsガイドには以下の記述がある。

config.log_tags: 次のリストを引数に取ります(requestオブジェクトが応答するメソッド、requestオブジェクトを受け取るProc、またはto_sに応答できるオブジェクト)。これは、ログの行にデバッグ情報をタグ付けする場合に便利です。たとえばサブドメインやリクエストidを指定することができ、これらはマルチユーザーのproductionアプリケーションをデバッグするのに便利です。

色々付加してログに出せるっぽいですね。

Rails 6.0.3.2でrails new した時点で、config/environments/production.rb にはconfig.log_tags = [ :request_id ]が設定されている。この :request_idrequestオブジェクトが応答するメソッド に合致する。この先はRackかと思ったのだけども迷子になってしまったのでまた別の機会の追う。

production.rb にある設定を config/environments/development.rb で同じように設定したうえでリクエストをすると、以下のような結果になる。

X-Request-Id ヘッダーなし

$ curl -I http://localhost:3000
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
Content-Type: text/html; charset=utf-8
ETag: W/"6e6be3ab17e553298ac68f8a42a50540"
Cache-Control: max-age=0, private, must-revalidate
Content-Security-Policy: script-src 'unsafe-inline'; style-src 'unsafe-inline'
X-Request-Id: d662620f-f16e-4b0f-b0df-34a45247f195
X-Runtime: 0.005475
# $ curl -I http://localhost:3000 の結果
[d6e516d6-8a13-4bb4-be3a-c0a0636b4b0e] Started GET "/" for ::1 at 2020-07-24 19:41:13 +0900
[d6e516d6-8a13-4bb4-be3a-c0a0636b4b0e]    (1.6ms)  SELECT sqlite_version(*)
[d6e516d6-8a13-4bb4-be3a-c0a0636b4b0e] Processing by Rails::WelcomeController#index as HTML
[d6e516d6-8a13-4bb4-be3a-c0a0636b4b0e]   Rendering /Users/ikaruga/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/railties-6.0.3.2/lib/rails/templates/rails/welcome/index.html.erb
[d6e516d6-8a13-4bb4-be3a-c0a0636b4b0e]   Rendered /Users/ikaruga/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/railties-6.0.3.2/lib/rails/templates/rails/welcome/index.html.erb (Duration: 10.1ms | Allocations: 295)
[d6e516d6-8a13-4bb4-be3a-c0a0636b4b0e] Completed 200 OK in 22ms (Views: 15.4ms | ActiveRecord: 0.0ms | Allocations: 1643)

X-Request-Id ヘッダーあり

$ curl -I -H "X-Request-Id: 1" http://localhost:3000
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
Content-Type: text/html; charset=utf-8
ETag: W/"6e6be3ab17e553298ac68f8a42a50540"
Cache-Control: max-age=0, private, must-revalidate
Content-Security-Policy: script-src 'unsafe-inline'; style-src 'unsafe-inline'
X-Request-Id: 1
X-Runtime: 0.018485
[1] Started HEAD "/" for ::1 at 2020-07-24 19:52:49 +0900
[1] Processing by Rails::WelcomeController#index as */*
[1]   Rendering /Users/ikaruga/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/railties-6.0.3.2/lib/rails/templates/rails/welcome/index.html.erb
[1]   Rendered /Users/ikaruga/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/railties-6.0.3.2/lib/rails/templates/rails/welcome/index.html.erb (Duration: 3.3ms | Allocations: 8)
[1] Completed 200 OK in 12ms (Views: 5.6ms | ActiveRecord: 0.0ms | Allocations: 303)

レスポンスヘッダーに付加して返すのはActionDispatch::RequestIdのしごとかな。 rails/request_id.rb at v6.0.3.2 · rails/rails

herokuのRequest id

では、herokuではどうしているかというと、 HTTP Request IDs | Heroku Dev Center に大体の事が書いてあるのでそれを読む。

リクエストを受けると、herokuのrouterがリクエストID(UUID)を自動生成し、X-Request-Id ヘッダーに付加してアプリケーションに流すっぽい。外から X-Request-Id を受けるときは、20~200文字の/[a-zA-Z0-9+=/-]/ が使えるみたい。これを満たさないヘッダーが来たときは、herokuがRequest idを自動生成して、アプリケーションに流すと。

各Webサーバーの Request Id の対応

Nginxは1.11.0から $request_id 変数が実装されて、HTTPリクエストが来るごとに16進16桁の文字列を生成するみたい。 これをnginx.conf X-Request-Id ヘッダーに付与してアプリケーションにリクエストを流すと良さそう。 Application Performance Management with NGINX Variables

Apacheにも mod_unique_id というモジュールがあって、 UNIQUE_ID 環境変数を取得することでリクエストごとのユニークなIDを取れそう。 mod_unique_id - Apache HTTP サーバ バージョン 2.4

UNIQUE_ID 環境変数は 112 ビット (32 ビット IP アドレス、32 ビット pid, 32 ビットタイムスタンプ、16 ビットカウンタの四つの組) をアルファベット [A-Za-z0-9@-] を用いて MIMEbase64 符号化と同様の方法により符号化し、19 の文字を生成することにより作成されます。MIMEbase64 のアルファベットは実際は [A-Za-z0-9+/] ですが、 + と / とは URL では特別な符号化が必要なので、あまり望ましくありません。 全ての値はネットワークバイトオーダで符号化されますので、 符号は違ったバイトオーダのアーキテクチャ間で比較可能です。 実際の符号化の順番は: タイムスタンプ、IP アドレス、pid, カウンタです。この順には目的がありますが、 アプリケーションは符号を解析するべきではないことを強調しておきます。 アプリケーションは符号化された UNIQUE_ID 全体を透過的なトークンとして扱うべきです。 UNIQUE_ID は他の UNIQUE_ID との等価性を調べるためだけにのみ使用できます。


Reqeust Idがうまく渡せると、色々なアプリケーションを経由するリクエストで横断的にログを見ることができてよいですね。

tigで1行だけStage / Unstage する

help には全部書いてある。 stage viewで 1 を押すんだ。

いつもgitのコミットや差分確認にtigを使っているんだけど、この行だけStageに上げたいなーとかその逆をやりたいというときが出てくる。VSCodeのGit管理ではDiffを見ながら右クリックメニューから「選択した範囲をステージ」みたいなことができるので、tigでもできるでしょう。と探したら見つけた。デフォルトのキーバインディングで設定されているはず。

[-] stage bindings
View-specific actions
                           u status-update       Stage/unstage chunk or file changes
                           ! status-revert       Revert chunk or file changes
                           1 stage-update-line   Stage/unstage single line
                           \ stage-split-chunk   Split current diff chunk

stage-update-line というのがそれ。 これで stage-split-chunk で切り分けられなかった差分も拾ってStageにあげれて便利〜。

jq で shuffle を実装する

諸事情で jq で Array shuffle を実装したので、 実装や実装に必要となった初歩的なアルゴリズムについて記録する。

とりあえずできたものはこちら。 jq -nrcf usage.jq でシャッフルされた配列が返ってくるはず。 gist.github.com

jqとは

jq には

jq is a lightweight and flexible command-line JSON processor.

と書かれている。 JSONを入力として、それに対するフィルターを書くことでJSONの加工が行えるくん。なのだけれども、JSONを入力とせずともフィルターのみでコーディングすればおおよそなんでもできる。

jqが用意してくれているもの

条件分岐

jq は if 文が使える。

2 | if . == 0 then "zero" elif . == 1 then "one" else "many" end
# => "many"

関数

jq は関数を作ることができる。作った関数はパイプを通すことができる。引数も渡せる。

def increment: . + 1;

1 | increment
# => 2

変数

jq は変数を定義できる。

["Amy", "Basil", "Clara"] as $children

と書いておけばパイプの後からでも $children を呼べる。

これら以外にもたくさん jq はできることがあって、それらは jq Manual (development version) に記載されている。変数宣言が "Advanced features" の項目にいるのは、本来の使い方を考えるとまあそうだよね。といった感じ。

モジュール

jq は別のjqファイルを読み込むことができる。 上の usage.jq でやっているように import PATH as Name でPATHを読み込む。読み込んだファイルの関数は Name::function のように呼べる。

デバッグ出力

jq は debug 関数を使うことで計算途中でも関数実行時点での値を出力することができる。実装中長いパイプを通す必要が出てきたときに重宝した。

["A", "B", "C", "D", "E", "F", "G"] | { "key": .[] }
# {"key":"A"}
# {"key":"B"}
# {"key":"C"}
# {"key":"D"}
# {"key":"E"}
# {"key":"F"}
# {"key":"G"}
["A", "B", "C", "D", "E", "F", "G"] | { "key": (.[] | debug) }
# ["DEBUG:","A"]
# {"key":"A"}
# ["DEBUG:","B"]
# {"key":"B"}
# ["DEBUG:","C"]
# {"key":"C"}
# ["DEBUG:","D"]
# {"key":"D"}
# ["DEBUG:","E"]
# {"key":"E"}
# ["DEBUG:","F"]
# {"key":"F"}
# ["DEBUG:","G"]
# {"key":"G"}

jq が用意してくれていないもの

乱数

jq には乱数が用意されていない。精度はまったく気にしていなかったので、擬似乱数について調べた。線形合同法を用いた疑似乱数が古典的でシンプルというのを見たので雑に書いた。定数はC言語のライブラリにある Park and Millerによって提案された「最小標準」の実装を参考にした。 ちょっと散らすために何回か繰り返しかけるようにしているが、個人的なおまじないレベル。

refs: Question 13.15

シャッフル

jq には配列のシャッフル関数が用意されていない。乱数が用意されていないのでそれはそう。どういったアルゴリズムが適しているのか調べると、 フィッシャー-イェーツのシャッフルというアルゴリズムがあることを知った。配列の要素ごとに無作為に選んだ要素と場所を入れ替える、というのを要素数分繰り返すことでシャッフルを実現している。畳み込みでも実現できそうだったので、今回は reduce を使って畳み込むことにした。配列の要素を指定してSwapした結果を別の配列として返す、というのが意外とめんどくさくて、上の実装では Array Slice を使って無理やり配列を組み立てている。もう少しどうにかなりそう。

refs: Fisher–Yates Shuffle

懸念していること

乱数の偏りについてなにも考えられていないので検証が必要。カルドセプト サーガを思い出した。

学び

  • 乱数、シャッフルの初歩的なアルゴリズムの復習ができた
  • jq 意外と何でもできることがわかった
    • 上記のような変なこと意外にも関数一通り眺めたので、どんなことができるかは把握できた。
  • ついでに配列、ハッシュの操作を行うコードも別件でちゃんと書いたので、大体jqわかった。

なぜこのようなことを

知り合いがjqでProject Eulerを解いているという話を聞いて興味が出たので。

参考

Raspberry Pi 4 + DAC + Volumio で音楽を聴けるようになるまで

仕事用のPC以外から家のNASに入ってる音楽を聴きたい〜って考えたとき、タブレットから再生するのが速そうと思ったのだけれども、Android / iOS だといい塩梅のアプリがなくてじゃあRaspiだとなったのであれこれ買って試した話。

用意したもの

Raspberry Pi 4

家に元々あったやつ。詳しくは後述。

Raspberry Pi 4のヒートシンク

Amazon | Raspberry Pi 4(Model B)用アルミヒートシンクケース (レッド) | Side3 | PCケース 通販

DAC

Kuman PIFI Digital DAC+ Amazonで買った。Raspberry Pi のソケットに刺さるタイプのDAC。チップとかそこまでこだわっていなかったので、適当に良さそうなのを選んだ。これがあるとRaspberry Pi の本体からよりは相対的に音が良くなる。

とても背の高いピンソケット

とても背の高いピンソケット2×20(40P) - スイッチサイエンス

スイッチサイエンスで買った。これも後述

Volumioの入ったmicro SD

Volumioという音楽再生に特化したディストリビューションがあって、それを焼いたもの。

組む

実はRaspi 3も家にあって、それを流用するつもりだったのだけれども、いざDACが届いたときに通電確認したらpow LEDが点灯すらせず文鎮と化したので予備で用意していたRaspi 4を渋々使った、という経緯がある。Raspi 4は熱がヤバいと聞いていたので、ヒートシンクも慌てて買った形となっている…

そして予想はしてたけれど、素の状態だとヒートシンクDACに干渉してしまったので、とても背の高いピンソケットを間に挟むことで解決した。スイッチサイエンス最高。

Volumioの設定

ハマったと言うか、調べないとわからなかったこと

NAS接続

使っているNASの問題なのか、 smb のバージョン設定を入れないと接続できなかった。 オプションに vers=1.0 を入れると接続できた。 f:id:uvb_76:20200616222136p:plain

コンピレーション問題

アルバムリストを表示したときにアーティストごとにアルバムが増殖するよくあるやつ。よく聴く音楽ジャンルの都合上そこそこの数のコンピレーションアルバムを管理している。iTunesだと何も考えずにコンピレーションとして認識させればよかったのだけれども、そんな親切なものは無いので、別の手段を使って、コンピレーションで有ることを伝える必要がある。今回はアルバムアーティストのタグを Compilation Metadata Fields として認識っせることで対処した。ただ、 mp3タグが不完全なアルバムもあったので、これを期に設定し直した。 f:id:uvb_76:20200616221505p:plain

使ってみて

同じネットワークからブラウザで volumio.local にアクセスするとVolumioの再生画面を表示できる。ブラウザで一通りの操作が完結できるのは強いと思う。スマホからでも同様にアクセスできる。レスポンシブデザイン対応しているのは親切だなーと感じた。ただ、Rapi4で音楽再生するだけなのはオーバースペックっぽいと思うので、別用途でもなにか動かせないか模索しているところ。また、Volumioで提供されている Spotify Plugin のログインが未だにうまく行っていないのでどうにかしたい。うまく行けばNexus 7から脱却できるんじゃ…

参考

Raspberry Piで部屋のオーディオ環境を改善した話 - しろうまの小屋

Volumioでコンピレーション・アルバムの再生ができない場合の対処方法 - Qiita