ちなみに

火曜日の空は僕を押しつぶした。

jqで簡易テンプレート

たとえばSlackの Incoming Webhook でメッセージを送るときに、メッセージ部分だけを置き換えてJSONを組み立てたいことがあります。 プログラミング言語が使えるなら簡単なんですが、ちょっと大げさな気もするので jq だけでJSONを組み立てられるとべんりそうです。

{"text":"hi"}

このようなJSONを組み立てて送信したいとして、"hi" の部分を変数にしてみます。

{"text":$text}

この $text の部分を任意の文字列に置き換えるには --arg オプションを使います。(-n / -c)

$ jq -n -c --arg text hi '{"text":$text}'
{"text":"hi"}

今回のケースでは不要ですが文字列以外の値に置き換えるには --argjson オプションを使うことが出来ます。

$ jq -n -c --argjson text 12345 '{"text":$text}'
{"text":12345}

目的はこのJSONをSlackに投げることだったので生成結果を curl に食わせます。 -d @- で標準入力をbodyとして送信できるのではこれを利用します。 ちなみに Object Construction が使えるので { text: $text } のような書き方が出来ることも覚えておくとよさそうです。

$ jq -n -c --arg text hi '{ text: $text }' | curl -H 'Content-Type: application/json' -d @- <Incoming Webhook URL>

これで目的は達することが出来ました。

Slackに投稿するときは、恐らくもうちょと 複雑なメッセージを組み立てる 必要があると思います。 そういった場合にはテンプレートとしてファイルを用意しておきそれを読み込むことも出来ます。

以下のような内容で template.js というファイルを用意します。.js 拡張子なのは object っぽい書き方なのでシンタックスハイライトが効くから。。。

{
  blocks: [
    {
      type: "header",
      text: {
        type: "plain_text",
        text: "CI"
      },
    },
    {
      type: "section",
      fields: [
        {
          type: "mrkdwn",
          text: "*Result:*\n\($result)"
        },
        {
          type: "mrkdwn",
          text: "*SHA1:*\n\($sha1)"
        }
      ]
    }
  ]
}

ファイルを受け取るには -f オプションが使えるので以下のようにして生成します。

jq -c -n --arg result Success --arg sha1 eeeaaa -f template.js | curl -S -H 'Content-Type: application/json' -d @- <Incoming Webhook URL>

f:id:Sixeight:20210925170351p:plain

ちなみに gojq でももちろん同じことが出来て圧倒的に速いので、大きなJSONを生成するときはおすすめです。

$ time gojq -c -n --arg result Success --arg sha1 eeeaaa -f template.js > /dev/null

________________________________________________________
Executed in    7.56 millis    fish           external
   usr time    2.54 millis    0.09 millis    2.45 millis
   sys time    3.83 millis    1.12 millis    2.71 millis
$ time jq -c -n --arg result Success --arg sha1 eeeaaa -f template.js > /dev/null

________________________________________________________
Executed in   31.71 millis    fish           external
   usr time   40.19 millis    0.12 millis   40.07 millis
   sys time   10.60 millis    1.06 millis    9.53 millis

ISUCON 11でISUCONに初出場して予選敗退した

f:id:Sixeight:20210822231314p:plain
最初にエンキューしたベンチマークジョブIDが941で幸先良いと喜んでいる様子

いつも日和って出れないので、今年こそはISUCONに出てみようと思っていたのですが予選の申し込みが一瞬で埋まったので意気消沈。 そんなとき id:karupanerura さんに声をかけていただいて、id:aereal さんと3人で「チームにゃんこ選抜」として出場することになりました。

初めてでだいぶ緊張していましたが、練習で丁寧に教えてもらったり、過去問の解説を読んだりして、ある程度落ち着いて当日を迎えることが出来ました。

競技開始後は最初に決めておいて手順に従って初期設定や情報収集、そのあと集まってマニュアルの読み合わせをした。 これまでの会と比べて得点計算が複雑だったのでそこを理解するのに時間がかかってしまって、作業開始がかなり遅くなってしまったうえに、結局結局グラフの改善までは到達出来てなかったので理解してたてた作戦が活きてないのでもうちょっと見切り発車でも良かったかも。

読み合わせ後は初手の作戦を決めてそれぞれ独立して作業、30分くらいごとに進捗確認をして、次の作戦を決めるという方式で進めていた。 独立して動いていても30毎に同期が取れるのであんまり迷走することはなくて動きやすかったです。

github.com

今回のリポジトリはこういう感じでした。

あとから周りのチームをみていると速度優先でコミットとかけっこうめちゃくちゃにやっていて、あ、それで良かったんだという発見があった。 けっこう丁寧にコミット詰んだりしてしまっていたので、次はもっと速度優先していこうと思った。

当日はDiscordで常時つなぎながら、GitHubとSlackで文字情報の共有をしていて、すぐに声をかけられたのでやりやすかったのでオンラインでの常時音声通信は必須だなあと感じました。

isucon.net

結果としては予選敗退だったが、とても楽しかったし、自分の実力が低すぎることを痛感することが出来て勇気を出して出場してよかったです。 ただし、チームのメンバーには迷惑をかけてしまったので、完全に素振りと普段の勉強が足りていなかった。

自分が唯一貢献できたのは isu_condition.condition を別カラムに分割する案と実装したこのプルリクくらいで、それ以外はただただあたふたしていた。 落ち着きも足りないね。

github.com

かなり慣れたつもりだったけどCorneliusでの入力が若干おぼつかないシーンがあって焦るとまだ駄目だなという感じ。 この記事はCorneliusで書きました。

Corneliusは最高

foostan さんの設計されたキーボード、Corneliusを組み立てました。

忙しいというか仕事が終わるとMPがつきている状態が続いていたので、届いてからしばらく放置したいたのですが先週末にやっと元気が出てがっとやりました。 スイッチは Diamond Linear をストックのまま。 キーキャップは Grab Bag に入っていたSAプロファイルのものをかき集めて。一部足りないところは刻印に重複があったりする。

もうこれでいいやんという境地に達している打鍵感と音で大満足。ただし、やはりLubeしていないスイッチはたまにスプリングのすれる音が聞こえたりして微妙なのでそのうち潤滑したい。

この記事は Cornelius で書きました。

おまけ。キーマップは現在こんな感じです。

f:id:Sixeight:20210817003621p:plain f:id:Sixeight:20210817003636p:plain f:id:Sixeight:20210817003651p:plain f:id:Sixeight:20210817003705p:plain

久しぶりのミステリ

Twitterを眺めていたら、衝撃のラストで続編が不可能と言われた作品のまさかの続編というあおりでミステリ小説が紹介されていたので気になって読んでみた。 最近小説を読む心の余裕がなくてなんだか久しぶりに楽しむ読書をした気がする。

内容は正直90%くらいは微妙だと思って読んでいて、なんとなくオチも見えてしまってうーん失敗したなと思っていたのだけれど、最後の10%で悔しいかな衝撃的と言わざるを得ないどんでん返しがあってめちゃくちゃ楽しんでしまった。 もちろん予想していたオチはひっかけで、最初から最後までちゃんと全ての証拠が提示されているまともなミステリだった。ご都合主義のラノベ崩れミステリとか思ってごめんなさい。

何を書いてもネタバレになりそうなので書けないのですが、とにかくつらくても最後まで読んでほしい一冊です。 本当に途中で読むのやめなくてよかった。

続刊は中編が3つ入ったものになっていて、こちらも面白く読んでいます。

この記事は Casasagi で書きました。

Casasagiを組み立てた

@hsgw さんの ガレージセール で購入した Casasagi のOリングマウントケースを組み立てた。

スイッチのはんだ付けが必要なキットなので、ホットスワップに甘やかされてきた僕にはどのスイッチを選ぶかが一番難しかった。 最初はタクタイルにしようとT1を準備していたのだけれど、Oリングマウントの柔らかさを活かすにはリニアだよなーなどと半日迷った結果 Everglide Tourmaline Blue Cyan を選んだ。

土曜日のうちに組んでしまいたかったけどスイッチが決まらなくて今日に持ち越し。 朝ご飯を食べてからえいやと組み立てた。

ホールスルーな部品のはんだ付けはだいぶつかめてきたのであんまり問題にならず。 数が多いのでちょっと面倒と思ったりしただけだった。

音は思ったより軽い感じになっていてもうちょっと低音が欲しい感じ。プレートフォームは入れたけどあんまり変わらず。 ケースフォーム入れるとか重り入れるかとかを考えたい。重りは軽いので入力中に本体が動いてしまったりしているのにも効きそう。ゴム足でも良いけど。

結局スタンダードなロースタッガードばっかり使っているので直交配列でのキーマップにめちゃくちゃ悩んでしまっていてなかなか決まらない。 あんまり普段から離れすぎると戻りにくくなるのでなんか良い感じになりたいな。

この記事は Casasagi で書きました。

久しぶりにランニングを再開した

最近睡眠が破滅していて、寝付きは良いのだけれど夜中に何度も目覚めてしまう。 それでも元気なんだったらいいのだけれど、全然大丈夫じゃなくて睡眠不足状態が続いていて生活もままならなくなっている。 具体的には家事をする元気がなくて、仕事終わったらすぐ寝てしまったりして家が荒れている。

どうにかしないといけないということで、別件でかかりつけの医師に相談したところ睡眠導入剤を処方してもらった。 長期間は眠れないけど、眠り始めの質を高めて睡眠時間が少なくてもなんとかなるようにする作戦。 しかし、結果としては全然効いてなくて、効果が全く感じられなかった。

原因として考えられるのはコロナ禍になって在宅勤務が続いて運動不足で太陽にもあたってないからというのがある。 こうなったらあとは運動だということで妻にも勧められたのでランニングを再開することにした。

久しぶりすぎるのでめちゃくちゃペースを落としてゆっくりと20分かけて3kmを走った。 これでもめちゃくちゃ疲れたのでやっぱり体力が地に落ちている。

しかし前回走ったときは初回がんばり過ぎて吐いてしまったので大人になった。

走ったあとはなんか頭もすっきりして良い感じだったのだけれど、そのあと疲れてぐったりしてしまって結局日曜日は溶けてしまった。 これだといつもよりひどい日曜日だ。

けどきっと睡眠は改善されるはず。さてさて。。。

f:id:Sixeight:20210614091506j:plain

うーん、ひとまず続けてみてどう変わるかを確認しよう。

久しぶりに Gem を作った

github.com

リハビリも兼ねてちょっと欲しかった Gem を作ってみました。

作り始める前に調べていたつもりだったのだけれど モロかぶりしているGem がすでに存在して名前もまったく一緒だったので諦めて simple みたいな無意味な名前になってしまった。

やりたかったことは Rails のアクションにアノテートしておいて一律でチェック出来るようにするという感じのこと。

class HogeController < ApplicationController
  include SimpleAnnotation::Annotatable

  def index
  end

  annotates :premium
  def show
    # 課金ユーザーだけ見られる何か
  end
end

こういう感じで書いておいてフィルターなどでチェックする想定です。 #annotate ってメソッド名に出来なかったのは ActiveRecord::QueryMethods#annotate があるから。

class PremiumFilter
  def self.filter(controller, current_user)
    action = controller.action_name
    return unless controller.class.annotated?(action, with: :premium)

    controller.redirect_to plans_path unless current_user.premium?
  end
end

class ApplicationController
  before_action -> { PremiumFilter.filter(self, current_user) }
end

こんなことしなくても実現は出来そう。

まあ、メタプログラミング完全に忘れていていたのでいい復習になりました。