手元にあるパッチの一覧を確認するシェルスクリプト
ブログに書いていなかったことに気づいたので、残しておく。
モチベーション
OSSや仕事のコードを修正してるときに「まだ動作確認ができていない」「コミットメッセージの説明が不十分」なコミットが手元に溜まることがある。
このパッチの一覧を確認するため、シェルスクリプトを使っている。
シェルスクリプト
このファイルを PATH の通ったディレクトリに git-patch
というファイル名で置く。
$ cat << 'EOF' > ~/bin/git-patch #!/bin/bash ls ~/ghq/github.com/*/*/.git/refs/heads/patch/* grep refs/heads/patch ~/ghq/github.com/*/*/.git/info/refs EOF $ chmod +x ~/bin/git-patch
Gitは git-***
という命名規則の実行可能ファイルをサブコマンドで実行できるので、以下のように使用できる。
$ git patch
このスクリプトを実行すると patch/ で始まるブランチの一覧を確認できる。
Railsの DATABASE_URL で指定できるパスワードの文字種
Railsアプリケーションを動かすAWSリソースをTerraformで作る場合、random_password を使ってDBパスワードを生成します。
しかし、 DBパスワードに一部の記号が含まれていると環境変数 DATABASE_URL
で渡す際に URI::InvalidURIError
が発生してしまうため、使える文字種を調べた。
調査
- 環境変数のフォーマットは database.yml に記載されている
- 例:
DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
- 参考: railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml.tt
- 例:
- この文字列の解析には
URI.parse
が使用されてそう - RFCのUser Informationに文字種は書かれていない*1
URI.parse
を使ってるのは分かったので、実際に解析して調べた。
require 'uri' specials = '!@#$%&*()-_=+[]{}<>:?'.chars check = -> { URI.parse("postgres://myuser:#{_1}@localhost/somedatabase").password rescue nil } specials.select(&check).join #=> "!$&*()-_=+:" specials.reject(&check).join #=> "@#%[]{}<>?"
結果
使える文字種は "!$&*()-_=+:"
なので、DBパスワードを生成するときの定義は以下の通り。
resource "random_password" "password" { special = true override_special = "!$&*()-_=+:" }
*1:他の箇所に記載あったかもしれないが、詳細は調べてない
GitHubのPull Requestでコミットメッセージの文字数を調べる
GitHubのPull Requestで各コミットメッセージの文字数や変更行数を取得する方法を調べたのでまとめておく。
GraphQL APIで取得できたので、雑にcsv形式にして出力した。
stat-commit-messages.sh
#!/bin/bash set -eu OWNER=${1:-rails} NAME=${2:-rails} NUMBER=${3:-20000} echo 'author,subject,body,additions,deletions,changes' gh api graphql -F owner="$OWNER" -F name="$NAME" -F number=$NUMBER -f query=' query($name: String!, $owner: String!, $number: Int!) { repository(owner: $owner, name: $name) { pullRequest(number: $number) { commits(first: 100) { nodes { commit { author { name }, messageHeadline, messageBody, additions, deletions } } } } } } ' --jq '.data.repository.pullRequest.commits.nodes[] | map("\"\(.author.name)\",\(.messageHeadline|length),\(.messageBody|length),\(.additions),\(.deletions),\(.additions + .deletions)") | join("\n")'
実行結果
$ ./stat-commit-messages.sh author,subject,body,additions,deletions,changes "Ryuta Kamizono",43,1714,4,4,8 $ ./stat-commit-messages.sh rails rails 46762 author,subject,body,additions,deletions,changes "David Heinemeier Hansson",24,78,63,0,63 "David Heinemeier Hansson",47,465,105,22,127 "David Heinemeier Hansson",27,0,3,0,3 "David Heinemeier Hansson",35,0,18,4,22 "David Heinemeier Hansson",40,0,7,0,7 "David Heinemeier Hansson",41,0,2,2,4 "David Heinemeier Hansson",3,0,2,0,2 "David Heinemeier Hansson",36,0,9,4,13 "David Heinemeier Hansson",28,0,7,2,9 "David Heinemeier Hansson",9,0,0,2,2 "David Heinemeier Hansson",54,0,1,0,1 "David Heinemeier Hansson",35,0,5,0,5 "David Heinemeier Hansson",31,0,2,1,3 "David Heinemeier Hansson",9,0,18,0,18 "David Heinemeier Hansson",47,0,4,0,4 "David Heinemeier Hansson",13,0,3,0,3 "David Heinemeier Hansson",57,0,7,3,10 "David Heinemeier Hansson",29,0,2,1,3 "David Heinemeier Hansson",40,14,1,4,5 "David Heinemeier Hansson",45,0,2,1,3 "David Heinemeier Hansson",19,0,2,0,2 "David Heinemeier Hansson",13,0,28,0,28 "David Heinemeier Hansson",14,0,5,5,10
実際のGitHubのPull Requestは以下の2つで、取得した値もあってそう。
- The default collation should not be change by kamipo · Pull Request #20000 · rails/rails · GitHub
- Add default Dockerfiles by dhh · Pull Request #46762 · rails/rails · GitHub
参考ページ
GraphQLのドキュメント docs.github.com
Rubyの定数の探索順序
Ruby技術者認定試験の勉強のため、この機会にちゃんと理解しておく。
バージョン
$ ruby --version ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [arm64-darwin21]
継承
子クラスに定数がない場合、親クラスの定数を参照できる。
class A FOO = "foo" BAR = "bar" end class B < A BAR = "bar-B" end puts A::FOO #=> foo puts A::BAR #=> bar puts B::FOO #=> foo puts B::BAR #=> bar-B
include, prepend
モジュールも継承と同様に参照できる。
継承関係で探索するため、 include
と prepend
が混ざった場合は prepend
で追加したモジュールの定数が優先される。
module M1 FOO = "foo" BAR = "bar" end module M2 FOO = "foo-M2" end class A include M1 BAR = "bar-A" end class B prepend M1 BAR = "bar-B" end class C include M1 include M2 end class D prepend M1 include M2 end puts M1::FOO #=> foo puts M1::BAR #=> bar puts A::FOO #=> foo puts A::BAR #=> bar-A puts B::FOO #=> foo puts B::BAR #=> bar-B puts C::FOO #=> foo-M2 puts D::FOO #=> foo
スコープ
継承関係よりもネスト関係の方が優先されるが、トップレベルの定数はネスト外部とは対象外になる。詳細は 変数と定数 (Ruby 3.1 リファレンスマニュアル) を参照。
CONST = "top-level" module M CONST = "M" class A CONST = "A" end class B < A puts CONST #=> M end end class M::A puts CONST #=> "A" end class M::A::C puts CONST #=> "top-level" end
Rubyのフリップフロップ (flip-flop) 構文
Ruby技術者認定試験の勉強をしていて、初めて知った機能だったのでまとめておく。
Ruby長年やってるけど、条件式の範囲式とか初めて見た。
— 神速 (@sinsoku_listy) 2022年10月6日
5.times { (_1==2)..(_1==3) ? p(_1) : 0 } みたいなやつ。
公式ドキュメント
挙動
基本
公式ドキュメントから引用。
5.times{|n| if (n==2)..(n==3) p n end } #=> 2 # 3 5.times{|n| if (n==2)...(n==3) p n end } #=> 2 # 3
テキストの処理
複数行のテキストから範囲を取得する際に便利らしい。
以下の例ではマークダウン記法でRubyのコード部分を取得するために使用している。
input = <<-EOF The reproduction code is as follows. ### pattern 1 ```rb puts "hello" ``` ### pattern 2 ```rb puts "world" ``` EOF input.each_line do |line| puts line if (line =~ /```rb$/)..(line =~ /```$/) end #=> ```rb # puts "hello" # ``` # ```rb # puts "world" # ```
アルファベットの1, 2, 10, 11, 20, 21文字目を表示
('a'..'z').each_with_index { |x, i| print x if (i%10==0)..(i%10==1) } #=> abkluv
「..」と「...」の違い
公式ドキュメントから引用。
5.times{|n| if (n==2)..(n==2) p n end } #=> 2 5.times{|n| if (n==2)...(n==2) p n end } #=> 2 # 3 # 4
違いは分かるけど、有効な使い道がよく分からん...。
Ruby 2.6で非推奨になるも、Ruby 2.7で復活
クックパッドの記事は読んだはずなのに、フリップフロップ構文を使う機会が無さ過ぎて記憶に全く残っていなかった。
フリップフロップ構文を覚えて
資格試験のためだけに覚えるけど、仕事で使うことはないかな...
Ruby 3.1.xだと継承ツリーに後からincludeでモジュールを追加できる
Ruby技術者認定試験Goldの勉強をしていてRubyのバージョンによって変わっている挙動を知ったのでメモ。
検証コード
module M1 def method_1 __method__ end end class C include M1 end p C.new.method_1 module M2 def method_2 __method__ end end module M1 include M2 end p C.new.method_2
Ruby 2.7.6
:method_1 Traceback (most recent call last): a.rb:23:in `<main>': undefined method `method_2' for #<C:0x0000000148184c48> (NoMethodError) Did you mean? method method_1 methods
Ruby 3.1.2
:method_1 :method_2
Rubyのensure節の挙動
Ruby技術者認定試験の対象バージョンがRuby 3.1.xになったので、試験勉強していて気づいた挙動をブログにメモっておく。
ensure節の値は無視される
ensure 節が存在する時は begin 式を終了する直前に必ず ensure 節の本体を評価します。
begin式全体の評価値は、本体/rescue節/else節のうち最後に評価された文の値です。また各節において文が存在しなかったときの値はnilです。いずれにしてもensure節の値は無視されます。引用: https://docs.ruby-lang.org/ja/latest/doc/spec=2fcontrol.html
def foo "foo" ensure puts "ensure" "bar" end puts foo #=> ensure #=> foo
ensure節にreturnがあると無視されない
def foo "foo" ensure puts "ensure" return "bar" end puts foo #=> ensure #=> bar