Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ(20200608前編)RubyKaigi 2020が開催中止に、ネステッドSTIを避けるべき理由、rails newがインタラクティブになるかもほか

こんにちは、hachi8833です。9月のRubyKaigi 2020の開催が中止となりました😢。

臨時ニュース: RubyKaigi 2020の開催が中止に

別途オンラインイベントを構想中とのことです。


つっつきボイス:「この時期はもうしょうがないですね」「開催3か月前だとそろそろチケットやら宿やらを取らないといけなくなる時期ですし、正しい判断だと思います」「決定するのつらかっただろうな...」「Kaigiで会いたい海外勢いたのに〜😢」「来日する人たちもまずスムーズに入出国できないでしょうし」「そうか!😭」「空港を通過するたびに2週間ずつ足止めくらうでしょうから、行って帰るまで1か月はかかっちゃうでしょうし」「会社もそこまで許してくれなさそう😢」「チケットの他にスポンサー代も返金になるのか、とりあえず連絡待ちかな」


「ところで日本は東京アラート出ている割には解除ムードのせいかあまり様子が変わってないような気が😆」「それサーバー運用だったらダメなヤツ😆」「アラート鳴いてるのに『鳴いてるね、うんうん』で終わるみたいな😆」


以下はつっつき後に見つけたツイートです。


  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄

Rails: 先週の改修(Rails公式ニュースより)

以下のコミットリストやRails Discussionを元に見繕いました。

コントローラレベルのforce_sslを削除


つっつきボイス:「え、force_sslが消えた?」「マジで?😆」「コントローラレベルでは消されましたけど、たぶんコンフィグのforce_sslはあるのかな🤔」「これ↓見たらautoloadされなくなっただけなのかなと思ったら、actionpack/lib/action_controller/metal/force_ssl.rbがバツっと削除されてるし😳」「metalからファイルが抹殺された😇」「前からdeprecatedだったのね」

# actionpack/lib/action_controller.rb#L32
    autoload :EtagWithFlash
    autoload :FeaturePolicy
    autoload :Flash
-   autoload :ForceSSL
    autoload :Head
    autoload :Helpers
    autoload :HttpAuthentication

あとで探すと、2018年に以下のプルリクでforce_sslがdeprecatedにされていました。せっかくconfig.force_sslがあるのにApplicationControlerforce_sslを書いてしまう例があとを絶たなかったのでdeprecateしたそうです。

「まあ考えてみれば、今後Railsで作るアプリのコントローラでforce_sslをわざわざ書くことってまずないと思いますし」「ないないっ😆」

「でも昔はコントローラにforce_sslを書かざるを得ないことってありましたし: ログインしてないユーザーにはhttpsじゃなくてhttpでアクセスさせよ、とか😢」「httpsのアクセスが遅かった昔なんかはそうでしたね」「自分は『お、force_sslあるじゃんやった〜😋』みたいな感じで書いたことあったかも😅」「force_ssl、割と地雷でしたね🧨」

require_dependencyhelperから削除

require_dependency削除作業の一貫のようです。


つっつきボイス:「require_dependencyまだ残ってたのか」「Zeitwerkがやってきて要らなくなったヤツですね」「どんどん削られてる〜」

Rails 6 Beta2時点のZeitwerk情報(要訳)

helper UtilsHelper?」「Changelogに以下が追記されてました↓」「ああなるほど、前はhelperにモジュール名を文字列やシンボルで渡してたけど、UtilsHelperみたいにモジュール名を直接書けるようになったのね」「たしかにこの方が直感的ですし😋」

コントローラ向けのhelperクラスメソッドで文字列やシンボルとして読み込むヘルパーモジュールは、require_dependencyではなくString#constantizeで読み込まれるようになる。
文字列やシンボルのサポートは単なるお便利APIに過ぎないことをお忘れなく。以下のようにモジュールオブジェクトをいつでも渡せる。

helper UtilsHelper

そしてこれはシンプルかつストレートなのでおすすめ。helperは文字列やシンボルを受け取ると、同じモジュールオブジェクトを得るためにその引数を操作してinflectionで活用しているに過ぎない。
Xavier Noria、Jean Boussier
Changelogより

fixture_file_uploadで使うファイルパスがfixture_pathからfile_fixture_pathに変更

fixture_file_uploadではfile_fixture_pathを使うこと。
file_fixture_pathがなかった頃はfixture_pathを使っていたが、今後はfile_fixture_pathを使うべき。
fixture_pathを使うのはActive Recordだけなのだが、ActionPackに何かの間違いで紛れ込んだ様子。
file_fixture_pathのもうひとつのメリットは、fixture_file_upload('../files/blabla.png')みたいにいちいちディレクトリを1階層上げなくてもよいこと。
同PRより大意


つっつきボイス:「file_fixture_upload見たことなかったな〜」「fixtureだからテスト用ですね」

# actionpack/lib/action_dispatch/testing/test_process.rb#L17
      def fixture_file_upload(path, mime_type = nil, binary = false)
        if self.class.respond_to?(:fixture_path) && self.class.fixture_path &&
            !File.exist?(path)
-         path = File.join(self.class.fixture_path, path)
+         original_path = path
+         path = Pathname.new(self.class.fixture_path).join(path)
+
+         if !self.class.file_fixture_path
+           ActiveSupport::Deprecation.warn(<<~EOM)
+             Passing a path to `fixture_file_upload` relative to `fixture_path` is deprecated.
+             In Rails 6.2, the path needs to be relative to `file_fixture_path` which you
+             haven't set yet. Set `file_fixture_path` to discard this warning.
+           EOM
+         elsif path.exist?
+           non_deprecated_path = path.relative_path_from(Pathname(self.class.file_fixture_path))
+           ActiveSupport::Deprecation.warn(<<~EOM)
+             Passing a path to `fixture_file_upload` relative to `fixture_path` is deprecated.
+             In Rails 6.2, the path needs to be relative to `file_fixture_path`.
+             Please modify the call from
+             `fixture_file_upload(#{original_path})` to `fixture_file_upload(#{non_deprecated_path})`.
+           EOM
+         end
+       elsif self.class.file_fixture_path && !File.exist?(path)
+         path = file_fixture(path)
        end
+
        Rack::Test::UploadedFile.new(path, mime_type, binary)
      end
    end

config.generators.after_generateが追加

# 同PRより
config.generators.after_generate do |files|
  system("bundle exec rubocop --auto-correct " + files.join(" "), exception: true)
end

つっつきボイス:「@y-yagiさんのプルリクですね」「フックポイントがまたひとつ増えた」「after_generateでフックかけたいことってありますね😋」「その中でまたジェネレーターを呼び出してループしたりして😆」「😆」

番外: ActiveRecord::Core.[]を追加してArel属性オブジェクトにアクセスできるようにする(closed)

このコミットはActiveRecord::Core[]メソッドを実装する。この機能のポイントは、開発者がArel属性オブジェクトにアクセスできるようにして、もっと複雑なクエリを以下のように簡単に構築できるようにすること。

Developer.where(Developer[:salary].gt(9000))

単純な==や!=に収まらない複雑な条件でクエリを構築できる。このメソッドは他のテーブル内のカラムも正しく参照できるので、joinsでのクエリ制約も「そのまま適用できる」。
同コミットより


[]じゃない方法を考える方に賛成。こちらのコードベースではfinderお便利メソッドとしてself.[]を既存のモデルに実装しているのだけれど、他のプロジェクトでそうしてなかったとするといささか驚きかもしれない。

たしかにとてもよい指摘。
自分もModel[:xxx]よりModel::Table.xxxの方がいいかもと思えてきた。この方がコードがやっていることもクリアだし、[]の他の実装との衝突を気にしなくてよくなるし。
このプルリクはcloseしようと思う。@flanger001へ、もし時間があればTableを実装するプルリクを作ってこのプルリクを引用してもらえるだろうか?聞いてみただけなので無理ならいいです😆。
#39198のコメントより


つっつきボイス:「最近公式のRuby on Rails Discussionsがめちゃ面白くておすすめなんですけど、その中で以下のやりとりを元に@tenderloveさんが上のプルリクを投げて、その後説得を受けてcloseしてました」「Arelをpublicにして欲しい人っているのかな〜?😅」「Arel使って欲しくないんですけど😅」「Arelが欲しいのはSQL絶対書きたくないマンでしょう😆」「😆」

Arelよもやま話

「Arelってなんか黒魔術っぽいから🧙‍♀️、SQL書くかActive Record書くかどっちかにして欲しいです〜😢」「これまでも話しましたけど、どういうSQLを吐くかを想像しながら一生懸命Arelで組み立てるのって、なんか人生の無駄遣いだなって思いますし😆」「😆」「Arelは他とパラダイムの違うラッパーなのに、変換先のSQLをのことを考えないといけないのってどんな縛りプレイなのって😆」「日本人同士なのに英語でしゃべってるような気持ちになりそう😆」「それそれ、しなくていい苦労をしてる感😆」

「ただ、どのタイミングで生SQLに切り替えるかというさじ加減が人によって違うんですよ😅」「それありますよね」「機能が確定していてそれ以外の使い方をしないなら生SQLでいいんですけど、取ってきたオブジェクトをこねくり回すことが多いとArelでやりたくなる気持ちもわからなくもないですし」「気持ちはわかるんですけど😅」「気持ちどまり😆」

「個人的にはチームでArel使おうとする人がいたらたぶん血相を変えて止めるだろうなって😆」「Arelで書くようになると、そのうちどんなSQLが生成されるかをコメントに貼っておかないとやっていけなくなったりしますよね😆」

koicさんの最近の記事も貼っておきます。

Rails

rails newをインタラクティブにやりたい -> ではお願い(DHH)(Ruby on Rails Discussionsより)


つっつきボイス:「これもRails Discussionsの投稿で、rails newをインタラクティブにしたいという投稿にDHHが『じゃ任せるから』的にOKを出して、現在別の人から以下のプルリクが上がっています」「お、たしかにrails newはインタラクティブになってもいいかも😋」「Action Mailer要らないとか、Action Mailbox要らないとかを対話的に指定できるということでしょうね😋」

rails new --interactive使えるようになったらとてもよさそう❤️」「とてもいいと思います👍」「実際rails newってたまにしかやりませんし、オプションを調べる方が面倒ですし😆」「今までだとオプション間違えては消してやり直しを繰り返しがち😆」「たいていgit initした後だからさらに面倒になりますし😆」「もういくつ寝るとできるのかな〜😋」

Rails Discussionsが好調


discuss.rubyonrails.orgより

「というような話題も見つかったりするので、Railsやってる人はRuby on Rails DiscussionsA May of WTFカテゴリあたりをときどきチェックするととてもいいと思います😋」「Railsコミッターがこれを通じてコミュニケーションしようという動きが急に加速してきましたね」「しかもいい感じに盛り上がってますし❤️」「前は過疎ってましたけど😆」

「Railsコミッターがこの板に積極的に関わるようになってきたのが大きいですね」「DHHが書き込むと『Resolve✅』チェックマークが付くことが多い感じです😆」「このサイト、RSSあるかな?👀」「ありますヨ、私もSlackでRSS読んでますし😋」「じゃやろうっと😋」「お、ページのソースにRSSフィードのURLが隠れてた↓😆」「最近RSSボタンがないサイト、多いですよね😆」「カテゴリごとにもフィードできたと思います」

    <link rel="alternate" type="application/rss+xml" title="Latest posts" href="https://discuss.rubyonrails.org/posts.rss" />
    <link rel="alternate" type="application/rss+xml" title="Latest topics" href="https://discuss.rubyonrails.org/latest.rss" />
    <link rel="alternate" type="application/rss+xml" title="RSS feed of topics in the 'A May Of WTFs' category" href="https://discuss.rubyonrails.org/c/a-may-of-wtfs/8.rss" />

「こうやってRails Discussionsが回るようになったことで、GitHubにあるRailsリポジトリのissuesが荒れなくて済むという効用もあるでしょうね👍」「たしかに!」「Railsのissue、読みきれないほど増えてますし😆」「issueじゃないものまでジャカスカ書き込まれてましたし😆」「それissueじゃないからみたいなヤツ😆」「discussしたいのにissueに書き込んだり😆」

「Railsぐらい大規模なフレームワークになってくるとissueがパンクしがちなので、こういうふうに雑談的なトピックをディスカッションできるRails Discussionsという場が回るようになったのはいいですね☺️」


「上には貼らなかったんですけど、『Railsのconcernsの正しい使い方をドキュメント化して欲しい』みたいなトピックもRails Discussionsに上がってました」「それ欲しいかも😆」「concernsは単なるモジュールインクルードだから正しい使い方と言われてもね😅...みたいなレスが付いてました😆」「実際concernsはconcernsという機能でしかないので、正しく使うかどうかは設計の問題なんですよね☺️」「DHHもさすがに一刀両断には回答してませんでした」「たぶんですけど、昔のRubyだと気を付けないとハマりがちな部分をサポートするために、Active Supportにconcernsという形のラッパーが入ったんだろうと思いますし🧐」「なるほど」「Active Supportの典型的なサポート機能だと思います☺️」

これでした↓。

参考: ActiveSupport::Concern

ネステッドSTIを避けるべき理由


つっつきボイス:「STI(Single Table Inheritance)のネストは避けろだそうです」「ネステッドSTIはキツい🤣」「ネステッドは確実にやめた方がいいです、マジで🤣」

参考: 5 シングルテーブル継承 (STI) -- Active Record の関連付け - Railsガイド

「もう記事を読むまでもないと😆」「STIをネストすると絶対おかしくなりますって😆」「そもそもちゃんと動くんだろうか」「やれるのかもしれないけど、やろうと思ったことがない😅」「誰がやりたいと思うんでしょう?」「たぶんRDBを意識したことのないオブジェクト指向プログラマーが、STIは単なるリポジトリレイヤの話でしかないよねとばかりにオブジェクト指向で突っ走るとこうなっちゃったりして😆」「そんなバカな😆」「この間みたいにdelegated typingしましょうよ(ウォッチ20200601)😆」


同記事より

「そういえばこういう図↑って、15年ぐらい前のオブジェクト指向の教科書によくありましたよね😆」「動物シリーズなつかしい🦁」「独習Javaあたりに書いてありそう😆」

オブジェクト指向の説明で動物を例えに使うと誤解を招くのでよくないという話はとてもよく耳にしますね。

参考: オブジェクト指向の教え方で「動物を例えにする」説明は誰が始めた... - Yahoo!知恵袋

「STIキライな人って多いですけど、参照するクエリやインデックスをちゃんと考慮したうえでSTIを導入するのであれば十分リーズナブルになることはかなりあると思いますし🧐」「毛嫌いしたらあかんと」「自分はその意味でSTIはそんなにキライじゃないです😋」「STIを使うべきじゃないところで使う人があとを絶たなくて、そのせいでSTIがキラワれるんだろうと思いますし😆」


記事見出し:

  • 説明のためのコンテキスト
  • STIはlazyなコード読み込みでうまく回らない
  • STI + lazy loading + nested models = 予想外の振る舞い
  • 普通のSTIのベースクラスならうまくいくのに中間クラスだとうまくいかない理由
  • 回避方法はあるのか
  • 私はこう解決するのが好き

RailsアプリでStripeのWebhookイベントを安全に扱う方法(Awesome Rubyより)

stripe_eventというgemが紹介されています。


つっつきボイス:「Webhookをいかに安全に扱うかについてはいろいろ気にすべき点がありますね」「おぉ?」「Webhookは基本的に公開URLに対して投げるものなんですけど、サーバーがそのWebhookを本当にさばいても大丈夫なのかどうかを本来ちゃんと考えておかないといけないんですよ🧐」「ふむふむ」「特に不可逆な操作を行うWebhookは要注意☢️」

参考: Webhookって何?を子どもでもわかるように描いてみた | kintone hive online

「まあWebhookについてはベストプラクティス的なものはだいたいあります☺️」「Stripeのようにお金を扱うサービスだともちろん安全にやらないとヤバいんですけど、Stripeにはその辺をverifyするしくみは入ってますので😋」「ありがたいです🙏」

Stripe決済を自社サービスに導入してわかった5つの利点と2つの惜しい点

Railsアプリのルーティングを多面的に捉える(Hacklinesより)


つっつきボイス:「ルーティングは...やっぱり難しいよね😅」「難しいです😆」「ジェダイとかライトセーバーが例えに使われてる⚔️」

REST vs GraphQL

「最近GraphQLが気に入っているんですけど、GraphQLだとこの記事にあるようなルーティングを避けられるのがメリットですね😋」「おぉ?」「正確に言うと避けてはいないんですけど😆、少なくとも無理やりRESTfulにしようとした結果ルーティングがネステッドすることは避けられます👍」

参考: GraphQL - Wikipedia

「ルーティングをこねくり回すぐらいならGraphQLにする方がリーズナブルかなって思いますし、RESTだと合わないものもあるよなって思いますし☺️」「ルーティングこじらせたくないです😅」「RESTがネストしてくると同じリソースにアクセスするのに複数の方法を作るようになってきて面倒くさい😆」「ふむふむ」「以下で言うと2行目でも4行目でもlightsabers:showにアクセスするとか: この場合どっちを叩いても同じアクションが呼ばれることになりますし」

# 同記事より
resources :jedis, only: [:index] do
  resources :lightsabers, only: [:show]
end
resources :lightsabers, only: [:index, :show]

「でも以下のようにmodule:を使う方法↓にも引っかかりどころはあって、たしかにこうすれば違うアクションを参照できるんですけど、果たしてそれでいいんだろうかと思ったり😅」「あ〜😳」「ルーティングは悩ましいです😭」

# 同記事より
resources :jedis, only: [:index] do
  resources :lightsabers, only: [:show], module: :jedis
end
resources :lightsabers, only: [:index, :show]

「GraphQLか〜🤔」「まあGraphQLにしたらしたで、今度は別の悩みが発生するんですけどね😅: クライアント側のビューにも影響してくるので」「たしかに」「でもGraphQLならスキーマで提供できるものしか渡さないので、こっちのルーティングでもあっちのルーティングでも取りたいものがあるというようなときには比較的柔軟にやれると思います❤️」

「GraphQLのいいところは1つのリクエストにクエリを複数乗せられること: システム的には1つしか提供していないリソースでも、複数の場所で取りたければクライアントがそれぞれクエリすれば済みますし😋」「ほほぅ😋」「RESTだとN+1的なクエリがHTTPレベルで発生しがちなんですけど、それを回避できる点でGraphQLはエラいなと思います💪」

「GraphQLでそういうクエリになってくると今度はサーバー側の負荷がつらくなってくるんですけどね🤣」「どうやらGraphQLはちゃんと勉強しないとダメそうかな...🤓」「その辺はちゃんと調べないといけませんね🧐」「でもGraphQLにはその手のサーバーサイド寄りの資料があんまりなくて😢」「ネットでその辺を試行錯誤している人たちの記事を探して自分でもやってみたりしているんですけど、結局バッチジョブでキャッシュに乗せるみたいな普通のことしか書いてない😆」「要は普通のことをやれと😆」

サーバーの数を減らしてパフォーマンスを上げた(Hacklinesより)


つっつきボイス:「Babbel.comは語学学習サイトのようなんですけど、サーバーの数を減らしつつパフォーマンス改善したそうです」「オートスケーリング周りって、AWSとかでやっていて一番考えずに済ませたい部分😆」「😆」「真面目にやってたらすんごく大変😅」「きりがないですね😅」

AWSのおまかせオートスケーリング

「そういえば最近だとAWSでオートスケーリング周りをおまかせでやってくれる機能が増えましたよね❤️」「お!」「最近はスケーリングポリシーで済ませることが多い😆」

参考: Amazon EC2 Auto Scaling(需要に合わせてコンピューティング性能を拡張)| AWS
参考: Amazon EC2 Auto Scaling のステップスケーリングポリシーおよび簡易スケーリングポリシー - Amazon EC2 Auto Scaling (日本語)

「このターゲット追跡スケーリングポリシー↓が一番新しくて、AWS推奨」「おぉ😍」「これにパラメータをちょこっと渡すとよしなにやってくれることになってます☺️」

参考: Amazon EC2 Auto Scaling のターゲット追跡スケーリングポリシー - Amazon EC2 Auto Scaling (日本語)

「昔からオートスケールって信用ならないという印象があって自分で作ったりしたことあるんですけど😅、最近のオートスケールってどうなんでしょう?」「オートスケーリング自体は普通に動くけど、反応は遅いかも🤔」

負荷試験はつらいよ

「まあ結局どれを使おうと、パフォーマンス試験は必ずやらないといけませんし🚜」「たしかに」「でも計測する環境を整えるのが特に面倒で工数がかかっちゃう😢」「あ〜結局そうなりますよね😅」「テストシナリオを作るのにも時間かかりますし、サーバーが十分あふれるだけのリクエストをちゃんと投げられる能力のあるクライアントを構築するのも面倒😭」「う〜む」「厳密なことを言えば、自分の構築した計測環境が正しいかどうかの確認も本当は必要ですし、ホント大変😅」「自分も、これでいいのかな?本当にいいのかな?って思いながらやったりしますし😅」

「その意味では、パフォーマンス試験をやれる人が2人は欲しい」「わかります、ダブルチェックしてもらわないとコワいです😅」「自分はこういうポリシーでこういう部分を測定するように設計しているから十分な負荷はかけられるはず、と思って作るんですけど」「でも後で『あれ、こっちだったかな?』『これ抜けてたかな?』なんてなったり🤣」「アミバ様みたいに🤣」「とにかく負荷試験は自分ひとりだけだとコワい」「自分の書いた負荷試験シナリオの妥当性を誰もレビューしてない状態でやるのはドキドキものですよ」「プルリクを見もしないでマージする的な😆」

「自分ひとりでやった負荷試験を頼りに上を説得したことありましたし😅」「負荷試験を本気でやるといくらお金があっても足りないので、もし負荷試験で問題があったらこのパラメータを変えればよくなるはず、というところまで落とし込んでから報告するのが現実的でしょうね☺️」「でないといつまでたってもリリースできませんし」「安全弁は用意しておいて、問題が起きたらこの安全弁を外せというところまで固めてからリリースすると」「安全弁が万一安全でなかったら外した途端にサービス止まりますけど🤣」「そのときはゴメンナサイするしか🤣」


前編は以上です。

バックナンバー(2020年度第2四半期)

週刊Railsウォッチ(20200602後編)JSONストリームパーサーyajl-ruby、ruby-buildとopenssl、GoogleのCloud SQL、Rubyと機械学習ほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。

Rails公式ニュース

Ruby on Rails Discussions

Awesome Ruby

Hacklines

Hacklines


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。