Hugoのコードブロックに併せてファイル名を表示する

やりたいこと 以下のようにコードブロックと併せてファイル名も表示したい。 test.txt Hello, World! Hugoのデフォルトのコードブロックにはファイル名を表示する機能がない。 また自分の使っているPaperModというテーマでもサポートされていない。 そこで自分でテンプレートを拡張してコードブロックのファイル名を表示できるようにしてみた。 なお、使用しているテーマによっては拡張せずともファイル名を表示できる可能性があるので、まずはそちらの確認をすることをおすすめする。 環境 Hugo 0.145.0 PaperMod (commit e2e1011bdecaf84d59c70fa42ff3d2c29c537b65) テンプレートを作成 公式のGitHubにあるコードを参考にして作成した。 公式のコード 上記のコードではTailwindが使われていたが、自分のテーマではTailwindは使われていなかったので、Tailwindを使わずに.cssファイルを使ってスタイリングをする方式に変更した。 またcopy、details、open、summaryといった将来使わなそうな不要なオプションはメンテが面倒になりそうだったので削除した。 copyに関してはPaperModでコピー機能が実装されているので削除したが、使用しているテーマでコピー機能がサポートされていない場合は採用するのもありだと思う。 というわけで、以下のコードを/layouts/_markup/render-codeblock.htmlとして保存する。 Hugoはコードブロックを見つけるとrender-codeblock.htmlを呼び出すと事前取り決めてあるので、ファイル名も配置場所も一文字でも間違えたらダメなことに注意。 /layouts/_markup/render-codeblock.html {{- /* Forked from https://github.com/gohugoio/hugo/blob/master/docs/layouts/_markup/render-codeblock.html At 2025-04-13 Use .css file instead of Tailwind Deleted copy, details, open, summary params */}} {{/* prettier-ignore-start */}} {{/* Renders a highlighted code block using the given options and attributes. In addition to the options available to the transform.Highlight function, you may also specify the following parameters: @param {string} [file] The file name to display above the rendered code. @returns {template.HTML} {{/* prettier-ignore-end */}} {{- $file := or .Attributes.file "" }} {{- $ext := strings.TrimPrefix "." (path.Ext $file) }} {{- $lang := or .Type $ext "text" }} {{- if in (slice "html" "gotmpl") $lang }} {{- $lang = "go-html-template" }} {{- end }} {{- if eq $lang "md" }} {{- $lang = "text" }} {{- end }} <div class="codeblock-wrapper"> {{- with $file }} <div class="codeblock-file"> {{ . }} </div> {{- end }} <div> {{- transform.Highlight (strings.TrimSpace .Inner) $lang .Options }} </div> </div> CSSファイルを作成 PaperMod用のスタイリングの一例が以下。 ...

2025-04-13

LOVEマシーンが好きだと言える人間になりたい

大学生の頃、モーニング娘。にハマっていた。 何をきっかけにハマったのかは覚えていないが、おそらくYouTubeでなんかのPVを見たのがきっかけだったんじゃないかと思う。 ハマってからは昔の曲から最近の曲まで色々な曲を聴き漁った。 歴史のあるアイドルグループなので曲数は膨大だったが、暇さえあればずっとモー娘。の曲を聴いていた。 モー娘。が好きだということは周りに公言していたので、何人かの知り合いは「一番好きな曲は何?」と尋ねてくれた。 その質問に対し自分は「『ここにいるぜぇ!』が一番好き」と答えていた。 『ここにいるぜぇ!』はとても良い曲だ。 曲調も歌詞も明るく、PVでもメンバーが元気いっぱいに飛び跳ねていて、それなのにどこか物悲しい雰囲気も感じられて、その雑多な感じが好きだった。 でも、実を言うと『ここにいるぜぇ!』は一番好きな曲ではなく二番目に好きな曲だった。 本当に一番好きな曲は『LOVEマシーン』だった。 Aメロ、Bメロ、サビ、全てのパートがキャッチーで完璧な曲だと思っている。 実際、CMやドラマなどのタイアップが無かったにも関わらずCD175万枚を売り上げたのだから、当時の世間の人達も「この曲良い曲だな」と感じたのだろうと思う。 でも、大学生のときの自分は「一番好きな曲は『LOVEマシーン』です」と答えることができなかった。 サザンオールスターズのファンが「『TSUNAMI』が一番好きです」なんて言うか? Mr.Childrenのファンが「『Tomorrow never knows』が一番好きです」なんて言うか? モー娘。のファンが「『LOVEマシーン』が一番好きです」なんて言うか? ...

2025-04-12

timeout付きsubprocess.runの途中までの出力を取得

問題 AtCoderのサンプルテスト用のCLIを作成している。 一部抜粋したコードが以下。 import subprocess (...中略...) result = subprocess.run( ["python3", f"contests/{args.contest}/{args.problem}.py"], input=input, capture_output=True, text=True, timeout=3, ) timeoutを指定することによって、AtCoderでいうところのTLE(Time Limit Exceeded)を実現している。 タイムアウトしても処理が打ち切られる寸前までの標準出力を使ってデバッグをしたいことがあったので、以下のようなコードを書いて標準出力の中身を取得していた。 しかしこのコードを実行すると標準出力の中身が空になることが何度かあった。 import subprocess (...中略...) try: result = subprocess.run( ["python3", f"contests/{args.contest}/{args.problem}.py"], input=input, capture_output=True, text=True, timeout=3, ) except subprocess.TimeoutExpired as error: print_yellow(f"TLE: Test {i}") print_yellow(error.stdout.decode()) # これがまれにNoneになる 解決策 Pythonの標準出力はコードが終了するかバッファがいっぱいになるまではバッファに溜め込まれてしまう。 つまり出力量が少なく、かつタイムアウトになるようなコードの出力は標準出力にフラッシュされることなく闇に葬られてしまう。 そこで以下のようにバッファに溜め込まないように-uオプションを付けることで解決。 result = subprocess.run( ["python3", "-u", f"contests/{args.contest}/{args.problem}.py"], input=input, capture_output=True, text=True, timeout=3, ) 参考 実際のコード - GitHub

2025-04-06

【書評】体系的に学ぶ 安全なWebアプリケーションの作り方 第2版

技術面接でボコボコにされた備忘録に書いた通り、先日おこなわれた技術面談でボコボコにされた。特にセキュリティ周りの質問に回答できなかったのが悔しかった。 そこでセキュリティの勉強をするために『体系的に学ぶ 安全なWebアプリケーションの作り方 第2版』なる書籍を読んだ。 せっかくなのでその書評をば。 #PR 体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 楽天 Amazon 良かった点 体系的、網羅的に学べる この書籍は667ページもあって、内容は体系的かつ網羅的に書かれている(と思う)。 まずWebアプリケーションのセキュリティに関するページは膨大で、これだけで400ページくらいある。 特にXSSやCSRFの説明はいろいろなパターンで何度も何度も出てくるので、自然と脳に定着すると思う。 自分は技術面接でCSRFのことを聞かれてクリックジャッキングのことを話してしまったが、もう今後間違うことはないだろう。 さらに、終盤では脆弱性診断の方法、ネットワークやマルウェアに関連する脆弱性、開発マネジメントの手法などについても触れられていて、かなり手広くカバーされているという印象。 一家に一台欲しいタイプの本だと思う。 サンプルが充実している 脆弱性を抱えるソースコードが大量に掲載されているので、脆弱性がどのようにして発生しているのかを理解しやすい。 加えてサンプルアプリケーション用のDocker環境も配布されているので、手元で気軽に脆弱性を体験できるのも良い。 微妙だった点 Docker環境のソースコードをいじれない 本書のサンプルはPHPで書かれているので、PHP初心者の自分的には「今このセッションの中身はどうなっているんだろう」みたいにデバッグしたくなることがあった。 ただDocker環境ではソースコードが編集できないようになっていたので(厳密には.tar.gzを展開した上でソースコードを編集し再圧縮した上でDockerイメージを再ビルドすれば編集できる)、理解が難しい点もあった。 PHPが得意な人にとっては問題ないかも。 認証周りの説明がやや少ない 例えばJWTやOAuthについての説明がほとんどなかった。 自分は仕事ではJWTやOAuthを利用することが多いので、もう少し詳しく知りたかった。 もし第3版が出るなら、ここらへんの説明をもう少し充実させてほしい。 まとめ 総じてかなり良い本だという印象。 上にも書いた通り、一家に一台欲しいタイプの本だと思う。 この本を読んでセキュリティに対するモチベーションが上がってきたので、秋にはセキュリティスペシャリストの試験も受けてみたい。

2025-04-02

更生した不良は偉くないと言う人がいるけど

更生した不良や犯罪者に対して「更生して偉い」と言ったら、「最初から真っ当に生きている人間の方が偉い」と説教してくる人をSNSではたまに見る。 でも「更生して偉い」という発言の裏で行われている比較って「不良」と「真っ当な人間」の比較じゃなくて、「更生した不良」と「更生していない不良」の比較だよな、多分。 だからそういう説教ってめちゃくちゃ的外れだと思う。 例えば運動会の徒競走で1位になった子どもに「偉いね」と褒めただけなのに、「いやボルトの方が何倍も速いし偉いだろ」と説教してくるのと本質的には変わらない。 ある地域、ある学校、ある徒競走の組の中で1位になったことを褒めているのであって、別にプロアスリートと比較して褒めているわけじゃないんだよ。 それと同じように、一度過ちを犯したら更生できない人がたくさんいる中で、それでも更生できた人のことを褒めているだけなんだよな。

2025-03-30

Hugoでtagの先頭文字が大文字になるのを回避する

Hugoはデフォルトだとフッターなどに表示されるtagの先頭文字が大文字になってしまう。 たとえばtags=['hoge']と設定すると、Hugoの.GetTermsで取得したデータはHogeになっている。 ちなみに.Data.Termsだとそのまま取得できるっぽい。 個人的にはtagsの値は入力そのままで表示したいのでこれを回避する方法を調べた。 環境 Hugo 0.145.0 PaperMod (commit e2e1011bdecaf84d59c70fa42ff3d2c29c537b65) 方法 hugo.yamlに以下の設定を追加するだけ。 hugo.yaml titleCaseStyle: none 公式ドキュメントにもちゃんと書いてあるが、なかなか見つけられずにハマった。 デフォルトはapでnone以外にもchicagoとかgoとか色々ある。 ちなみに今回の記事はtagsの話として書いているが、categoriesに関しても同様の設定が適用されているはず(自分はcategoriesを使っていないので未確認)。

2025-03-27

tcardgenを使ってHugoでOGPを設定する

Go製のtcardgenというツールを使って、Hugoの各記事にOGP画像を設定する方法を紹介する。 今回実際に生成した画像は以下の通り。 環境 Hugo 0.145.0 PaperMod (commit e2e1011bdecaf84d59c70fa42ff3d2c29c537b65) tcardgen 0.9.0 tcardgenをインストールする tcardgenの公式ドキュメントを参考にインストールする。 今回はHomebrewを使ってインストールした。 brew install Ladicle/tap/tcardgen tcardgen用のディレクトリを作成する tcardgen用のフォントやテンプレートや設定ファイルはHugoのビルド時には不要なのでなるべくHugoからは切り離したい。 そこで今回はトップレベルに/tcardgenというディレクトリを作成してそこにtcardgenに関連するファイルをまとめた。 トップレベルのディレクトリ構成は以下の通り。 . ├── README.md ├── archetypes ├── assets ├── content ├── hugo.yaml ├── layouts ├── public ├── static ├── tcardgen └── themes フォントをダウンロードする tcardgenの公式ドキュメントを参考にして、均等フォントをダウンロードした。 今回はKintoSans-Bold.ttf、KintoSans-Medium.ttf、KintoSans-Regular.ttfだけを/tcardgen/fontsに放り込んだ。 後述する設定ファイルで他の種類のフォントを指定したい場合はそれらのフォントも同様に/tcardgen/fontsに放り込む。 テンプレートを作成する OGPのテンプレートを作成して、/tcardgen/template.pngとして保存した。 Canvaを使うと簡単にできた。 今回は以下のようなテンプレートを作成した。 tcardgenの設定ファイルを作成する フォントの色や場所などを変更するための設定ファイルを作成した。 今回は公式ドキュメントのtemplate3.config.yamlを参考にして以下のような設定ファイルを作成した。 /tcardgen/config.yaml template: config.yaml title: start: px: 125 py: 185 fgHexColor: "#000000" fontSize: 72 fontStyle: Bold maxWidth: 970 lineSpacing: 10 category: enabled: true start: px: 120 py: 110 fgHexColor: "#555555" fontSize: 42 fontStyle: Regular info: enabled: true start: px: 220 py: 480 fgHexColor: "#555555" fontSize: 38 fontStyle: Regular separator: " | " timeFormat: "2006-01-02" tags: enabled: true limit: 0 titleCaseEnabled: false start: px: 1080 py: 110 fgHexColor: "#FFFFFF" bgHexColor: "#545454" fontSize: 32 fontStyle: Medium boxAlign: Right boxSpacing: 6 boxPadding: top: 6 right: 10 bottom: 6 left: 10 OGP生成用のシェルスクリプトを作成する 毎回tcardgenのコマンドを打つのは面倒なので、シェルスクリプトを作成して一発でOGP画像を生成できるようにする。 ...

2025-03-25

Hugoで外部リンクを新規タブで開くようにする

Hugoはデフォルトだとリンクが同じタブで開かれる。 デフォルトの設定を変更し、外部リンクを新規タブで開くようにする方法を紹介する。 環境 Hugo 0.145.0 PaperMod (commit e2e1011bdecaf84d59c70fa42ff3d2c29c537b65) リンク用のHTMLを作成 /layouts/_markup/render-link.htmlを作成する。 Hugoではリンク用のHTMLは/layouts/_markup/render-link.htmlに定義すると決まっているので、一文字も違わないように/layouts/_markup/render-link.htmlを作成する。 実装は以下。 /layouts/_markup/render-link.html {{- /* Forked from https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_markup/render-link.html At 2025-03-25 Added target="_blank" to external links */}} {{- $u := urls.Parse .Destination -}} {{- $href := $u.String -}} {{- if strings.HasPrefix $u.String "#" -}} {{- $href = printf "%s#%s" .PageInner.RelPermalink $u.Fragment -}} {{- else if and $href (not $u.IsAbs) -}} {{- $path := strings.TrimPrefix "./" $u.Path -}} {{- with or ($.PageInner.GetPage $path) ($.PageInner.Resources.Get $path) (resources.Get $path) -}} {{- $href = .RelPermalink -}} {{- with $u.RawQuery -}} {{- $href = printf "%s?%s" $href . -}} {{- end -}} {{- with $u.Fragment -}} {{- $href = printf "%s#%s" $href . -}} {{- end -}} {{- end -}} {{- end -}} <a href="{{ $href }}" {{- if strings.HasPrefix $href "http" }} target="_blank" rel="noopener" {{- end }} {{- with .Title }} title="{{ . }}" {{- end }}>{{ .Text }}</a> {{- /**/ -}} HugoのGitHubからリンクの実装をコピペして {{- if strings.HasPrefix $href "http" }} target="_blank" rel="noopener" {{- end }} を追加しただけである。 この実装ではhrefがhttpで始まっていたら外部URLだと判定するようにしている。 リンクを使う 上記の実装を終えたら、普通にリンクを使うだけで外部リンクが新規タブで開くようになる。 [もくもくブログ](https://blog.zurukumo.dev)

2025-03-25

Hugoに自作のアフィリエイト用のショートコードを組み込む

Hugoにはショートコードという機能がある。 Reactのコンポーネントみたいな感じで、予め定義したHTMLに引数経由で情報を注入することができる。 今回は以下のようなアフィリエイト用のショートコードを作成したので、その作り方を紹介する。 #PR FACTFULNESS 10の思い込みを乗り越え、データを基に世界を正しく見る習慣 楽天 Amazon 環境 Hugo 0.145.0 PaperMod (commit e2e1011bdecaf84d59c70fa42ff3d2c29c537b65) 要件定義 ショートコードの引数として以下を設定した。 rakuten_href: 楽天リンクのURL amazon_href: AmazonリンクのURL src: 画像のURL title: 本の名前 ショートコード用のHTMLを作成 /layouts/shortcodes/affiliate.htmlを作成する。 /layouts/shortcodes/affiliate.html <div id="affiliate_wrapper"> <div id="affiliate_pr">#PR</div> <img src='{{ .Get "src" }}' id="affiliate_image" alt='{{ .Get "title" }}'/> <p id="affiliate_title">{{ .Get "title" }}</p> <div id="affiliate_link_wrapper"> <a href='{{ .Get "rakuten_href" }}' target="_blank" id="affiliate_rakuten"> 楽天 </a> <a href='{{ .Get "amazon_href" }}' target="_blank" id="affiliate_amazon"> Amazon </a> </div> </div> 本当はidではなくclassを使ったほうが良いと思うが、テーマのCSSに詳細度で負けることがあるのでidを使っている。 ショートコード用のCSSを作成 カスタムCSSに関してはHugoでサポートされているわけではなく、テーマごとにサポートされているっぽい。 たとえばPaperModだと、/assets/css/extendedにCSSファイルを追加すると自動でCSSが読み込まれる。 もしテーマ側でカスタムCSSがサポートされていない場合は自分でテンプレートを編集してheadタグ内でCSSを読み込んだりする必要がある。 もしhoverのような疑似クラスを利用しないなら、style属性に直接スタイルを書く方が楽かもしれない。 ということでPaperModなら/assets/css/extended/affiliate.cssを作成する。 /assets/css/extended/affiliate.css #affiliate_wrapper { display: flex; flex-direction: column; align-items: center; row-gap: 10px; padding-top: 10px; padding-bottom: 20px; width: 252px; background-color: white; border: 1px solid #95a5a6; border-radius: 10px; box-shadow: none; margin-bottom: 20px; } #affiliate_pr { width: 220px; color: #0b80a5; font-size: 14px; text-align: right; } #affiliate_image { max-width: 240px; height: 240px; margin: 0; padding: 0; border-radius: 0; } #affiliate_title { max-width: 200px; color: #1f1f1f; font-size: 14px; margin: 0; padding: 0; } #affiliate_link_wrapper { width: 210px; display: flex; justify-content: space-between; align-items: center; } #affiliate_link_wrapper a { width: 100px; height: 30px; border-radius: 18px; font-size: 14px; line-height: 30px; text-align: center; box-shadow: none; } #affiliate_rakuten { background-color: #bf0000; color: white; } #affiliate_rakuten:hover { background-color: #990000; } #affiliate_amazon { background-color: #ffa41c; color: black; } #affiliate_amazon:hover { background-color: #e68a00; } ショートコードを呼び出す {{< affiliate rakuten_href=https://a.r10.to/hNab5q amazon_href=https://amzn.to/4hG1NVS src=https://hbb.afl.rakuten.co.jp/hgb/46510050.5165b4d2.46510051.bf2ae0c3/?me_id=1278256&item_id=17966596&pc=https%3A%2F%2Fthumbnail.image.rakuten.co.jp%2F%400_mall%2Frakutenkobo-ebooks%2Fcabinet%2F7708%2F2000007017708.jpg%3F_ex%3D240x240&s=240x240&t=pict title="FACTFULNESS 10の思い込みを乗り越え、データを基に世界を正しく見る習慣" >}} {と<の間にスペースを入れてしまって20分ほど溶かしたので注意。 ...

2025-03-25

【書評】プロになるJava

転職先でJavaを使うことになったのでJavaの入門書である『プロになるJava』を購入した。 #PR プロになるJava-仕事で必要なプログラミングの知識がゼロから身につく最高の指南書 楽天 Amazon ぶっちゃけ言語の入門書ってどれを買ってもそんなに大差はない気がするので、今回は書評の練習のつもりで手抜きしてサラッと書いてみる。 良かった点 IDEの説明が豊富だった 大量の画像付きでIDEの使い方を詳しく説明していて、IDE初心者の自分はとても助かった。 以前Javaを勉強したときはIDEの使い方が分からなくて挫折したが、今回は説明が丁寧だったので平気だった。 著者の感想が書かれていた たとえば、「Javaには他の言語にある〇〇って機能がないけど、将来的には欲しいよね」みたいな感想が結構載っていた。 そういう感想があるとJavaがその機能を「あえて採用していない」のか「流行に少し遅れているだけ」なのかを区別できたりする。 あと、「ServletやJSPは保守案件をやるわけじゃないならキャッチアップしなくてもいいよ」とかもJavaのトレンドを理解していない人間にとっては有用な情報だった。 感想や雑談からしか得られないコンテキストって間違いなく存在すると思うので、その点がよかった。 実務面もカバーされていた ウェブフレームワークのSpring BootとテストフレームワークのJUnitの解説にぞれぞれ1章ずつが割かれていたのが良かった。 微妙だった点 サンプルコードがやや不親切 たとえば aaa bbb という既存のコードに新しく処理を追加して aaa ccc bbb というコードに変更するステップがあったとする。 このとき本書では ccc のように追加すべきサンプルコードしか記載されておらず、どこにこのコードを追加すればよいのかが分かりにくかった。 文章でどこに追加すべきかという説明は書いてあったが、個人的にはGitHubのコード差分のような形式で変更箇所の前後2行くらいずつもコードで表示してくれるほうが脳に負荷がかからなくてありがたい。 削除するコードに関しても文章で説明されているだけだったので、斜め読みしていると気づかないことがあった。 まとめ サンプルコードの差分が分かりにくいこと以外は良かったと思う。

2025-03-23