tapioca: Missing `ActiveRecord#save` signature

I was expecting to find save and save! in the methods generated by https://github.com/Shopify/tapioca/blob/main/lib/tapioca/dsl/compilers/active_record_relations.rb, together with build, create and all the others, but for some reason it’s missing.

Is it somewhere else, or was it simply missed? And if it’s missing, would you be open to a PR to add it there?

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 17 (9 by maintainers)

Commits related to this issue

Most upvoted comments

The only thing I don’t understand is how save and save! are any different from build, create and all the other ActiveRecord method that are already correctly generated by tapioca in one of the compilers with the correct signature. Why is it harder/against the spirit to do the same for save and save! 🤔 ?

@iMacTia I’m mostly rephrasing @paracycle’s response but it may be of help. dsl command’s job is to expose metaprogramming to Sorbet. Relations compiler’s documentation can paint a more detailed picture. It exposes methods to ActiveRecord::Base by representing the underlying relationships to Sorbet using synthetic entities. Methods such as build aren’t available to Sorbet without this compiler due to metaprogramming. They exist in ActiveRecord::Relation which isn’t statically mixed into ActiveRecord::Base.

On the other hand save and save! are methods available to Sorbet after a “simpler” gem RBI generation since Sorbet knows that ActiveRecord::Base includes ActiveRecord::Persistence. All tapioca is doing is loading the gems and writing the constant definitions, method definitions and the mixin information to a file (docs). You can check the RBI generated for activerecord and find

class ActiveRecord::Base
  include ::ActiveRecord::Persistence
  ...

You can also find the definitions for save and save! in the same file under the ActiveRecord::Persistence module. This means we already have gem RBIs generated for these methods unlike build/create.

Now if you write a DSL generator to improve the signature of save for each Model in your application it would conflict with the method definitions in gem RBIs since there will be 2 definitions for the same method. It’s also against the spirit since the methods aren’t defined by metaprogramming, which was why dsl command was introduced. save/save! are statically available to ActiveRecord::Base and don’t need any extra processing unlike build/create.

Also one thing to note is that if you went the DSL compiler route it doesn’t have to live inside tapioca. You can write your custom compiler and tapioca will execute it during dsl command.

Hope this clears things up a bit.

Yes that’s correct. My bad earlier, I forgot about that one. I can’t think of an edge case that invalidates T.self_type so it’s a good replacement to returning record itself given the usage is sensible. Keep in mind though in some more complex cases receiver could be T.untyped and therefore return type would also be T.untyped.