Emacs でグローバル IP を取得してクリップボードにコピーする
(leaf request :ensure t :leaf-defer t) (defun globalip-me () (interactive) (request "https://globalip.me" :parser 'buffer-string :success (cl-function (lambda (&key data &allow-other-keys) (let ((globalip (substring data 0 (- (length data) 1)))) (kill-new globalip) (message globalip))))))
Emacs の関数で文字列をコピーする
kill-new で文字列をコピーできる。
https://www.gnu.org/software/emacs/manual/html_node/eintr/kill_002dnew-function.html
Stimulus の target に型をつける
https://stimulus.hotwired.dev/reference/using-typescript#define-target-properties
import { Controller } from "@hotwired/stimulus" export default class MyController extends Controller { static targets = [ "input" ] declare readonly hasInputTarget: boolean declare readonly inputTarget: HTMLInputElement declare readonly inputTargets: HTMLInputElement[] }
Rails の force_ssl を有効にしたまま Nginx から Strict-Transport-Security ヘッダーを追加する
force_ssl: true の際に ActionDispatch::SSL は必ず Strict-Transport-Security を付与する。
ssl_options の hsts に false を設定した場合にも max-age=0 でつけてくる。
Nginx のみで Strict-Transport-Security をつけるようにしたかった。
以下の issue で ssl_options の設定で HSTS ヘッダーを無効にする提案があった。 https://github.com/rails/rails/issues/48609
結果としては、Rails では HSTS ヘッダーを必ずつけるようにして安全側に倒す。 リーバスプロキシで HSTS ヘッダーを設定したい場合はリバースプロキシで Rails がつけた HSTS ヘッダーを削除せよとのこと。
削除コマンドも提示されていた。
https://github.com/rails/rails/issues/48609#issuecomment-1662511786
location @app { # … proxy_hide_header Strict-Transport-Security; add_header Strict-Transport-Security "max-age=31536000;"; # … } http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_hide_header
ちなみに同じ値であっても重複してつけた場合は https://hstspreload.org/ で以下のようにエラーが出る
Error: Multiple HSTS headersResponse error: Multiple HSTS headers (number of HSTS headers: 2).
Strict-Transport-Security ヘッダーの max-age は 2 年が推奨
hstspreload.org で2年が推奨されている
https://hstspreload.org/#deployment-recommendations
また、Mozilla でも 2 年を推奨
https://wiki.mozilla.org/Security/Server_Side_TLS
これらを受けて Rails も 2 年がデフォルトになっている。
https://github.com/rails/rails/pull/38345
cURLコマンドでヘッダー情報のみ出力する
-I または –head オプションでできる。
$ curl -I https://example.com HTTP/2 200 content-encoding: gzip accept-ranges: bytes age: 500505 cache-control: max-age=604800 content-type: text/html; charset=UTF-8 date: Fri, 01 Mar 2024 05:28:20 GMT etag: "3147526947" expires: Fri, 08 Mar 2024 05:28:20 GMT last-modified: Thu, 17 Oct 2019 07:18:26 GMT server: ECS (sac/257F) x-cache: HIT content-length: 648 curl –head https://example.com HTTP/2 200 content-encoding: gzip accept-ranges: bytes age: 59493 cache-control: max-age=604800 content-type: text/html; charset=UTF-8 date: Fri, 01 Mar 2024 05:28:50 GMT etag: "3147526947" expires: Fri, 08 Mar 2024 05:28:50 GMT last-modified: Thu, 17 Oct 2019 07:18:26 GMT server: ECS (sac/2520) x-cache: HIT content-length: 648 cURL の man を抜粋
routes.rb でリダイレクト
get '/stories', to: redirect('/articles') https://railsguides.jp/routing.html#%E3%83%AA%E3%83%80%E3%82%A4%E3%83%AC%E3%82%AF%E3%83%88
Arch Linux で Kernel をダウングレードする
起動しなくなったためダウングレードする。
6.7.5 から起動しなくなったので 6.7.4 に下げたい。
Live USB から起動して以下のコマンドでダウングレードした。
mount /dev/nvme0n1p3 /mnt mount /dev/nvme0n1p1 /mnt/boot arch-chroot /mnt cd /var/cache/pacman/pkg ls -al | grep linux-6.7 pacman -U linux-6.7.XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
パスワード入力フォームのautocomplete
https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/password
https://developer.mozilla.org/ja/docs/Web/HTML/Attributes/autocomplete
input type="pasword" の autocomplete は 2 種類ある
new-password current-password サインアップとパスワードリセットでは new-password
パスワードの Confirmation も new-password
サインインでは current-password
サインアップとパスワードリセットで、メールアドレスやユーザ名の入力とパスワードの入力が別ページになっている場合も ユーザ名・パスワードのフィールドはパスワードのフィールドと同じページに表示しないとパスワードマネージャがユーザ名・メールアドレスを保存できない。
入力の必要がないフィールドは CSS で表示しないようにしてもよい。
https://www.chromium.org/developers/design-documents/form-styles-that-chromium-understands/
ローカルでRustの標準ライブラリのAPIリファレンスを見る
以下のコマンドを実行るうとブラウザが開き std の APIリファレンスを確認できる
rustup doc –std std の API リファレンス以外にも Rust By Example や Rustlings などへのリンクが書かれている Rust Documentation を
rustup doc で開くことができる
input タグのプレイスホルダーの使用は避けよう
https://developer.mozilla.org/ja/docs/Web/HTML/Element/input
メモ: placeholder 属性は、フォームを説明するためには他の方法ほど意味的に有用ではなく、コンテンツに予期せぬ技術的な問題を引き起こす可能性があります
とあった。同じページ内の詳細を確認してみると
どのように見えるかのヒントを提供するために使用されるもので、説明やプロンプトではない スクリーンリーダーがアクセスできない ユーザが入力していると非表示になる 自動ページ翻訳機能で翻訳されない が理由として挙げられていた。
c.f. : 入力欄(フォーム入力)要素 - HTML: ハイパーテキストマークアップ言語 | MDN #プレイスホルダーはアクセシブルではない
メモ: 避けることができるのであれば、placeholder 属性を使用しないでください。 要素にラベルを付ける必要があるのであれば、 属性を使用してください。
とまで書かれているため使用は避けよう
word-break: break-word は非推奨
https://developer.mozilla.org/ja/docs/Web/CSS/word-break
メモ: word-break: break-word は非推奨ですが、指定した場合は overflow-wrap プロパティの値とは関係なく、 word-break: normal や overflow-wrap: anywhere と同じ効果になります。
break-word を使った場合
overflow-wrap プロパティの値とは関係なく、 word-break: normal や overflow-wrap: anywhere と同じ効果になります。
ということなので break-word のかわりに overflow: anywhere を使うことにする。
https://developer.mozilla.org/ja/docs/Web/CSS/overflow-wrap
GNOME Keyring から SSH の機能が無効化されたので gcr-ssh-agent を導入する
GNOME Keyring パッケージを更新したところ、Git 操作で SSH 鍵のパスフレーズを求められるようになった。
1:46 から SSH の機能がデフォルトで無効化された。
ArchWiki に書かれている通り gcr-4 パッケージに含まれる /usr/lib/gcr-ssh-agent を有効化する。
yay -S gcr-4 systemctl enable –user gcr-ssh-agent 後は SSH_AUTH_SOCK 環境変数 に $XDG_RUNTIME_DIR/gcr/ssh を設定する。
VSCode や Emacs を GUI で使っているため .xprofile に設定した。
export SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/gcr/ssh と設定した。
keyword_init on Struct.new Is No Longer Needed in Some Cases
I occasionally use Struct class when I am programming with Ruby. From Ruby 2.5, objects can be initialized with keyword arguments if keyword_init: ture is passed to Struct.new. I had used this since then because it is more readable and flexible in most cases.
From Ruby 3.2, keyword arguments are accepted by default.
Film = Struct.new(:title, :director) #
keyword_init: true
is no longer needed from Ruby 3.2 Film.new(title: 'Inception', director: 'Christopher Nolan') # => #<struct Film title="Inception", director="Christopher Nolan"> It’s not such a gorgeous update but it definitely became more natural and convenient!Git のバンドルファイル
git clone でホスティング先から clone できないような場合にバンドルファイルを作成して共有することができる。
git bundle create repo.bundle HEAD main でバンドルファイルを作成する。
ファイルをもらった側は
git clone repo.bundle repo でホスティング先から clone したのと同じように Git リポジトリを復元できる。
c.f. Git のさまざまなツール - バンドルファイルの作成
Custom Scalars Should Have Spec URL in GraphQL October 2021 Edition
Today I learned that in the latest GraphQL specification (October 2021 Edition), when defining a custom scalar type, the GraphQL service should provide a URL of a human-readable specification. It can be provided via either the new built-in @specifiedBy directive or the specifiedByURL introspection field. The @specifiedBy directive takes the URL as its argument like this:
scalar DateTime @specifiedBy(url: "https://tools.ietf.org/html/rfc3339") See Also https://spec.graphql.org/October2021/ https://github.com/graphql/graphql-spec/issues/635
ESLint no-unused-vars Rule for TypeScript
The default no-unused-vars complains even for parameters in interface declarations. It does not mean you have to write an eslint-disable comment or put a _ prefix for these parameters every time. There’s an extended version of this rule for TypeScript. With disabling the default no-unused-vars and enabling @typescript-eslint/no-unused-vars, the problem mentioned here disappears.
By the way, there’s an ESLint plugin that makes unused imports auto-fixable. This plugin uses either the default no-unused-vars or the other one.
Better Use classnames
Merging two or more className could be a troublesome text processing. For instance, if you are not used to JavaScript’s undefined, and are not careful enough, you could end up embedding an unexpected “undefined” class without knowing.
function Foo({ className }: { className?: string }) { return <div className={
default-class ${className}
}>foo</div> } // <Foo /> will be rendered as <div class="default-class undefined">foo</div> It can be avoided by setting its default value as an empty string, but still, there’s another minor problem.Generics and Arrow Functions in TSX Files
When a function needs a generic type as its return value, it can be written like this:
const Foo = <T>(a: T) => a Of course, the same thing can be done in .tsx files but the syntax has to be slightly different. Since <T> could be interpreted as a JSX tag, the example above will cause a syntax error like JSX element ‘T’ has no corresponding closing tag.ts(17008).
A trailing comma helps to avoid this ambiguity and make it clear as a list of generic types in TSX files.
GraphQL Description in Markdown
Today I learned that GraphQL description allows Markdown syntax (CommonMark). Here’s an example GraphQL schema:
type Query { """ The description will be interpreted as Markdown. Good to know :) * Hello * Hola * こんにちは """ whatever: String } Of course, GraphiQL will render the description as Markdown like this:
See Also https://spec.graphql.org/June2018/#sec-Descriptions
AWS CLIで指定したプロファイルを使う
–profile で指定するか、環境変数 AWS_PROFILE で指定できる。
シェルスクリプトでラップしていることが多いので AWS_PROFILE を使うのがよさそう。
cf. https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-profiles.html#using-profiles
プロジェクト内の特定のファイルを自分だけgitignoreする
自分だけ EditorConfig や Language Server を使いたいときなど、ファイルを追加したいが他のメンバーに影響がでないようコミットはしたくない場合がある。 {PROJECT_ROOT}/.gitignore では差分が出てしまい、~/.gitignore では他のプロジェクトでgit管理している場合に困る。
プロジェクト内の特定のファイルを自分だけgitignoreするには、プロジェクトの .git/info/exclude に .gitignore と同じように除外ファイルを指定すればよい。。
cf. https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files#excluding-local-files-without-creating-a-gitignore-file
YAML Anchors Do Not Have to Be Unique
Believe it or not, according to the YAML specification, anchors do not have to be unique. In other words, an anchor name can be reused like this:
alpha: &foo # first one repeat: me bravo: *foo charlie: &foo # second one another: one delta: *foo Let’s say the YAML file name is a.yaml. Here’s an example result with Ruby’s YAML library:
require 'yaml' yaml = YAML.load_file('a.yaml', aliases: true) p yaml['bravo'] # => {"repeat"=>"me"} p yaml['delta'] # => {"another"=>"one"} For what?
Pathname#join ignores previous paths when absolute path passed
irb(main):009:0> Pathname('/foo').join('bar', 'baz') => #<Pathname:/foo/bar/baz> irb(main):010:0> Pathname('/foo').join('bar', '/baz') => #<Pathname:/baz> In second, join ignores first string because of second sring has absolute path format.
GTMの同意の設定
GTM では Cookie の利用について同意をとったかどうかを設定できる。
https://support.google.com/tagmanager/answer/10718549?hl=ja
GA において Cookie の利用について同意を取っていない場合は ping を送信することで Cookie とは関係なくアクセス数やコンバージョンを集計できる。
Use SSH Agent Forwarding in Docker
version: "3.7" services: app: build: context: . environment: - SSH_AUTH_SOCK=/tmp/ssh-agent.sock volumes: - .:/usr/src/app - "$SSH_AUTH_SOCK:/tmp/ssh-agent.sock"
Javascript Destructuring Assignment With Default Value
Destructuring assignments can have default values.
const [one, two, three = "tres"] = ["uno", "dos"] console.log(three) // "tres" const { uno, dos = "two", tres } = { uno: "one", tres: "three" } console.log(dos) // "two" The same goes for function parameters.
function foo({ uno, dos = "two", tres }) { console.log(dos) // "two" } foo({ uno: "one", tres: "three" }) See Also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters#destructured_parameter_with_default_value_assignment
Use ES Modules in your HTML files
Do you want to use ES Modules in your HTML files?
It’s easy! Just specify type="module" for the script element.
./lib/hello.js const hello = () => alert("Hello!") export default hello ./index.html <script type="module"> import hello from "./lib/hello.js" hello() </script> You also want to import and use React, right?
In that case, you need to find the URL for React distributed as ES Modules.
Hmmm… resolving dependencies seems to be a challenge too.
Disabling bin/rails console Autocomplete Feature
IRB provides an autocomplete feature today. It’s really great but since it makes full use of escape sequences, sometimes could cause trouble when sharing a terminal with others over the internet (using Visual Studio Code Live Share, tmate, or some such).
Today I learned that the autocomplete feature can be disabled temporarily by passing –noautocomplete option.
irb:
irb –noautocomplete bin/rails console:
bin/rails c – –noautocomplete See Also https://www.ruby-lang.org/en/news/2021/12/25/ruby-3-1-0-released/ https://ruby-doc.org/stdlib-3.1.2/libdoc/irb/rdoc/IRB.html#module-IRB-label-Command+line+options https://docs.ruby-lang.org/en/master/IRB.html
Apollo Client useMutation Takes onCompleted Callback
Today I learned that the useMutation API which Apollo Client provides takes onCompleted callback function. The callback will be called when the mutation successfully completes.
const MyPage: NextPage = () => { const router = useRouter() const [theMutation, { loading, error }] = useMutation(THE_MUTATION, { onCompleted() { router.push('/transition-to-here-after-the-mutation') }, }) // the rest of the component implementation goes here… } The onCompleted option is listed in the document for sure and I should have known it but somehow been overlooking it.
Crash Course Supervisord on Docker
Want to run multiple processes on a single container? That’s where Supervisord comes into play. Supervisord is a process management tool and it is easy to set up on top of Docker images.
Originally, I wanted to set up a VNC server on an Ubuntu-based image (Playwright). To that end, I had to run the following commands on the same container:
Xvfb: display server x11vnc: VNC server fluxbox: window manager websockify: proxy to serve noVNC client Since Docker’s entrypoint runs only one process I needed something that invokes and takes care of these 4 processes.
Docker Entrypoint That Takes Args Needs to Be Array
Dockerfile’s ENTRYPOINT instruction accepts both string or array as its argument. However, the specified entrypoint won’t take any argument if it is passed as a string. Because of that, it should be an array if the entrypoint is intended to take arguments.
For example, images created by the Dockerfile below do not take any arguments:
FROM debian:11-slim ENTRYPOINT "/bin/bash" # passing as a string That’s why the following docker run command does not output anything.
Proxy HTTP Requests for localhost
Let’s say you want to proxy http://localhost:4000
to somewhere else like http://another-host:4000
. Then you can simply do that with socat(1)
.
socat TCP-LISTEN:4000,fork,reuseaddr TCP:another-host:4000