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

週刊Railsウォッチ(20210330後編)Active Recordモデル属性暗号化が標準で入る可能性、Flipper Cloud、awesome_printほか

こんにちは、hachi8833です。

週刊Railsウォッチについて

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

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

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

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

🔗 RemoteIPミドルウェアでtrusted_proxiesに単独の値代入を非推奨化


つっつきボイス:「app.config.action_dispatch.trusted_proxiesに渡すIPアドレスは単独でも配列でも渡せるけど、前者を非推奨化したということのようですね↓」

# 同PRより
# Adds 4.2.42.0/24 to default list of trusted proxies
app.config.action_dispatch.trusted_proxies = IPAddr.new("4.2.42.0/24")

# Replaces default list of trusted proxies with 4.2.42.0/24
app.config.action_dispatch.trusted_proxies = [IPAddr.new("4.2.42.0/24")]

「RackミドルウェアのRemoteIptrusted_proxiesは、たとえばRailsの手前にリバースプロキシを置いてある場合、リクエストの流れがリバースプロキシ->Railsアプリの場合はレスポンスを返すけど、リバースプロキシ以外の怪しいIPアドレスからRailsアプリにリクエストが来た場合はレスポンスを返さないようにするのに使ったりします(IP Spoofing対策)」「あ、そうやって使うんですね」

参考: IPスプーフィング - Wikipedia

「こうした制約をインフラレベルだけでかけられれば理想なんですが、CDNなどの制約が絡んだりしてインフラが複雑になると穴を塞ぐのが大変になるので、そういうときにtrusted_proxiesを使うことがあります」

参考: コンテンツデリバリネットワーク - Wikipedia

trusted_proxiesに配列を渡せるなら単独の値もカバーできるけど、単独の値を非推奨化する必要はあるのかな?🤔」「まだしばらくは単独の値も使えそう」「今後はenumerableな値を渡せば大丈夫ですね」

🔗 個別のテストファイル実行でパラレルテストが無効になった


つっつきボイス:「bin/test test/controller/parameters/accessors_test.rbのようにファイルを1個だけ指定する場合はパラレルテストをオフにするようになったのか: たしかに1ファイルだけのテストでパラレルテストのワーカーをセットアップするのは完全に無駄ですね」「たしかに」「IDEの保存時実行機能やguard gemなどを使って保存ファイルだけテストを自動実行するのが速くなりそう👍」

# 改修前
actionpack $ bin/test test/controller/parameters/accessors_test.rb
Run options: --seed 48261

........................................................................

Finished in 0.211923s, 339.7460 runs/s, 552.0873 assertions/s.
72 runs, 117 assertions, 0 failures, 0 errors, 0 skips
# 改修後
actionpack $ bin/test test/controller/parameters/accessors_test.rb

Run options: --seed 5461

........................................................................

Finished in 0.008411s, 8560.2189 runs/s, 13910.3557 assertions/s.
72 runs, 117 assertions, 0 failures, 0 errors, 0 skips

guard/guard - GitHub

🔗 strict_loading!n_plus_one_onlyモードが追加


つっつきボイス:「ビックリマーク付きのstrict_loading!n_plus_one_onlyオプションが入ったそうです」「Changelogの方が見やすそう↓」

  • レコードレベルのstrict_loading!にモード引数を追加
    この引数は、単一レコードに対してstrict loadingを有効にし、N+1クエリに対してのみエラーをraiseできる。
developer.strict_loading!(mode: :n_plus_one_only)
developer.projects.to_a # Does not raise
developer.projects.first.client # Raises StrictLoadingViolationError

従来はstrict loadingを有効にすると、lazy loadされたすべての関連付けでエラーがraiseされた。n_plus_one_onlyモードを指定すると、単一のクエリでフェッチされたbelongs_tohas_manyなどの関連付けをlazy loadできる。
Dinah Shi
同Changelogより大意

n_plus_one_onlyを有効にすると、N+1問題が発生していないとき(レコードを1件だけ読み込む場合)なら関連付けをlazy loadingできて、N+1問題が発生している場合だけエラーをraiseするようになったということのようですね: strict loadingはN+1クエリ問題が発生しないときに行いたいことが多いし、N+1が発生したときはstrict loadingのところで止めたくなると思うので、これが欲しい気持ちはわかります」「なるほど」「n_plus_one_onlyモードは自分的にはなかなか実用的な印象👍」

レコードをstrict_loadingモードに設定する。レコードが関連付けをlazy loadingしようとするとエラーをraiseする。

user = User.first
user.strict_loading!
user.comments.to_a
=> ActiveRecord::StrictLoadingViolationError

api.rubyonrails.orgより大意

🔗 Enumerable#in_order_ofが追加

ActiveRecord::Base#findは、引数として使われている主キーの順でレコードを返す。これはindex_bymapの合せ技で、#find以外でも有用。このプルリクはそのパターンをEnumerable#in_order_ofに切り出したもの。
同PRより大意


つっつきボイス:「これは今年2月にマージされたので少し前のPRですが、DHH自らによるものでした」「Active Supportに入ったんですね」「このコードサンプルがわかりやすいかな↓: [1, 5, 3]を渡すと、idを持つものをその順序で返す」「あ、なるほど」

  #   [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
  #   => [ Person.find(1), Person.find(5), Person.find(3) ]

「Enumerableに追加されたのがいいですね」「順序を指定せずに取りたいことの方が多いと思うけど、このメソッドがあることを知っていれば順序を指定して取りたい場合に使うかも」

# activesupport/lib/active_support/core_ext/enumerable.rb#L194
+ def in_order_of(key, series)
+   index_by(&key).values_at(*series).compact
+ end

🔗 (マージ前)Active Recordモデルの属性を暗号化する機能(Ruby Weeklyより)


つっつきボイス:「現時点(2021/03/25夜)ではまだマージされていませんが、Ruby Weeklyで取り上げられていました」「おぉ、属性の暗号化がついにRailsに統合されそう?」

# 同PRより
class Person < ApplicationRecord
  encrypts :name
end

「Action TextやActive Recordなどにも変更が入っている」「よく見たら更新ファイルが82個もあるんですね」「変更多いな〜」「これまでなかった機能が丸ごと入ったから変更量は多いでしょうね」

「Active Record属性の暗号化といえばattr_encrypted gemがかなり昔からよく使われています↓」「そうそう」「GitLabでもattr_encryptedを使っていたと思います」「類似のlockboxやvault-railsもプルリクメッセージで紹介されていますね↓」

attr-encrypted/attr_encrypted - GitHub
ankane/lockbox - GitHub
hashicorp/vault-rails - GitHub

「しかもこれはBasecampがやっているHEYのコードから切り出したそうですよ↓」「お〜」「HEYで実績を積んでいるコードが使われているのはありがたい」「encryptorもちゃんと独自に指定できる: これはないと困る機能」「HEYを立ち上げる前にセキュリティファームがこのライブラリをレビューしたと書かれていますね」

# 同PRより
config.active_record.encryption.encryptor = MyEncryptor.new

「属性暗号化機能は多くのアプリで使われるので、Rails標準で入っていてもいい気がしますね」「今どきのフルスタックWebフレームワークなら入っていてもおかしくないかも」「"いいね"もすごくたくさん付いてますね」「これはRailsにあっていい機能👍」「Rails 7にこれが入るなら目玉機能になるでしょうね」


本日(2021/03/30夕方)時点ではまだマージされていません。

🔗Rails

🔗 authorizationのつらみをFlipper Cloudで軽減する(Ruby Weeklyより)


つっつきボイス:「上のFlipper Cloudというサービスを推している記事のようです」「サイトを見た感じでは、いわゆるフィーチャーフラグをクラウドベースで管理できるオープンソースのサービスということらしい」

参考: フィーチャートグル - Wikipedia

「gemも公開されている↓:gemでやる場合は自分たちで管理する必要があると思いますが」「設定はIRBでもFlipper::UIでもできるとありますね」

jnunemaker/flipper - GitHub


同リポジトリより

「コードはフィーチャーフラグそのものという感じなので↓、シンプルなフィーチャーフラグの実装に使えそうかな」「おぉ」

# 同リポジトリより
require 'flipper'

# check if search is enabled
if Flipper.enabled?(:search)
  puts 'Search away!'
else
  puts 'No search for you!'
end

puts 'Enabling Search...'
Flipper.enable(:search)

# check if search is enabled
if Flipper.enabled?(:search)
  puts 'Search away!'
else
  puts 'No search for you!'
end

「眺めた限りでは、リポジトリの★も多いし、フィーチャーのオンオフとかも含めて割とよくできていそうな予感がしますね: 興味があれば調査してみてもいいかも」


「元記事の方は、たとえばこれはよくあるポリシー型の権限管理ですね↓」「Flipper Cloudはユーザーごとの権限管理までやれそうに見える」

# 同記事より
class TokenPolicy < ApplicationPolicy
  def show?
    project_member?
  end

  def update?
    project_member?
  end

  def destroy?
    project_member?
  end

  private 

  def project_member?
    record.project.member?(user)
  end
end

「URLエンドポイントレベルのシンプルな権限管理ならこうしたgemやサービスでやれると思いますが、エンティティレベルで権限管理をしようとするとおそらくpunditのようなものが必要になってくるでしょうね」「なるほど」「そこにGraphQLが絡むとさらに複雑になるんですよ」

varvet/pundit - GitHub


「ところで、記事ではmemoistというgemも紹介されていますね↓」「サンプルコードのとおり、いわゆるメモ化を簡単に行えるようですね」「今はRailsからなくなったActiveSupport::Memoizableから切り出したらしい」「ポリシーは実行中に変えないのが普通なので、記事ではこれでメモ化して高速化を図っているんでしょうね」

matthewrudy/memoist - GitHub

# matthewrudy/memoistより
require 'memoist'
class Person
  extend Memoist

  def social_security
    puts "execute!"
    decrypt_social_security
  end
  memoize :social_security
end

person = Person.new

person.social_security
# execute!
# => (returns decrypt_social_security)

person.social_security
# => (returns the memoized value)

参考: メモ化 - Wikipedia

🔗 sqldef: 冪等なスキーマ管理ツール

k0kubun/sqldef - GitHub

Goで書かれています。


つっつきボイス:「k0kubunさんの上の記事でsqldefを知りました」「ridgepoleのオルタナ的なツールをk0kubunさんが自分で作ったんですね」「スキーマ管理をSQLで書けるというツールなのか: そういえばずっと前に見たことがあったかも」「実はだいぶ前のウォッチで取り上げてたんですが、ridgepole的なツールとは気づきませんでした(ウォッチ20191119)」

winebarrel/ridgepole - GitHub

「スキーマ管理をSQLで書きたいor書く必要がある場合にいいかも: たとえばプロジェクト横断的な案件で、Railsでないシステムのデータベースをリードオンリーで共有している場合を考えると、RailsのマイグレーションはRubyがないと動かないのでRubyをインストールするなどの対応が必要ですが、こうやってSQLで書けるならそういう場合に便利かもしれませんね」「ちょうどk0kubunさんの記事の脚注にもそのことが書かれていたので引用しました↓」「そうそう、こういうふうに非Railsプロジェクトとデータベーススキーマを共有したいとか、Railsを知らない人がスキーマ管理するときなどに使えそう」

元々ActiveRecordが入っているRailsだとRidgepoleを使わない理由はほとんどない気がするけど、同じDBをActiveRecord以外からも読むので素のSQLで管理されてる方が扱いやすい (今回のアプリがそれ) とか、DDLは覚えてるけどActiveRecordのDSLは調べないとわからないというようなニッチな用途には便利かもしれない。
同記事脚注より


以下のスライドでもsqldefが取り上げられているのを見つけました。

🔗Ruby

🔗 dead_end: endの対応関係の誤りを検出する(Ruby Weeklyより)

zombocom/dead_end - GitHub


つっつきボイス:「endの誤りを検出するという、よくある感じのgemですが、このdead_endという名前がいいですね👍」「ネーミングセンスが光ってる」「RubyMineなどのIDEもやれますけどね」

# 同リポジトリより: endが足りない場合
class Dog
  def bark
    puts "bark"

  def woof
    puts "woof"
  end
end
# => scratch.rb:8: syntax error, unexpected end-of-input, expecting `end'
# 同リポジトリより: endが余分な場合
class Dog
  def speak
    @sounds.each |sound| # Note the missing `do` here
      puts sound
    end
  end
end
# => scratch.rb:7: syntax error, unexpected `end', expecting end-of-input

🔗 シェルのディレクトリ一括作成


つっつきボイス:「koicさんのRuboCop記事でシェルの話が載っていました」「mkdir {a,z}は当然できるけど、mkdir {a..c}mkdir {1..10}でディレクトリを一括作成できるとは知らなかった」「これ初めて見ました」「そういえばシェルに..記法があったのを今思い出した」「最近シェルスクリプト書いてないな〜😅」

# 同記事より
% mkdir {a,z}
% ls 
a z

% mkdir {a..c}
% ls 
a b c 

% mkdir {1..10}
% ls 
1 10 2 3 4 5 6 7 8 9

% mkdir {01..10}
% ls
01 02 03 04 05 06 07 08 09 10

参考: Bashによるパス名の展開まとめ | LFI

「そして{}が空だと{}というディレクトリができてしまう、たしかに」

「そういえばシェルのtestコマンドに[という別名がありますよね」「あ〜、ありますあります」「あれを最初知ったときはびっくりしましたけど、わかってみるとシェルすごいかもという気持ちになりますね」「人によりそうですけどね😆」

参考: test と [ と [[ コマンドの違い - 拡張 POSIX シェルスクリプト Advent Calendar 2013 - ダメ出し Blog

🔗 awesome_printが強くなって帰ってきた(Ruby Weeklyより)

awesome-print/awesome_print - GitHub


つっつきボイス:「awesome_printは、以前は色付けに使うgemという印象でしたが、知らないうちにいろいろ機能が足されているようです」「お〜、オプションがずいぶん増えてますね」

「こういう感じで表示できるのはいい↓👍」「いいな〜」

# 同リポジトリより
$ rails console
rails> require "awesome_print"
rails> ap Account.limit(2).all
[
    [0] #<Account:0x1033220b8> {
                     :id => 1,
                :user_id => 5,
            :assigned_to => 7,
                   :name => "Hayes-DuBuque",
                 :access => "Public",
                :website => "http://www.hayesdubuque.com",
        :toll_free_phone => "1-800-932-6571",
                  :phone => "(111)549-5002",
                    :fax => "(349)415-2266",
             :deleted_at => nil,
             :created_at => Sat, 06 Mar 2010 09:46:10 UTC +00:00,
             :updated_at => Sat, 06 Mar 2010 16:33:10 UTC +00:00,
                  :email => "info@hayesdubuque.com",
        :background_info => nil
    },
    [1] #<Account:0x103321ff0> {
                     :id => 2,
                :user_id => 4,
            :assigned_to => 4,
                   :name => "Ziemann-Streich",
                 :access => "Public",
                :website => "http://www.ziemannstreich.com",
        :toll_free_phone => "1-800-871-0619",
                  :phone => "(042)056-1534",
                    :fax => "(106)017-8792",
             :deleted_at => nil,
             :created_at => Tue, 09 Feb 2010 13:32:10 UTC +00:00,
             :updated_at => Tue, 09 Feb 2010 20:05:01 UTC +00:00,
                  :email => "info@ziemannstreich.com",
        :background_info => nil
    }
]

「リポジトリにcodeclimate.ymlが置かれている: ちなみにCode Climateは自動コードレビューなどが使えるサービスです」「Code Climateの設定はこういう感じで書くんですね」

「こういうgemが環境に入っていて使えたら嬉しいけど、ただGemfileには書いて欲しくない気持ちがあります」「あ、それわかります」「このgemならrequireに書いてもいいぐらい好きですけど、Gemfileのdevelopmentブロックにgemをいっぱい足すのはあまり好みではないんですよ」「たしかに」

🔗 AWS SDK RubyでRuby 2.2以前のランタイムサポートが終了


つっつきボイス:「お、サポート終了のお知らせか」「ここにリストされているバージョンならRubyソースコードも更新されなくなっていますし、影響はそんなにないと思いますね」

  • Ruby 1.9.3 – EOL began on 2015-02-23
  • Ruby 2.0.0 – EOL began on 2016-02-24
  • Ruby 2.1 – EOL began on 2017-03-31
  • Ruby 2.2 – EOL began on 2018-03-31
    aws.amazon.comより

後編は以上です。

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

週刊Railsウォッチ(20210329前編)特集: Rails更新版の臨時リリースとmimemagic gemのGPL問題

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

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

Rails公式ニュース

Ruby Weekly


CONTACT

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