- Ruby / Rails関連
週刊Railsウォッチ(20210120後編)Ruby 3.0の新機能で遊ぶ、RubyスニペットをJSに変換するRuby2JS、rspec-parameterized gemほか
こんにちは、hachi8833です。
- 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
- 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
- お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙇
TechRachoではRubyやRailsの最新情報などの記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)
⚓Ruby
⚓ Ruby3.0の新機能で遊んでみた(Ruby Weeklyより)
つっつきボイス:「そうそう、これこの間ちょっと読みました: いい記事👍」「なるほど、画面に映っているブラウザで記事リンクがクリック済みになってるのが見えますね」「右代入記号=>
(編注: 右代入記号については本項末尾を参照)はこう使えるとか、パターンマッチングの話などの新機能の使い方が書かれていて、短いけど読み応えあります」
「たとえばこれ↓、=>
でこんなことができます」「ええと、真ん中の1と2はそのままで、両側にある要素をsplatの*
で取り出せる...のか!」「=>
の右辺と左辺で共通する1と2でピン留めする感じでこうやって両端を取り出せるんですよ」「へ〜!」「ちょっと不気味かも...」「Rubyコミッターの@mametterさんが会心の笑みを浮かべそう」「こう書けたらうれしいよね、と思いつつやってみたら本当にできたという驚きがありますね」
# 同記事より
[-1, 0, 1, 2, 3] => [*left, 1, 2, *right]
# left == [-1, 0], right == [3]
「この書き方を何て呼んだらいいのかわからない...」「お気持ち代入とでも呼んでみたくなる」「人間の直感には沿っていますよね」「動くのがわかっていてもちょっぴり不安になりますね」「なるなる」
「さっきと同じような中間のピン留めを変数でもやりたい場合は、以下のようなpin operator ^
が使える↓」「おぉ〜、^
ってこうやって使えるんですね」「中間の変数は代入の対象ではなく、その変数の評価値をピン留めに使って欲しいということを^
で伝えないと、Rubyがどう代入したらいいのか判断できないはずなので、この^
がパターンマッチングへのヒント的に渡されるんでしょうね」「なるほど!」
# 同記事より
int = 1
[-1, 0, 1, 2, 3] => [*left, ^int, *right]
# left == [-1, 0], right == [2, 3]
「この記事を読んで、パターンマッチングと=>
でこんなことができるんだという発見がいろいろあったのがとてもよいです」「同感ですね」「=>
を入れたがってた人たちはたぶんこういうことを考えていたのかなと想像して思わず感動しました」「この記事好き!」
⚓ =>
はパターンマッチング用
「同じことを普通の代入でもできないのかな?」「Rubyのドキュメント↓を見ると、どうやら=>
は右代入ではなくてパターンマッチング構文の一部ということみたいですね」「あ、ホントだ」「記号の形は右代入っぽく見えますよね」「Ruby難しい...」
- Rubyドキュメント: pattern_matching - Documentation for Ruby 3.0.0
# docs.ruby-lang.orgより
<expression> => <pattern>
<expression> in <pattern>
後で通常の代入=
で試してみるとエラーになりました。
irb(main):001:0> [*left, 1, 2, *right] = [-1, 0, 1, 2, 4]
Traceback (most recent call last):
3: from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.0.0/bin/irb:23:in `<main>'
2: from /Users/hachi8833/.anyenv/envs/rbenv/versions/3.0.0/bin/irb:23:in `load'
1: from /Users/hachi8833/.config/anyenv/envs/rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
SyntaxError ((irb):1: syntax error, unexpected '=', expecting end-of-input)
[*left, 1, 2, *right] = [-1, 0, 1, 2, 4]
⚓ Ruby2JS: RubyスニペットをJSに変換する(Ruby Weeklyより)
つっつきボイス:「RubyコードをJavaScriptに変換するといえば、@youchanさんがRubyKaigiなどで精力的に発表しているOpalが知られていますけど、他にもあるとは」
「記事を見てみた感じでは、Ruby2JSはこういうRubyの小さなコードスニペットをJSに変換するのが中心のようですね」
# 同記事のRuby2JSのサンプルより: Rubyコード
class MyClass
def my_method(str)
ret = "Nice #{str} you got there!"
ret.upcase()
end
end
# 同記事のRuby2JSのサンプルより: 変換後のJavaScript
class MyClass {
myMethod(str) {
let ret = `Nice ${str} you got there!`;
return ret.toUpperCase()
}
}
「Opalの場合は以下のようにもっと大きなJSコードに変換される↓」「おぉ」「Opalが持つランタイムを呼び出すためなので、コードが増えるのはある程度仕方がない」「Ruby2JSとOpalの使い分けについても記事に書かれていますね」
/* Generated by Opal 1.0.3 */
(function(Opal) {
var self = Opal.top, $nesting = [], nil = Opal.nil, $$$ = Opal.const_get_qualified, $$ = Opal.const_get_relative, $breaker = Opal.breaker, $slice = Opal.slice, $klass = Opal.klass;
Opal.add_stubs(['$upcase']);
return (function($base, $super, $parent_nesting) {
var self = $klass($base, $super, 'MyClass');
var $nesting = [self].concat($parent_nesting), $MyClass_my_method$1;
return (Opal.def(self, '$my_method', $MyClass_my_method$1 = function $$my_method(str) {
var self = this, ret = nil;
ret = "" + "Nice " + (str) + " you got there!";
return ret.$upcase();
}, $MyClass_my_method$1.$$arity = 1), nil) && 'my_method'
})($nesting[0], null, $nesting)
})(Opal);
⚓ rspec-parameterized: テスト構文をパラメータ化
つっつきボイス:「これまで知らなかったんですが、rspec-parameterizedは日本のRubyistの方が中心になって作っているgemのようです」「なるほど、README冒頭のサンプルコード↓でやりたいことは見当が付きました」
# 同リポジトリより
# Table Syntax Style (like Groovy spock)
# Need ruby-2.1 or later
describe "plus" do
using RSpec::Parameterized::TableSyntax
where(:a, :b, :answer) do
1 | 2 | 3
5 | 8 | 13
0 | 0 | 0
end
with_them do
it "should do additions" do
expect(a + b).to eq answer
end
end
end
「たとえば上のコードの真ん中に置かれている表の要素を変数に順次入れて調べるテストコードを書ける、というものですね」「なるほど、こうやって表の形でデータを与えられるのか」
「普通の書き方だとテストケースが増えたときにテストが網羅できているかどうかを把握するのが難しくなってくることもあるので、普段は全ケースをテストしないとしても、網羅できてるかどうかが不安なときにこういうgemを要所要所で使うのもよさそう👍」「なるほど」「テストの書き方によっては表の項目を増やすとテストの実行時間が指数関数的に増加する可能性もあるので、テストケースをどれくらい増やすのかは実行時間とのバランスも見ながら考えたいですね」
⚓ その他Ruby
- 元記事: Docker Compose: a nice way to set up a dev environment(Ruby Weeklyより)
- サイト: Ruby Profiler -- Julia Evansさんのサイト
つっつきボイス:「rbspyなどの作者であるJulia Evansさんのブログですが、docker-compose↓を初めて使って感激したと書かれていたのがちょっと意外だったので拾ってみました」
「Julia Evansさんの記事を覗いてみると、docker-composeでDNSサーバーを誰が動かしているのかが早速気になって調べているのがスゴい」「さすがですね」
「自分もこの間BPS社内勉強会で発表する前に同じことが気になって調べたので気持ちわかります」「そうだったんですね」「docker-composeを使っているうちに気がつくとついついdocker-composeの中の仕組みを追いかけていたという感じですか」「だって気になるじゃないですか」「わかりますその気持!」
「docker-composeといえば、docker contextにAWS ECSを追加することで、ローカルで実行するdockerコマンドのDocker HostとしてAWSのECSやFargateを使うことができるようになりましたね↓」
参考: Docker ComposeによるAmazon ECS対応がGAに!コンテナをローカル環境と同じノリでECS環境で起動できるぞ!! | Developers.IO
追いかけボイス:「参考までに、以下はECSのタスク定義にdocker-compose構文が使えるというECSのドキュメントです」
参考: Docker Compose ファイル構文の使用 - Amazon Elastic Container Service
⚓DB
⚓ activerecord-postgres_enum: PostgreSQLのenum型をサポート(Ruby Weeklyより)
つっつきボイス:「TechRachoの翻訳記事でお馴染みのEvil Martiansがスポンサーになっているgemです」「activerecord-postgres_enum、名前から想像が付く感じですね」
⚓ RailsとPostgreSQLのenum型
「ところでRailsではPostgreSQLのenum型を標準でサポートしているのかな?」「あ、どうだったかな...」「記事をググってみると、自力でPostgreSQLのenumをサポートしないといけない感じなのかな」「うーむ」
「RailsにEnumってありませんでしたっけ?」「あ、それは以下のActiveRecord::Enum
のことですよね?値をintegerなどに変換して持つヤツ」「あ、そうでした😅」「今話しているのはPostgreSQLがサポートしているEnum型の方です」
- Rails API:
ActiveRecord::Enum
-
PostgreSQL 12ドキュメント: 8.7. 列挙型
「RailsのEdge Guides↓を見ても、PostgreSQLのサポートするenumについては"Currently there is no special support for enumerated types."と書かれている」「ドキュメント自体WIP(work in progress: 作業中)となっていますね」「しかもEdge GuideにはPostgreSQLのEnumははVALUEをDROPできないと書かれてる...」「ありゃ」
参考: Active Record and PostgreSQL — Ruby on Rails Guides
「個人的にはですが、Active RecordのEnumよりもぽすぐれのEnumの方がどちらかというと好きかな: Active Recordの層で変換するかPostgreSQLの層で変換するかの違いですけど、Rails以外でPostgreSQLを使うときはよくEnumを使っています」
追いかけボイス:「せっかくなのでactiverecord-postgres_enum gemとRailsガイドのマイグレーション方法を並べてみました」
# activerecord-postgres_enum gemで書けるマイグレーション(同リポジトリより)
create_enum :mood, %w(happy great been_better)
create_table :person do
t.enum :person_mood, enum_name: :mood
end
# Rails Edge Guidesのマイグレーション(Edge Guidesより)
def up
execute <<-SQL
CREATE TYPE article_status AS ENUM ('draft', 'published');
SQL
create_table :articles do |t|
t.column :status, :article_status
end
end
# NOTE: It's important to drop table before dropping enum.
def down
drop_table :articles
execute <<-SQL
DROP TYPE article_status;
SQL
end
追いかけボイス:「RailsのActiveRecord::Enum
ではなくDBMSのENUMを使うメリットについて補足: ActiveRecord::Enum
だとDBに入っている数値と意味の対応をソースコードから読み解く必要があるので一手間かかるのに対し、DBMSのENUMを使う場合は実行されるSQLにhuman readableなVALUEが含まれるようになるので、RailsだけでなくRedashやBIツールとかからデータ参照するSQLを書いたり読んだりするときにぱっと見で読みやすくなる、というものがあります」
「もちろん値変換済みのVIEWを別途作っておけばよいのですが、『DB定義書やソースは手元にないけど、DBに入ってるデータをとりあえずSELECTして眺めたい』というようなケースは運用上よくあることなので、そういうときには便利ですね」
参考: Redash helps you make sense of your data
⚓その他
⚓ NandGame
- 元記事: NandGame が面白過ぎて年末年始ハマった(途中ネタバレあり)
つっつきボイス:「BPSの社内Slackにあがっていて知りました」「そうそう、これ面白そうなロジックゲームですよね、自分はやってませんけど」「上の記事タイトルにネタバレ注意があるので、見ないように頑張ってます!」
「NandGameって、教育用途ではなくてゲームということかな?」「教育用にも使えそうですけどね」「いかにも最近の大学で使ってそうですし、これで全加算器とか半加算器とかを作れるならよさそう」「たしかに原理的にはNAND回路を組み合わせればどんなロジックでも作れますね」「まあそうなんですけど😆」「デジタル論理回路の理解を助けるゲーム、いいですよね」
「へ〜、ラッチ回路やらALUやらいろいろある」「こういうのはだいたいパタヘネ本↓を読めば作れるようになります」「たしかに😆」「NandGame、さすがにOpcodesあたりから先の複雑な回路はどう組んだらいいかすぐには見当がつかない...」(以下延々)
後編は以上です。
バックナンバー(2021年度第1四半期)
週刊Railsウォッチ(20210113後編)Ruby 3.0 Ractor解説記事、Vercelホスティングサービス、教育用OS xv6ほか
今週の主なニュースソース
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。