Quantcast
Channel: hachi8833の記事一覧|TechRacho by BPS株式会社
Viewing all 1838 articles
Browse latest View live

Railsセキュリティ修正6.0.4.1と6.1.4.1 がリリースされました

$
0
0

Ruby on Rails セキュリティ修正6.0.4.1と6.1.4.1がリリースされました。

英語版Changelogをまとめて見るにはGItHubのリリースタグ↓が便利です。v6.0.4.1タグの日付は日本時間の2020/08/20 1:15、v6.1.4.1は2020/08/20 1:25でした。

詳しくは以下のコミットとRuby on Rails Discussionsのトピックをご覧ください。

🔗 セキュリティ修正の概要

セキュリティ修正は1件です。

🔗 1. CVE-2021-22942 ActionDispatch::HostAuthorizationミドルウェアでオープンリダイレクトの可能性

影響

特殊な細工を施されたX-Forwarded-Hostヘッダーと特定の”allowed host”フォーマットが組み合わさることで、Action PackのHostAuthorizationミドルウェアによってユーザーが悪意のあるWebサイトにリダイレクトされる可能性があります。

“allowed host”の先頭にドットが付加されているアプリケーションが影響を受けます。たとえば設定ファイルに以下があるとします。

config.hosts <<  '.EXAMPLE.com'

“allowed host”の先頭にドットが付加されていると、特殊な細工を施されたヘッダーを用いて悪意のあるWebサイトにリダイレクトされる可能性があります。
この脆弱性はCVE-2021-22881と似ていますが、CVE-2021-22881ではドメイン名の大文字小文字の区別は考慮されていませんでした。
discuss.rubyonrails.orgより

影響を受けるRailsバージョン
Rails 6.0.0以降
影響を受けないRailsバージョン
Rails 6.0.0未満
修正済みバージョン
Rails 6.0.4.1および6.1.4.1
パッチ
discuss.rubyonrails.orgにあり

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

関連記事

Rails 6にDNSリバインディング攻撃防止機能が追加された(翻訳)

Railsセキュリティ修正6.1.3.2/6.0.3.7/5.2.4.6/5.2.6がリリースされました

The post Railsセキュリティ修正6.0.4.1と6.1.4.1 がリリースされました first appeared on TechRacho.


『Polished Ruby Programming』(Jeremy Evans著)を読みました

$
0
0

こんにちは、hachi8833です。夏休み課題図書というわけではありませんが『Polished Ruby Programming』を2回読み、Webチーム内でも発表しました。

既にjnchitoさんが書評記事を書いてくださっているので、なるべく違う切り口で、かつネタバレにならないように書いてみようと思います。

著者について

Jeremy EvansさんはRubyコミッターであり、Railsにもコントリビューションしていた時期もあります。OpenBSD版のRubyのメンテナーでもあります。

jeremyevans/openbsd-ruby-ports - GitHub

昨年末にRuby Award 2020を受賞した直後のインタビュー記事を以下でも読めます。また、RubyKaigi Takeout 2021にもスピーカーとして登壇が決まりました。

参考: Interview with Jeremy Evans, OpenBSD Ruby ports maintainer by Evrone

私が最初にJeremy Evansさんを知ったのは2019年のRubyKaigiでトリを務めたのを見たときでした(以下の動画)。特濃のキーノートスピーチが忘れられません。今チラ見すると本書との共通点があちこちにあるように思えました。

Jeremy Evansさんは以下のgemを手掛けていることで知られています(リポジトリには他にも多数のgemがあります)。Sequelは引き継ぎでメンテナンスを行っているそうです。本書でもSequelとrodaは頻繁に登場しています。

jeremyevans/sequel - GitHub

jeremyevans/roda - GitHub

jeremyevans/rodauth - GitHub

jeremyevans/ruby-warning - GitHub

jeremyevans/erubi - GitHub

読み方

ちなみにKindle版を買いました。

1回目はKindleでマーカーを付けながらざっと駆け抜け、2回目はサンプルコードを動かしながらDocbaseにmarkdownで自分用に17章をエイヤでまとめました。

これはあくまで自分用に作ったドッグフーディング的なまとめであり、翻訳ではありません。当然ながら非公開なのであしからず。

書籍のサンプルコードは以下のリポジトリで公開されています。当然ながら書籍がないとさっぱりわかりませんが、読み進めながら動かしてみたところすべて問題なく動きました。

PacktPublishing/Polished-Ruby-Programming - GitHub

同書の特徴

Rubyに特化した中小規模ライブラリ設計の全方位的な知見を網羅

週刊Railsウォッチでも言及されていたように、Rubyに特化した最適なコードの書き方や知見が盛りだくさんです。Railsアプリのユーザーコードよりは、ライブラリの作成やメンテナンスを対象とした中規模または小規模の設計が中心という印象です。

参考: 週刊Railsウォッチ(20210810)『Polished Ruby Programming』

プロのRuby開発者に既に知られている内容もそれなりにあるかと思いますが、Rubyのコアのパフォーマンスを知り尽くした人が網羅的かつ統一的に書いているのがありがたい点です。

情報が新しい

Ruby 3.0をベースにしているので情報が新しく、過去バージョンのRubyとの違いについても必要に応じて言及されています。

特に、Ruby 2.7からRuby 3.0にかけて行われたキーワード引数の改修は著者が大きく貢献している部分だけあって、Ruby 3.0がリリースされてから満を持して本書を執筆したのだろうと想像しています。

参考: プロと読み解く Ruby 3.0 NEWS - クックパッド開発者ブログ

Rubyの言語設計思想をベースにしている

同書の解説は、Ruby言語が「何を重視していて」「何を重視していないか」を出発点としていると感じました。

たとえばRubyではローカル変数が他のインスタンス変数やクラス変数などより重視されている理由や、Rubyのハッシュが[]のキーが無効の場合にnilを返す理由、シンボルと文字列の違いや適切な使い分けについての解説などのさまざまな知見を、Rubyの言語設計思想と特性を踏まえて詳しく説明しています。

最適化の知見が豊富

全般に、Rubyコードの高速化はいかにオブジェクトの生成を避けるかが大きなポイントと感じました。ただしその都度、最初は最適化よりもきれいな設計を目指すべきとも述べられています。「最適化を急ぐな」「今のRubyは最適化しなくてもたいてい速い」は再三強調されています。

スコープゲート

本書でスコープゲートという用語を今頃知りました。どうやら「メタプログラミングRuby」でも使われているようです。

参考: passingloop • Ruby の class_eval でスコープゲートを乗り越えるもの、乗り越えられないもの

メタプログラミングとDSL

Rubyは他の言語と異なり、通常のプログラミングとメタプログラミングの間に大きな差はないと説いています。

そしてメタプログラミングやDSLはどうしても抽象度が高くなり、それがよい抽象なのか悪い抽象なのかを見分けるのは簡単ではないとしています。本書の目的のひとつが、そうした抽象の良し悪しを見分けられるようになることだそうです。

プラグインシステム

プラグインシステムの書き方についても1章を割り当てていて、可能ならライブラリにプラグインシステムを導入することを推奨しています。著者のRodaやRodauth gemはプラグイン方式を取り入れているので、その経験が生かされているのでしょう。

テストとリファクタリング

テストとリファクタリングにも1章ずつ費やしていて、同じく力が入っています。プロファイリングとベンチマークを繰り返しながら達人がコードを次第にきれいにしていく様子が手に取るようにわかります。

「カバレッジ100%に意味はない」という記述も印象的でした。

コードのスタイル

コードのスタイルについても1章を割いています。プログラマーを大きく「詩人タイプ(いろんなスタイルで書きたい派)」と「哲学者タイプ(スタイルを揃えたい派)」に分けて論じているのが面白い点です。

SOLID原則やデザインパターン

よく言われているように、本書でもRubyではJava方面由来のSOLID原則やGoFのデザインパターンはそのまま適用できるとは限らないと説いています(オープン/クローズ原則はRubyのコアライブラリでも完全に無視されているなど)。

デザインパターンにも1章を費やしていますが、扱われているのはJavaでおなじみのGoFのデザインパターンよりはるかに少ない8個に絞り込まれています。Visitorパターンについても、訪問先にメソッドを追加しない修正版Visitorパターンを紹介しています。GoFにないObject Poolデザインパターンを本書で初めて知りました。

参考: デザインパターン (ソフトウェア) - Wikipedia
参考: Object Pool Design Pattern - GeeksforGeeks

「デザインパターンはRubyではさほど重要ではないが、それでも知っておくと役に立つ」という記述が印象的でした。なお本書には「継承vsコンポジション」のようなトピックはありません。

Web

Webについては、最後の3章でデータベース選びと設計、フレームワーク選びやORM選び、セキュリティなどについて解説しています。

フレームワークはRailsだけではなく、Sinatra、Grape、そしてRodaを扱っています。Rodaは著者自ら作ったフレームワークだけあって解説にも力が入っており、ルーティングツリーとアクションが一体化した独特の作りと高速性に興味を惹かれました。

読んでみて

『Polished Ruby Programming』は、Rubyらしい素直で読みやすい書き方が大半を占めている印象です。黒魔術やメタプロも少し登場しますが、その都度警告しています。

ベテランらしく、設計の解説ではトレードオフ(主にパフォーマンスときれいな設計のバランス)がしばしば強調されています。「どちらの設計がよいかは場合による」がほとんどで、「常にこう書くべき」「こう書いておきさえすればよい」という書き方は少なく、自分好みの設計を出しすぎないよう可能な限り配慮しているのが伝わってきます。それでも著者の設計の好みがほんのりと感じられるのも面白い点です。

RuboCopのコードサイズ制限については珍しく「悪影響しかない」とやや強めの口調でした。気持ちわかります。

英語についても、あいまいになりやすい代名詞を避けて読みやすく書かれている点に好感が持てました。その分1つの文が長めで係り結びも増えますが、おそらく機械翻訳でかなりスムーズに翻訳できると思います。著者のドキュメンテーション能力の高さを実感しました。個人的な趣味ですが、アメリカンジョークがほとんどない点も好きです。

Sandi Metz『オブジェクト指向設計実践ガイド』はRuby向けのオブジェクト指向設計を学ぶ本として有名ですが、『Polished Ruby Programming』はSandi Metz本とはだいぶ趣が異なるように思いました。

  • Sandi Metz本はオブジェクト指向ありきでパフォーマンスにはあまり言及せず、Rubyに限定しない印象
  • Jeremy Evans本はRuby言語の設計思想と内部パフォーマンスを土台にしていて、オブジェクト指向やデザパタは必ずしも主役ではない印象

『Polished Ruby Programming』にはC言語のコードはまったく登場していません。おそらくRuby内部のC言語にまで言及したらきりがなくなってしまうからだろうと想像しました。なお、以下の『Rubyのしくみ』はRuby内部のC言語コードがビシバシに登場します。本書を読んでおくと役に立ちそうです。

本のつくりとしては、Kindleで読んだせいなのか、地の文とコードサンプルがなぜか同じフォントになっている点がちょっと読みづらく感じました。また、章内の見出しはほとんどがh2h3レベルでしたが、h4レベルの見出しまでブレイクダウンされていたらさらに読みやすくなりそうです。


Polished Ruby Programmingより

以下のkakutaniさんのgistにもあるように、本書には類書がほとんどないのも特徴です。

参考 Polished Ruby Programming翻訳査読書(のようなもの)

jnchitoさんのチェリー本を卒業してRubyやRailsのコードを実務で書くようになった中級者以上におすすめだと思います。また、他の言語の経験者がRubyを詳しく知るにも向いていそうです。ライブラリ作者がどんなことを考えているかを知るのに絶好の本です。

私の血となり肉となるにはだいぶ時間がかかりそうですが、『Polished Ruby Programming』は私にとって「繰り返し読みたい本」となりました。Docbaseにまとめたおかげでいつでも自分用に検索できるのがうれしい😂


The post 『Polished Ruby Programming』(Jeremy Evans著)を読みました first appeared on TechRacho.

週刊Railsウォッチ: Rails 7でのimport maps導入、Steepで型を導入、KubernetesでRailsを動かすためのガイドほか(20210830前編)

$
0
0

こんにちは、hachi8833です。以下をお見逃しの方はTwitterの#ginzarailsタグである程度追いかけられると思います。

週刊Railsウォッチについて

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

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

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

今回は以下の公式更新情報からです。本家が2つ先まで進んでいます。

🔗 deprecation warningを一括オプトアウト可能に


つっつきボイス:「config.active_support.report_deprecations = falseを指定すればdeprecation warningをまとめてオプトアウトできるようになった: 乱用は避けたいけどやむを得ず一時的にそうしたくなることもあるヤツ」「以前は以下を両方指定しないといけなかったんですね」「オプトアウトできる機能はあってもいいと思います👍

# changelogより
config.active_support.deprecation = :silence
config.active_support.disallowed_deprecation = :silence

🔗 Action CableのJSをES Moduleとしてトランスパイルせずに出力

Railsの全JSを可能な限りES2017およびESM(ES Module)をターゲットとするよう更新しよう。これによってバンドルなしでこれらの出力をブラウザで直接利用できるようになり、かつES Moduleを使えるようになる。これはWebpackerなしのデフォルトへの布石となる。
ここでは後方互換性問題の考慮が必要。これが互換性を妨げるのであれば2つの異なる出力を生成してもよい。
同PRより

参考: ブラウザで覚えるES Modules入門 - JavaScriptでモジュールを使う時代 - ICS MEDIA


つっつきボイス:「Rails 7でWebpackerレスを目指す方向」「ESMへトランスパイルせずにJSを出力するようになるといえば、Rails 7のJSがimport mapsベースになる話も出ていますね」「ちょうどこの後でも取り上げています」「考えてみればAction CableのJSライブラリは元からオープンなコードなので、難読化のためにわざわざトランスパイルすることもないんですよね」「たしかに」

参考: トランスコンパイラ - Wikipedia

🔗 development環境のNoDatabaseError画面に[Create database]ボタンを追加


つっつきボイス:「お〜、ActiveRecord::NoDatabaseErrorエラー画面にCreate databaseボタンができた」「当然ながらdevelopmentモードにしか出ないようになっている」「UIがますます親切になった👍


同PRより

🔗 ActiveRecord::QueryMethods#in_order_ofを追加


つっつきボイス:「おぉ、ついにActive Recordにもin_order_ofが入った🎉」「別のものが前からあったんでしょうか?」「少し前にRailsにEnumerable#in_order_ofが入っていたんですが↓、今回それのActive Record版もできて適用範囲が広がってきた感じですね」「なるほど!」

参考: rails commit log流し読み(2021/08/05) - なるようになるブログ

概要
これにより、SQL式に基づいてレコードを返す際に明示的な順序を指定できるようになる。デフォルトでは次のようにCASEで実現される。

Post.in_order_of(:id, [3, 5, 1])

上は以下のSQLを生成する。

SELECT "posts".* FROM "posts" ORDER BY CASE "posts"."id" WHEN 3 THEN 1 WHEN 5 THEN 2 WHEN 1 THEN 3 ELSE 4 END ASC

しかしこの機能はMySQLではFIELD関数の形で組み込まれているので、コネクションアダプタは代わりに次のようなSQLを生成する。

SELECT "posts".* FROM "posts" ORDER BY FIELD("posts"."id", 1, 5, 3) DESC

この機能は#41333でEnumerableに追加された機能に強くインスパイアされている。cc:@dhh
同PRより

「なるほど、in_order_ofのSQLはCASE文に続いてWHEN 3 THEN 1 WHEN 5 THEN 2...みたいに展開されるのか: まあそうなるでしょうね」「改行なしだと一瞬考え込んでしまったけど今理解しました」「力技だけどその分わかりやすいかも」「MySQLだとFIELD関数があるのでこう書けるんですって↓」「in_order_ofはあっていい機能👍

-- Changelogより
SELECT "posts".* FROM "posts" ORDER BY FIELD("posts"."id", 1, 5, 3) DESC

🔗 ActiveRecord::Relation#structurally_compatible?が追加


つっつきボイス:「呼び出し元のActiveRecord::Relationと引数に渡したActiveRecord::Relationが構造的に互換かどうか、つまり両者が同じカラムをselectしていて互換であればtrueを返すということのようですね」「おぉ?」「たとえば以下の2つ目だとjoinsで両者のカラムが異なっているのでfalseが返る」

# activerecord/lib/active_record/relation/query_methods.rb#751
# 与えられたリレーションがこのリレーションと構造的に互換性があるかどうかをチェックし、
# エラーを出さずに`#and`や`#or`メソッドを使えるかどうかを判断する。
# 「構造的に互換性がある」は、両者が同じモデルをスコープしていて
# `#where`(`#group`が定義されていない場合)または#having(`#group`が存在する場合)
# によってのみ異なることと定義される。
Post.where("id = 1").structurally_compatible?(Post.where("author_id = 3"))
# => true

Post.joins(:comments).structurally_compatible?(Post.where("id = 1"))
# => false

「そして以下のように両者を#orでつなげる場合はcurrent.or(other)で、つなげない場合はModel.where(id: current)のようにラップしてから#orでつないでいる」「なるほど」「特にスコープでjoinsされた場合だと#and#orでつないでいいか考えることがちょくちょくあるので、これはあっていいメソッド👍」「名前はちょっと長いけど、知っていたら使うかも」

# 同PRより
relations = [...]
relations.drop(1).inject(relations.first) do |current, other|
  if current.structurally_compatible?(other)
    current.or(other)
  else
    Model.where(id: current).or(Model.where(id: other))
  end
end

🔗Rails

🔗 RailsプロジェクトにSteepで型を導入


つっつきボイス:「これは自分も読みました: RBSを自動生成するところから始めてひととおりやれる、具体的ないい記事👍」「RubyMineのRBS対応も進んでいますし、小さな新規プロジェクトでやってみてもよさそう」

「ちょうどこのツイートも見つけました」「RBSとSteepはRuby 2.6以降から使える、いいですね〜」

🔗 AWS LambdaでRails

aws/aws-lambda-ruby-runtime-interface-client - GitHub


つっつきボイス:「取り上げるのが遅れましたが、7月末の銀座Rails #35の@joker1007さんの発表です」「これもいい発表でした」

「元々Amazon ECSのタスク実行機能が使いにくいという問題があって、この問題は実際に踏んでみないとわかりにくいんですが、その部分をLambdaでやる方がいいという趣旨」

「AWSのコンテナをスケジューリングで動かす方法は現在2とおりあります: 1つはFargateやECSのscheduled taskと呼ばれるもので、コンテナをワンショットでバッチ的に実行する」「ふむふむ」「しかしECSの実行そのものがかなり重いのが不便: 具体的にはECSのイメージが置かれているリポジトリのパスがECSコンテナのタスク定義ファイルに書かれていて、起動のたびにそこからイメージをダウンロードしてECSの実行環境に展開することではじめて実行される」「聞くからに重そうですね…」「下手すると起動に数分かかることもあります」

参考: スケジュールされたタスク - Amazon ECS

「2つ目のLambdaは同じようにコンテナを起動しますが、ECSのタスク起動よりずっと速い」「お〜」「Lambdaにはコンテナサイズや実行時間に制限がありますが、実行時間が短くて何度も実行するようなものならLambdaでやる方がずっといい、というのがスライドのこのあたりの話↓」「なるほど」「ECSは常にECR(Elastic Container Registry)からDockerイメージをダウンロードするのと、タスクスケジューラが毎回コールドスタートするので遅いんですが、Lambdaは起動済みのDockerコンテナがあればウォームスタートできるので数秒おきや数分おきに起動するタスクだと特に速い: ちょうど自分もこのあたりにハマったことがありました」

参考: Amazon ECR(Docker イメージの保存と取得)| AWS

🔗 Rails 7でのimport maps導入

rails/importmap-rails - GitHub


つっつきボイス:「ついこの間話題になった、Rails 7のJSコードをWebpack経由でトランスパイラを通す代わりにimport mapsを使う話の動画について、DHHが記事も書いていたので取り上げました」「これについては既にTwitterにも書きましたけど、今後のRailsがこういうWebpackerレスな流れになることはほぼDHHが決めているようなので、このDHHの記事と解説動画、あとこれに関連する上の#42856の改修内容は一度見て押さえておくことをおすすめします: 技術的にもそれほど難しいものではありませんし、字幕をオンにすれば英語もそれほど大変ではないので」「なるほど」「RailsでTypeScript使いたい人たちはどうなるんだろう」

「ところで、DHHはReactの場合の動画も後追いで公開したんですよ↓」「これは知りませんでした」

「2本目のReact動画は、Railsがトランスパイラを使わなくなるとReactのJSXやTypeScriptなどが書けなくなるという問題を自分も含めていろんな人が指摘したので↓、DHHがJSXについては『こうすれば書けるよ』という回答として公開したものです」

参考: JSX の導入 – React

「ところが2本目の動画を見ると実際にはJSXそのものを書けるのではなくて、どうやらJSファイルにJSXライクなコードを書けるhというヘルパーを使うみたいなんですよ: import html from "htm"というコードをたどるとdevelopit/htmというライブラリがどうもそれみたい」「ありゃ」


Alpha preview: Using React with importmaps on Rails 7 – YouTubeより

「developit/htmのREADMEにも『プレーンなJavaScriptにトランスパイラなしでJSXライクな構文で書ける』とあるのでたぶんこれかなと思います」

developit/htm - GitHub

🔗 徳丸先生のRailsセキュリティ関連解説動画


つっつきボイス:「こちらの動画は、銀座Rails #34の徳丸先生のセッションを主催者承認のうえでYouTube動画で公開したそうです」「たしかに銀座Railsで見たヤツ」

「ところで徳丸先生のYouTubeチャンネルにもだいぶ動画が増えていますね」「動画だと見るのに時間がかかるのが大変」「そこですよね」「文章と動画とどちらを好むかは年齢層によって変わってきそうですけど」「せめて動画内でセリフや文字を検索できたらいいかも」「たしかに」「話し言葉と書き言葉はどうしても違ってくるので、自分は記事で読みたいかな」

🔗 KubernetesでRailsを動かす決定版ガイド


つっつきボイス:「KubernetesでRailsを動かす情報を、ドメインまで取って公開しているサイトです」「Kubernetesで単にRailsのDockerイメージを動かしてログを取るぐらいならさほど難しくありませんが、secretの扱いやrakeタスクやマイグレーションのようなRailsで最低限必要なことをひととおり網羅しているようですね: KubernetesでRailsを初めて動かす人向けの入り口として便利そう👍」「お〜」

🔗 その他Rails


つっつきボイス:「@yasaichiさんの発表が以下のスライド↓の続編になりそうなので期待しています」「9/15(水)開催だからRubyKaigi Takeout 2021の翌週か、とりあえず申し込んでおこう」


前編は以上です。

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

週刊Railsウォッチ: SorbetのRuby AOTコンパイラが公開、「Compiler Explorer」にRubyが追加、Ractorで非同期通信ほか(20210823後編)

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

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

Rails公式ニュース

Ruby Weekly

The post 週刊Railsウォッチ: Rails 7でのimport maps導入、Steepで型を導入、KubernetesでRailsを動かすためのガイドほか(20210830前編) first appeared on TechRacho.

週刊Railsウォッチ:TruffleRubyでdig_fetchを実装、ruby/debug gem、AWSハンズオン教材ほか(20210901後編)

$
0
0

こんにちは、hachi8833です。遅ればせながら私もRubyKaigi Takeout 2021のTシャツを注文しました。

参考: Novelties - RubyKaigi Takeout 2021

週刊Railsウォッチについて

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

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

🔗Ruby

🔗 TruffleRubyでdig_fetchを実装して高速化

# 同記事より
def self.dig_fetch(obj, idxs)
  idxs_size = idxs.size
  n = 0
  while n < idxs_size
    idx = idxs[n]

    obj = obj[idx]
    if obj.nil?
      raise KeyError.new("key not found: #{idx.inspect}", :receiver => self, :key => idx)
    end
    n += 1
  end

  obj
end

つっつきボイス:「dig_fetchというメソッド名がスゴい」「Rubyのdigfetchはデータ構造が見えていれば高速化の余地はあるかも」「そういえばruby-jp Slackで最近Rubyのdigfetch周りが話題になっていたのを見かけた気がします」「お、知りませんでした」

参考: instance method Hash#fetch (Ruby 2.3.0)
参考: Hash#dig (Ruby 3.0.0 リファレンスマニュアル)

「TruffleRubyはOracle Labsがこれだけ精力的に手を加え続けているんだから、どこかのproductionで使われていてもよさそう」「気になりますね」

oracle/truffleruby - GitHub

後で探すと、ShopifyがTruffleRubyのproduction利用に向けて実験を重ねているという記事を見かけました。またRubyKaigi Takeout 2021でもShopifyのメンバーがTruffleRubyの正規表現について発表することになっています。

参考: Optimizing Ruby Lazy Initialization in TruffleRuby with Deoptimization — Development (2021)
参考: Just-in-Time Compiling Ruby Regexps on TruffleRuby — Schedule - RubyKaigi Takeout 2021

🔗 ko1さんのdebug gemブログ


つっつきボイス:「ko1さんがruby/debugのブログを始めたそうです」「お〜、こういう情報が記事として記録されていくのは大事👍」「歴史大事ですね」「そうでないと端から失われていってしまうので」「relineを使ってdebugにREPL機能も追加されたのか」「記事でも紹介されているst0012さんのruby/debug紹介記事↓は近々TechRachoで翻訳を公開します」

参考: A Sneak Peek of Ruby’s New Debugger! - DEV Community
参考: REPL - Wikipedia

ruby/reline - GitHub

🔗 その他Ruby


つっつきボイス:「大倉さんがRubyConf 2021に登壇🎉」「英語で登壇つよい」「RubyConf 2021はオンラインとオフライン両方で開催とは頑張ってる」「開催地のデンバーは米国コロラド州で、州の布告に基づいてイベント開催してるようですね」

🔗クラウド/コンテナ/インフラ/Serverless

🔗 Amazon MemoryDB for Redis


つっつきボイス:「Amazon MemoryDB for RedisはRedisの互換実装によるサービスで、先々週ぐらいに発表されていましたね: 値段はお安くないですが、Redisを自前で立てると運用が大変なので、こういうサービスはやはり便利だと思います👍

🔗 東大のAWSハンズオン教材


つっつきボイス:「このAWSハンズオン教材は昨年公開されていたヤツかな」「ホントだ、2020年となってました」「東大クラスでないとなかなかこれだけの教材は用意できないでしょうね」「特に考えなくても上から順にやっていけば終わるので、東大の単位としては取りやすい方かも」「たまに考えないといけない場面で詰まるかもしれないので、TAやSAのサポートは必要かもしれませんね」

参考: 大学職員のTA・SAの仕事の違いは? | 大学職員の仕事・なり方・年収・資格を解説 | キャリアガーデン

「逆に東大のCPU実験授業はCPUつくってコンパイラつくって動作テストするところまでやるというエグさ」「そうそう、東大の名物授業」

参考: ほんとうのコンピュータ自作/CPU実験 — 東大 理学部情報科学科/大学院情報理工学系研究科|情報科学科NAVIgation

「東大のAWSハンズオン教材は、内容の賞味期限が切れないうちにやるのがいいと思います👍」「そうそう、今年なら十分いける」「こういうハンズオン授業は教材アップデートのためにも毎年やって欲しいですね」

🔗 その他クラウド

つっつきボイス:「Dockerfileでヒアドキュメントが書ける機能が正式にリリースされた🎉」「これで&& \を書かなくてよくなりますね」「変数展開もできる↓」「今までなかったのが不思議だったぐらい」

# moby/buildkit READMEより
# syntax = docker/dockerfile:1.3-labs
FROM alpine
ARG FOO=bar
COPY <<-eot /app/foo
    hello ${FOO}
eot

参考: Dockerで新しくサポートされるようになったヒアドキュメントを試してみました。 | オスースBlog

🔗CSS/HTML/フロントエンド/テスト/デザイン

🔗 ブラウザのデザインモード


つっつきボイス:「これ今頃知りました」「ブラウザコンソールでdocument.designMode = "on"を入力するとブラウザが編集可能になるのね」「ずっと以前からHTMLElement.contentEditableという属性を使うとどの要素でも編集可能にできますけど、designModeは初めて見た」

参考: Document.designMode - Web API | MDN
参考: HTMLElement.contentEditable - Web API | MDN

「ちなみにブラウザ用WYSIWYGエディタによってはHTMLElement.contentEditableを使って実装しているものがあります」「お〜」「しかもHTMLElement.contentEditableを使うとHTMLのスタイルタグも自動的に付けられるのでHTML的に扱いやすいんですよ」

HTMLElement.contentEditableは相当昔からあった」「IE5の独自仕様だったのが取り入れられたという記事↓があったのでかなり古そう」「document.designModeもMDNにIE6に関する記述があるので同じぐらい古そうですね」

参考: HTMLのcontenteditable属性 - 備忘帳 - オレンジ工房
参考: “contentEditable” | Can I use... Support tables for HTML5, CSS3, etc

🔗言語/ツール/OS/CPU

🔗 クラック


つっつきボイス:「朝起きたら自宅の3Dプリンタからこんなのが出力されてビビったというReddit記事だそうです」「あぁクラックされたのね」「これはビビる」「普通のネットワークプリンタなんかもクラッキングされやすいという話は昔からありますけどね」

「ところで3Dプリンタも昔よりだいぶ使いやすくなりましたよね」「自宅に置くまではまだやれてないです」「よく使うなら買いたいけど、まだそこまではいかない」「コンビニで3Dプリントできたらいいんですけどね」「専有時間が長いから難しいんじゃないかな」「専門店で3Dプリントやったことならあります」「試行錯誤やトラブルシュートとかも考えると、最初はサポートのある場所でやってみるのがいいかもですね」


後編は以上です。

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

週刊Railsウォッチ: Rails 7でのimport maps導入、Steepで型を導入、KubernetesでRailsを動かすためのガイドほか(20210830前編)

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

The post 週刊Railsウォッチ:TruffleRubyでdig_fetchを実装、ruby/debug gem、AWSハンズオン教材ほか(20210901後編) first appeared on TechRacho.

ActiveRecordで日付・時刻の範囲検索をシンプルに書く方法

$
0
0

更新情報

  • 2016/08/19: 初版公開
  • 2021/08/26: 更新

こんにちは、hachi8833です。

Active Recordで日付範囲を指定して読み出そうとすると、おそらく次のようなコードになるでしょう。

Pattern.where(“updated_at BETWEEN ? AND ?”, from, to)

社内のSlackチャンネルのログを遡ってて、Active Recordでwhere(updated_at: range_obj_start..range_obj_end)のように、Rangeオブジェクトを#whereの値指定として渡せるというやりとりを見つけたので、確認してみました。

範囲演算子とは

RubyのRangeオブジェクトでは、.....という範囲演算子を使えます。

条件式以外の場所では式1から式2までの範囲オブジェクトを返します。範囲オブジェクトはRangeクラスのインスタンスです。...で生成された範囲オブジェクトは 終端を含みません
範囲式 (Ruby 3.0.0 リファレンスマニュアル)より(強調は筆者)

終端を含まないのは.....のどっちだったかときどきわからなくなったりしますね。

なお、数学用語では端の値を含む範囲を「閉区間」、端の値を含まない範囲を「開区間」と呼んでいます(Wikipedia: 区間(数学))。

Range#newでオブジェクトを生成できます。ここでの範囲演算子は..なので閉区間ですね。

Range.new(Time.zone.now, Time.zone.now.tomorrow)

pry-rails gemを導入したRailsコンソールで出力しました。

range_new

.....で日時を範囲指定

範囲演算子を思い出したところで、適当なRailsプロジェクトをbundle exec rails cでコンソール起動し、Active Recordの適当なモデル(ここではPatternというモデル)のupdated_atカラムに次のクエリをそれぞれ実行してみます。両者の違いはRubyの範囲指定子.....だけです。

Pattern.where(updated_at: Time.zone.today.beginning_of_day..Time.zone.today.end_of_day).to_sql
Pattern.where(updated_at: Time.zone.today.beginning_of_day...Time.zone.today.end_of_day).to_sql

AR_range

1番目の..のSQLでは、ストレートにBETWEENを使っています。
2番目の...のSQLでは、end_of_dayに終端を含まないよう、<を使って自動展開しています。

SELECT `patterns`.* FROM `patterns` WHERE (`patterns`.`updated_at` BETWEEN '2016-08-18 00:00:00' AND '2016-08-18 23:59:59')
SELECT `patterns`.* FROM `patterns` WHERE (`patterns`.`updated_at` >= '2016-08-18 00:00:00' AND `patterns`.`updated_at` < '2016-08-18 23:59:59')

もし終端値のクラス(DateとかDatetimeとか)に応じて<<=を切り替えようとすると、クラスのチェックが必要になるので煩雑になってしまいます。

BETWEEN>=<の切り替えなら、終端値のクラスを気にせず、大小関係が定義されている値の範囲にシンプルに適用できます。ささやかですが、うまい処理ですね。

参考

関連記事

ActiveRecordのRangeHandlerクラスとRubyの範囲メソッドRange#exclude_end?

The post ActiveRecordで日付・時刻の範囲検索をシンプルに書く方法 first appeared on TechRacho.

Rubyの新しいデバッガの機能を先行紹介(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。なお翻訳記事公開時点のデバッガバージョンはv1.0.0rc1になりました。

Rubyの新しいデバッガの機能を先行紹介(翻訳)

ruby/debug - GitHub

debugはRubyの新しいデバッガで、Ruby 3.1に同梱される予定です。近頃はこのデバッガにコントリビューションしつつ自分でも使ってきたので、1.0が正式にリリースされる前にそろそろ皆さんにいち早く紹介するときが来たと感じています。

(本記事執筆時点のdebugはまだ正式リリースではないため、本記事で紹介する機能は正式リリースまでに変更または削除される可能性があります)

(追記: 本プロジェクトのリード開発者@ko1氏が以下のデバッガ記事連載を始めましたので、こちらもチェックよろしくお願いします😉

🔗 あらまし

上述のように、このデバッガはRuby 3.1の標準ライブラリになる予定です。現時点では以下のようにgemとしてインストールできます。

$ gem install debug --pre

または

# Gemfile
# 開発が活発なので、可能ならGitHubをソースとして指定するのがおすすめ
gem "debug", github: "ruby/debug"

debugは、機能面では有名なGDBデバッガやRubyのbyebug gemと似ています。豊富なデバッグコマンドの他に、独自の機能をいくつも備えています。

READMEより引用します。

debug.rbには以下のような多くの利点がある。

  • 高速性: ノンステップモードやノンブレークポイントでパフォーマンスを損なわない
  • リモートデバッグをネイティブでサポート
  • 拡張性: 以下のようにさまざまな方法でアプリケーションにデバッグを導入可能
    • rdbgコマンド
    • コマンドラインで-rオプションを指定
    • 明示的なRubyメソッド呼び出し
  • その他
    • スレッド(ほぼ完了)とRactor(ToDo)のサポート
    • ほぼいつでもCtrl-Cでコンソールデバッグのオンオフを切り替え可能
    • バックトレースコマンドでのパラメータ表示

以下は私が気に入っている機能です。

  • カラー表示

  • backtraceコマンドでバックトレースを表示するとメソッドの「引数」「ブロック引数」「戻り値」も表示される
=>#0    Foo#forth_call(num1=20, num2=10) at target.rb:20 #=> 30
  #1    block {|ten=10|} in second_call at target.rb:8
  • binding.breakでデバッグコマンドをスクリプト化して手動操作を軽減できる(後述の組み合わせセクションのサンプルを参照)
  • breakcatchwatchなどのコマンドを用いてブレークポイントをさまざまな条件で起動できる

🔗 binding.break(エイリアス: binding.b

私のようなpryのヘビーユーザーなら、pryで慣れ親しんだbinding.breakbinding.bだけでもよい)を用いてデバッグセッションを開始できます。

ただし、実際のbinding.breakには以下のようにコマンドを渡せるので、binding.pryよりも強力です。

  • binding.b(do: "catch CustomException"): デバッガーはcatch customExeptionコマンドを実行してプログラムを続行する
  • binding.b(pre: "catch CustomException"): デバッガーはcatch customExeptionコマンドを実行してその行で停止する

(コマンドを複数実行したい場合は"cmd1 ;; cmd2 ;; cmd3"のように;;で区切ります)

🔗 よく使うコマンド

新しいデバッガには強力なコマンドが多数搭載されています。その中でも私が最も多用しているものを以下に紹介します。

🔗 breakコマンド(エイリアス: b

class A
  def foo; end
  def self.bar; end
end

class B < A; end
class C < A; end

B.bar
C.bar

b1 = B.new
b2 = B.new
c = C.new

b1.foo
b2.foo
c.foo
基本の用法
  • b A#foo: b1.foob2.fooc.fooが呼び出されると停止する
  • b A.bar: B.barC.barが呼び出されると停止する

  • b B#foo: b1.foob2.fooが呼び出されると停止する

  • b B.bar: B.barが呼び出されると停止する

  • b b1.foo: b1.fooが呼び出されると停止する

コマンド
  • b b1.foo do: cmd: b1.fooが呼び出されるとcmdを実行する(ただし停止しない)

  • b b1.foo pre: cmd: b1.fooが呼び出されるとcmdを実行して停止する

🔗 catchコマンド

class FooException < StandardError; end
class BarException < StandardError; end

def raise_foo
  raise FooException
end

def raise_bar
  raise BarException
end

raise_foo
raise_bar
  • catch StandardError: StandardErrorFooExceptionBarExceptionも含む)のインスタンスがraiseされると停止

  • catch FooException: FooExceptionがraiseすると停止

🔗 backtraceコマンド(エイリアス: bt

以下は出力例です。

=>#0    Foo#forth_call(num1=20, num2=10) at target.rb:20 #=> 30
  #1    block {|ten=10|} in second_call at target.rb:8
  #2    Foo#third_call_with_block(block=#<Proc:0x00007f9283101568 target.rb:7>) at target.rb:15
  #3    Foo#second_call(num=20) at target.rb:7
  #4    Foo#first_call at target.rb:3
  #5    <main> at target.rb:23
  • bt: スタックの全フレームを表示
  • bt 10: スタックの最初の10フレームのみを表示

  • bt /my_lib/: my_libにマッチするパスのフレームのみを表示

🔗 outlineコマンド(エイリアス: ls

outlineコマンドは、irbやpryのlsコマンドと同様に使えます。

🔗 binding.bとコマンドの組み合わせ

🔗 binding.b(do: "b Foo#bar do: bt")

この機能を使うと、メソッド定義に手を加えたりコマンドを手動で入力したりせずにメソッド呼び出しのバックトレースをinspectできます。

以下はスクリプト例です。

binding.b(do: "b Foo#bar do: bt")

class Foo
  def bar
  end
end

def some_method
  Foo.new.bar
end

some_method

以下は出力です。

DEBUGGER: Session start (pid: 75555)
[1, 10] in target.rb
=>    1| binding.b(do: "b Foo#bar do: bt")
      2|
      3| class Foo
      4|   def bar
      5|   end
      6| end
      7|
      8| def some_method
      9|   Foo.new.bar
     10| end
=>#0    <main> at target.rb:1
(rdbg:binding.break) b Foo#bar do: bt
uninitialized constant Foo
#0  BP - Method (pending)  Foo#bar do: bt
DEBUGGER:  BP - Method  Foo#bar at target.rb:4 do: bt is activated.
[1, 10] in target.rb
      1| binding.b(do: "b Foo#bar do: bt")
      2|
      3| class Foo
=>    4|   def bar
      5|   end
      6| end
      7|
      8| def some_method
      9|   Foo.new.bar
     10| end
=>#0    Foo#bar at target.rb:4
  #1    Object#some_method at target.rb:9
  # and 1 frames (use `bt' command for all frames)

Stop by #0  BP - Method  Foo#bar at target.rb:4 do: bt
(rdbg:break) bt
=>#0    Foo#bar at target.rb:4
  #1    Object#some_method at target.rb:9
  #2    <main> at target.rb:12

🔗 binding.b(do: "b Foo#bar do: info")

メソッドが呼び出されたときのメソッドの環境(引数など)をinspectできます。

以下はスクリプト例です。

binding.b(do: "b Foo#bar do: info")

class Foo
  def bar(a)
    a
  end
end

def some_method
  Foo.new.bar(10)
end

some_method

以下は出力です。

DEBUGGER: Session start (pid: 75924)
[1, 10] in target.rb
=>    1| binding.b(do: "b Foo#bar do: info")
      2|
      3| class Foo
      4|   def bar(a)
      5|     a
      6|   end
      7| end
      8|
      9| def some_method
     10|   Foo.new.bar(10)
=>#0    <main> at target.rb:1
(rdbg:binding.break) b Foo#bar do: info
uninitialized constant Foo
#0  BP - Method (pending)  Foo#bar do: info
DEBUGGER:  BP - Method  Foo#bar at target.rb:4 do: info is activated.
[1, 10] in target.rb
      1| binding.b(do: "b Foo#bar do: info")
      2|
      3| class Foo
      4|   def bar(a)
=>    5|     a
      6|   end
      7| end
      8|
      9| def some_method
     10|   Foo.new.bar(10)
=>#0    Foo#bar(a=10) at target.rb:5
  #1    Object#some_method at target.rb:10
  # and 1 frames (use `bt' command for all frames)

Stop by #0  BP - Method  Foo#bar at target.rb:4 do: info
(rdbg:break) info
%self = #<Foo:0x00007fdac491c200>
a = 10

私はRails開発者なので、以下のようなコードをコントローラやアクションの冒頭に置いて使うことがよくあります。

class SomeController < ApplicationController
  def index
    binding.b(pre: "b User#buggy_method do: info")
    # 他のコード
  end
end

後はデバッガーでコマンドを実行するもよし、見たいメソッドでデバッガーを停止するもよしです。
おかげで、binding.pryputsを追加してファイルからファイルへジャンプする必要がなくなりました😎

🔗 小さな問題点(その後解消)

しかし新しいデバッガーは(まだ)完璧ではありません。byebugやpryのようにRubyの式を直接評価できません。

(rdbg) 1 + 1
unknown command: 1 + 1

式を評価するには、以下のようにpppを追加する必要があります。

(rdbg) p 1 + 1
=> 2

ただし本プロジェクトのメンテナーである@ko1コメントによると、1.0の正式リリースまでに式の評価機能をサポートするかもしれないとのことです。

続報

その後#227がマージされたおかげで、この問題は解消されました😉

最後に

新しいデバッガーはまだ公式にはリリースされていませんが、私は日々の業務で使い始めています。近い将来、すべてのRubyistのツールボックスに常備されると信じています。新しいデバッガーの機能に興味をお持ちでしたら、ぜひお試しください😉

関連記事

The post Rubyの新しいデバッガの機能を先行紹介(翻訳) first appeared on TechRacho.

週刊Railsウォッチ:ActiveRecord::QueryLogs追加、spring gemがデフォルトから削除、fast_gettextほか(20210906前編)

$
0
0

こんにちは、hachi8833です。今週はいよいよRubyKaigi Takeout 2021ですね。

週刊Railsウォッチについて

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

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

お知らせ: 来週の週刊Railsウォッチはお休みいたします🙇

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

公式に追いつくべく今回は2回分取り上げます。

import-mapsは先週取り上げたので(ウォッチ20210831)、importmaps-railsリポジトリを貼ります。

rails/importmap-rails - GitHub


つっつきボイス:「DHHは今後このgemを推していくんでしょうね」「予想はしていたけどRails 7以上なのか〜」「今やってるプロジェクトでimport mapsが使えたらと思ったんですが残念」

🔗 Marginalia gemがActiveRecord::QueryLogsとして追加


つっつきボイス:「Marginalia?」「Basecampのgemだそうです」「そのgemがネイティブのActiveRecord::QueryLogsとして追加されたみたい」

basecamp/marginalia - GitHub

「MarginaliaはSQLクエリのログにどのアプリのどのコントローラのどのアクションからのクエリかというコメントをこういうふうに追加するのね↓: そういえばこんなgemがあった」「名前でぱっとわかりにくそう」「造語かと思ったら”傍注”という意味でした」「入っていたら使うかも👍

# basecamp/marginalia READMEより
Account Load (0.3ms)  SELECT `accounts`.* FROM `accounts` 
WHERE `accounts`.`queenbee_id` = 1234567890 
LIMIT 1 
/*application:BCX,controller:project_imports,action:show*/

ActiveRecord::QueryLogsを追加

Active Recordで生成されるすべてのSQLクエリにコンフィグ可能なタグが自動追加可能になった。

# config/application.rb
module MyApp
  class Application < Rails::Application
    config.active_record.query_log_tags_enabled = true
  end
end

デフォルトでは、クエリタグに「アプリケーション」「コントローラ」「アクション」の詳細が追加される。

class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end
GET /books
# SELECT * FROM books /*application:MyApp;controller:books;action:index*/

静的な値やProcを含むカスタムタグをアプリケーション設定で定義可能。

config.active_record.query_log_tags = [
  :application,
  :controller,
  :action,
  {
    custom_static: "foo",
    custom_dynamic: -> { Time.now }
  }
]

Keeran Raj Hawoldar, Eileen M. Uchitelle, Kasper Timm Hansen
同Changelogより

以下は作成中の移行ガイドだそうです。

参考: upgrade.md

🔗 マルチDBのrails db:setuprails db:resetで特定のデータベースを指定できるようになった

これは、データベース固有のセットアップとリセットのタスクを可能にする試み。自分たちはマルチプルデータベースを広範囲に使っていて、すべてのデータベースを一度にリセットすることは避けたいと考えている。この変更により、名前空間ごとにデータベース固有のセットアップやリセットのタスクが追加される。デフォルトのセットアップタスクやおよびリセットタスクは、説明文を除いて変更していない。

ひとつ問題があるとすれば、db:seedタスクはデータベースに依存しないため、データベースに依存するタスクはすべてをseedすることになる点。特定のデータベースだけをseed可能だろうか?見たところ、seedファイルはどのActive Recordモデルでも参照できるので、できなさそうに思える。

たとえば、primaryとeventsという2つのデータベースがある場合、以下のタスクが利用できる。

rails db:reset                   # Drops and recreates all databases from their schema for the current environment and loads the seeds
rails db:reset:events            # Drops and recreates the events database from its schema for the current environment and loads the seeds
rails db:reset:primary           # Drops and recreates the primary database from its schema for the current environment and loads the seeds
rails db:setup                   # Creates all databases, loads all schemas, and initializes with the seed data (use db:reset to also drop all databases first)
rails db:setup:events            # Creates the events database, loads the schema, and initializes with the seed data (use db:reset:events to also drop the database first)
rails db:setup:primary           # Creates the primary database, loads the schema, and initializes with the seed data (use db:reset:primary to also drop the database first)

同PRより


つっつきボイス:「マルチプルDBで特定のデータベースだけを設定したりリセットしたりできるrakeタスクが追加されたんですね」「今までできなかったとは」「これは欲しい機能👍

🔗 spring gemをデフォルトインストールから削除


つっつきボイス:「development環境やtest環境でRailsをプリロードして起動を速くするspring gemがRailsのデフォルトから消えるそうです」「いいねがかなり多いですね」

rails/spring - GitHub

コンピュータが高速になったので、小〜中規模アプリでspringを使う大きなメリットがほぼなくなった。よって、たまに問題を起こすspringをデフォルトで入れてつらい思いをする必要はもうない。
同PRより

「自分もspringはいつも真っ先にオフにしてる」「私も」「テストが理由なく落ちるときにspringを無効にすると解消するということがちょくちょくありましたね」「マイグレーションがなぜか失敗したときもよくありました」「ネイティブ環境だとspringもそれなりに有用なのかもしれないけど、最近はDocker環境で使う人が増えていますし、それもあって外したのかもしれませんね」

🔗 classicモード廃止に伴う孤立メソッド削除


つっつきボイス:「ActiveSupport::Dependencyからorphanな(孤立した)メソッドが続々削除されたそうです」「privateなインターフェイスだし消しても大丈夫そう」

参考: 定数の自動読み込みと再読み込み (Classic) - Railsガイド

「もうひとつのプルリクはActiveSupport::Dependencyにあるsafe_constantizeが削除された」「constantizeは使っていたけどsafe_constantizeは知らなかったな〜」

参考: constantizeString

(これはclassicモードの削除に伴うActiveSupport::Dependenciesのお掃除の一環)

ActiveSupport::Dependenciesconstantizeメソッドとsafe_constantizeメソッドはprivateでオートローディングとは関係しておらず、単にinflectorに転送される。これらは歴史的な理由で残されていたが、今や既に不要。
publicなインターフェイスはStringクラスにある。

model_name.constantize

したがって以下の代わりに上のように書ける。

ActiveSupport::Dependencies.constantize(model_name)

既にフレームワークの大半がこのようになっており、残りはわずか。
#43058より

🔗 weekday_options_for_selectビューヘルパーが追加


つっつきボイス:「ブラウザで平日の曜日選択のプルダウンを表示するweekday_options_for_selectってありそうでなかったのか」「ビューのフォームヘルパーなんですね」「これはあっていいメソッド👍

自分はこれまで多くのRailsアプリで平日の曜日を選択するヘルパーを手作りしなければならなかった。Railsには優秀なヘルパーがほとんど揃っているので、このヘルパーがないことにいささか驚いていた。ちょうどヘルパーをまた実装しなければならなくなり、他の開発者もこのヘルパーがないことに驚いていたので、Railsにプルリクを投げてもいい頃合いだと思った。

このプルリクはFormOptionHelperFormBuilderに2つのメソッドを追加し、Tags::WeekdaySelectクラスを追加する。

weekday_options_for_select
# => "<option value=\"Sunday\">Sunday</option>\n<option value=\"Monday\">Monday</option>\n
# <option value=\"Tuesday\">Tuesday</option>\n<option value=\"Wednesday\">Wednesday</option>\n
# <option value=\"Thursday\">Thursday</option>\n<option value=\"Friday\">Friday</option>\n
# <option value=\"Saturday\">Saturday</option>"

weekday_options_for_selectではselected値を受け取るほかに:index_as_valueオプションと:day_formatオプションも受け取れる。

weekday_options_for_select(nil, day_format: :abbr_day_names)
# => "<option value=\"Sun\">Sun</option>\n<option value=\"Mon\">Mon</option>\n
# <option value=\"Tue\">Tue</option>\n<option value=\"Wed\">Wed</option>\n
# <option value=\"Thu\">Thu</option>\n<option value=\"Fri\">Fri</option>\n
# <option value=\"Sat\">Sat</option>"

weekday_options_for_select(nil, index_as_value: true)
# => "<option value=\"0\">Sunday</option>\n<option value=\"1\">Monday</option>\n
# <option value=\"2\">Tuesday</option>\n<option value=\"3\">Wednesday</option>\n
# <option value=\"4\">Thursday</option>\n<option value=\"5\">Friday</option>\n
# <option value=\"6\">Saturday</option>"

weekday_options_for_selectは以下のような場合で使うヘルパーメソッド。

weekday_select(:model, :weekday)
# => "<select name=\"model[weekday]\" id=\"model_weekday\"><option value=\"Sunday\">Sunday</option>\n
# <option value=\"Monday\">Monday</option>\n<option value=\"Tuesday\">Tuesday</option>\n
# <option value=\"Wednesday\">Wednesday</option>\n<option value=\"Thursday\">Thursday</option>\n
# <option value=\"Friday\">Friday</option>\n<option value=\"Saturday\">Saturday</option></select>"

weekday_selectメソッドはFormBuilderでも使われるので、以下のように書くと

<!-- 同PRより -->
<%= form_for @digest do |f| %>
  <%= f.weekday_select :weekday %>
  <%= f.submit %>
<% end %>

以下のようなHTMLが生成される。

<!-- 同PRより -->
<select name="digest[weekday]" id="digest_weekday">
  <option value="Sunday">Sunday</option>
  <option value="Monday">Monday</option>
  <option value="Tuesday">Tuesday</option>
  <option value="Wednesday">Wednesday</option>
  <option value="Thursday">Thursday</option>
  <option value="Friday">Friday</option>
  <option value="Saturday">Saturday</option>
</select>

同PRより

🔗 特定データベースのyaml設定にdatabase_tasks: falseオプションが追加


つっつきボイス:「通常はたとえばrails db:migrateを実行するとすべてのデータベースにコネクションを張るけど、database_tasks: falseを指定したデータベースにはコネクションを張らなくなるようですね」「ふむふむ」「以下のmy_animals_databaseでアクセスを一切行いたくない場合に使うということだと思います: コマンドラインでもいいような気はしますが、yamlで設定できるとより便利そう👍

データベース設定オプションdatabase_tasksを追加
「スキーマ管理」「マイグレーション」「seed」などの管理タスクがない外部データベースに接続したい場合、データベースごとにdatabase_tasks: falseオプションを設定できるようになった。

# config/database.yml
production:
  primary:
    database: my_database
    adapter: mysql2
  animals:
    database: my_animals_database
    adapter: mysql2
    database_tasks: false

Weston Ganger
同Changelogより

🔗 削除されたオプション


つっつきボイス:「どちらもDHHによるプルリクです」「#42996で--skip-gemfileオプションが削除されて、#42998で--skip-pumaオプションが削除されたんですね」「歴史的なオプションみたい」

いにしえの小競り合いの記念碑をいつまでも残しておく必要はない。
#42996より
必要な設定を削除するためのオプションには意味がない。
#42996より

🔗Rails

🔗 大量のActionMailジョブをSidekiqで一括処理する(Ruby Weeklyより)


つっつきボイス:「deliver_laterで個別のジョブを大量に投げて詰まるのはよくあるヤツ」「1万通のメールで40分待ちはつらそう…」「この記事ではジョブの登録の詰まりが問題になっているみたいですね」

「Sidekiqのpush_bulkを使うと複数ジョブをまとめて投げられるのか」「メールジョブごとにパラメータを渡したい場合はActionMailer::Parameterized::DeliveryJobを使う必要があるのね」「メールによって送り先や文面をパラメータで変えるのはよくありますね」「ジョブを用意する段階でパラメータを渡しておいてからpush_bulkでまとめてプッシュする方が負荷が小さくなる、たしかに」「アトミックな処理を行う大量のジョブを1個ずつ登録するのはやりたくないですね」

参考: Method: Sidekiq::Client#push_bulk — Documentation for mperham/sidekiq (master)

# 同記事より
def enqueue_many_parametrized_mails(mail_class, template, args_array)
  job = ActionMailer::Parameterized::DeliveryJob

  # convert template and args array into an array of arrays containing args
  # for ActionMailer::DeliveryJob objects
  mailer_job_args = args_array.map { |args|
    [job.new(
      mail_class.name,
      template.to_s,
      "deliver_now",
      {some_arg: "foo"},
      *args
    ).serialize]
  }

  Sidekiq::Client.push_bulk(
    "class" => ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper,
    "wrapped" => job,
    "queue" => MAILER_QUEUE,
    "args" => mailer_job_args
  )
end

参考: Rails6 のちょい足しな新機能を試す41(MailDeliveryJob 編) - Qiita

🔗 fast_gettext: 高速i18n gem(Ruby Weeklyより)

grosser/fast_gettext - GitHub

# 同リポジトリより
FastGettext.with_locale 'gsw_CH' do
  FastGettext._('Car was successfully created.')
end
# => "Z auto isch erfolgriich gspeicharat worda."

つっつきボイス:「i18n(国際化)のgemのようです」「gettextライブラリをrubyで再実装したみたい」

参考: gettext - Wikipedia

「fast_gettextはgettextより12倍高速でガベージが530分の1でスレッドセーフですって」「Active SupportのI18n::Simpleも比較されてる」「大量の国際化テキストを処理しないといけない場合に必要になってくるんでしょうね: 自分はあまりその必要に迫られたことはありませんが、海外のサイトだと国際化の言語数が多い分切実なのかも」

Hash FastGettext GetText ActiveSupport I18n::Simple
Speed* 0.08s 0.14s 1.75s 3.75s
Objects* 11K 15K 8017K 7107K
Included backends db, yml, mo, po, logger, chain mo yml (db/key-value/po/chain in other I18n backends)

同リポジトリより


前編は以上です。

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

週刊Railsウォッチ:TruffleRubyでdig_fetchを実装、ruby/debug gem、AWSハンズオン教材ほか(20210901後編)

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

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

Rails公式ニュース

Ruby Weekly

The post 週刊Railsウォッチ:ActiveRecord::QueryLogs追加、spring gemがデフォルトから削除、fast_gettextほか(20210906前編) first appeared on TechRacho.

週刊Railsウォッチ: 責任あるモンキーパッチの当て方、gem脆弱性スキャンツール、Docker Desktop課金プラン改定ほか(20210907後編)

$
0
0

こんにちは、hachi8833です。来月出るんですね。

週刊Railsウォッチについて

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

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

お知らせ: 来週の週刊Railsウォッチはお休みいたします🙇

🔗Ruby

🔗 Twist: 出版原稿レビュープラットフォーム(Ruby Weeklyより)

radar/twist-v2 - GitHub


つっつきボイス:「ややこしいですが、本を執筆している著者が出版前に原稿をレビューしてもらうためのプラットフォームを作ったのがこのTwistだそうです」「自分でこのアプリを作ってみたさまざまな知見を記事にした感じかな: よさそうな記事👍

「twist-v2はバックエンドを切り離した今風の設計っぽい↓」「ディレクトリ構成はRailsっぽく見えるしZeitwerkもあるけど、RailsアプリでもSinatraアプリでもなくHanamiで、dry-rbrom-rbyなどもいろいろ使って作ってみたらしい、へ〜」「フロントエンドはReactで、GraphQL経由でサーバーを呼び出しているのね」

DB <-> Backend Repositories <-> Backend GraphQL endpoint <-> Frontend <-> Browser
radar/twist-v2 READMEより

「そういえば作者のRyan Biggさんは以下の記事を書いた人でした↓」「CurrentAttributesが有害というのはワカル」

Railsの`CurrentAttributes`は有害である(翻訳)

🔗 責任あるモンキーパッチの当て方(Ruby Weeklyより)


つっつきボイス:「ちょっと長い記事です」「2011年にRuby 1.8.7で巨大Railsアプリをやっていた話がじわじわ来ますね」「え、そのときにString#%にモンキーパッチを当てたのか!」「マジで?」「String#%は文字列フォーマット用メソッドですね: こういうどこでも使われそうなメソッドにモンキーパッチを当てるのは怖い…」

# 同記事より: String#%にモンキーパッチを当てたときの挙動
replacements = {
  horse_count: 3,
  horses: {
    one: "is 1 horse",
    other: "are %{horse_count} horses"
  }
}

# "there are 3 horses in the barn"が出力される
"there %{horse_count:horses} in the barn" % replacements

参考: String#% (Ruby 3.0.0 リファレンスマニュアル)

「モンキーパッチが失敗する主な原因↓とかいろいろ面白そう」

  • パッチそのものが壊れた場合: 上述のコードベースでは、同じメソッドで複数の実装が競合するのみならず、「勝った」メソッドも動いてくれなかった
  • 仮定が正しくなかった場合: ホストコードが更新されてパッチが期待どおりに当たらなくなっていた
    同記事より

「そういうときのためにRubyにはrefinementがあるのに、と思ったら、意外にもこの記事にはrefinementの話がまったくなかった」「あら、ホントだ」

RubyのRefinement(翻訳: 公式ドキュメントより)

「ざっと眺めた限りではRubyの経験が豊富な人という印象: ちゃんと読んだら学びがありそう👍」「翻訳してみたくなりました」

🔗 Snykのgem脆弱性スキャンツールとRails脆弱性データベース(Ruby Weeklyより)


つっつきボイス:「このSnykは自社でさまざまな言語を対象とする脆弱性スキャンCLIツールを出していて、それを使ってGemfileのセキュリティチェックを行う記事のようですね」「価格表を見た感じではこのツールでビジネスをやっているみたい」「今でもGitHubのDependabotとかを使えば脆弱性を通知できますけどね」「フリープランもあって自動テストでこういうツールを回せるならちょっと使ってみてもいいかも」

snyk/snyk - GitHub

dependabot/dependabot-core - GitHub

参考: Language support summary – Docs Library | Snyk

「Snykはこういうgemごとの脆弱性データベースも公開しているそうです↓」「お、これはなかなか便利そう」「npmやpipなどの脆弱性情報もあるんですね」

参考: rails vulnerabilities | Snyk

🔗 pnglitch: pngファイルを壊すgem(Ruby Weeklyより)

ucnv/pnglitch - GitHub


つっつきボイス:「以下のサイトにあるような感じでpngファイルをいい感じに壊すgemだそうです」「ああpngファイルってたしかにこういうふうに壊れますよね😆」「面白い😆」「pnglitchって文字どおりpngのglitch(故障)なのね」

参考: The Art of PNG Glitch


ucnv.github.ioより

「何のためのgemなんでしょうね?」「それがよくわからなくて、その割に★がたくさんあるのも謎です」「デスクトップやZoomの背景にしてビックリさせるとか?😆」「そういえば液晶に線が入ったような感じになるスクリーンセーバーもありましたね」

READMEによると、単に壊れたpngを眺めて楽しむためのgemのようです。後でart of pnglitchでググるとそれっぽいサイトがたくさん出てきました。

🔗 Unixのepoch時間をRubyオブジェクトに変換する(Ruby Weeklyより)

参考: UNIX時間 - Wikipedia


つっつきボイス:「Time#atin:オプションのドキュメントがなかったので、記事書いた人がプルリクを投げたのね↓」「マージされてよかった🎉

参考: Update docs for Time#at method by prathamesh-sonpatki · Pull Request #2929 · ruby/ruby
参考: class Time (Ruby 3.0.0 リファレンスマニュアル)

「お、これは例のmilitary time zone」「『タイムゾーン呪いの書』にも載っていた、1文字で表すタイムゾーンですね(ウォッチ20210713)」

# 同記事より
>> Time.at(ts, in: "E")
#=> 2020-03-02 09:13:24 +0500
>> Time.at(ts, in: "Z")
#=> 2020-03-02 04:13:24 UTC

参考: List of military time zones - Wikipedia

「このエラーメッセージを見ると、military time zoneにはA〜IとK〜ZはあるけどJがないのが面白い↓」

# 同記事より
>> Time.at(ts, in: "IST")
Traceback (most recent call last):
        2: from (irb):12
        1: from (irb):12:in `at'
ArgumentError ("+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset)

🔗DB

🔗 UUIDとULID


つっつきボイス:「はてブで見つけました」「記事にもあるように、UUID(バージョン1)はランダム部が桁の上位にあるために、データベースのプライマリキーに使うとクラスタインデックスのデータ分布が散ってしまう点がパフォーマンス上不利ですね」

「このあたりはデータのlocality(局所性)の話題で、一般にlocalityが高いほどキャッシュヒットしやすくなるのでパフォーマンス上有利な代わりに、障害に弱くなる傾向があるというトレードオフの関係がありますが、RDBMSのインデックスの話であれば一部だけが破損するようなケースは想定しにくいのでlocalityが高い方が有利です」「なるほど」「逆に分散システムだと、パフォーマンスを少々犠牲にしてでもlocalityを下げることもあります」

参考: 参照の局所性 - Wikipedia

「分散システムではデータが一様に分布する方が障害に強くなりますが、この記事のような単一のデータベースシステムの場合は基本的にlocalityが高い方がパフォーマンス上有利」「ふむふむ」「もともとUUIDの設計はパフォーマンスよりも分散システム上でIDが衝突しないことがメインなんですよ: RDBMSでlocalityが高い方が効率がよくなるならULIDを使うのがいいでしょうね」「なるほど!」

参考: ULID - shimojubox

「元記事は図も含めて丁寧に書かれているので、UUID周りを知らなかった人にはおすすめ👍: このサイトは他のデータベース記事もよいです」

🔗クラウド/コンテナ/インフラ/Serverless

🔗 Docker Desktopの課金プラン改定


つっつきボイス:「Docker Desktopの件は今だいぶ騒がれているので、そのうち方針が変わるんじゃないかなと思いたい」「乗り換えを考える記事もいろいろ出始めてますね」

参考: Docker Desktop有料化の影響 - Qiita

「今回発表された課金プランだとDocker Desktopがいろいろ使いにくくなってしまうのが残念」「ですよね」「課金されるかどうかは売上ベースか従業員数ベースで決まるんですが、Docker Desktopを使ってない他部署の売上も課金の閾値に影響することになりますし、大企業がこれからDocker Desktopを導入しようとしたときに売上が見込めないうちから課金対象になると話が通しにくくなりそう」「あ〜」「課金とは別の話ですが、アカウントを”人”に紐付けるのが必須なので、開発メンバーの引き継ぎとかがやりづらい」「誰か退職するとCIが止まりそうですよね」

「ツイートにも書きましたけど、自分たちはローカルでDocker Desktopをクライアントとして使えればよくて、それでいて有償のクラウド機能に欲しいものがないところに食い違いを感じるんですよ」「そこですよね」「課金そのものはあっていいと思うので、もっと有用感のある課金プランを検討して欲しい気持ち」「それこそお金を払ってでもPersonal版を使いたい企業もありそう」

参考: Docker Pricing & Monthly Plan Details | Docker


後編は以上です。

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

週刊Railsウォッチ:ActiveRecord::QueryLogs追加、spring gemがデフォルトから削除、fast_gettextほか(20210906前編)

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

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

Ruby Weekly

The post 週刊Railsウォッチ: 責任あるモンキーパッチの当て方、gem脆弱性スキャンツール、Docker Desktop課金プラン改定ほか(20210907後編) first appeared on TechRacho.


Ruby: 静的型付けで解決しない問題とは(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

Ruby: 静的型付けで解決しない問題とは(翻訳)

Brandur Leach氏のブログ記事『Better typing in Ruby』は、Stripeの静的型付けチェッカーSorbetが誕生したときの状況と、Sorbetで得られたメリットについて解説しています。私はこれまで静的型付けのメリットについては割り引いて考えてきましたが、この記事を読むうちに、私がそう考えるに至った原因となる点がいくつも浮かび上がってきました。同記事では、Stripeのコードベースにある問題の中で、静的型チェックが作業の軽減に有用なケースについて言及しています。しかし静的型付けはそれらの問題を「解決」するものではなく、静的な型付けが存在し続けることで別のコストが発生する可能性もあります。

私は何も、Stripeの方法に間違いがあるとか、ほかの方法を採用すべきだったなどと申し上げたいのではありません。私は彼らほど大規模なRubyコードベースで仕事をしたことはなく、はるかに小さなコードベースでの仕事しか経験していないので、彼らの決定を評価する立場にはありません。彼らの場合は問題の発生が避けられず、静的型チェックが問題解決としてベストだった可能性も十分考えられます。

むしろ同記事をきっかけとして、そこで抽象的に説明されている問題について考察し、自分が関わるプロジェクトでその問題が発生したときに自分ならどうするかを考えてみたいと思います。私にとって静的型付けは、最初に手をつけるソリューションではありません。

以上を踏まえて、問題点を見ていくことにしましょう。

問題に適用される静的型付け

Rubyはモジュール化の促進が苦手なので、コードは巨大な不定形のblob(binary large object)のようになり、あらゆる場所からあらゆる場所が呼び出され、コードの境界は本質的に理論止まりになる。

上の文で述べられているのは実際には因果関係です。「Rubyはモジュール化の促進が苦手なので」が原因の部分で、その結果「コードは巨大な不定形のblobのようになり、あらゆる場所からあらゆる場所が呼び出され、コードの境界は本質的に理論止まりになる」という流れです。しかし不定形のblobが生じるのは本当に言語そのものが原因でしょうか?

コードを書くのはRuby言語ではなく開発者であり、Rubyにモジュール化を促進する機能がないとしても「あらゆる場所からあらゆる場所が呼び出されるコード」を書くかどうかを決めるのも開発者です。言語による効果とは、ある方向にどれだけ強く働きかけるかということです。「Rubyにはモジュール化を強力に促進する機能がないので、開発者はコードをモジュール化する形で書くべきなのに、そこに注意を払っていなかった」とも解釈できそうです。

(中略)エンジニアは新しいコードを書くよりも既存のコードを修正することがほとんどでした。コードのどの部分を精査するときでも、その型を知るために必要なのは名前だけで済み(中略)型がすぐにわからない場合でも、パスにpryを投げ込んでその部分を実行するテストケースを探し、実行時に変数を検査すれば十分調べられます(中略)

(中略)コードはテストの有無にかかわらず解析されるので、テストカバレッジに漏れがある場合の保護層にもなります。

コードのかなりの部分がテストされていないとしたら問題です。私のテストカバレッジは100%ではありませんし、外部システムやライブラリとの統合/アダプター層のテストを書くことはほとんどありませんが、アプリケーションを構成するコードについては徹底的にテストでカバーしたいと思っています。

これは「既存のコードを修正する」プロセスにおいて、コードをカバーするテストが存在するのかやテストがどこにあるかがわからない状況での話です。このケースに該当する場合、変数の型がわかるだけでは保証が不十分です。私は自分の変更によってビジネスロジックが壊れないことを確認したいので、そのコードがテストされている必要があります。

(中略)該当部分を実行するテストケースを探し出して、実行時に変数を検査するのは耐え難いほど時間のかかる作業でした(Rubyはインタプリタ言語なので、コードが増えると起動時のオーバーヘッドが増えます)(中略)
(中略) 静的解析の実行速度はテストスイート1件(つまり1つのファイル)よりも高速で、多くの場合テストケース1件よりも速くなりました(当社のテストは起動時のオーバーヘッドが大きい)。

Rubyでは、テストが遅いのはインタプリタそのものが遅いことよりもフレームワーク全体を起動してしまうことが原因です。フレームワークを起動するようなテストを書くよりも、フレームワークや他の外部オブジェクトに依存しないオブジェクトを実行する単体テストを書くのがベストです。そのようなテストではフレームワークが起動されず、インタプリタで実行するコードの量も最小限で済みます。

コード作者が当初意図した変数の型は1種類だったかもしれませんが、その後新たな用途が生まれ、可能な型を多数含む形でインターフェースが拡張される可能性もあります(中略)

(中略)元々は整数の利用が想定されていたのが、あるとき誰かが文字列でも動作することに気づき、文字列も渡すようになる可能性もあります。

コードベースの開発者は、そのコードがどう使われるかを理解しておくことが重要です。確かにコード片の使われ方はコードのライフタイムの間に移り変わるものですが、コード片を再利用できるというのは大きなメリットです。このような場合には、そのコードを使う他の人にもわかるように、コードに関する知識を周知することが重要です。コードベースが大規模になると、プルリクをひとつ残らずレビューすることも、アプリケーションのあらゆる細部を把握することも不可能になるので、コードの細かな使い方を周知するには他の方法が必要です。入力テストはそのための方法のひとつであり、しかも実行可能であるというメリットがあります。

同記事の著者は入力される型のテストについても言及していますが、その主張にはあまり同意していません。もちろん、メソッドを変更するときにその変更が入力型をすべて網羅しきれていないことがテストで判明したら、その点を考慮する必要があります。しかしこれではメソッドに型制約を与えるのと大差ありません。あるメソッドが複数の型で使われるなら、それらの型をサポートするように書く必要があります。静的型がなくても、そのときに必要な作業量は増えたりしません。

開発者が何かを変更すると、テストスイートはすべてパスしてひとつも失敗しなかったので問題は存在しないという誤った仮定を立ててしまいがちで、テストされなかったパスが本番で500回ほど実行されたときにやっと気づきます(なお「理論上は」テストをそこまで増やすべきではないという建前ですが、実際の私たちは大量のテストを書いています)。

テスト戦略がパスをろくにカバーしていないとしたら、静的型を使っているかどうかに関わらず非常にまずいことになります。重要なのは各テストのパスでビジネスロジックが正常に動くことを確認できることであり、静的型ではそうした点を確かめられません。

私はテストカバレッジを徹底するために、単体テストと受け入れテストを両方を用いることで、互いの強みを活かし弱みを補うという戦略を採用しています。

  • 単体テスト: 各クラスを徹底的にテストすることで、すべてのコードパスのカバーを含めて振舞いを十分規定できる
  • 受け入れテスト: アプリケーション全体を通してテストを実施することで、単体テストされた部分が協調動作していることを確認できる

受け入れテストであらゆるパスの組み合わせをカバーしようとすると、大変なコストがかかります。代わりに、単体テストで保証されている部分を考慮して、必要な部分を受け入れテストでカバーするように設計します。大規模アプリケーションでは、変更をかけるたびに受け入れテストをすべて実行するのは無理だとしても、リリース前なら確実に実行できますし、実行すべきです。私の経験では、これで潜在的なエラーを残らず検出とまではいかなくても、大半を検出できるはずです。

静的解析は確かに素晴らしいものですが、型シグネチャは人間のためのものでもあることを忘れてはいけません。コードを読むときに特定の変数やメソッドで期待される型がわかれば、理解を深めるうえで非常に有用です。もちろん優秀なIDEでも同じことはできますが、両方使える方がいいと思いませんか?Rubyの静的解析は無料です。

この記事では型シグネチャのメリットを説明するときにデメリットを伏せているのみならず、型シグネチャが「無料」である点を強調していて、あたかも「欠点は存在しない」かのように見えます。しかし多くの人々が動的言語での型シグネチャのメリットについて議論を重ね、「型シグネチャにはトレードオフもある」と述べています。どんなトレードオフでしょうか。

極端なケースでは、複雑な型シグネチャによってコードが乱雑になり、ロジック自体がかすんでしまうこともあります。書籍『Swift Style』に掲載されているSwift言語で書かれた例を見てみましょう。

Excerpt from *Swift Style* showing function with many more lines of type signatures than implementation

Swriftが作り出すコードのスタイルはひときわトップヘビーになります。このため、メソッドやイニシャライザの本体に到達する前にSwiftの処理が多数発生する可能性があります。ここで紹介するのは、双方向のコレクションのインスタンスを初期化するSwiftの標準ライブラリの例です。
元記事の引用画像より

わかりにくいと思いますが、アプリケーションコードと言えるのは末尾のself.base = baseの部分だけで、それ以外の部分は複雑極まる型シグネチャがびっしり並んでいます。

型アノテーションは、シンプルで典型的なケースでもコードが煩雑になってしまいます。私がRubyで気に入っている点のひとつは、クリアな構文によってビジネスロジックが前面にくっきりと浮かび上がってくることです。対照的に、型アノテーションを用いる言語のコードは、ほとんどの場合情報がてんこ盛りになってしまうことが私の目には明らかです。

MatzはRubyConf 2019の基調講演で、Rubyが型アノテーションを追加せずに別のファイルに置く理由を説明した際に同様の点に触れています(動画↓)。型シグネチャはDRYではないので、型アノテーションをRubyコードの外に追いやることでコードをDRYにできると述べています。

重複はコストを強います。コードを読むたびに脳が型の記号を処理しなければならなくなり、既にわかりきった型ではその労力に何のメリットもありません。たとえ労力はわずかでも、時間とともに降り積もっていきます。

静的型が有用な場合とは

以上の引用から、静的型付けのメリットを最大化できる場面が浮かび上がってきます。

静的型付けはモジュール性の低いアプリケーションを書くときに有用: しかし、そもそもモジュール性の低いアプリケーションを書かないのがベストであり、静的型付けがあってもモジュール性の低さは将来に渡って問題になります。私は大規模なチームで大規模なモノリスを書いて良好なモジュール性を失わずに済んだという経験がないので、実践は難しそうです。しかし少しでもその境地に近づけるなら、それに越したことはありません。新しい開発者をしっかり教育し、徹底したコードレビューを行うことで前進できると思います。

静的型付けはコードがテストで一貫してカバーされていない場合に有用:  テストされていないコードは何をしているのかわかりにくく、変更したときに安全かどうかも見当がつきません。静的型付けはそのような場合に有用です。しかし静的型付けを行う場合でもコードをテストすることは引き続き重要です。コードの単位を個別にテストすると同時に、アプリケーション全体のユーザーフローもテストする必要があります。動的型付け言語ではテストの重要性が他の言語より高くなるので、Rubyでもテストの重要性が強調されています。ほとんどの型システムでは、静的型付けではビジネスロジックが正しいことを保証できないので、やはりそのためのテストが必要です(使ったことはありませんがIdris言語のような例外もあるようです)。コードベースやチームの規模に関わらず、ほとんどのコードでテストを要求することは理にかなっています。徹底したコードレビュー(私の好みです)やコードカバレッジのメトリクス (これはこれで大きな問題がありますが、徹底したコードレビューが行われないなら何もしないよりマシでしょう) を使うかどうかにかかわらず、コードのほとんどの部分がテストされるようにすることは可能です。

静的型付けは書いたテストが遅いときに有用: ある時期にRubyコミュニティで高速なテストが求められてきた理由がこれです。結合テストを高速化する回避方法はいくつかありますが、最も信頼できる方法は、フレームワークを使わないPORO(plain old Ruby object)を書き、それらのオブジェクトを分離した状態でテストする「真の単体テスト」を書くことです。これを実践するには規律の徹底が必要で、しかも最初のうちは規律の必要性もはっきりしません。しかし静的型付けを用いるにしても、ビジネスロジックの動作確認は欠かせないので、高速なテストはどちらにしても重要です。

静的型付けはコードがどこでどう使われているかわからないときに有用: ある目的を持ったコードが目的外の形で使われると、理解できない形でコードが使われていることになります。しかし自分のコードがどこでどう使われているかを把握しておくことは重要です。型が一致したとしても、コードが自分のあずかり知らない方法で使われればバグやメンテナンスコストの増加につながります。コードを理解していれば、そのコードが何に使われているかをいつでも把握できます。あるコードの用途が変われば、それに対する理解も変える必要があります。新しいケースをカバーするテストを追加したり(#addを整数の他に文字列にも使うなど)、適用範囲を拡大するためにメソッド名やクラス名を変更したり、ドキュメントを更新したりすることで理解を深められます。これはアプリケーションを進化させるための規律あるアプローチです。静的型付けはこのアプローチを強制し、忘れた場合にキャッチする方法ですが、前述のように認知のオーバーヘッドを増やしますし、型は同じでもコードのビジネスユースケースが異なる場合はキャッチできません。

まとめると、静的型付けは「自分が把握していない」「モジュール性が低い」「テストが不完全」「テストが遅い」コードで特に有用です。そのような状況では静的型付けがおそらく役に立つでしょう。

よりよいソリューション

しかし静的型付けがあっても、モジュール性が低く、テストも不完全で遅い、自分の把握していないコードベースを相手にするのは困った状況です。やはり自分が把握しているモジュール性の高いコードベースで、徹底的なテストを高速に実行したいものです。そうなれば静的型付けの値打ちは大幅に下落し、代わりに可読性のコストが増加します。

最後にもう一度申し上げます。私は大規模なチームで大規模なRubyコードベースを相手に仕事をした経験がないので、コードベースを前述のような理想的な状態に保つことがどのくらい難しいかは見当がつきません。そうした状況では静的型付けが有用という業界常識も数多く見かけます。しかし、そう言う人たちが動的型付けのメリットについて合意が取れているかどうかについては、私には何とも言えません。「コードの把握」「モジュール化」「徹底した高速テスト」を追求するのであれば、おそらく静的型付けはある程度のガードレールとして有用でしょう。

しかし小規模プロジェクトや少人数チームであれば、コードベースの深い理解、モジュール化、徹底した高速テストの実施は可能であることを経験から知っています。静的型付けと動的型付けのどちらを使うにしても、そこを目指しましょう。これが実現され、Rubyコードの見た目や動作に惚れ込んでいるのであれば、静的型付けは使わなくてもよいかもしれません。

関連記事

2020年のRailsでブラウザテストを「正しく」行う方法(翻訳)

The post Ruby: 静的型付けで解決しない問題とは(翻訳) first appeared on TechRacho.

RubyKaigi Takeout 2021のスライド(Day1)

$
0
0

こんにちは、hachi8833です。RubyKaigi Takeout 2021 Day1が終了しました。主催者・スポンサーの方々疲れさまでした&ありがとうございます!参加者の皆さまもお疲れさまでした。

参考: #rubykaigi - Twitter検索 / Twitter

主に自分用に、現時点でわかっている発表スライドを取り急ぎまとめました。可能なものについてはいずれ公式サイトに資料が掲載されると思います。

なお、見出しの時間はリスケ後のものです。

Day1 10:00 – 10:55

#rubykaigiA 『TypeProf for IDE: Enrich Dev-Experience without Annotations@mametter)』

最後は焚き火(?)のクワインで締めくくられました。

来年はTRICKが復活しますように。

過去のTRICK: https://github.com/tric/

Day1 11:00 – 11:25

#rubykaigiA 『Why Ruby’s JIT was slow@k0kubun)』

参考: Ruby 3 JIT can make Rails faster. I’ve wondered Why Rails becomes slow… | by k0kubun | Medium

Day1 13:00 – 13:25

#rubykaigiA 『Optimizing Partial Backtraces in Ruby 3jeremyevans)』


Optimizing Partial Backtraces in Ruby 3より

字幕付きのスライドがありがたいです🙏

#rubykaigiB 『RuboCop in 2021: Stable and Beyond@koic)』

RuboCopのラージ-Aオプションは安全なオートコレクトと知りました。

Day1 14:00 – 14:25

#rubykaigiB 『The Art of Execution Control for Ruby’s Debugger@ko1)』

スライドはPDF形式です。


The Art of Execution Control for Ruby’s Debuggerより

ruby/debug - GitHub

なお、RailsのGemfileのbyebugがruby/debugに置き換わったそうです。

Day1 14:30 – 14:55

#rubykaigiB 『Toycol: Define your own application protocolMisaki Shioi)』

shioimm/toycol - GitHub

Day1 15:30 – 15:55

#rubykaigiB 『Story of Rucy – How to “compile” a BPF binary from Ruby@udzura)』

udzura/rucy - GitHub

以下は感想戦のお知らせです。

関連記事

BPS株式会社はRubyKaigi Takeout 2021 Gold Sponsorに登録しました

The post RubyKaigi Takeout 2021のスライド(Day1) first appeared on TechRacho.

RubyKaigi Takeout 2021のスライド(Day2)

$
0
0

こんにちは、hachi8833です。昨日に引き続き、RubyKaigi Takeout 2021 Day2のスライドや資料をわかった範囲でまとめました。

参考: #rubykaigi - Twitter検索 / Twitter

Shopifyの以下のイベントも開催されていたことに、終わってから気づきました。

Day2 10:00 – 10:55

#rubykaigiA『The Future Shape of Ruby ObjectsChris Seaton)』

Day2 11:00 – 11:25

#rubykaigiB『PRK Firmware: Keyboard is Essentially RubyHitoshi HASUMI)』

Day2 11:30 – 11:55

#rubykaigiA『YJIT – Building a new JIT Compiler inside CRubyMaxime Chevalier-Boisvert)』

RubyKaigi Takeout 2021の発表資料は見つかりませんでしたが、発表者が今年6月に公開した同じタイトルの記事と、これも今年3月に公開された同じタイトルの動画を見つけましたので参考までに貼っておきます。

#rubykaigiB『The newsletter of RBS updates@ pocke)』

Day2 13:00 – 13:25

#rubykaigiA『Parsing RubyKevin Newton)』

発表の内容は「Parsing Ruby」サイトに凝縮されています。

#rubykaigiB『iunclude/prepend in refinements should be prohibited

Day2 13:30 – 13:55

#rubykaigiA『Ractor’s speed is not light-speed)』

#rubykaigiB『Graphical Terminal User Interface of Ruby 3.1@aycabta)』

最後の方しか見られませんでしたが、発表内容は以下に凝縮されているとのことです。

Day2: 14:00 ———– 18:00 ———- …

#rubykaigiA『Ruby Committers vs the World

記事執筆時点でまだ終わっていません😳 濃厚すぎてとてもまとめられない…

Rubyアソシエーション開発助成金2021公募開始

Ruby Committers vs the World延長戦で以下の公募の話題に触れられていたのでツイートを貼っておきます。

The post RubyKaigi Takeout 2021のスライド(Day2) first appeared on TechRacho.

RubyKaigi Takeout 2021のスライド(Day3)

$
0
0

こんにちは、hachi8833です。RubyKaigi Takeout 2021がDay 3まで無事終了しました🎉

主催およびスポンサーの皆さま、お疲れさま&ありがとうございます!参加者の皆さまもお疲れさまでした。

ありがたいことに、何と土曜日のうちにセッションの動画アーカイブがYouTubeで公開されました🙏

既に公式サイトスケジュールの各セッションのページにも動画が埋め込まれていますので、そちらからたどることもできます。本シリーズではクロージングキーノートを除いて特に動画は埋め込みませんでした。

今年は正規表現の話題が多かったのが個人的に嬉しい点でした。毎度のことですが、たとえようもないチャットの盛り上がりとライブ感、そしてモチベの爆発的な高まりはやはりイベントに参加してこそ得られると改めて感じました。今回も全日参加して本当によかったとしみじみ思います😂

togetterでツイートがまとめられました。

なお、来年の開催について「開催場所・時期・形態については追ってお知らせします」「次回はTRICKを再開します」とクロージングでアナウンスされ、その後開催形態については以下が公開されました。

TRICKはRubyKaigiで何度か催されたコードコンテストです。RubyKaigi 2018で開催されたときの動画をご覧いただくと雰囲気が掴めるかと思います。

Day3 10:00 – 10:25

こちらでスライド・資料・関連リポジトリを見つけられたもののみ掲載しています。

#rubykaigiA 『Do regex dream of Turing Completeness?』(Daniel Magliola

以下のリポジトリが公開されています。

dmagliola/regex_game_of_life - GitHub

参考: ライフゲーム - Wikipedia

#rubykaigiB 『Use Macro all the time ~ マクロを使いまくろ ~』(@osyo

osyo-manga/gem-rensei - GitHub

Day3 10:30 – 10:55

#rubykaigiB『Charty: Statistical data visualization in Ruby』(@mrkn

red-data-tools/charty - GitHub

Chartyの開発に参加したい方は以下のイベントにどうぞ。

Day3 11:00 – 11:25

#rubykaigiA『Building Native Extensions. This Could Take A While…』(Mike Dalessio

以下はサンプルコードのリポジトリです。

flavorjones/ruby-c-extensions-explained - GitHub

なお、Sutouさんによる昨年のRubyKaigi発表も趣旨が近いそうです。

参考: Goodbye fat gem - Kouhei Sutou - Rabbit Slide Show

#rubykaigiB『Dive into Encoding』(Mari Imaizumi

ima1zumi/encoding_iroha - GitHub

Day3 11:30 – 11:55

#rubykaigiA『Beware the Dead End!!』(Richard Schneeman

zombocom/dead_end - GitHub

以下は関連が深いと思われる@schneemsさんの過去記事です。

参考: Squash Unexpected-End errors with syntax_search

#rubykaigiB『How to develop the Standard Libraries of Ruby?』(@hsbt

GitHubの以下のページでRubyの各種リポジトリを見られるそうです。

参考: The Ruby Programming Language

Day3 13:00 – 13:25

#rubykaigiA『It is time to build your mruby VM on the microcontroller?』(蒼時弦也

以下のリポジトリのようです。

elct9620/mruby-toy-vm - GitHub

#rubykaigiB『Ruby, Ractor, QUIC』(@unasuke

Day3 13:30 – 13:55

#rubykaigiB『Red Arrow – Ruby and Apache Arrow』(Sutou Kouhei

Red Arrowのリポジトリは現在Apache Arrowに移行しています↓。

apache/arrow - GitHub

Red Arrowの開発に参加したい方も以下のイベントにどうぞ。

Day3 14:00 – 15:00

#rubykaigiA『Matz Keynote』(@matz

Matzのクロージングキーノートは当日無料で配信されました。

スピーチの中で、Pythonも高速化プロジェクトを立ち上げたことを知りました。

YJITのベンチマークを以下で見られます。

スピーチの中で呼びかけられたRubyへの協賛先は以下です。

Day3後の感想戦

本編終了後の感想戦も長らく盛り上がりました。以下で質疑応答やメモを参照できます。

その中でハッシュのショートハンド記法について議論されていました。自分の体力が持たなかったので途中抜けしましたが、その後受理およびコミットされていることに気が付きました。

参考: オブジェクト初期化子 - JavaScript | MDN

その他感想戦などの情報

既に開催されたものも含め、以下からたどれます。

早くも感想動画をアップしている方もいらっしゃいました。

関連記事

RubyKaigi Takeout 2021のスライド(Day1)

RubyKaigi Takeout 2021のスライド(Day2)

The post RubyKaigi Takeout 2021のスライド(Day3) first appeared on TechRacho.

Railsコンソールの思いがけない便利技(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

Railsコンソールの思いがけない便利技(翻訳)

.irbrcを調整してインタラクティブなコンソールを使いやすくすると、生産性も目に見えて向上し、フラストレーションも減らせるのでやりがいのある作業です。最近のRuby Weeklyでは、このあたりのトピックを扱う記事をいくつも見かけます。

これまでも.irbrcにいろいろ手を加えて便利にしてきましたが、変更内容をリモートサーバーにも反映するのがいつも面倒でした(Herokuでも使えるようにするなど)。この問題で悩んでいるのは私だけではありません。もちろん方法はいくつもありますが、以下のようにアプリのコードに近い方法が最もしっくりきました。

# script/likeasir.rb

# productionのコンソールを開くときにあると便利なもの
# load 'script/likeasir.rb'

def event_store
  Rails.configuration.event_store
end

def command_bus
  Rails.configuration.command_bus
end

# ...

コンソールを開いたら、irbセッションで以下を実行してヘルパーを読み込みます。

load 'script/likeasir.rb'

ところが、弊社のKubaが上の読み込み手順を完全に不要にするうまい方法を教えてくれました。

# config/application.rb

module MyApp
  class Application < Rails::Application
    # ...

    console do
      module DummyConsole
        def event_store
          Rails.configuration.event_store
        end

        def command_bus
          Rails.configuration.command_bus
        end
      end
      Rails::ConsoleMethods.include(DummyConsole)
    end
  end
end

これで、bin/rails cを読み込めばcommand_busメソッドとevent_storeメソッドをいつでもirbセッションで使えるようになります。

以上、私がずっと思いつかなかった小技を紹介いたしました。

お知らせ

ARKADEMY.DEVに参加してArkencyのトップクラス教育プログラムコースにアクセスしましょう!「Railsアーキテクトマスタークラス」「アンチ”IF”コース」「忙しいプログラマーのためのブログ執筆コース」「Async Remoteコース」「TDD動画クラス」「ドメイン駆動Rails動画コース」以外にもさまざまなコースが新設中です。

関連記事

Railsコントローラのアクションがrenderで終わるとは限らない(翻訳)

The post Railsコンソールの思いがけない便利技(翻訳) first appeared on TechRacho.

Railsの技: カスタムヘルパーとStimulusで軽量コンポーネントを構築(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

Railsの技: カスタムヘルパーとStimulusで軽量コンポーネントを構築(翻訳)

Railsのカスタムヘルパーのよさは見落とされがちですが、軽量コンポーネントのビルドや、Stimulusコントローラの定形コードを削減するときの最適なオプションにもなります。

Stiumulusの良い点は、マークアップの属性を見るだけで即座に機能を推測できることです。しかし、値やアクションを複数持つコンポーネントの場合は実装の詳細の一部を隠蔽することにメリットがあります。

私の過去記事『Building GitHub-style Hovercards with StimulusJS and HTML-over-the-wire』からコード例を引用します。

<div class="inline-block"
  data-controller="hovercard"
  data-hovercard-url-value="<%= hovercard_shoe_path(shoe) %>"
  data-action="mouseenter->hovercard#show mouseleave->hovercard#hide"
>
  <%= link_to shoe.name, shoe, class: "branded-link" %>
</div>

このhovercard_controllerurl値を渡される必要があり、かつマウスオーバーされたカードの表示と非表示用のアクションを追加する必要もあります。このコントローラはリンクタグでラップされており、アプリで用いるホバーカードの種類に応じてカスタムスタイルを適用できるようになっています。

このコントローラを数箇所で使うだけなら大した手間ではありませんが、さまざまな種類のホバーカードに対してこのコントローラを再利用したいのであれば、Railsのカスタムヘルパーを追加してみてはいかがでしょう。

利用法

app/helpersディレクトリの下に置かれたモジュールは、自動的にアプリのビューで利用できるようになります。

# app/helpers/hovercard_helper.rb
module HovercardHelper

  # ヘルパーを用いてStimulusコントローラ属性の繰り返しを避ける
  def hovercard(url, &block)
    content_tag(:div,
      "data-controller": "hovercard",
      "data-hovercard-url-value": url,
      "data-action": "mouseenter->hovercard#show mouseleave->hovercard#hide",
      &block)
  end

  # 独自の軽量「コンポーネント」をビルドする
  def repo_hovercard(repo, &block)
    hovercard hovercard_repository_path(repo), &block
  end

  def user_hovercard(user, &block)
    hovercard hovercard_user_path(user), &block
  end
end

ヘルパーを使えば、独自の「コンポーネント」をRubyで作成して実装の詳細を抽象化できるようになります。Rubyのブロックは強力なので、用途に応じてカスタマイズ可能な柔軟なコンポーネントを作れます。

<!-- app/views/timeline.html.erb -->

<%= user_hovercard(@user) do %>
  <%= link_to @user.username, @user %>
<% end %>

<%= repo_hovercard(repository) do %>
  <div class="flex items-center space-x-2">
    <svg></svg> <!-- Some icon -->
    <%= link_to repository.name, repository %>
  </div>
<% end %>

たとえばrepo_hovercardというヘルパーを作って、Repositoryモデルとレンダリングするブロックを受け取れるようにしたとします。これにより、ページのコンテキストに応じて表示内容を完全に制御でき、Stimulusイベントの適切な組み合わせを気にする必要もありません。

アプリのStimulusコントローラを変更したい場合も、コントローラが多くのビューに分散せず1箇所に集約されます。

参考情報

  • Rails APIドキュメント: Helpers

関連記事

Railsの技: 特定スコープ内でuniquenessバリデーションをかける(翻訳)

The post Railsの技: カスタムヘルパーとStimulusで軽量コンポーネントを構築(翻訳) first appeared on TechRacho.

Unicodeで絶対知っておくべきセキュリティ5つの注意(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

画像はすべて元記事からの引用です。

  • 2017/11/28: 初版公開
  • 2021/09/08: 更新

Unicodeで絶対知っておくべきセキュリティ5つの注意(翻訳)

先週の「WhatsApp Androidの偽アプリ出現」のニュースによると、公式アプリと同じ開発者に見える名前で偽アプリが提供されました。この詐欺師は、印刷に現れないスペース文字を開発者名に混ぜ込むことでバリデーションのすり抜けに成功しました。このハックによって、Play Storeのメンテナーが気づくまでに100万人以上が騙されました。

Unicodeの標準としての価値は計り知れません。UnicodeのおかげでPCやスマホ、ウォッチを問わず同じメッセージを同じ方法で世界中どこでも表示できます。残念なことに、Unicodeが複雑なせいで、詐欺師やいたずら小僧どもにとって格好の金の鉱脈になってしまっています。Unicodeで引き起こされる基本的な問題をGoogleなどの巨大企業が食い止められないのであれば、中小企業にとっては勝ち目のない戦いに感じられてしまうのではないでしょうか。しかし、問題を引き起こしているのは、ほとんどが一握りの乱用です。詐欺行為を防ぐために、開発者なら誰もが知っておくべきUnicodeの知識トップ5をご紹介いたします。

1. 画面に表示されないUnicodeポイントがたくさんある

Unicodeには「ゼロ幅」のコードポイントがいくつもあります。たとえばzero-width joiner(U+200D)zero-width non-joiner(U+200C)はハイフネーションのヒントとして使われます。これらの文字は画面上には表示されませんが、文字列を比較するときに影響を与えます。WhatAppアプリの詐欺が長い間発覚しなかった理由はこれです。こうした文字の多くはGeneral Punctuationブロック( U+2000〜U+206F)に含まれています。一般に、このブロックのコードポイントをIDの文字に利用する必然性はないのでフィルタは簡単です。しかし、Mongolian Vowel Separatorのように、この範囲外にも特殊な非表示コードがいくつか存在します。

一般に、Unicodeの一意性制約に単純な文字列比較を使うのは危険です。考えられる回避法としては、悪用される可能性のあるIDやその他のデータに許可されている文字セットを制限することです。残念ながら、これですべての問題を解決できるわけではありません。

2. 見た目が極めて似ているコードポイントがたくさんある

Unicodeは世界中のあらゆる手書き言語で使われるシンボルをすべてカバーしようとしているため、人間には区別不可能なほど見た目が互いによく似ている文字がどうしても増えてしまいます。しかしコンピュータならあっさりと区別できます。この問題を利用したいたずらとしてMimicというお遊びユーティリティがあり、これはソフトウェア開発でよく使われるシンボル(コロンやセミコロンなど)を見た目のよく似た別のUnicode文字に差し替えてしまいます。コード編集ツールはカオスと化し、開発者が混乱する可能性があります。

見た目のよく似たシンボルの問題は、単なるいたずらにとどまりません。homomorphic(準同型)攻撃と呼ばれる、これを悪用した攻撃手法によって深刻なセキュリティ問題が生じることがあります。2017年4月、セキュリティの研究者は異なる文字セットを混ぜ込むことでapple.comと見た目のそっくりなドメインを登録し、さらにSSL証明書まで取得してみせました。主要なブラウザは何も知らずにSSL鍵を表示し、このドメインを「安全」なものとして扱ってしまいました。

表示可能文字と非表示文字の混在と同様、ID(特にドメイン名)の文字に別の文字セット名の混用を許可する正当性はまずありません。ほとんどのブラウザでは、文字セットの混在したドメイン名を16進Unicode値で表示することでペナルティをかけ、ユーザーが簡単に惑わされないようにしています。ユーザーにIDを表示しようとする場合(検索結果など)は、混乱防止のため同様の手法を検討してください。しかし、これも完全な解決方法ではありません。sap.comやchase.comといった一部のドメインは、非ラテン文字セットのブロック1つだけで完全に構成できてしまいます

Unicodeコンソーシアムは紛らわしい文字一覧を公開しており、詐欺の可能性を自動チェックするときの資料として有用かもしれません。その他に、紛らわしい文字の簡単な作り方をお探しの方はShapecatcherというサイトをチェックしてみてください。このツールは、手書きの文字に似ているUnicodeシンボルのリストを表示します。

3. 正規化で正しく正規化されるとは限らない

ユーザー名などのIDで、ユーザーのID入力方法が異なる場合にも処理を統一するためには、正規化(normalization)が重要です。IDの正規化によく使われる方法のひとつは、すべて小文字に変換することです。たとえばJamesBondはjamesbondと同じになります。

互いによく似た文字や重複したセットが多数存在するため、(自然)言語が異なっていたりUnicode処理ライブラリが異なっていたりすると、適用される正規化戦略もそれによって変わることがあります。正規化が複数箇所に分散していると、セキュリティ上のリスクにつながる可能性があります。要するに、アプリのさまざまな部分で行われる小文字変換の動作がすべて同じであると仮定すべきではないということです。SpotifyのMikael Goldmann氏は、同社のユーザーのひとりがアカウントハイジャック手法を発見した後、この問題について2013年に見事なインシデント分析を行いました。攻撃者が他人のユーザー名をUnicode異体字で登録すると(ᴮᴵᴳᴮᴵᴿᴰなど)、同じ正規アカウント名(bigbird)に変換される可能性があります。語の正規化手法がアプリのレイヤごとに異なる場合、偽アカウントを登録してパスワードをリセットできてしまいます。

4. 画面上に表示される文字の長さとメモリサイズはまったく別物

基本的なラテン文字セットや多くのヨーロッパ文字セットでは、画面や紙の上のテキスト片が占める幅は、シンボルの個数におおむね比例し、テキストのメモリ上のサイズにもおおむね比例します。EM(「M」という文字の幅)やEN(「N」という文字の幅)が長さの単位としてよく使われる理由はこれです。しかしUnicodeではこうした仮定はことごとく危険です。Bismallah Ar-Rahman Ar-Raheem(U+FDFD)のように、たった1文字でそこらの英単語よりも長くなる愛すべきシンボルは山ほどあり、Webサイトで仮定される表示上の囲みからあっさりはみ出してしまいます。つまり、文字列の文字長をベースとするワードラップやテキスト折り返しのアルゴリズムはことごとく破綻してしまうということです。ターミナルで動作するCLIプログラムのほとんどは固定長フォントを前提としているので、ターミナルにこうした文字を表示すると、引用符の位置が完璧にずれてしまうのがわかります。

これをいたずらに使ったzalgo text generatorというサイトは、入力したテキスト片の周りにゴミ文字を大量に追加して、テキストが上下方向に大きくはみ出すように変えてしまいます。

もちろん、非表示コードポイント全体の問題によってメモリサイズと画面上のサイズの関連性が失われてしまうため、画面サイズに収まるよう巧妙に調節したテキストをフィールドに入力すればデータベースのフィールドがパンクする可能性もあります。固有のスペース(幅)を持たない例は他にも膨大にあるので、非表示文字をフィルタするだけでは不十分です。

U+036BU+036Cなどの「combining latin character」は直前の文字の上に配置されるので、1行のテキスト内に複数行のテキストを書けるようになります(N\u036BO\u036CはNͫOͬと表示される)。ヘブライ語聖書の経典朗唱向けのイントネーションを表すカンティレーション記号(参考)は、同じ表示スペースにいくつでも重ねられるので、画面上のたった1つの文字に大量の情報をエンコードするという悪用が簡単にできてしまいます。Kartin Kleppeは、古典的なライフゲーム(参考)のブラウザ版実装にカンティレーション記号をエンコードしてみせました。このページのソースコードを見た方は相当なショックを受けるでしょう。

5. Unicodeは単なるパッシブデータではない

コードポイントによっては、表示可能な文字の表示方法に影響を与えるよう設計されているものがあります。つまり、ユーザーがコピペしているのは単なるデータ以上のものであり、処理インストラクションも入力できるということです。よくあるいたずらとして、right-to-left override(U+202E)文字でテキストの進行方向を変えてしまうというのがあります。たとえば、Google MapsでNinjasを検索してみてください。このクエリ文字列は実際には検索ワードを逆順にしたものになっているので、ページには「ninjas」と表示されていますが、実際には「sajnin」を検索しています。

この手口は有名になり、XKCDでネタとして使われたほどです。

「これはまだましな方だ!最悪なのは」「U+202e」「らすれそ」「?たき起が何」「たっがやし何めて」

データと処理インストラクション(事実上の実行可能コード)の混在は絶対によくありません。ユーザーが処理インストラクションを直接入力できてしまう場合は特にそうです。ユーザー入力をそのままページに表示すれば大きな問題になります。ほとんどのWeb開発者はそのことを知っているので、ユーザー入力からHTMLタグを除去してサニタイズしますが、ユーザー入力にUnicodeの制御文字が含まれる可能性にも注意が必要です。これは禁止用語やコンテンツのフィルタリングをすり抜ける初歩的な方法であり、語の文字を逆順にしてright-to-leftを語の最初に置いてオーバーライドするだけでできてしまいます。

right-to-leftをハックして悪意のあるコードを埋め込むのはさすがに無理かもしれませんが、気をつけないとコンテンツ表示を台無しにされる、つまりページ全体の文字が逆順で表示される可能性があります。よく行われている対策は、ユーザーが入力するコンテンツの置き場所をinputフィールドやtextエリアに限定して、処理インストラクションがせめてページの他の部分に効かなくなるようにすることです。

もうひとつ、表示の処理インストラクションで特に大きな問題として、異体字(variation)セレクタがあります。色違いの絵文字ごとにコードを作成するのを避けるために、Unicodeでは異体字セレクタで基本シンボルと色をミックスできるようになっています。white flagにrainbow異体字セレクタを適用するとrainbow flagになります。しかし異体字がすべて有効とは限りません。2017年1月、iOSのUnicode処理のバグが原因で、ある仕掛けを施したメッセージを送りつけるだけでiPhoneをリモートでクラッシュさせるといういたずらが発生しました。このメッセージには1つのwhite flag、1つの異体字セレクタ、そして1つのゼロが含まれていました。iOS CoreTextは正しい異体字を取り出そうとしてパニックになり、iOSがクラッシュしました。このトリックはダイレクトメッセージやグループチャットの他、共有連絡先カードにも通用し、iPadや一部のMacbookまでこの問題の影響を受けました。この悪用によるクラッシュの防止策はほとんどありませんでした。

この種のバグは数年に1度は発生します。2013年には、アラビア文字の処理でOS XやiOSをクラッシュさせる可能性のあるバグが見つかりました。こうしたバグはOSのテキスト処理モジュールの奥底に潜んでいるため、一般のクライアントアプリ開発者には防止策がまったくありません。

その他の興味深い処理インストラクションについては、GitHubで公開されているAwesome Codepointsリストで確認できます。Unicodeによって引き起こされる問題についてもっと詳しくお知りになりたい方は、拙著『Humans vs Computers』をぜひチェックしてください。

(image credits: Amador Loureiro)


関連記事

Rubyの内部文字コードはUTF-8ではない…だと…?!

Unicodeにおける日本の元号の開始日・終了日の定義について

The post Unicodeで絶対知っておくべきセキュリティ5つの注意(翻訳) first appeared on TechRacho.


Rails 7: belongs_to関連付けに変更トラッキングが追加(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

Rails 7: belongs_to関連付けに変更トラッキングが追加(翻訳)

アップデートとしては地味でも、Railsユーザーにとって嬉しさ最大の機能が登場することがあります。Railsの最近のアップデートで、belongs_to関連付けの変更を監視する機能が導入されました。Rails wayに沿って進めたい開発者にとっては大歓迎です。

変更トラッキングは、Railsですぐ使える便利メソッドの一種です。Active Recordを使っていると、データベースで変更が生じているかどうかを調べられます。これはトリガーやイベントをビルドするときに便利です。サンプルを見てみましょう。

データベース構造に投稿(Post)とカテゴリ(Category)があるとします。

class Post
  belongs_to :category
end

class Category
  has_many :posts
end

投稿のタイトルが変更されたときのアクティビティを記録しておきたいとします。これは以下のように書けます。

class Post
  belongs_to :category

  before_update :record_activity

  private
  def record_activity
    if self.title_changed?
      # record an activity
    end
  end
end

title属性の値が変更されたかどうかはtitle_changed?メソッドで監視できます。

変更前

同じことを、投稿のカテゴリが変更された場合についてもやってみましょう。

class Post
  belongs_to :category

  before_update :record_activity

  private
  def record_activity
    if self.category_id_changed?
      # アクティビティを記録する
    end
  end
end

この書き方は目障りですね。categoryという関連付けがあるにもかかわらず、その関連付けの変更を追えないので、category_idの変更を監視するというかなり大雑把な方法を取らざるを得ません。これではRails wayから遠ざかってしまいます。

変更後

ありがたいことに、Rails 7でこれを行う方法が導入されます(#42751)。これなら以下のように書けます。

class Post
  belongs_to :category

  before_update :record_activity

  private
  def record_activity
    if self.category_changed?
      # アクティビティを記録する
    end
  end
end

この関連付け名_changed?メソッドは、元と異なる関連付けオブジェクトが代入されている場合はtrueを返し、次回のsave!で外部キーが更新されます。

また、関連付け名_previously_changed?を使えば、以下のように直前のsaveで関連付けが更新されて元と異なる関連付けオブジェクトを参照する場合はtrueを返すこともできます。

  post.category # => #<Post category_id: 123, title: "Welcome to Rails!">
  post.category_previously_changed? # => false

  post.category = Category.second # => #<Post category_id: 456, title: "Welcome to Rails!">
  post.save!

  post.category_previously_changed? # => true

関連記事

Rails 6.1: Active Storageのファイルをプロキシ経由で配信する(翻訳)

The post Rails 7: belongs_to関連付けに変更トラッキングが追加(翻訳) first appeared on TechRacho.

Railsの技: Turbo Frameとskeleton loaderでHotwireのコンテンツをlazy loadingする(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

Railsの技: Turbo Frameとskeleton loaderでHotwireのコンテンツをlazy loadingする(翻訳)

HotwireはBasecampが提供する新しいフロントエンドツール集で、最小限のJavaScriptを書くだけで「リアクティブな」Railsアプリを構築できます。

Hotwireの機能の中で、サーバーサイドでレンダリングしたHTMLをリアルタイムでストリーミングできる機能が最大の目玉だと考える人もいますが、私の推しはTurbo Frameが追加されたことです。

Turbo Frameはiframeの超高性能版で、使ってみると実に感心します。Frameはページの一部を表現し、そこに独自のナビゲーションコンテキストを内包しています。

中でもひときわ強力なのは、Turbo Frameのlazy-loading(遅延読み込み)機能です。このパターンの例としては、普段よく目にするGitHubのactivityフィードがあります。

GitHub Activity Feed: Lazy load

最初にページの「外殻」を読み込み、続いてAJAX呼び出しを実行すると、コンテンツの残りをフェッチしてページを埋められます。これは遅いページの高速化に絶大な効果を発揮します。

しかし、この機能を使うとページコンテンツががたついてしまうという欠点があります。「読み込み中」のスピナー表示は小さい四角形ですが、表示されると多数のイベントがフィードされて四角形が下に展開されます。

これを解決する方法のひとつは、“skeleton screen”か”skeleton loader”を使うことです。このUIパターンでは、空白のコンテンツをプレースホルダとして用いることで、コンテンツが最終的に読み込まれたときのがたつきを軽減します。

この2つの概念は、ピーナッツバターとゼリーのように相性抜群です。

使い方

Turbo Frameの基本的なlazy-loadingは以下のようになります。

<turbo-frame id="feed" src="/feed">
 /feedが読み込まれるとこのコンテンツが置き換えられる
</turbo-frame>

ここにsrc属性を指定すると、Frameはページ読み込み時に自動的にAJAXリクエストを発行し、レスポンス内で<turbo-frame>にマッチする部分をそのコンテンツで置き換えます。

さらに、loadingプロパティに"eager"または"lazy"も設定できます。"eager"は直ちに読み込みを開始し、"lazy"はフレームがページに出現したときに読み込みを開始します。

GitHub ActivityのフィードをRailsで行うと、以下のような感じになるでしょう。

<!-- app/views/home.html.erb -->

<div>(他のコンテンツ)</div>

<%= turbo_frame_tag :feed, src: activity_feed_path, loading: :lazy do %>
  読み込み中...
<% end %>

この基本的な「読み込み中…」メッセージを独自のskeleton loaderに置き換えることでページをレベルアップできます。Tailwind CSSに組み込まれているanimate-pulseクラスのおかげで実に簡単です。

適当な灰色の四角形をフレームの初期コンテンツとして追加します。

<!-- app/views/home.html.erb -->

<div>(他のコンテンツ)</div>

<%= turbo_frame_tag :feed, src: activity_feed_path, loading: :lazy do %>
  <div class="flex flex-col space-y-6">
    <% 10.times do %>
      <div class="animate-pulse flex space-x-4">
        <!-- アバター -->
        <div class="rounded-full bg-gray-400 h-12 w-12"></div>

        <!-- 詳細 -->
        <div class="flex-1 space-y-4 py-1">
          <div class="h-4 bg-gray-400 rounded w-3/4"></div>
          <div class="space-y-2">
            <div class="h-4 bg-gray-400 rounded"></div>
            <div class="h-4 bg-gray-400 rounded w-5/6"></div>
          </div>
        </div>
      </div>
    <% end %>
  </div>
<% end %>

仕上げに、activity_feed_pathアクションが返すコンテンツをTurbo Frameにマッチするようラップしておきます。これによりフレームのコンテンツが自動的に差し替えられ、読み込み中のステートが更新されます。

class ActivityFeedController < ApplicationControler
  def show
    @events = Current.user.activity.last(20)
  end
end

注意: このレスポンスではFrameにsrc属性やloading属性を設定しないでください(設定すると無限ループになってしまいます)。

<!-- app/views/activity_feed/show.html.erb -->

<%= turbo_frame_tag :feed do %>
  <%= render partial: "feed_item", collection: @events %>
<% end %>

Turbo Frameの真の力を理解できれば、昔ながらのHTMLページをlazy-loadingできるようになり、アプリのあらゆる場所で役に立つでしょう。

参考情報

関連記事

HotwireはRailsを「ゼロJavaScript」でリアクティブにできるか?前編(翻訳)

The post Railsの技: Turbo Frameとskeleton loaderでHotwireのコンテンツをlazy loadingする(翻訳) first appeared on TechRacho.

週刊Railsウォッチ: Rails 7 Alpha 1と2が公開、Rubyハッシュのショートハンド記法、iCare Dev Meetupほか(20210921)

$
0
0

こんにちは、hachi8833です。RubyKaigi Takeout 2021が終わりましたね。

週刊Railsウォッチについて

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

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

今週の週刊Railsウォッチは祝日が多いので短縮版でお送りします。また、来週の週刊Railsウォッチはお休みいたします🙇

🔗 特集: Rails 7 Alpha 1と2がリリース

なおつっつきの後で、Alpha 1と同じ日にAlpha 2もリリースされていたことに気づきました。


つっつきボイス:「Alpha 1出た🎉」「ちょっと前からDHHのYouTube動画チャンネルにもRails 7 Alphaプレビュー情報が出ていたのでそろそろ出る頃かもと思っていたところ」「DHHの動画はコードも見えるので比較的イメージを掴みやすいと思います: おすすめ👍」「あとで見ようっと」

🔗 Rails 7は選択肢を複数用意する方向へ

「これまでのRailsはConvention over ConfigurationのようなRails wayに沿って進めるのが望ましいという立ち位置でしたが、今回のRails 7 Alpha 1リリースを眺めていると、Hotwireが入るけどHotwire一択ではないという点がこれまでと違うかなという気がします」「あ、そうかも」「これまでRailsを使い慣れている人はRails wayから適切に外れるノウハウを身につけていて、人によってはたとえばTurbolinksを速攻で外したりすることもありましたよね」

turbolinks/turbolinks - GitHub

「Railsも成熟したんだなという気持ち」「Webpackも使いたければ使っていいよというのはありがたい🙏」「そういえば最近はリリースノートでも言及されているesbuildがいいらしいという噂もちらほら見かけますね」

evanw/esbuild - GitHub

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

「リリースノートにはこれまでチェックしてきたActive Record暗号化(ウォッチ20210412)やmarginalia-gem風のQueryLog追加(ウォッチ20210906)などがフィーチャーされていますね」「Active Record暗号化うれしい😂」「load_asyncってもう入っているような気持ちになっていたけどまだだったか(ウォッチ20210222)」「そういえば少し前の銀座Rails#33でもload_asyncを取り上げていましたね↓」

「Rails 7へのアップグレード手順は、JS環境を完全にHotwireベースに乗り換えるなら作業量が増えそうだけど、今の構成のままでよければそこまで増えなさそうかな」「アップグレード楽しみです😋」「デフォルトのvariant(サイズ違いの画像)生成がmini_magickからvipsに変わるとは知らなかった(#42744)」

libvips/ruby-vips - GitHub

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

今回は以下の公式更新情報を取り上げます。

🔗 sass-railsへのデフォルト依存を削除

最近のWebアプリケーションでは、Tailwind、Bootstrap、BulmaなどのCSSフレームワークを使うことが増えている。Railsは、まだすべてを手書きしているかのようなモデルごとのスタイルシート生成を行うべきではない。
また、Sassはdart-sassに注力することを選んだが、dart-sassはRailsがデフォルトで採用していないあらゆる依存関係を必要とする。そこで、Sassへの依存を減らしてオプションとして追加することにした。
同PRより


つっつきボイス:「sass-rails gemが消されたのかと思ったら、デフォルトではsass-ralisを使わないことにしたのね: Sassを引き続き使いたかったらSprocketsかWebpack-dev-serverかそれらに相当する何らかのアセットプリコンパイラが必要ということになるでしょうね」「たしかに」「もちろん必要なら自分でSassをインストールすればOK」「sass-ralisがデフォルトでなくなるのはそれはそれで嬉しいかも」

rails/sass-rails - GitHub

「アセットプリコンパイル、結局よくわからずじまいでした」「アセットプリコンパイルは果たしてサーバーサイド側の責務なのか、それともフロントエンド側なのかという問題は以前からありますね」「ありますあります」「Railsはこれまでサーバーサイドでアセットプリコンパイルするという考え方でしたね」「それが今回デフォルトからは消えたということか」「RailsコマンドにSassインストール用のオプションが入るといいですよね」「まだオプションは見当たらないけどあるとよさそう」

「ところでDHHはTailwind cssを推しているような気がしますね」「Tailwind、まだ使ったことないんですけど流行っているんでしょうか?」「いわゆるユーティリティ系CSSフレームワークでは世間的には有名ですね: フロントエンド界隈ではTailwindを使うことがデファクトというわけではないと思いますが、個人的にはユーティリティ系CSSフレームワークを使うならTailwindでもいいのではとは思います」「なるほど」

参考: Tailwind CSS - Rapidly build modern websites without ever leaving your HTML.

「TailwindはIDEがサポートする前提で人間が手書きする感じではないのが個人的に何となく合わないんですが、かといってSassならいいというほどでもないんですよね」「Tailwindが生成するCSSを見ていると、CSSのカスケーディングという仕様が現代のコンポーネント化されることを想定したWebページに合わなくなってきてつらいよなあ、という気持ちになります」

🔗 parse_floatvalid_float?をRuby 2.7以降向けに最適化

# actionview/lib/action_view/helpers/number_helper.rb#450
        def parse_float(number, raise_error)
          Float(number)
        rescue ArgumentError, TypeError
          raise InvalidNumberError, number if raise_error
          result = Float(number, exception: false)
          raise InvalidNumberError, number if result.nil? && raise_error
        end
# activesupport/lib/active_support/number_helper/number_converter.rb#176
        def valid_float?
          Float(number)
        rescue ArgumentError, TypeError
          false
          Float(number, exception: false)
        end

つっつきボイス:「ActiveSupport::NumberHelper#parse_floatActionView::Helpers::NumberHelper#valid_float?を、例外を投げ直さないようにする形で高速化したのね」「ここで使ったFloat(..., exception: false)はRuby 2.7以降のみと書かれている」「つまりRailsで必要な最小限のRubyバージョンがRails 7で2.7.0以上に変更されることで↓、Ruby 2.7の機能が使えるようになったということでしょうね」「たしかにif RUBY_VERSION的なコードがありませんね」「そういうコードは少ない方がみんなが幸せになれる」

Rails requires Ruby version 2.7.0 or later. It is preferred to use latest Ruby version. If the version number returned is less than that number (such as 2.3.7, or 1.8.7), you’ll need to install a fresh copy of Ruby.
edgeguides.rubyonrails.org Getting Started with Railsより

🔗 preload_link_tagを修正


つっつきボイス:「preload_link_tagって使ったことないかも」「ああ、リンクタグにrel="preload"を追加するビューヘルパーなのか」「一部の画像でas="image"が設定されなかったのが修正されたということのようですね👍

# #Lactionview/test/template/asset_tag_helper_test.rb#248
  PreloadLinkToTag = {
-   %(preload_link_tag '/sprite.svg') => %(<link rel="preload" href="/sprite.svg" as="image" type="image/svg+xml">)
+   %(preload_link_tag '/sprite.svg') => %(<link rel="preload" href="/sprite.svg" as="image" type="image/svg+xml">),
+   %(preload_link_tag '/mb-icon.png') => %(<link rel="preload" href="/mb-icon.png" as="image" type="image/png">)
  }

🔗 schema_cache_ignored_tables設定オプションが追加


つっつきボイス:「特定のテーブルをスキーマキャッシュにダンプしないようにできるオプションが追加されたんですね」「正規表現も使える」「これはどんなときに嬉しいんでしょうか?」「おそらく、Railsのマイグレーションで管理されていない外部データベースを読み込み専用で参照しているような状況で、スキーマがキャッシュされると困るときに欲しい機能でしょうね」「なるほど、ちょうどそういう感じの作業が降ってきたところです😅」「既存の方法でも全テーブル単位とかでダンプをオフにすることはできたと思いますが、コンフィグでより細かく設定できるようにしたということだと思います」「お〜」

  • スキーマキャッシュのダンプ時にテーブルを無視するコンフィグオプションを追加
    アプリケーションがスキーマキャッシュのダンプ時に特定のテーブルを無視するよう設定可能になった。
    この設定オプションは以下のようにテーブルの配列にすることも、正規表現にすることも可能。
config.active_record.schema_cache_ignored_tables = ["ignored_table", "another_ignored_table"]
config.active_record.schema_cache_ignored_tables = [/^_/]

Eileen M. Uchitelle
同Changelogより

🔗 ActiveModel::APIが追加


つっつきボイス:「Alpha 1のコミットの中からたまたま見つけました」「Action PackとAction Viewが参照するModelとして期待するAPI群をActiveModel::APIに切り出すことで、ActiveModel::Modelの見通しを良くしたリファクタリングのようですね」「ActiveModel::API、わかりやすそう」「persisted?はデフォルトでfalseになるのか、へ〜」

# activemodel/lib/active_model/api.rb(ドキュメントは略)
# frozen_string_literal: true

module ActiveModel
  module API
    extend ActiveSupport::Concern
    include ActiveModel::AttributeAssignment
    include ActiveModel::Validations
    include ActiveModel::Conversion

    included do
      extend ActiveModel::Naming
      extend ActiveModel::Translation
    end

    def initialize(attributes = {})
      assign_attributes(attributes) if attributes

      super()
    end

    def persisted?
      false
    end
  end
end

現在のActiveModel::Modelは、Action PackやAction Viewとやりとりするための最低限のAPIとして定義されている。
その名のとおりこれをincludeしてActive Recordモデルを作成できるが、モデル作成機能はごくわずかしかない。たとえばActiveModel::Attributesincludeすることが非常に多い。
ActiveModel::Modelの実装を新たなActiveModel::APIに移動することで、Action PackやAction Viewとやりとりするための最小限のAPI定義を維持できる。
これでActiveModel::ModelにはActiveModel::APIだけがincludeされるようになり、後方互換性を失わずにActiveModel::Modelに機能追加できるようになる。

なお、このプルリクは以前更新しそびれていた古い#42042と同じ。
同PRより

🔗Rails

🔗 iCare Dev Meetup #25開催


つっつきボイス:「昨日(2021/09/15水)開催されたiCare Dev Meetup #25、HotwireやBiTemporal Data Modelの話など、いろいろ充実したイベントでしたね」「イベントやってたの知らなかった…」「毎月開催されていますよ」

以下は#icare_meetup - Twitter検索 / Twitterより。

「早くも動画や資料が公開されていてありがたいです🙏」「いい内容でしたし動画もそれほど長くありませんので視聴をおすすめします👍

「BiTemporal Data Modelは、適用開始年月日と適用終了年月日を持つデータモデルで、昔からよく取沙汰される話題ですね」

「神速さんのRailsアップグレードの話もよかった: このアップグレード環境を構築するのは大変そうだけど、それを作れればしくみとして回せるようになるんだなと思えました」

「yasaichiさんの資料はしばらくお待ちくださいということですが、動画ではもう見られます」

つっつき後にyasaichiさんの資料が公開されました↓。

「そういえばTwitterもだいぶ前にCassandraを導入しようとして断念したことがありましたね: 新しい技術は一見よさそうに見えても、実際に使ってみるまでわからないところがあるから慎重にならざるを得ない」「ピクスタさんもこれが最終決定ではないそうなのでこれからですね」

参考: Apache Cassandra - Wikipedia
参考: Twitterが、Cassandraの本採用を断念。「いまは切り替えの時期ではない」 - Publickey

「ところで、yasaichiさんの発表はこれより前のバージョンを見た覚えがありますよ: たしか以前の銀座Railsかな」「お、例の『Ruby on Railsの正体と向き合い方』(ウォッチ20190401)とは別でしょうか?」「それではなくて、今回のと近いタイトルだったと思います: こういうふうに同じテーマを定期的に追い続けてくれる方がいると、状況の移り変わりなどもわかってとても助かります🙏

つっつき後に見つけました↓。

「yasaichiさんの発表を見ていて、必ずしも毎回新しいテーマでなくてもいいんだなって思ったので、機会があったらまたデータベースVIEWの話をしてもっと啓蒙してみようかな」「データベースVIEWは以前銀座Rails#10で発表していましたね↓」「3年経ったからそろそろ頃合いかも」

RDBMSのVIEWを使ってRailsのデータアクセスをいい感じにする【銀座Rails#10】

「ところで自分は聞いたことはありませんが、yasaichiさんの発表内容はこちらのtextafmポッドキャスト↓を普段から聞いていた人にはお馴染みだったようです」「そうそう、こうやっていろんな形で普及に努めているんだなって思いました」

🔗 その他Rails


つっつきボイス:「Railsクイズ、皆さんもぜひやってみるとよいと思います👍: 自分たちが普段やっていることなら見当がつくけど、2番のような、既にプロファイルが存在するのにuser.build_profileするという普段まずやらないような問題は難しかった」

🔗Ruby

🔗 ハッシュのショートハンド記法

参考: Shorthand hash syntax in Ruby - rubyonrails-talk - Ruby on Rails Discussions


つっつきボイス:「そうそう、RubyKaigi Takeout 2021最終日の感想戦でES2015風のハッシュのショートハンド記法をRuby 3.1に追加する話が出ていましたね」「結局マージされたんでしたっけ?」「c60dbcdでマージされていました」「お〜、割と前から要望があった構文ですけど、RubyKaigiの勢いに乗ってマージされたんでしょうね」

参考: Allow value omission in Hash literals · ruby/ruby@c60dbcd

参考: オブジェクト初期化子 - JavaScript | MDN

// developer.mozilla.orgより
// Shorthand property names (ES2015)
var a = 'foo', b = 42, c = {};
var o = {a, b, c};

RubyKaigi Takeout 2021のスライド(Day3)

「TechRacho翻訳記事でお世話になっているBrandon Weaverさんが早くもこの新構文で遊んでみた記事をアップしていました↓」


今回は以上です。

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

週刊Railsウォッチ: 責任あるモンキーパッチの当て方、gem脆弱性スキャンツール、Docker Desktop課金プラン改定ほか(20210907後編)

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

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

Rails公式ニュース

The post 週刊Railsウォッチ: Rails 7 Alpha 1と2が公開、Rubyハッシュのショートハンド記法、iCare Dev Meetupほか(20210921) first appeared on TechRacho.

Rails 7 Alpha 1、2がリリースされました(リリースノート翻訳)

$
0
0

Ruby on Rails 7 Alpha 1、Alpha 2が立て続けにリリースされました。

今回のRails 7 Alphaでは、GItHubのリリースタグ↓にChangelogがありません。代わりにRails 7 Alpha 1リリースノートの翻訳を掲載いたします。

詳しくは以下のコミットリストやAlphaプレビュー動画をご覧ください。

Rails 7.0 Alpha 1: JavaScriptへの新回答、実行時暗号化、クエリ生成元のログ出力、Zeitwerk一元化

概要

MITライセンスに基づいて翻訳・公開いたします。

Rails 7の最初のAlpha版リリースにようこそ。このリリースには、JavaScriptの扱いに関するきわめてエキサイティングな新回答、Active Recordの実行時暗号化への目覚ましいアプローチ、SQLクエリ生成元のログ出力、非同期クエリ読み込み、オートローディングのZeitwerk一元化、他にも多くの機能が盛り込まれています。

通常であればRailsのAlpha版をリリースすることはありませんが、フロントエンドの新しいアプローチが非常に抜本的な変更を伴うことを考慮し、通常の「Beta版->リリース候補版->最終版」というフローに乗せる前にもう少し検証を進めておくのがベストと考えました。

今年中にRails 7最終版をリリースできるよう、新機能のテストにどうかご協力をお願いいたします。

フロントエンドに関する新しい回答のすべて

これまで5年近くに渡り、RailsでモダンなJavaScriptを書くためのデフォルトの回答はWebpackerでしたが、次の段階に進むときが来ました。ブラウザでのES6やESMサポート強化、HTTP/2の普及、そしてimport mapsというエキサイティングな新標準が登場したことによって、Rails 7のJavaScriptをNodeに依存しないアプローチへの道が開かれました。しかもnpmパッケージの利用を諦めずにです。

従来のTurbolinksとRails UJSが、StimulusTurboを組み合わせたHotwireに置き換えられたことで、優れたRailsアプリケーションを書くためのかつてないほど完成度の高いフロントエンド向け詰め合わせセットアップを実現しました。node_modulesディレクトリに数千個ものnode依存関係を置く必要もモジュールバンドラーの設定で苦しむこともなくなり、JavaScript開発にありがちな困難から解放されます。

それと同時に、Railsで「JavaScript + CSSバンドラー」を必要とする方向けに、両者の統合も劇的に改善されました。rails newで2つの新しいコンパニオンgemを-javascript [bundler]オプションや-css [bundler]オプションで有効にすることで、import mapsを用いた新規アプリケーションの作成時や変更時に、esbuildrollup.jsWebpackTailwind CSSPostCSSDart SassBootstrapを簡単に使えるようになります。

Active Recordの実行時暗号化機能

HEYから切り出されたActive Recordの属性暗号化機能を追加しました。これにより、従来の保存時(at-rest)および通信時(in-transit)の暗号化に加えて、実行時(at work)1の暗号化機能もアプリケーションで提供できるようになります。

これによってただちに得られる実用上のメリットのひとつは、機密属性の暗号化によるセキュリティレイヤを追加できることです。たとえば、攻撃者がデータベースやデータベースのスナップショットやアプリケーションログにアクセスできたとしても、暗号化済みの情報を理解できなくなります。そうした犯罪者たちを考慮しないとしても、正当な理由でアプリケーションログを確認するときに顧客の個人情報が露出せずに済みます。

しかしより重要な点は、Active Record暗号化を用いてアプリケーション内の機密情報をコードレベルで定義できることです。これにより、機密情報へのアクセス方法をコントロールすることも、その周辺にサービスを構築することも可能になります。例として、暗号化済みデータを保護する「監査可能な」Railsコンソールや、コントローラのparamsを自動的にフィルタする組込みシステムのチェックを想像してみてください。

詳しくはedgeguidesで暗号化属性の利用法に関する完全なガイドを参照してください。

Marginalia gem方式のタグ付けによるクエリ生成元のトレース

10年ほど前にBasecampから切り出されたMarginalia gemは、SQLコメントにタグ付けすることでクエリ生成元をトレースします。この外部gemがQueryLogsクラスに昇格してActive Recordに取り込まれました

非同期クエリ読み込み

コントローラのアクションで、互いに関連のない2つのクエリを読み込む必要があるときに、Relation#load_asyncで同時に実行できるようになりました。実行に100msかかる複雑なクエリが3つある場合、これまでは300msかけて1つずつ実行していました。これらのクエリがパラレル実行可能になり、3つのクエリにかかる時間は合計100msで済みます。

Zeitwerk一元化

Railsのオートローディング機能は魔法のようなクオリティオブライフ(QOL)を実現する機能のひとつであり、あるのが当然と思われがちなほどです。かつて信頼を寄せられていたconst_missingによるアプローチは、動作が気まぐれで機能も不足していましたが、Zeitwerkコードローダーへの一元化という形で置き換えが完了しました。特に古いアプリケーションではアップグレード時に注意しておきたい問題点がいくつかありますが、アップグレードガイドを読めばすぐにでも対応を開始できるでしょう。

その他のハイライト

  • spring gemがデフォルトから外されました(コンピュータが高速になったことで大規模アプリケーション以外での必要性が下がったため)。
  • ActionController::Live#send_streamは、コントローラのアクションでオンザフライ生成されるファイルを手軽にストリーミングできます。
  • パラレルテストが、CPUコア数とテスト数を比較した結果に応じてパラレル化をスケールするようになりました。
  • Active Storageで、より高速かつセキュアなlibvipsデフォルトのvariantプロセッサとして使われるようになりました。

私たちから皆さんへのメッセージ

昨年Rails 6.1をリリースして以来、3,000件を超えるコミットがRails 7に取り込まれました。これは数百人にのぼるコントリビュータたちによる成果です。その中には今年初めてコントリビュートした200人以上の新コントリビュータたちも含まれており、長年Railsのコードベースに手を加え続けてきた約6,000人のコントリビュータリストに彼らも名を連ねました。

関連記事

HotwireはRailsを「ゼロJavaScript」でリアクティブにできるか?前編(翻訳)


  1. at-work encryptionはHEY独自の用語のようです。詳しくはhttps://www.hey.com/security/を参照してください。本記事では「実行時」をat-workの仮訳としています。 

The post Rails 7 Alpha 1、2がリリースされました(リリースノート翻訳) first appeared on TechRacho.

Railsの技: to_sqlでActive Recordが生成するクエリを調べる(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

Railsの技: to_sqlでActive Recordが生成するクエリを調べる(翻訳)

joinsや複雑なwhere句を含んでいたり、複数テーブル間で特定の値をSELECTするような込み入ったActive Recordクエリを書こうとしているときに、Active RecordのDSLをすべて思い出すのは難しいでしょう。

joins(:orders)で書くべきか、それともjoins(:order)か?where(role: { name: 'Manager' })と書くべきか、それともwhere(roles: { name: 'Manager' })と書くべきか、といった具合です。

Railsコンソール上でこうしたクエリのテストを繰り返すのはよい考えですが、コードを実行するとたまに以下のような奇妙な結果が返って頭を抱えることもあります。

#<MyModel::ActiveRecord_Relation:0x23118>

この結果にアクセスしようとすると、以下のような謎エラーが発生してクエリが吹き飛んでしまうこともあります。

ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "permission"
LINE 1: ...."id" = "permissions_users"."permission_id" WHERE "permissio...

使い方

問題点をデバッグするために、生成されたSQLを検査したくなることがあります。

実際にはActive Recordの機能を使えばとても簡単です。クエリでto_sqlメソッドを呼び出せば、SQLを実行する代わりに完全なクエリを出力してくれます(SQLが無効な場合にも出力されます)。

User.where("email LIKE '%?'", "gmail.com").to_sql
#=> "SELECT \"users\".* FROM \"users\" WHERE (email LIKE '%'gmail.com'')"

なるほど、%?構文で失敗していることがわかりました。

User.where("email LIKE ?", "%gmail.com").to_sql
#=> "SELECT \"users\".* FROM \"users\" WHERE (email LIKE '%gmail.com')"

この方法は、特にデータベースで複数のテーブルを扱っている場合に有用です。

User.joins(:permissions).where(permission: { key: :edit_posts }).to_sql
#=> "SELECT \"users\".* FROM \"users\" INNER JOIN \"permissions_users\" ON \"permissions_users\".\"user_id\" = \"users\".\"id\" INNER JOIN \"permissions\" ON \"permissions\".\"id\" = \"permissions_users\".\"permission_id\" WHERE \"permission\".\"key\" = 'edit_posts'"

今度はwhereに以下のように複数形のpermissionsを書く必要があることがわかりました。

User.joins(:permissions).where(permissions: { key: :edit_posts })

この技のおかげで、複雑なクエリのデバッグに要する時間を大幅に節約できました。また、期待どおりのSQLクエリをRailsが生成しているかどうかを確認するときにも、この技を使って複雑なクエリを検証するようになりました。

参考情報

関連記事

Railsの技: 特定スコープ内でuniquenessバリデーションをかける(翻訳)

The post Railsの技: to_sqlでActive Recordが生成するクエリを調べる(翻訳) first appeared on TechRacho.

Viewing all 1838 articles
Browse latest View live