Note

3年後の自分のために書いています

zsh のリダイレクトによる上書き with prezto

おや、 > で上書きできない?

$ zsh --version
zsh 5.7.1 (x86_64-apple-darwin18.2.0)
$ echo foo > bar.txt
$ cat bar.txt
foo
$ echo baz >> bar.txt
$ cat bar.txt
foo
baz
$ echo hoge > bar.txt
zsh: file exists: bar.txt
$ echo hoge >| bar.txt
$ cat bar.txt
hoge

シェルスクリプトでリダイレクトによる上書き防止 - Qiita

こちらに書かれていることと挙動が違うぞ?おかしいなーと思ったら prezto の設定 で CLOBBER が無効になっていました。

参考

zsh: 7 Redirection

シェルの環境変数にいい感じにクレデンシャルを渡す方法

例えば CLI アプリケーションを実行するのに何らかのクレデンシャルが必要な時、アプリケーション側で環境変数に対応してくれている場合はこんな感じでやるといいよと職場の方に教えてもらった。

自分は zsh を使ってるので、その前提で話を進めますがどのシェルでも大きく変わらない話のはずです。

ホームディレクトリ以下に .credentials 的なディレクトリを作り、クレデンシャル名と同じファイル名に値を書いておく。

# ~/.credentials
$ echo "INSERT_YOUR_USER_ID" > hoge_user_id
$ echo "INSERT_YOUR_ACCESS_TOKEN" > hoge_access_token

そして .zshenv などで環境変数にセットしてあげる。

# .zshenv

# environment variables
export HOGE_USER_ID=$(<~/.credentials/hoge_user_id)
export HOGE_ACCESS_TOKEN=$(<~/.credentials/hoge_access_token)

👆 は export HOGE_USER_ID=`cat ~/.credentials/hoge_user_id` 的な感じでも書けるようだ。バッククオート記法の方が sh との互換性があるそう。

これでOK。

$ echo $HOGE_USER_ID
INSERT_YOUR_USER_ID

でもそもそも環境変数にクレデンシャル入れるのセキュリティ的にどうなのって議論もあるようですね。

セキュリティ、環境変数、そして - diary.sorah

参考

変数を使用する | UNIX & Linux コマンド・シェルスクリプト リファレンス

neovim でステータスラインのメッセージを英語にしたい

neovim にしたらステータスラインのメッセージが「挿入」とか日本語になってつらいと思って、 vim のドキュメント 見ながら let $LANG='en'init.vim に追加したらヤンクがバグって複数行のヤンクができなくなった。

let $LANG='en_US.UTF-8' と書いたら解決した。

$ echo $LANG
ja_JP.UTF-8

WindowsMac では $LANG の値が違うのか…

参考

https://vim-jp.org/vimdoc-ja/mlang.html#multilang-messages

vim のインデントがおかしい時は filetype をチェックしよう

自動インデントも改行時のインデントもおかしい、、という時は :filetype indent on になっているか確認しましょう。

vim で適当なファイルを開いて :filetype コマンドで確認できます。

# こんな感じで表示される
filetype detection:ON  plugin:OFF  indent:OFF

ここでファイルを開いたまま :filetype indent on コマンドを打っても ON にはなりますが、すでに開いているファイルに対しては何も変わらない(変更後に開くファイルに対しては有効になる)ので .vimrc に以下を追記しましょう。

" .vimrc
filetype indent on

以下の書き方で同時に plugin も ON にできるようです。(defaults.vim ではこの書き方がされている)

" .vimrc
filetype plugin indent on

参考

https://vim-jp.org/vimdoc-ja/usr_05.html#vimrc-filetype

https://vim-jp.org/vimdoc-ja/filetype.html#:filetype-indent-on

https://vim-jp.org/vimdoc-ja/starting.html#defaults.vim

RubyKaigi 2019 レポート & 所感

はじめに

RubyKaigi 2019 に参加してきました。自分の聞いた発表のレポートや所感を書いていきます。(現時点で Twitter でスライドが公開されているものは載せておきます)

自分の理解・関心の度合いによって、文量に差が出ていますがご容赦ください。

間違っているところなどあればご指摘いただけると幸いです。

各発表のスライドはドリコムさんの以下の記事がよくまとまっていると思うので貼っておきます(全て揃っているわけではありませんが)

先にまとめ

  • Ruby 自体に関する発表は型の話とパフォーマンス改善(並行・並列処理、 JIT コンパイラなど)の話が主だった
  • Ruby を用いた実践的な設計・実装やライブラリにまつわる発表(DSLの設計、API クライアント開発、RSpec など)などもあり、とても勉強になった
  • mruby 楽しい
  • (主に英語力と技術的な基礎知識のなさが原因で)理解できる発表の方が少なかったことが悔しくて英語を学び始め、技術書を5冊ポチった
  • 発表に対する理解度は低かったけど、とても良い刺激を受けられたので、このタイミングで RubyKaigi に行けてよかった

Day 1

Keynote: The Year of Concurrency

  • Ruby 自体に関する発表は型の話とパフォーマンス改善(並行・並列処理、 JIT コンパイラなど)の話が主だった

先にまとめ で 👆 と書いた通り、 Ruby 3 で導入する(しない)「型」と「パフォーマンス改善策」についての話がメインだった。

あとは新機能としてブロック変数を明示的に書かずとも @1 とか(まだ決まっていない)で参照できる Numbered Block Parametercase ~ in によるパターンマッチ構文の話などをしていた。

Ruby 3 Progress Report

静的解析とか Bundler とかキーワード引数の仕様変更の話をしていた気がする。

Terminal Editors For Ruby Core Toolchain

Ruby 2.7 からは irb が便利になりますよって話。

Determining Ruby Process Counts: Theory and Practice

英語がほとんど聞き取れなかったので、スライドや Twitter でのハッシュタグツイートで必死に補完していた。

  • Request Queue を理解するのが大事
    • New Relic を使っていれば LoadBalancer や Nginx で使った時間が見られる
    • ここら辺 かな?
  • AverageResponseTime x 4 x RequestArrivalRate = Ruby Process Count にするとよい
  • Ruby は GVL があるので thread 数でなく process 数で考えるべき
  • JRuby は parallel に実行できるので process 数ではなく thread 数で考えるべき

Woker の設定とか全然わかってないな。勉強しよう。

A Type-level Ruby Interpreter for Testing and Understanding

Type Profiler の話。

Type Profiler を使えば Ruby コードに対して静的解析を行い、実行時エラーを事前に検出したり、型定義を吐き出すことができるようになる。 型定義の出力はあくまでドキュメントとしてのみで Ruby に型注釈は導入しないということを何度も念押ししていた。

Fibers Are the Right Solution

パフォーマンスの高いアプリケーションを書くために Fiber を使おうよという話。理解度は低い。 発表の最初に並行性・並列性についての説明をしてくれていたので、もし動画がアップされたら再度観たい。

GraphQL Migration: A Proper Use Case for Metaprogramming?

REST API などから GraphQL API へ移行する際に、メタプロを使って動的に GraphQL の Type 定義を生成するようにするとよいかもね、という話。

肝心のメタプロの実装部分が出てこなかったのと、実際やるとしてもテストがむちゃくちゃ激しくなりそうなので、アプリケーション内に組み込むイメージは出来なかった。(自分の理解が足りないだけかもしれない)

Day 2

All bugfixes are incompatibilities

Ruby の安定版のメンテナンスの話。(そもそも Ruby では安定版メンテナーと開発版メンテナーが分かれているというのを初めて知った)

最近は昔と比べて互換性を意識してやってますよ、という話だった気がする。

終盤で「ほとんど使われない部分のバグ修正に対応するよりも、より多くのユーザに対するメリットを優先する」的な話をしていて、これは SaaS というかアプリケーション全般にも言えることだよな、と思った。

How RSpec works

RSpec 自体の実装の話。(RSpecGoogle の中の人が作ってるらしい)

rspce-core, rspec-mocks, rspec-expectations のそれぞれの役割と内部実装に触れていてむちゃくちゃ面白かった。

個人的に今回の RubyKaigi の中で一番プレゼンが上手いと思ったので、動画がアップされたら何度も観直して参考にしたい。

Ovto: Frontend web framework for Rubyists

俺が作った最高の Ruby フロントエンドフレームワークを紹介するぜ。というお話。

Opal をベースに作っているらしい。コードは読みやすかった。

State of Sorbet: A Type Checker for Ruby

Stripe のチームによる静的型解析器 Sorbet の話。

現在はプライベートβ版だが、もうすぐ OSS 化されるとのこと。ここ から触れる。

むちゃくちゃ便利そうだけど、 Type Profiler の発表の時に言ってた「Ruby に型注釈は導入しない」の流れとは反している気がする。

A light weight JIT compiler project for CRuby

既存の MJIT のパフォーマンス改善を主目的とした軽量 JIT 基盤開発プロジェクト(MIR プロジェクト)の話。 今回の RubyKaigi の中で一番理解度が低い発表。聞いてる時はほとんど何も分からなかった。

GCCLLVM の話が最初にでてきて、「あれ GCC って Ruby と関係あったっけ? LLVM ってなんだ?」ってなっていた。

何も分からなかったがゆえにこの発表が後述する技術書を買う一番のモチベーションになった。

What is Domain Specific Language?

RubyDSL を書くお話。

  • なぜ internal DSL は言語と見なされるのか?
    • 独自のセマンティクスを持っているから
  • なぜ DSL にすると読みやすいのか?
    • プログラマやそれ以外の人でもそのドメインの知識があればコードの意味を理解できるように設計されているから
  • どうすればよい DSL を設計できるか?
    • Ruby の黒魔術を適度に利用する :imp:

そういえばメタプロ Ruby にも「Ruby はその柔軟性ゆえに DSL を書くのに適している」というような記述があった気がする。

Lightning Talks

個人的に良いと思った2つの発表スライドを載せておきます。

Day 3

Ruby Committers vs the World

以下のような議題で公開開発会議が行われていた。

  • Range の () を省略したときでも良い感じに書くための演算子を導入したい(e.g. a..b |> each
  • 右代入を導入したい
  • Numbered Block Parameter をどの記号にするか?

個人的には Numbered Block Parameter も含めて 👆 全部不要では?と思っている勢です。

このセッションで「Ruby を作る人達は別のパラダイムの言語を知った上でそこからインスピレーションを受けて Ruby を開発しているんだ」ということを知り、後述する Haskell 本を買うモチベーションになった。

(partially) Non-volatile mruby

mruby のメモリ管理の話。

実はこの前夜 mruby のワークショップに行っていて、 mruby にだいぶハマっていたため、とても面白かった。

mruby で遊ぶ用のマイコンが欲しい。

Best practices in web API client development

Ruby で書く Web API クライアントのお話。 API クライアント開発におけるアンチパターンとベストプラクティスがだいぶ実践的で、とても勉強になった。

発表内で紹介されていた リファクタリング Ruby 読みたいけどめっちゃ高いので買うか悩む。

The future of the Bundled Bundler with RubyGems

Bundler の話。

Bundler を RubyGems にマージしたいと言っていたが、どういうことかあんまりわかっていない。 library rubygems のコードに組み込むということかな?

Reducing ActiveRecord memory consumption using Apache Arrow

Apache Arrow を使って ActiveRecord #pluck よりパフォーマンスのよいクエリ実行に成功したよ、という話。

マルチスレッド(10スレッド)の場合で、#pluck と比べてメモリ消費量は2倍になったが、実行時間は 1/5 になったとのこと。

The send-pop optimisation

send-pop を最適化して Ruby のパフォーマンスを改善したよという話。

send-pop とは、 Ruby のメソッドで必ず返される(それが使われるかどうかは別として)何らかの返り値について、それが使われなかった時に捨てる、という一連の操作のこと(メッセージを send したあとに使わなければ pop する)

結論から言うと、メソッドの返り値を使うかどうかのフラグをもたせて、そのフラグを見て後処理を最適化することで処理速度をアップさせたらしい。(やりたいことは分かったが、実装部分の説明では理解が追いつかなかった)

Optimization Techniques Used by the Benchmark Winners

スピード狂によるスピード狂のための Ruby で究極の速さを追い求める話。

「それ、C言語でいいのでは?」 とか絶対に言えない雰囲気で、可読性完全無視で Ruby で最高のパフォーマンスを発揮する手法について語られていた。

発表されていたご本人が一番楽しそうでなによりでした ^^

所感とか

  • 刺激を受けて技術書を 5 冊買いました。
  • 半分ぐらい英語の発表でしたが全然聞き取れず、自身の英語力の無さを痛感したので、英語を学び始めました。(まずはリスニングからかなと思っています)
    • Web 上のドキュメントを読む分には Google 翻訳さえあればぶっちゃけそんなに困らないが、こういうリアルなカンファレンスや動画コンテンツや活字での情報収集が英語でできるのとできないのとでは、全然情報量が違ってくると思った。 あと、カンファレンスに登壇まではできずとも英語で鋭い質問ができるとかっこいいなという憧れもある。
  • mruby で遊ぶ用のマイコンが欲しい。
  • 今見返すとレポートというより感想文ですね、おそらく理解度が低いためです。もっと勉強します。

ActiveRecord の `#all`, `#find_each`, `#find_in_batches`, `#in_batches` の違いについて

表題の件、ずっと曖昧に理解していた。

1.2 複数のオブジェクトをバッチで取り出す - Active Record クエリインターフェイス - Rails ガイド

Rails のコードも読みながら ↑ の内容にプラスα してまとめる。

結論

処理するレコードが 1000 件以下なら何使っても一緒。

1000 件以上になってくると、 #all ではなく、 #find_each, #find_in_batches, #in_batches など ActiveRecord::Batches のメソッドを用途に合わせて使い分けていくのが良い。

#all

Returns an ActiveRecord::Relation scope object.

該当コードは ここActiveRecord::Relation オブジェクトを返す。

users = User.all
users.size # Fires "select count(*) from  users" and returns the count
users.each {|user| puts user.name } # Fires "select * from users" and loads user objects

テーブル全体を一度に取り出し、レコード 1 行ごとにオブジェクトを生成し、そのオブジェクトの配列をメモリに載せるので、メモリを圧迫する可能性がある。

#find_each, #find_in_batches, #in_batches

大事なのはここから。

#find_each, #find_in_batches, #in_batchesActiveRecord::Batches として定義されており、バッチサイズで指定したレコード数ごとに処理できるため、大量のレコードを扱う際にメモリ消費量を削減できる。デフォルトのバッチサイズが 1000 件なので、それよりレコード数が少ない場合は #all を使っても変わらない。

実は上記メソッドの関係は #find_each の中で #find_in_batches を呼び出し、 #find_in_batches の中で #in_batches を呼び出す、という関係になっている。

柔軟性でいうと #in_batches > #find_in_batches > #find_each というわけだ。

#in_batches から見ていく。

#in_batches

Yields ActiveRecord::Relation objects to work with a batch of records.

該当コードは ここ。長いので読めてない。 ActiveRecord::Relation オブジェクトをブロックに渡すので、一番柔軟な使い方ができる。

User.where("age > 20").in_batches do |relation|
  # Relation オブジェクトに対する処理が書ける
  relation.destroy_all
end

relation.where でつなげて絞り込みしたりとか relation.pluck 的な形でも使える。

#find_in_batches

Yields each batch of records that was found by the find options as an array.

該当コードは ここ#in_batches で取り出したバッチを #to_a してブロックに渡す。

User.where("age > 20").find_in_batches do |users|
  # バッチ前処理が書ける
  # 配列に対する処理が書ける(Relation オブジェクトに対する処理は書けない)
  users.each { |user| user.drink_alcohol! }
  # バッチ後処理も書ける
end

#find_each

The find_each method uses find_in_batches with a batch size of 1000 (or as specified by the :batch_size option).

該当コードは ここ#find_in_batches で取得したモデルの配列を #each してブロックに渡す。

User.where("age > 20").find_each do |user|
  # バッチ処理の前後に処理がないなら一番スッキリ書ける
  user.drink_alcohol!
end

実装から学ぶ HTTP 通信 〜 Ruby 標準ライブラリ編〜

社内勉強会のコピペ。


HTTP とは

HTML など をやり取りする時の決まりごと

Web は URL で指定した HTML など を HTTP という決まりごとに沿ってやり取りしている

詳しくは Webを支える技術 という本に書いてあります

今日話すこと

  • Ruby の標準ライブラリで HTTP 通信がどう実装されているかをコードリーディングしながら見ていきます(Ruby の話が若干多めかも?)

今日話さないこと

登場する標準ライブラリ

先に結論

HTTP 通信は実装的にはネットワーク越しに(ソケットを通して?)ファイルの読み書きをしているだけ!

たとえばこんなコード

require 'webrick'

srv = WEBrick::HTTPServer.new({ :DocumentRoot => './',
                                :BindAddress => '127.0.0.1',
                                :Port => 20080})
srv.mount('/', WEBrick::HTTPServlet::FileHandler, 'index.html')
trap("INT"){ srv.shutdown }
srv.start
<div>
  Hello!
</div>
require 'net/http'
require 'uri'

uri = URI.parse('http://localhost:20080/')
puts Net::HTTP.get(uri)

実行するとこうなります

$ ruby http_server.rb
[2019-03-22 07:40:24] INFO  WEBrick 1.4.2
[2019-03-22 07:40:24] INFO  ruby 2.5.3 (2018-10-18) [x86_64-darwin18]
[2019-03-22 07:40:24] INFO  WEBrick::HTTPServer#start: pid=1786 port=20080

$ ruby http_client.rb
<div>
  Hello!
</div>

この時何が起きているか、実装を覗いてみる。

Webrick サーバ起動まで@webrick

URI のパース@uri

HTTP クライアントが TCP コネクションを開くとこまで@nethttp

HTTP (GET) リクエスト送信まで@nethttp

HTTP リクエストのパースまで@webrick

HTTP レスポンスの作成まで@webrick

HTTP クライアントでレスポンスを表示するまで@nethttp

  • HTTP#transport_request に戻って HTTPResponse.read_new とかでレスポンスを読み込む
  • 読み込まれたレスポンスがいろいろ通って HTTP.get の中の #get_response の返り値としてもどってきて、その body を取り出している…!!!

結論

HTTP 通信は実装的にはネットワーク越しに(ソケットを通して?)ファイルの読み書きをしているだけ!