Skip to content

Add annotations for the alba gem - #422

Merged
KaanOzkan merged 2 commits into
Shopify:mainfrom
mokevnin:add-alba-annotations
Sep 10, 2026
Merged

KaanOzkan merged 2 commits into
Shopify:mainfrom
mokevnin:add-alba-annotations

Conversation

@mokevnin

@mokevnin mokevnin commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Type of Change

  • Add RBI for a new gem

Changes

Alba ships no signatures, so tapioca emits every Alba::Resource::ClassMethods DSL macro sig-less. This adds signatures for the class-level macros used to define a resource. Every parameter list matches the one tapioca generates; every type is taken from the method body and its YARD doc.

  • attribute / attributesname is String/Symbol, if: is Symbol or Proc (Alba::ConditionalAttribute#condition_passes? branches on exactly those two), extra keywords are the untyped name => type pairs handed to Alba::TypedAttribute.
  • association and its aliases one, many, has_one, has_many — types taken from the YARD @param tags on association, which the aliases share; resource:/serializer: accept a resource class, a String, or a Proc.
  • nested_attribute / nested, trait — block is class_eval'd, so it is bound to an untyped self and returns nothing useful.
  • on_errorhandler is one of :raise, :ignore, :nullify; the block is called as on_error.call(error, obj, key, attribute, klass) and its [key, value] return is destructured, hence T::Array[T.untyped].
  • on_nil — called as instance_exec(obj, key, attribute, &nil_handler).
  • meta, layout, helper, collection_key, root_key, root_key!, root_key_for_collection, transform_keys, prefer_object_method!, prefer_resource_method! — direct reads of the assignments in each body.

Two notes:

  • The block on attribute (and on trait and nested_attribute) is declared non-nilable. It is syntactically optional, but all three raise ArgumentError immediately when it is absent, so a call without a block can only ever fail at runtime.
  • transform_keys! is left unannotated: it returns the anonymous dup of the resource class, is marked experimental in the source, and I could not type it honestly.

Instance methods (serialize, as_json, serializable_hash) are deliberately left out of this first pass — serializable_hash returns a Hash or an Array depending on whether the object is a collection, and a union return there would tighten code that type-checks today.

bundle exec repo check --gem --ref upstream/main passes locally on all five checks (index, rubocop, rubygems, runtime, static).

@mokevnin
mokevnin requested a review from a team as a code owner September 2, 2026 17:45
Alba ships no Sorbet signatures, so tapioca emits its `Alba::Resource`
DSL macros sig-less. Annotate the class-level macros used to define a
resource.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mokevnin
mokevnin force-pushed the add-alba-annotations branch from 55b203c to a65c176 Compare September 6, 2026 16:53
Comment thread rbi/annotations/alba.rbi Outdated
sig do
params(
handler: T.nilable(Symbol),
block: T.nilable(T.proc.params(error: T.untyped, object: T.untyped, key: T.untyped, attribute: T.untyped, resource_class: T.untyped).returns(T::Array[T.untyped])),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
block: T.nilable(T.proc.params(error: T.untyped, object: T.untyped, key: T.untyped, attribute: T.untyped, resource_class: T.untyped).returns(T::Array[T.untyped])),
block: T.nilable(T.proc.params(error: T.untyped, object: T.untyped, key: T.untyped, attribute: T.untyped, resource_class: T.untyped).returns(T.untyped)),

Looks like it can return any constant? https://github.com/okuramasafumi/alba/blob/v4.0.0/test/usecases/on_error_test.rb#L60-L63

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — the return goes straight back to the caller, so T::Array[T.untyped] was wrong. Applied in d63f627.

Comment thread rbi/annotations/alba.rbi Outdated
Comment on lines +8 to +9
resource: T.nilable(T.any(Module, String, Proc)),
serializer: T.nilable(T.any(Module, String, Proc)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems better

Suggested change
resource: T.nilable(T.any(Module, String, Proc)),
serializer: T.nilable(T.any(Module, String, Proc)),
resource: T.nilable(T.any(T::Class[Alba::Resource], String, Proc)),
serializer: T.nilable(T.any(T::Class[Alba::Resource], String, Proc)),

https://github.com/okuramasafumi/alba/blob/fd805a70f8bedbfd1cfc50eb589afa553469ace4/lib/alba/resource.rb#L392-L393

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly for one, has_many, has_one, many

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, T::Class[Alba::Resource] is the honest type. Applied in d63f627.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — association, one, many, has_one and has_many all carry the same signature now. bundle exec repo check --gem alba is green.

Address review: resource:/serializer: take a resource class, not any
Module, on association and all four aliases; the on_error handler's
return value is passed through as-is, so it is not an Array.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@KaanOzkan
KaanOzkan merged commit 579dde6 into Shopify:main Sep 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants