[Rails][Sorbet][Tapioca] Tips
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
昔 https://note.hiko1129.com/posts/1660573211/ って書いてたけどこれは誤り
https://sorbet.org/docs/faq#does-sorbet-work-with-rake-and-rakefiles に書いてあることが正しい
# typed: true
class Hoge
extend Rake::DSL
task :fuga do
end
end
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
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受け取りするように書かないといけないっぽい。