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

週刊Railsウォッチ(20210517前編)Bootstrap 5リリース、productionでSQLiteがwarning表示、rails-ujsの舞台裏ほか

こんにちは、hachi8833です。

週刊Railsウォッチについて

  • 各記事冒頭には🔗でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙇

TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)

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

今回は以下のコミットリストのChangelogを中心に見繕いました。

🔗 CSPヘッダーのサポートを2つ追加


つっつきボイス:「require-trusted-types-fortrusted-types、知らないヘッダーだ」「issue #42034を見ると、MDNでexperimentalとなってるそうです」「これらはサーバー側が返すヘッダなので、Railsが対応してくれることでexperimentalな最新の機能を利用できるということですね」

参考: CSP: require-trusted-types-for - HTTP | MDN
参考: CSP: trusted-types - HTTP | MDN

# actionpack/lib/action_dispatch/http/content_security_policy.rb#L108
    MAPPINGS = {
      self:           "'self'",
      unsafe_eval:    "'unsafe-eval'",
      unsafe_inline:  "'unsafe-inline'",
      none:           "'none'",
      http:           "http:",
      https:          "https:",
      data:           "data:",
      mediastream:    "mediastream:",
      blob:           "blob:",
      filesystem:     "filesystem:",
      report_sample:  "'report-sample'",
      strict_dynamic: "'strict-dynamic'",
+     script:           "'script'",
      ws:             "ws:",
      wss:            "wss:"
    }.freeze

    DIRECTIVES = {
      base_uri:        "base-uri",
      child_src:       "child-src",
      connect_src:     "connect-src",
      default_src:     "default-src",
      font_src:        "font-src",
      form_action:     "form-action",
      frame_ancestors: "frame-ancestors",
      frame_src:       "frame-src",
      img_src:         "img-src",
      manifest_src:    "manifest-src",
      media_src:       "media-src",
      object_src:      "object-src",
      prefetch_src:    "prefetch-src",
+     require_trusted_types_for:  "require-trusted-types-for",
      script_src:      "script-src",
      script_src_attr: "script-src-attr",
      script_src_elem: "script-src-elem",
      style_src:       "style-src",
      style_src_attr:  "style-src-attr",
      style_src_elem:  "style-src-elem",
      worker_src:      "worker-src"
      script_src:                 "script-src",
      script_src_attr:            "script-src-attr",
      script_src_elem:            "script-src-elem",
      style_src:                  "style-src",
      style_src_attr:             "style-src-attr",
      style_src_elem:             "style-src-elem",
 +    trusted_types:              "trusted-types",
      worker_src:                 "worker-src"
    }.freeze

参考: Rails アプリケーションのセキュリティ対策(CORS/CSP/HSTS)

🔗 SQLiteをproductionで使うとwarningを出力

SQLiteをproductionで実行するとwarningをログ出力する。
warning出力はconfig.active_record.sqlite3_production_warning=falseで無効にできる。
closeするissue: #34715
同PRより


つっつきボイス:「SQLiteをproductionで使うとwarningを出すようになったそうです」「アプリの性質によってはSQLiteをproductionで使う可能性もなきにしもあらずですし、warning出すほどでもない気はしますけどね」「#34715のコメントには『自分はproductionで問題なくSQLite使ってるよ』という書き込みもありました」「コンフィグでwarning出さないようにできるのか」

参考: SQLite Home Page

「現実問題としてSQLiteをproductionで使う可能性はかなり低いと思うので、多いのは『間違ってSQLiteを使ってしまう』ケースでしょうね」「それありそうですね」

参考: SQLiteと他のデータベースの違いを紹介します。「SQLiteの特徴を教えてください」 - Python学習チャンネル by PyQ

ですが、SQLiteができるロックはそれほど柔軟でないのが特徴です。ロックする範囲をデータベース全体でしか制御できない(テーブル単位、行単位でのロックができない)特徴を持っています。
また、SQLiteはローカルのファイルを直接データベースとして扱うので、複数のサーバーとネットワークごしに接続するのには不向きです。Webサーバーを複数台起動する場合などはMySQLやPostgreSQLなどを使うほうが良いでしょう。
blog.pyq.jpより

「データベースの内容がユーザーから一切変更できないようなアプリなら、そうしたデータの保存にSQLiteを使うこともありますし、Androidのゲームアプリなんかでは、SQLiteのファイルをソフトウェア本体にbundleしてSQL互換アクセスできるread onlyなマスタデータベースとして使うなんてこともありますよね」「なるほど」「特に最近のRailsはマルチプルデータベースに対応しているので、変更が生じないマスターデータについてはSQLiteに入れるという選択肢はありでしょうね」「たしかに、私のenno.jp↓というアプリもデータ更新は管理者しか行わないので本当はSQLiteにしたかったんですけど、HerokuではSQLiteを利用できない仕様になってました😢」

日本語エラーチェックサイトenno.jpを作った理由

「SQLiteは読み出し専用で使っている分には決して遅くありませんし、SQLiteのようなファイルベースのDBにはひとついい点があるんですよ: データがすべてOSのファイルキャッシュに乗ってくれること」「あぁそうか!」「OSのファイルキャッシュに乗るということは、プロセスをまたいでもキャッシュが効くということで、これが大きい」「そうそう😋」「RailsのSQL キャッシュは基本的にアクション単位なのでアクションをまたぐと解放されてしまいますが、OSのファイルキャッシュはプロセスをまたいで効くのが強い」

参考: 1.8 SQLキャッシュ -- Rails のキャッシュ機構 - Railsガイド

「個人的にはproductionでのSQLiteでwarningを出すまでもないような気はしますけど、誤用を防ぐという意味では理解できます」「そうですね、理解したうえでSQLiteをproductionで使う人は自分でwarningをオフにできるでしょうし、それと誤用の可能性を秤にかけた結果この改修になったんでしょうね」「自分も本番でSQLiteを使うときは、よ〜く考えてからにしています」


SQLiteをRailsのproduction環境で使う主な問題は、

  • これがデフォルトになっているので初心者が気づかないまま使ってしまう可能性があること
  • SQLiteがディスクベースのデータベースでありながら、それを過渡的なクラウドベースの世界で使ってしまうこと

「自分のRailsアプリでは明確な意図を持ってSQLiteをproductionで使います」勢が多数派とは思えない。
#34715コメント(by DHH)より大意

🔗 has_one through:disable_joinsオプションが追加


つっつきボイス:「マルチプルデータベース関連でこれとよく似た改修がこの間もありましたね(ウォッチ20210426): あのときはhas_namy through:が対象だったかな」「今回はhas_one through:でもdisable_joinsが使えるようになったんですね」

#41937has_many ... through:が追加されたように、has_one ... through:リレーションにdisable_joinsオプションを追加する。目的は、マルチプルデータベースのRailsアプリでテーブルが別のデータベースクラスタに置かれている場合にhas_oneリレーションシップを使えうようにすること。この場合、クラスタ間でのJOINは行えないのでJOINを回避する必要がある。
同PRより大意

🔗 PostgreSQLでtimestamp with time zoneをネイティブでサポート


つっつきボイス:「お、PostgreSQLでタイムゾーンありのタイムスタンプを使えるtimestamptzが追加されたんですね」「デフォルトはタイムゾーンなしの方なのか」

  • PostgreSQL: ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_typeを導入
    この設定は、マイグレーションやスキーマでdatetimeを呼び出す際に、Active Recordで使うネイティブ型を制御する。この設定は、設定済みのNATIVE_DATABASE_TYPESのいずれかに対応するシンボルを受け取る。デフォルトは:timestampつまり、マイグレーションで、t.datetimeを使うと「timestamp without time zone」カラムが作成される。「timestamp with time zone」を使うには、イニシャライザで:timestamptzに変更する。
    これを変更した場合には、bin/rails db:migrateを実行してschema.rbを再構築する必要がある。
    Alex Ghiculescu
    同Changelogより大意

「たしかに、これまではPostgreSQLのtimestampを使うとタイムゾーンなしになってたんですよ」「タイムゾーンありの方が好きかも」「timestamptzという名前が微妙に長いけど、タイムゾーンのありなしを選べるようになったのはいいですね👍」

🔗 Active SupportのEnumerable#sumArray#sumを非推奨化


つっつきボイス:「Active SupportにあるEnumerable#sumArray#sumが非推奨になったのね」「RubyネイティブのEnumerable#sumArray#sumの方が断然高速なので今後はそちらを使って欲しいそうです」「お〜、Active Supportのメソッドが昇格してRuby本体に取り入れられることがときどきありますけど、これもそのパターンですね」「Rails側としては冥利に尽きますね🎉」

Ruby(2.4以降)にはパフォーマンスが著しく向上したネイティブのsum実装が含まれている。Rails 7.1からはEnumerable#sumArray#sumを削除してRubyのネイティブ実装を使うことにする。
このコミットでは、適切な初期引数を持たない非数値引数の呼び出しに非推奨の警告を追加している。以下で議論しているとおり、RailsからActive Support版の機能が削除されたときにこれらの引数が必要になる。

#41669 (comment)

以下のような例でdeprecation warningが表示されるようになった。

%w[foo bar].sum
[[1, 2], [3, 4, 5]].sum

warningを回避するには以下のように呼び出す必要がある。

%w[foo bar].sum('')
[[1, 2], [3, 4, 5]].sum([])

Rails 7.1での非推奨化を準備するため、Rubyのネイティブ実装でTypeErrorになる[nil].sum == 0についても非推奨化する。
同PRより大意

🔗Rails

🔗 Bootstrap 5がリリース


つっつきボイス:「そうそう、Bootstrap 5出ましたね」「IEサポートもついに終了」「今年の春学期に担当する大学の授業では検索のしやすさからBootstrap 4を使いますが、来年度やるならBootstrap 5でもいいかもですね」

「jQueryも必須でなくなったのはいい👍」「jQueryの目玉機能だった$("要素名")みたいなセレクタも今やquerySelectorAll()でできるようになりましたよね」「う、jQueryなしでできるようになってたとは知らなかった😅」「querySelectorAll()は普通にどのメジャーなブラウザでも使えるはずですよ」

参考: Document.querySelectorAll() - Web API | MDN
参考: Can I use... Support tables for HTML5, CSS3, etc -- querySelectorAll()

「jQueryが当時画期的だったのは、$("")を1個書くだけでCSSセレクタでDOM要素を簡単に選択できたこと」「あれは感動的なインターフェイスでしたね」「jQueryのeach()的なメソッドも当時のブラウザでは標準で使えなかった」「現在のJavaScriptがそうしたものを標準として取り入れるようになったので、jQueryは役目を終えつつあるかなと感じますね」「今の人があの当時のJavaScriptを触ったらあまりに別物でびっくりするかも」

参考: .each() | jQuery 1.9 日本語リファレンス | js STUDIO

🔗 Hanami 2.0.0 alpha2がリリース(Ruby Weeklyより)


つっつきボイス:「おぉ、Hanamiの2.0.0が出そう」「コアを完全に書き直したそうです」「まったく新しいアプリをSinatraで書くぐらいならHanamiで書いてもいいかなという気持ちがありますね」「Hanamiのルーティングにroda gemを使うという提案がだいぶ前にあったのを思い出したんですが、結局ルーターもHanami独自のものを新たに作ったそうです」「ルーターはWebアプリで必ず通るところなので重要ですね: ここが重いと全体が重くなってしまうので」「たしかに」

jeremyevans/roda - GitHub

🔗 RSpecのネスト


つっつきボイス:「RSpecはsubjectletを使うかどうかみたいな議論になりがちなところがあるので、今はMinitestの方が好きです」「わかります」「RSpecだとネステッドなcontextを書きすぎてしまうんですよね」

「RSpecで書かれたテストが複雑になりすぎると、RSpecのテストに対するテストが必要なんじゃないかとすら思えるときもあります」「RSpecで頑張って抽象度を高めるほどそうなりがちかも」「テストコードに不安を抱えるぐらいならMinitestでベタに書きたい派」「RSpecはテストをきれいに書きたくなるところがあるんですけど、本来やりたいのはアプリケーションコードが正しく動作していることを確認することだという気持ちが強まって、今はMinitestで書いてます」

参考: Rails テスティングガイド - Railsガイド

RSpecえかきうた

🔗 Rails UJSの舞台裏(Ruby Weeklyより)


つっつきボイス:「RailsのUJS(Unobtrusive JavaScript: 控えめなJavaScript)についてかなり詳しく解説しているようです」

参考: Rails で JavaScript を使用する - Railsガイド

「RailsでAjaxコードを書くようになるとUJSの理解が重要になってきますね: この記事のようにremote: trueから追いかけ始めて、data-method属性のようなdata-系属性をRailsで書けるのはなぜかを調べたりするようになりますよ」「ふむふむ」

# 同記事より
link_to "Example", "#", remote: true
=> "<a href='#' data-remote>Example</a>"
# 同記事より
Rails.handleMethod = (e) ->
  link = this
  # gets the method provided
  method = link.getAttribute('data-method')
  return unless method
  ...

参考: Rails学習者にrails-ujsの動作説明したら感動された話 - INODEVLOG

「rails-ujsのコードはそんなに大きくないはずですし、jQueryがRailsから外されたことで以前よりもシンプルになっていて読みやすいので、一度読んで見ることをおすすめします🎓」「お〜!」


前編は以上です。

バックナンバー(2021年度第1四半期)

週刊Railsウォッチ(20210511後編)AWS Lambda関数ハンドラをDSLで書けるyake gem、VPC Peeringが同一AZ転送量無料化ほか

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

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

Rails公式ニュース

Ruby on Rails Discussions

Ruby Weekly

StatusCode Weekly

statuscode_weekly_banner


CONTACT

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