Skip to content

Add annotations for ransack - #421

Open
mokevnin wants to merge 2 commits into
Shopify:mainfrom
mokevnin:add-ransack-annotation
Open

mokevnin wants to merge 2 commits into
Shopify:mainfrom
mokevnin:add-ransack-annotation

Conversation

@mokevnin

@mokevnin mokevnin commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Type of Change

  • Add RBI for a new gem
  • Modify RBI for an existing gem
  • Other:

Changes

ransack ships no signatures, so tapioca emits these five methods sig-less. They are the methods application code actually touches: every host model must override the four allowlist methods, and result is how a search is turned into a query.

  • Ransack::Adapters::ActiveRecord::Base#ransackable_attributes / #ransackable_associationsT::Array[String]: the default implementations return authorizable_ransackable_attributes / _associations, which are column_names + _ransackers.keys + _ransack_aliases.keys + attribute_aliases.keys and reflect_on_all_associations.map { |a| a.name.to_s } — strings in both cases, and the doc comments say "as an array of strings".
  • #ransortable_attributesT::Array[String]: delegates to ransackable_attributes(auth_object).
  • #ransackable_scopesT::Array[Symbol]: returns [], and its doc comment specifies "a whitelist array of symbols".
  • Ransack::Search#resultActiveRecord::Relation: returns @context.evaluate(self, opts); Context.for only ever builds Ransack::Adapters::ActiveRecord::Context (the AR adapter is the only one the gem ships), and both branches of its evaluate (relation.distinct or relation) return a relation.

auth_object is left T.untyped — the gem passes whatever the host app puts in Search's auth_object: option and never inspects it.

The index entry needs requires: ["active_record", "ransack"]: require "ransack" alone defers ActiveRecord to an ActiveSupport.on_load(:active_record) hook that never fires, so ActiveRecord::Relation does not resolve in the static check. No dependencies entry — activerecord is already a runtime dependency of the gem.

bundle exec repo check --gem --ref origin/main passes all five checks locally.

@mokevnin
mokevnin requested a review from a team as a code owner September 2, 2026 17:41

@amomchilov amomchilov left a comment

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.

Ran the CI, LGTM.

Marking these methods overridable would better express their intent. Could you please try that out and see if it causes any issues?

Comment thread rbi/annotations/ransack.rbi Outdated
Comment on lines +9 to +19
sig { params(auth_object: T.untyped).returns(T::Array[String]) }
def ransackable_associations(auth_object = nil); end

sig { params(auth_object: T.untyped).returns(T::Array[String]) }
def ransackable_attributes(auth_object = nil); end

sig { params(auth_object: T.untyped).returns(T::Array[Symbol]) }
def ransackable_scopes(auth_object = nil); end

sig { params(auth_object: T.untyped).returns(T::Array[String]) }
def ransortable_attributes(auth_object = nil); end

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
sig { params(auth_object: T.untyped).returns(T::Array[String]) }
def ransackable_associations(auth_object = nil); end
sig { params(auth_object: T.untyped).returns(T::Array[String]) }
def ransackable_attributes(auth_object = nil); end
sig { params(auth_object: T.untyped).returns(T::Array[Symbol]) }
def ransackable_scopes(auth_object = nil); end
sig { params(auth_object: T.untyped).returns(T::Array[String]) }
def ransortable_attributes(auth_object = nil); end
sig { overridable.params(auth_object: T.untyped).returns(T::Array[String]) }
def ransackable_associations(auth_object = nil); end
sig { overridable.params(auth_object: T.untyped).returns(T::Array[String]) }
def ransackable_attributes(auth_object = nil); end
sig { overridable.params(auth_object: T.untyped).returns(T::Array[Symbol]) }
def ransackable_scopes(auth_object = nil); end
sig { overridable.params(auth_object: T.untyped).returns(T::Array[String]) }
def ransortable_attributes(auth_object = nil); end

@mokevnin
mokevnin force-pushed the add-ransack-annotation branch from bb4c596 to dc82d61 Compare September 6, 2026 16:51
@mokevnin

mokevnin commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — overridable is in, pushed as bb4c596 just after your review landed; all four allowlist methods carry it now, and CI stayed green. Rebased on main so the branch isn't behind.

mokevnin and others added 2 commits September 19, 2026 12:42
`ransack` ships no signatures. The four allowlist class methods every host
model overrides, and `Search#result`, are the gem's whole public surface for
application code, and tapioca emits all five without a sig.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@mokevnin
mokevnin force-pushed the add-ransack-annotation branch from dc82d61 to f6d4685 Compare September 19, 2026 16:42
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