- Ruby / Rails関連
週刊Railsウォッチ(20200721後編)『パーフェクトRuby on Rails』増補改訂版発売間近、scan_left gemでレイジーなinjectほか
こんにちは、hachi8833です。今週木金は祝日のため、来週7/27、7/28の週刊Railsウォッチは通常記事となります🙇。
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
⚓臨時ニュース: 『パーフェクトRuby on Rails【増補改訂版】』が7/25より発売🎉
つっつきの後で知りました。紙の書籍と電子版同時発売だそうです。
パーフェクトRails増補改訂版、PDF/EPUB版と、kindle版も7/25同日発売になりな雰囲気です!やった〜。ヽ(・∀・)ノhttps://t.co/eA8aC7IAHG
— igaiga (@igaiga555) July 20, 2020
Kindle/EPUB版もあるのがありがたいです🙏。詳しくは以下をどうぞ。
パーフェクトRails【増補改訂版】を読んで、パーフェクトすごい…って思いました:)|tatsuosakurai @tatsuoSakurai #note https://t.co/4HKjU7yENL
あれ?ハッシュタグって #パRails でいいんでしたっけ(?_?)— tatsuosakurai (@tatsuoSakurai) July 18, 2020
⚓Ruby
⚓Polyphony: Rubyでfine grainedコンカレンシー(Ruby Weeklyより)
# 同サイトより
require 'polyphony'
# Kernel#spin returns a Fiber instance
counter = spin do
count = 1
loop do
sleep 1
puts "count: #{count}"
count += 1
end
end
puts "Press return to stop this program"
gets
名前が好きです。
つっつきボイス:「RubyのFiberをいい感じに使えるらしい」「fine-grainedって日本語だと高粒度?この方面ではあまり日本語では言わないかな〜」
「Railsアプリのビジネスロジックでコンカレンシーをふんだんに使うことはあまりないと思いますけど、それこそRackサーバーを自分で書いて複雑なことをさせたいときとか、RubyでSocketをリッスンするようなアプリを書くときとか、後はサイトにもあるようにデータベースコネクタのコネクションプール↓なんかだとコンカレンシーを制御したくなるでしょうね」「そういえばPolyphonyにはHTTPSもできるフル機能のWebサーバーも含まれているそうです」「Webサーバーはまさにコンカレンシーが求められるところですね」
# 同サイトより
DB_CONNECTIONS = Polyphony::ResourcePool.new(limit: 5) do
PG.connect(DB_OPTS)
end
def query_records(sql)
DB_CONNECTIONS.acquire do |db|
db.query(sql).to_a
end
end
「通常のコンカレンシーだとスレッドの管理になりますけど、PolyphonyはFiberまで使うところがfine-grainedということなんでしょう」「Fiberレベルのコンカレンシーgemということですね」
⚓Huginn: IFTTTやZapier的なWeb連携自動化(Ruby Weeklyより)
- リポジトリ: huginn/huginn: Create agents that monitor and act on your behalf. Your agents are standing by!
参考: Webサービスの連携を自動化する Huginn の紹介 - Qiita
つっつきボイス:「ずっと前のウォッチでごく簡単に触れたことがありますが、IFTTTやZapier的なことをやれるRailsアプリだそうです」「オープンソースなのね」
参考: IFTTT: Every thing works better together
参考: Zapier | The easiest way to automate your work
「こういうWeb連携アプリって自分で運用すると大変なので誰かにやって欲しい😆」「そうかも」「インスタンス管理とかOSアップデートとかの面倒見るのがほんとつらいし、止まったときにいろんなところでお困りが発生しますし」「それを考えると、高くてもZapierを使いたくなるかな〜」
「まあ今だとKubernetesが流行ってますし、このHuginnをコンテナでサクッとそこにデプロイできるのであればそんなに大変じゃないかもしれませんけど」「コンテナならやりやすくなりそうですね」
⚓scan_left: inject
をレイジー&インクリメンタルに(Ruby Weeklyより)
- 元記事: #scan_left: A lazy, incremental alternative to Ruby’s #inject | by Parker Finch | Building Panorama Education | Jun, 2020 | Medium
- リポジトリ: panorama-ed/scan_left: A tiny Ruby gem to provide the 'scan_left' operation on any Ruby Enumerable.
# 同リポジトリより
require "scan_left"
# 比較のために#injectも記載
ScanLeft.new([]).scan_left(0) { |s, x| s + x } == [0]
[].inject(0) { |s, x| s + x } == 0
ScanLeft.new([1]).scan_left(0) { |s, x| s + x } == [0, 1]
[1].inject(0) { |s, x| s + x } == 1
ScanLeft.new([1, 2, 3]).scan_left(0) { |s, x| s + x } == [0, 1, 3, 6]
[1, 2, 3].inject(0) { |s, x| s + x } == 6
# オプション: `ScanLeft`クラスの利用を明示的に回避したい場合は
# 以下のようにEnumerableでrefinementを使う手もある
#
# このrefinementは`#scan_left`メソッドをEnumerableに直接追加して
# 構文をより明瞭にできる
using EnumerableWithScanleft
[].scan_left(0) { |s, x| s + x } => [0]
[1].scan_left(0) { |s, x| s + x } => [0, 1]
[1, 2, 3].scan_left(0) { |s, x| s + x } => [0, 1, 3, 6]
つっつきボイス:「inject
のオルタナですか」「そういえばここにもinject
の好きな人が約1名」「ワイのことだ〜😋」「記事にあるように、Enumerableでlazy的なinject
をやれそうですね😋」
⚓遅延評価
「遅延評価は、使う側がどこかで処理を止めたいときには速くなりますね」「遅かれ早かれやらなければならない処理はスキップしようがないので、遅延評価にすれば速くなるわけではないでしょうけど」「やらなくてよかった計算をスキップできれば効果が大きい」
「@kamipoさんがRailsの改修でよくやっていますけど、Active Recordベースのカラムって参照されないものの方が多いなんてこともよくあるじゃないですか」「そうそうっ」「カラムはいっぱいあるけど実はidしか取ってなかったとか」「そういうオブジェクトを毎回作ると無駄が大きい」
「ライブラリコードなら使うかどうかわからないようなものを遅延評価で書いておけば効果は大きいと思いますけど、サービスのコードだと、ある程度下のレイヤならともかく、遅延評価を取り入れたところでどうせ全部実行されることの方が多そうですし😆」「おっしゃるとおり😆」
「逆にそこら辺を遅延評価で書いてしまうとプロファイラで見ても評価が後ろにずれてしまってどこが遅いのかわかりにくくなりますし」「遅延評価でデバッグしづらくなるの、あるある」「まあ遅延評価にすればいいってもんじゃないよということで」
なお、作者はこの機能リクエストをRuby本体にも投げているそうです↓(審議中)。
⚓無名structリテラル続報(Ruby Weeklyより)
いいですね。そういうのを {|k1:v1, k2:v2|} みたいに作れればというアイディアを温めています。
— Yukihiro Matsumoto (@yukihiro_matz) June 25, 2020
つっつきボイス:「前回も扱った無名structリテラルですが(ウォッチ20200707)、今日久しぶりにつっつきに参加いただいたkazzさんに見せたかったので」「こういうふうに${}
でstructを定義できるヤツです↓」「ははぁ〜、なるほど〜😋」
# 同記事より
roxie = ${name: "Roxie", breed: "whippet-cross"}
「無名structリテラル、あってもいいですよね」「私も欲しい〜」「むしろこれがなかったから今までRubyのstructがあまり使われなかったんじゃないかって思ったり」
「Rubyのstructってnew
する割にはインスタンス1個のまま使われるイメージあるかな」「structはイミュータブルハッシュ的に使われることが多いですよね、自分もそうやって使ってますけど」「たしかにイミュータブルハッシュは欲しい!」
「でも考えてみれば、本来イミュータブルハッシュってハッシュでやるべきじゃないんですよね」「そうそうっ」「無名structを使うとイミュータブルであることを明示できるのがいいよね、と思うわけですよ」「あぁ〜わかりますそれ😂」
「無名structリテラルで作ったものはまさにValue Objectとして使えるわけですけど、Value Objectを手軽に定義できるのはとても嬉しい」「struct
で書くのタイプ量多くてダルいですし😆」「それでついついハッシュに流れてしまうという😆」「無名structリテラル、入ったら使おうかな〜」「もういくつ寝ると来るんでしょうね」
同記事より:
- 無名structリテラルが有用な理由:
- structならキーのタイポを防げる
- 無名structはイミュータブルであるという意図がはっきり伝わる
- structならドット記法でシンプルにアクセスできる
OpenStruct
は遅い
- 同プロポーザルにも「今はハッシュの方が速い」とあるが、ハッシュの利用頻度が高い分最適化が進んでいるだけではないか。
参考: class OpenStruct
(Ruby 2.7.0 リファレンスマニュアル)
⚓その他Ruby
- 元記事: First Class Experimentation in Ruby with Scientist | by Joe Letizia | Jun, 2020 | Building VTS(Ruby Weeklyより)
つっつきボイス:「ScientistはGitHubの公式gemなのね」「科学系か教育用かなと思ったけど、carefully refactoring critical pathとあるし、パフォーマンスをチェックしながらリファクタリングするgemっぽいですね」
# 同リポジトリより
require "scientist"
class MyWidget
def allows?(user)
experiment = Scientist::Default.new "widget-permissions"
experiment.use { model.check_user?(user).valid? } # old way
experiment.try { user.can?(:read, model) } # new way
experiment.run
end
end
⚓DB
⚓ENUMは銀の弾丸ではない
つっつきボイス:「ぽすぐれのenum型の話: データベースレベルで所定の値だけが入れられるようバリデーションする方法として以下が紹介されてますね」
- ENUM型
- シンプルなCHECK制約
- CHECK制約とFUNCTIONの組み合わせ
- 外部キー
「1.のENUMはスキーマの一部で、項目が増えたり減ったりするとスキーマを変更することになる: 増えるときはともかく減らすときは単純にALTER TABLE
するわけにもいかなくて面倒になりがちかな」「デメリットとしてはユーザー側で項目を増やしたり減らしたりできない: すぐに変わらないアプリケーションロジックと密に絡むENUMならまあ大丈夫かと思いますが、Redmineの選択項目みたいに運用で項目が増減する可能性があるなら果たしてENUMがいいのかどうかは気にするといいでしょう」
-- 同記事より
CREATE TYPE gender AS ENUM ('male', 'female');
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
gender gender NOT NULL
);
「なおRailsのenumだと文字列やシンボルを数値に置き換えますが、ぽすぐれのENUMはENUM型なのでSQLレベルではちゃんと要素がmale
とかfemale
みたいに現れますね」「なるほど、RailsのenumとPostgreSQLのENUMは違うんですね」「Railsのenumを使うとSQLクエリで数値になるのが読みづらくて、あまり好きでないかな〜」
「他にPostgreSQLのENUMのデメリットとしては、ENUMの値はそのままでは文字列を結合したりサブストリングを取り出したりできない: ぽすぐれだと一応キャストはできるようですが、意識しておく必要はありますね」
「ENUMに向いているデータとして記事では性別が挙げられてますが、バッチのステータスなんかもまず増減しないので向いているでしょうね」
「2.のCHECK
制約で書く方法」
-- 同記事より
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
gender TEXT CHECK (gender IN ('male', 'female'))
);
「2.のデメリットとして項目が増えすぎると破綻することがあると、なるほど↓」「ぽすぐれにはENUMがあるからこの書き方はせずに済みますけど、ENUMが入る前のMySQLでこの書き方を使った覚えがありますね」
-- 同記事より
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
country TEXT CHECK (country IN (
'ad',
'ae',
'af',
'ag',
'ai',
'al',
'am',
'an',
'ao',
...
))
);
「3.はCHECK制約の参照先をFUNCTIONにするというもので、単なるCHECK制約だと同じ項目を別のところでも使うとDRYでなくなってしまいますけど、FUNCTIONにすることで回避できます」
-- 同記事より
CREATE OR REPLACE FUNCTION valid_gender(TEXT) RETURNS BOOLEAN AS $$
BEGIN
RETURN ($1 IN ('male', 'female'));
END
$$ LANGUAGE plpgsql;
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
gender TEXT CHECK (valid_gender(gender))
);
「4.は正規化レベルを1つ上げて外部キーとして扱うというもの: これが一番RDBらしい解決方法かなと思います」「デメリットとして追加テーブルがめちゃくちゃ増える可能性があるというのもごもっとも」
-- 同記事より
CREATE TABLE countries (
code TEXT PRIMARY KEY
);
INSERT INTO countries (code) VALUES ('ad'), ('ae'), ('af'), ('ag'), ('ai'), ('al'), ('am'), ('an'), ('ao'), ...
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
country TEXT NOT NULL,
FOREIGN KEY (country) REFERENCES countries(code)
);
「記事の末尾にまとまっているこの表↓は実装方法を複数知っておくうえでなかなかいいですね」「Railsの場合、Railsのenumという列がもうひとつ加わるかな」「タイトルの銀の弾丸云々も、どちらかというとENUM以外の方法もあるよという点に重きを置いている感じですね」「実践的でいい記事だと思います👍」
⚓JavaScript
⚓Elmとは
Haskellライクな言語だそうです。
// https://guide.elm-lang.orgより
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
main =
Browser.sandbox { init = 0, update = update, view = view }
type Msg = Increment | Decrement
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]
つっつきボイス:「エラーメッセージが読みやすいとかTSとReduxより楽という噂を聞いたのでElmをエントリしてみました」「まあ使いたい人が使うヤツでしょう」「一応Webpackerで入れられるそうですが、Railsでの利用例の記事は今のところあまり見かけない感じでした」「最近だとフロントエンドとバックエンドでリポジトリを分けて、RailsはAPIとかGraphSQLを担当するみたいなのが増えてる感じはあるので、ElmみたいなのをRailsと同じリポジトリに入れることはそんなになさそうな気もしますけどね」
⚓CSS/HTML/フロントエンド/テスト/デザイン
⚓QUIC
つっつきボイス:「この記事はてブで見ましたけど、よくここまでやったなという感じ」「最近はDNSもQUIC実装が進んでたりしますけど、この記事みたいにBGP over QUICやるのはアツい」「まあBGPを触る機会がそもそもありませんけど😆」
参考: BGP(Border Gateway Protocol)とは
「結局、TCPは便利だけどあらゆるニーズに応えられるとは限らないということでしょうね: フローコントロールを自分でやりたいならTCPはいらないでしょうし」
⚓その他フロントエンド
つっつきボイス:「前にも話題にした気がしますけど(ウォッチ20200204)ついに始まったのね」「受験料1万円か〜」「試験を受ける金銭的なメリットはぱっと思いつきませんけど、セキュリティを勉強するエンジニアが理解度を確かめるのには有用だと思います👍」「徳丸先生のことだから問題もそれなりに難しそうだし、範囲も相当広いんじゃないかしら」
⚓番外
⚓ゲーム音楽サイトはRails+React製
つっつきボイス:「ゲーム音楽って知らない世界なんですが、はてブ民が泣いて喜んでたので」「泣かないけど全俺が喜んだ: ありがとうございます、ありがとうございます😂」
「そうそう、このサイトのソースコードがGitHubリポジトリに上がってますよ↓: しかもRailsベースのGraphQLとReactベースのフロントエンド」「お〜マジですか!」「作者のブログもありますし↓」
- リポジトリ: himanushi/music-server: Apple Music と Spotify と iTunes の音楽を自動取得しGraphQLで公開出来るフレームワーク
- リポジトリ: himanushi/music-client: music-server のAPIを使用し曲検索と音楽が再生出来るフレームワーク
- 元記事: ゲーム音楽一覧サービス作った話|himanushi|note
「しかしいつの間に?」「いや単に自分の好きな曲がなかったので、どうやってデータをクローリングしてるのかなと思って昨日ソースコードを見てた🤣」「そうでしたか🤣」「シューティングの曲が少ないな〜と思いながらざっと見た感じでは、どうやらクローラじゃなくて自分でデータ入れてるみたい」「それもスゴい💪」
後編は以上です。
バックナンバー(2020年度第3四半期)
週刊Railsウォッチ(20200720前編)10月開催「Kaigi on Rails」CFP募集中、enumにデフォルト値設定機能、RailsでBitemporal Data Modelほか
- 20200714後編 ruby-warning gemでワーニングを手軽に抑制、rubocop -aの振る舞いが変わる、書籍『MySQL徹底入門 第4版』ほか
- 20200713前編 rspec-openapiでスキーマ自動生成、Rails Architect Conf動画、
where()
ハッシュキーに比較演算子条件を書ける機能ほか - 20200707後編 Rubyで無名structリテラル提案、書籍『AWS認定ソリューションアーキテクト』、21世紀のC言語ほか
- 20200706前編 Railsでのマルチテナンシー実装戦略を比較、Railsでサブクエリを使う、URI.parserが非推奨化ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。