Closed4

[Rails][Sorbet][Tapioca] Tips

hiko1129hiko1129

https://github.com/Shopify/tapioca/blob/main/manual/compiler_activerecordrelations.md への対応

元々はsorbet-railsを使えば良いって感じで https://zenn.dev/hiko1129/articles/36c122c6610fdd で書いたけどsorbet-railsがアーカイブされてしまったので https://github.com/Shopify/tapioca/issues/1140#issuecomment-1233158782 を参考にinheritedをオーバーライド

class ApplicationRecord < ActiveRecord::Base
  class << self
    extend T::Sig

    sig { params(subclass: T.untyped).void }
    def inherited(subclass)
      super

      subclass.const_set(
        :PrivateRelation,
        subclass.const_get("#{subclass.name}::ActiveRecord_Relation"),
      )
      subclass.const_set(:PrivateRelationWhereChain, ActiveRecord::QueryMethods::WhereChain)
      subclass.const_set(
        :PrivateAssociationRelation,
        subclass.const_get("#{subclass.name}::ActiveRecord_AssociationRelation"),
      )
      subclass.const_set(
        :PrivateAssociationRelationWhereChain,
        ActiveRecord::QueryMethods::WhereChain,
      )
      subclass.const_set(
        :PrivateCollectionProxy,
        subclass.const_get("#{subclass.name}::ActiveRecord_Associations_CollectionProxy"),
      )
      subclass.const_set(
        :RelationType, # sorbet-railsのRelationType相当
        T.type_alias do
          T.any(
            subclass.const_get("#{subclass.name}::PrivateRelation"),
            subclass.const_get("#{subclass.name}::PrivateAssociationRelation"),
            subclass.const_get("#{subclass.name}::PrivateCollectionProxy"),
          )
        end,
      )
    end
  end
end
hiko1129hiko1129

RSpecやFactoryBotを利用している箇所でtyped: true以上を指定したい場合にはT.bind(self, T.untyped)を使用する。
typed: falseよりは型支援が受けられるようになる。

ref: https://stackoverflow.com/questions/74842832/how-to-configure-sorbet-with-rspec

# typed: strict
# frozen_string_literal: true

require "rails_helper"

RSpec.describe(Hoge) do
  T.bind(self, T.untyped)
end

FactoryBot.define do
  T.bind(self, T.untyped)
  factory :hoge, class: "Hoge" do
  end
end
hiko1129hiko1129

SorbetだとDataクラスはまだ(2024年2月6日時点では)まともに使えない。
ref: https://github.com/sorbet/sorbet/issues/7272

T::Structは同一性比較なので、 https://github.com/samuelgiles/sorbet-struct-comparable を使って同値性比較する。

Hashから構造体にマッピングするときとかは https://github.com/chanzuckerberg/sorbet-coerce を使う。
T::StructじゃなくてPOROにマッピングしようとするとinitializeでhash受け取りするように書かないといけないっぽい。

このスクラップは2ヶ月前にクローズされました