Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rails6.1で新しく入る機能について

 Rails6.1で新しく入る機能について

技術顧問が語る最新Ruby on Rails/Vue.js iCARE Dev Meetup #12 で話すスライドです

https://icare.connpass.com/event/183716/

Shinichi Maeshima

August 21, 2020
Tweet

More Decks by Shinichi Maeshima

Other Decks in Technology

Transcript

  1. 最近の Rails リリース⽇ 6.0.0 (2019/08/06) 5.2.0 (2018/04/09) 5.1.0 (2017/04/27) 5.0.0

    (2016/06/30) だいたい1 年くらいでマイナー、メジャーバージョンが上がる RailsConf( 毎年だいたい4 末くらいに開催) がターゲットになっていそ う 3
  2. 5

  3. ⼀応 6.1.beta1 ⽤のブログエントリの準 備⽤ PR はある ( けどいまアクティブでは ない )

    Add post about 6.1.beta1 release by eileencodes · Pull Request #222 · rails/weblog 6
  4. 6.1 のリリースはまだだけど、この 1 年 ですでに master にマージされた機能は たくさんあるので、今⽇はそのうちの ⼀部を紹介します (6.0

    の機能を知り たい⼈はパーフェクト Ruby on Rails と いう素敵な本があるのでそちらを御覧 ください ) 8
  5. ビューで「どのテンプレートを呼び出 しているか」が HTML コメントで表⽰ できるようになった .annotate_template_file_names annotates HTML output with

    template names by joelhawksley · Pull Request #38848 · rails/rails Add the configuration option for annotating templates with file names to the generated app by prathamesh-sonpatki · Pull Request #39204 · rails/rails 9
  6. 10

  7. config/routes.rb の内容を別ファイルに 分割できるようになった config/routes.rb の内容を別ファイルに外だしできるようになった 昔(Rails 4.0 がリリースされる前) ⼊った内容なんだけどDHH のお気

    に召さなかったようでrevert→6 年たってDHH の気が変わった模様 これまでもゴニョゴニョすればできたけど、公式のやり⽅が提供さ れたので安⼼して使えるようになったのがよいですね 12
  8. ↓ のように書く # config/routes.rb Rails.application.routes.draw do draw(:admin) end # config/routes/admin.rb

    get :foo, to: 'foo#bar' routes.rb が⼤量にあって、かつ分けやすい箇所がある(ex: 管理画⾯、 API) のであれば試してみるとよいのでは 13
  9. signed_id = User.first.signed_id( expires_in: 15.minutes, purpose: :password_reset ) User.find_signed(signed_id) #

    => nil (purpose がない) User.find_signed(signed_id, purpose: :password_reset) # => User.first # 16 分後... User.find_signed(signed_id, purpose: :password_reset) # => nil (expire した) User.find_signed!("bad data") # => エラー コード例のように、パスワードリセット機能などを作るときに便利で は 15
  10. strict_loading の導⼊ strict_loading メソッド経由で取得したオブジェクトは、以後SQL の発 ⾏を伴う関連を呼び出せない( 呼び出すとエラー) 。これによって includes などのeager

    load を強制させることができる。 user = User.strict_loading.first user.posts.to_a #=> ActiveRecord::StrictLoadingViolationError user = User.strict_loading.includes(:posts).first user.posts.to_a #=> OK 16
  11. モデル単位で設定することもできる。 class User < ApplicationRecord self.strict_loading_by_default = true has_many :posts

    end user = User.first user.posts.first # => ActiveRecord::StrictLoadingViolationError Exception ↓ のどちらかで、AR 全体にstrict_loading_by_default を設定することも できる ActiveRecord::Base.strict_loading_by_default = true Rails.application.config.active_record.strict_loading_by_default = true 19
  12. これはあえてstrict_loading にしたくないんですよ、という場合は次の ようにできる user = User.strict_loading(false).first user.posts.to_a #=> ok strict_loading

    を使うとpreload が強制されて、N+1 を防ぐことができ るぞ!というのは便利そうだけど、代わりに↓ のように無駄にオブジ ェクトを作ってしまうケースが出てきそうですね。なにも考えずにコ ードを書けるようになるわけではない。 user = User.includes(:posts).first user.posts.last #=> ⼀つだけPost があればいいのに全件分のPost オブジェクトを⽣成してしまう! 20
  13. 複数 DB の sharding 対応が⼊った Rails6.0 でプライマリ/ レプリカの複数DB に対応した Rails6.1

    ではそれを拡張して、sharding にも対応 6.0 では次のように、プライマリ/ レプリカを指定していた class ApplicationRecord < ApplicationRecord connects_to database: { writing: :primary, reading: :secondary } end 21
  14. connects_to メソッドに、次のようにsharding の定義を書けるよう になった。 ⾒てわかるようにsharding とprimary/replica を併⽤できる 6.0 の記法からから1 階層増えた感じ

    class ApplicationRecord < ActiveRecord::Base self.abstract_class = true connects_to shards: { default: { writing: :primary, reading: :primary_replica }, shard_one: { writing: :primary_shard_one, reading: :primary_shard_one_replica } } end 22
  15. 6.0 のときと同じようにconnected_to でDB を切り替える ActiveRecord::Base.connected_to(shard: :default) do @id = Record.create!

    # デフォルトのshard のDB でレコードを作成する end ActiveRecord::Base.connected_to(shard: :shard_one) do Record.find(@id) # もう⼀つのshard からfind しているのでレコードは⾒つからない end role とshard を⼀度に両⽅切り替えることもできる ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one) do Record.first end 23
  16. Deprecation warning の代わりに例外 を発⽣させることができるようになっ た Rails がバージョンアップしたときにdeprecated になったメソッド を実⾏すると、deprecation warning

    なメッセージが表⽰される メッセージを表⽰する代わりに、例外を発⽣させることができるよ うになった 24
  17. 条件を満たすときだけ許可することもできる ActiveSupport::Deprecation.allow [:bad_method], if: Rails.env.production? do User.do_thing_that_calls_bad_method end deprecation warning

    に頑張って対応したPR をマージしたあとに、す ぐまた別のPR でdeprecation warning が発⽣する、ということを防ぐ ことができて便利! 28
  18. Active Storage でアップロードしたファイルへ直接ア クセスできる URL を使えるようになった Active Storage でアップロードしたファイルへのアクセスは次のよう な挙動だった

    1. まずクライアントがRails サーバへリクエストを送る 2. 時間制限( デフォルト5 分) つきのファイルへのURL を⽣成してリダイ レクトする 3. ファイルをダウンロードする 29
  19. Active Storage のサムネイル管理⽅法 の変更と改善 これまで、Active Storage でサムネイルを表⽰するときには次のよう な挙動になっていた( ストレージがS3 だと仮定)

    1. サムネイルがS3 に存在するかどうかをチェック 2. 存在しなければ元画像をダウンロードしてサムネイルを作りS3 にア ップロードする 3. サムネイルの画像⽤のURL を作成する 31
  20. ActiveModel::Errors は実質的に2 つのHash オブジェクトから構成さ れている messages ex: {:email=>[" を⼊⼒してください"]} details

    ex: {:email=>[{:error=>:blank}]} Hash をゴニョゴニョするのがめんどくさいケースが有る ex: 特定のメッセージに紐づくdetail を取得したいときに、 messages のindex を取得して details[:email][index] としなけれ ばいけない 34
  21. 今回の修正により、ActiveModel::Errors は実質的に ActiveModel::Error オブジェクト( 新設) の集合となった これによりエラー内容に対しての細かい操作が、よりオブジェクト 指向っぽい書き⽅ができるようになった(Hash もオブジェクトでは あるけどね)

    関連先のエラーもいい感じに表現できている (ActiveModel::Errors のネストができる) Errors にwhere メソッドが⽣えた model.errors.where(:name, :foo, bar: 3).first message に紐づくdetail が簡単に取得できるようになった 35
  22. ⾮互換な変更なので対応が必要なものもある book.errors[:title] << 'is not interesting enough.' のよう な、直接Hash をいじるような操作はdepricated

    になった book.errors.add(:title, 'is not interesting enough.') のよ うに書く book.errors.each do |attribute, error_message| ... は depreated book.errors.each do |error| のように変更する 関連モデルを⼤量に保存するようなアプリケーションだと、エラー 内容の解析やらなんやらが楽になってよいのでは 36
  23. check 制約に対応した add_check_constraint :products, "price > 0", name: "price_check" remove_check_constraint

    :products, name: "price_check" create_table :distributors do |t| t.string :zipcode t.check_constraint "zipchk", "char_length(zipcode) = 5" end MySQL の場合は8.0.16 以降サポート そもそもMySQL は8.0.16 までcheck 制約に対応していなかった これまでも直接SQL を発⾏すればcheck 制約を追加できたけど、そ の場合はschema.rb をやめてstructure.sql にする必要があった 39
  24. enum にデフォルト値を設定できるよう になった class Book < ActiveRecord::Base enum status: [:proposed,

    :written, :published], _default: :published end Book.new.status # => "published" キーが _default なのは「既存のカラムにdefault があったときに壊れ るから」とのこと 40
  25. CSRF トークンのエンコード形式の変更 CSRF トークンのエンコード⽅式がbase64 からurlsafe 版のbase64 に 変更されました base64 はa-z,A-Z,0-9,+,/

    でエンコードする⽅式 + と / はURL エンコードしないといけない→ それぞれ - と _ に変 更した Ruby にはurlsafe 版のbase64 エンコード⽤メソッドがある Base64.urlsafe_encode64 CSRF トークンをcookie としてそのまま送りたいようなケースで、 クライアント側でエンコード・デコードが必要になるのがだるいの でこうなったとのこと 42
  26. ActiveRecord の merge メソッドの挙動 変更 Rails6.1 までのmerge は同じカラムがmerge されるときに、Hash の

    ように後勝ち(merge の引数側が採⽤される) になるケースと、なら ないケース( 両⽅のクエリが統合される) がある 後勝ちになったりならなかったりするのは意図的なものではないの で後勝ちになるように修正する 6.1 では後勝ちにならないケースでdeprecate メッセージが表⽰さ れ、6.2 でデフォルト後勝ちになる 6.1 で6.2 の挙動( 後勝ちにしたければrewhere オプションを指定する) 47
  27. # Rails 6.1 で後勝ちになるパターン(IN 句) Author.where(id: [david.id, mary.id]).merge(Author.where(id: bob)) #

    => [bob] # Rails 6.1 で両⽅の指定が採⽤されるパターン # where id between "david のid" and "mary のid" and id = bob のid Author.where(id: david.id..mary.id).merge(Author.where(id: bob)) # => [] # 6.1 で6.2 相当の挙動を使うにはrewhere を使う Author.where(id: david.id..mary.id).merge(Author.where(id: bob), rewhere: true) # => [bob] # Rails 6.2 ではどのようなケースでも後勝ち Author.where(id: [david.id, mary.id]).merge(Author.where(id: bob)) # => [bob] Author.where(id: david.id..mary.id).merge(Author.where(id: bob)) # => [bob] 48
  28. 後勝ちにしない、を明⽰的にするためのメソッドも追加ずみ david_and_mary = Author.where(id: [david, mary]) mary_and_bob = Author.where(id: [mary,

    bob]) # => [bob] david_and_mary.merge(mary_and_bob) # => [mary, bob] david_and_mary.and(mary_and_bob) # => [mary] david_and_mary.or(mary_and_bob) # => [david, mary, bob] 49