You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Sharing this little "hack" that we recently implemented in our codebase.
There's only that much we can achieve by adding signatures to Grape::Entity, especially because of the awful signature of expose that makes use the splat (*) operator for its arguments.
The other "issue" we wanted to address what that there's no way to know what the object in a serialiser is, especially in a big codebase with tens of people working on it. It would be great if Grape::Entity could be defined as generic (like Grape::Entity[ObjectType], but sorbet doesn't allow you to re-define an existing class as generic without forcing you to define the ObjectType on ALL the existing entities. This was just not viable for us due to the huge amount of existing entities.
Solution
In order to achieve the goals above and make this process "opt-in", we therefore introduced a Grape::TypedEntity wrapper.
The current version, which I'm sure can be improved, looks like this (it's split into an .rb and .rbi file due to sorbet's limits):
When defining a TypedEntity you need to specify the two generic members:
classUserDTO < Grape::TypedEntityObjectType=type_member{{fixed: ::User}}ObjectTypeTemplate=type_template{{fixed: ::User}}expose:email,as: :contact# now sorbet knows exactly what options you can pass and their typeexpose:full_namedeffull_name# Here sorbet knows that `object` is of type `User`, so it will raise an error# if you try to access a method that does not exist."#{object.first_name}#{object.last_name}"endend
Known issues
The signature for expose is not perfect yet.
Options that take a proc will not type-check object and options correctly (this is a sorbet limitations on procs).
Moreover, the expose method sig only accepts a block without parameters (for nesting), so as should be used for defining complex fields
The text was updated successfully, but these errors were encountered:
Thanks @iMacTia! This is great. I had something very similar in mind, down to the Grape::TypedEntity name :)
Having to declare the generic type twice is annoying, but probably inevitable due to how Grape is designed and Sorbet's own limitations with generics.
If we're going to introduce this generic class, I would also seize the opportunity to replace the expose method with 3 different aliases, to address the issue that was discussed on the Sorbet Slack:
expose: default one, same as what you have here but without the block argument to force the use of one of the following when passing a block
expose_nested: block variant for nested exposure (block takes no argument and is evaluated in the class context)
expose_runtime: block variant for runtime exposure (block takes 1 or 2 arguments and is evaluated in the instance context)
Problem summary
Sharing this little "hack" that we recently implemented in our codebase.
There's only that much we can achieve by adding signatures to
Grape::Entity
, especially because of the awful signature ofexpose
that makes use the splat (*
) operator for its arguments.The other "issue" we wanted to address what that there's no way to know what the
object
in a serialiser is, especially in a big codebase with tens of people working on it. It would be great ifGrape::Entity
could be defined as generic (likeGrape::Entity[ObjectType]
, but sorbet doesn't allow you to re-define an existing class as generic without forcing you to define theObjectType
on ALL the existing entities. This was just not viable for us due to the huge amount of existing entities.Solution
In order to achieve the goals above and make this process "opt-in", we therefore introduced a
Grape::TypedEntity
wrapper.The current version, which I'm sure can be improved, looks like this (it's split into an
.rb
and.rbi
file due to sorbet's limits):Usage
When defining a
TypedEntity
you need to specify the two generic members:Known issues
The signature for
expose
is not perfect yet.Options that take a
proc
will not type-checkobject
andoptions
correctly (this is a sorbet limitations onproc
s).Moreover, the
expose
method sig only accepts ablock
without parameters (for nesting), soas
should be used for defining complex fieldsThe text was updated successfully, but these errors were encountered: