{"id":14981520,"url":"https://github.com/figma/sorbet-rails","last_synced_at":"2026-02-16T20:32:57.593Z","repository":{"id":40009576,"uuid":"433183951","full_name":"figma/sorbet-rails","owner":"figma","description":"Forked from https://github.com/chanzuckerberg/sorbet-rails","archived":false,"fork":false,"pushed_at":"2024-02-27T21:08:13.000Z","size":1836,"stargazers_count":4,"open_issues_count":29,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-21T02:49:20.689Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/figma.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-29T20:16:10.000Z","updated_at":"2025-03-12T18:56:21.000Z","dependencies_parsed_at":"2024-09-24T07:01:33.014Z","dependency_job_id":"55a47550-9416-49eb-a956-abc69f3ef7e0","html_url":"https://github.com/figma/sorbet-rails","commit_stats":{"total_commits":468,"total_committers":60,"mean_commits":7.8,"dds":0.6452991452991452,"last_synced_commit":"3ec211bd102666de2ac833c107eb9b8839776668"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/figma/sorbet-rails","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/figma%2Fsorbet-rails","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/figma%2Fsorbet-rails/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/figma%2Fsorbet-rails/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/figma%2Fsorbet-rails/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/figma","download_url":"https://codeload.github.com/figma/sorbet-rails/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/figma%2Fsorbet-rails/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29517613,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T18:37:19.720Z","status":"ssl_error","status_checked_at":"2026-02-16T18:36:46.920Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T14:03:45.227Z","updated_at":"2026-02-16T20:32:57.570Z","avatar_url":"https://github.com/figma.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sorbet-rails\n[![Gem Version](https://badge.fury.io/rb/sorbet-rails.svg)](https://badge.fury.io/rb/sorbet-rails)\n[![Build Status](https://github.com/chanzuckerberg/sorbet-rails/actions/workflows/ci-master.yml/badge.svg)](https://github.com/chanzuckerberg/sorbet-rails)\n[![codecov](https://codecov.io/gh/chanzuckerberg/sorbet-rails/branch/master/graph/badge.svg)](https://codecov.io/gh/chanzuckerberg/sorbet-rails)\n\nA set of tools to make the [Sorbet](https://sorbet.org) typechecker work with Ruby on Rails seamlessly.\n\nThis gem adds a few Rake tasks to generate Ruby Interface (RBI) files for dynamic methods generated by Rails. It also includes signatures for related Rails classes. The RBI files are added to a `sorbet/rails-rbi/` folder.\n\n`sorbet-rails` supports Rails 5+ or later.\n\n**Notice**: we no longer support Rails 4.2. [Version 0.5.6](https://github.com/chanzuckerberg/sorbet-rails/releases/tag/v0.5.6) is the last version supporting Rails 4.2.\n\n## Initial Setup\n\n1. Follow the steps [here](https://sorbet.org/docs/adopting) to set up the latest version of Sorbet, up to being able to run `srb tc`.\n\n2. Add `sorbet-rails` to your Gemfile and install them with Bundler.\n\n```\n# -- Gemfile --\ngem 'sorbet-rails'\n```\n\nAdd `sorbet-rails` to the [`:default` group](https://bundler.io/v2.0/guides/groups.html) because of \"Features Provided at Runtime\" below.\n\n```sh\n❯ bundle install\n```\n\n3. Generate RBI files for your routes, models, etc\n```sh\n❯ bundle exec rake rails_rbi:routes\n❯ bundle exec rake rails_rbi:models\n❯ bundle exec rake rails_rbi:helpers\n❯ bundle exec rake rails_rbi:mailers\n❯ bundle exec rake rails_rbi:jobs\n❯ bundle exec rake rails_rbi:custom\n\n# or run them all at once\n❯ bundle exec rake rails_rbi:all\n```\n\n4. Update hidden-definition files and automatically upgrade each file's typecheck level:\n```sh\n❯ bundle exec srb rbi hidden-definitions\n❯ bundle exec srb rbi suggest-typed\n```\nBecause we've generated RBI files for routes, models, and helpers, a lot more files should be typecheckable now. Many methods in `hidden.rbi` may be removed because they are now typed.\n\n## Static RBI Generation\n\n### Models\n\nThis Rake task generates RBI files for all models in the Rails application (all descendants of `ActiveRecord::Base`):\n```sh\n❯ bundle exec rake rails_rbi:models\n```\nYou can also regenerate RBI files for specific models. To accommodate for STI, this will generate rbi for all the subclasses of the models included.\n```sh\n❯ bundle exec rake rails_rbi:models[ModelName,AnotherOne,...]\n```\nThe generation task currently creates the following signatures:\n- Column getters \u0026 setters\n- Associations getters \u0026 setters\n- Enum values, checkers \u0026 scopes\n- Named scopes\n- Model `Relation` class\n  - Scopes on `Relation`, but not [class methods](https://github.com/chanzuckerberg/sorbet-rails/issues/104#issuecomment-521763909)\n\nIt is possible to add custom RBI generation logic for your custom module or gems via the plugin system. Check out the [plugins section](#extending-model-generation-task-with-custom-plugins) below if you are interested.\n\nWe also add following methods to make type-checking easier:\n- [`find_n`, `first_n`, `last_n`](https://github.com/chanzuckerberg/sorbet-rails#find-first-and-last)\n- [`pluck_to_tstruct`](#pluck_to_tstruct-instead-of-pluck)\n- [`typed_enum`](#typed_enum-instead-of-enum)\n- [`Model::RelationType`](#relationtype-alias)\n\n#### `pluck_to_tstruct` instead of `pluck`\n\nThe [`pluck` method](https://apidock.com/rails/ActiveRecord/Calculations/pluck) in Rails is a performant way to query value without instantiating ActiveRecord objects. However, it doesn't have any type information: it doesn't have type information (or name) of the attribute plucked. Sorbet-rails provides `pluck_to_tstruct` method as a replacement that returns an array of `T::Struct` instead. The attributes plucked is based on props defined in the `T::Struct`\n\n```ruby\n# -- API\nArel.pluck_to_tstruct(TA[ \u003cTStructSubClass\u003e ].new)\n\n# -- example\nclass WizardStruct \u003c T::Struct\n  const :name, String\n  const :house, T.nilable(String)\nend\n\nWizard.pluck_to_tstruct(TA[WizardStruct].new)  # T::Array[WizardStruct]\nWizard.all.pluck_to_tstruct(TA[WizardStruct].new)  # T::Array[WizardStruct]\n```\n\nYou can also pass a keyword argument called `associations` that represents a mapping of a `T::Struct`'s keys to an associated table's columns.\n```ruby\n# -- API\nArel.pluck_to_tstruct(TA[ \u003cTStructSubclass\u003e ].new, associations: \u003c Hash\u003cSymbol, String\u003e \u003e)\n\n# -- example\nclass WizardWithWandStruct \u003c T::Struct\n  const :name, String\n  const :house, T.nilable(String)\n  const :wand_wood_type, String\nend\n\nWizard.joins(:wand).pluck_to_tstruct(\n  TA[WizardWithWandStruct].new,\n  associations: { wand_wood_type: \"wands.wood_type\" }\n)\nWizard.all.joins(:wand).pluck_to_tstruct(\n  TA[WizardWithWandStruct].new,\n  associations: { wand_wood_type: \"wands.wood_type\" }\n)\n````\n\nThis method is based on [pluck_to_hash](https://github.com/girishso/pluck_to_hash) gem.\n\n#### `typed_enum` instead of `enum`\n\nIf you use [Rails `enum`](https://guides.rubyonrails.org/active_record_querying.html#enums), `sorbet-rails` will generate a corresponding `T::Enum`. It will also include getters, setters, and scope methods in the rbi file it generates.\n\nie.\n\n```ruby\n  enum house: {\n    Gryffindor: 0,\n    Hufflepuff: 1,\n    Ravenclaw: 2,\n    Slytherin: 3,\n  }\n```\n\nWill generate this enum:\n\n```ruby\nclass Wizard\n  class House \u003c T::Enum\n    enums do\n      Gryffindor = new('Gryffindor')\n      Hufflepuff = new('Hufflepuff')\n      Ravenclaw = new('Ravenclaw')\n      Slytherin = new('Slytherin')\n    end\n  end\nend\n```\n\nAnd these methods:\n\n```ruby\n  sig { returns(T.nilable(String)) }\n  def house; end\n\n  sig { params(value: T.nilable(T.any(Integer, String, Symbol))).void }\n  def house=(value); end\n\n  sig { returns(T.nilable(Wizard::House)) }\n  def typed_house; end\n\n  sig { params(value: T.nilable(Wizard::House)).void }\n  def typed_house=(value); end\n```\n\nAlternatively, you can replace your call to `enum` with `typed_enum`. This will hide the Rails methods (`house`) from Sorbet static-check (they are still usable in un-checked files).\n\n```ruby\n  typed_enum house: {\n    Gryffindor: 0,\n    Hufflepuff: 1,\n    Ravenclaw: 2,\n    Slytherin: 3,\n  }\n```\n\nGenerates only typed enum setter \u0026 getter:\n\n```ruby\n  sig { returns(T.nilable(Wizard::House)) }\n  def typed_house; end\n\n  sig { params(value: T.nilable(Wizard::House)).void }\n  def typed_house=(value); end\n```\n\n#### `RelationType` alias\n\nThere are several kinds of relations of a model: `User::ActiveRecord_Relation`, `User::ActiveRecord_AssociationRelation` and `User::ActiveRecord_Associations_CollectionProxy`. Usually the code may need just any relation type. We add a `Model::RelationType` type alias for every model to use it.\n\n```ruby\nclass User\n  RelationType = T.type_alias do\n    T.any(\n      User::ActiveRecord_Relation,\n      User::ActiveRecord_AssociationRelation,\n      User::ActiveRecord_Associations_CollectionProxy\n    )\n  end\nend\n```\n\n### Controllers\n```sh\n❯ bundle exec  rake rails_rbi:custom\n```\n\n`sorbet-rails` adds `TypedParams` to extract typed controller parameters.\n\nTypedParams takes a parameter definition, which is a subclass of `T::Struct` and coerces the `params` object into an instance of that subclass using [sorbet-coerce](https://github.com/chanzuckerberg/sorbet-coerce):\n```ruby\nclass MyCoolController \u003c ApplicationController\n  class MyActionParams \u003c T::Struct\n    const :id, T.nilable(Integer)\n    const :show, T.nilable(T::Boolean)\n    const :wands, T::Array[Integer]\n  end\n  sig { void }\n  def my_action\n    typed_params = TypedParams[MyActionParams].new.extract!(params)\n    # T.reveal_type(typed_params) =\u003e MyActionParams\n    # T.reveal_type(typed_params.show) =\u003e T.nilable(T::Boolean)\n  end\nend\n```\nIf it fails to coerce the params into the right type, an `ActionController::BadRequest` exception will be raised with the coercion context for debugging.\n\nNote: The API `TypedParams[...].new.extract!` may seem verbose, but necessary to support this feature. Ideally, the API can be simply `TypedParams.extract!(...)`. However, `sorbet` [doesn't support](http://github.com/sorbet/sorbet/issues/62) defining a method that accept a type and return an instance of the type. If this feature is supported by `sorbet` in the future, it will be easy to codemod to be `TypedParams.extract(...)!` part from your code.\nNote: [`require_typed` and `fetch_typed`](https://github.com/chanzuckerberg/sorbet-rails/blob/v0.5.9.1/README.md) are deprecated in favor of `TypedParams`. They will be removed in v0.7.\n\n### Routes\n\nThis Rake task generates an RBI file defining `_path` and `_url` methods for all named routes in `routes.rb`:\n```sh\n❯ bundle exec rake rails_rbi:routes\n```\n\n### Helpers\n\nThis Rake task generates a `helpers.rbi` file that includes a basic module definition which includes the `Kernel` module and `ActionView::Helpers`, to allow for some basic Ruby methods to be used in helpers without Sorbet complaining.\n\n```sh\n❯ bundle exec rake rails_rbi:helpers\n```\n\nIf you have additional modules that are included in all your helpers and you want `helpers.rbi` to reflect this, you can configure it:\n\n```ruby\n# -- config/initializers/sorbet_rails.rb\nSorbetRails.configure do |config|\n  config.extra_helper_includes = ['ApplicationHelper', 'Devise::Controllers::Helpers']\nend\n```\n\n### Mailers\n\nThis Rake task generates RBI files for all mailer classes in the Rails application (all descendants of `ActionMailer::Base`)\n```sh\n❯ bundle exec rake rails_rbi:mailers\n```\n\nSince mailing action methods are based on instance methods defined in a mailer class, the signature of a mailing action method will be dependent on the signature the instance method has\n- If there is a (sorbet) sig written for the instance method, it generates a matching sig for the mailing action method\n- If not, all the params in the mailing action method will be T.untyped.\n- For return type though, the mailing action method will return `ActionMailer::MessageDelivery` instead of the return type of the instance method.\n\n### Jobs\n\nThis Rake task generates RBI files for all jobs classes in the Rails application (all descendants of `ActiveJob::Base`).\n```sh\n❯ bundle exec rake rails_rbi:jobs\n```\n\nIt will generate `perform_later` and `perform_now` methods for\nthe job classes matching the signature of the `perform` method of the job. If there is a (sorbet) sig written for `perform`, it will use the same sig for `perform_*` methods.\n\n## Runtime Features\n\nIn addition to features provided by the static generator, `sorbet-rails` can\nprovide additional features when `require`d. This is why the installation\ninstructions specify that `sorbet-rails` should be placed in the [`:default`\ngroup](https://bundler.io/v2.0/guides/groups.html) of the `Gemfile`, not a\nspecific environment group (eg. `development` only).\n\n- Model: The gem provides some helper method to a model to make type-checking easier:\n  - `find_n`, `first_n`, `last_n`\n  - `where_missing`\n  - `pluck_to_tstruct`\n  - `typed_enum`\n\n- Model Relation:\n  - Make relation classes public. By default, relation classes like `User::ActiveRecord_Relation`, `User::ActiveRecord_AssociationRelation` are private\n  - Add type alias, eg `Model::RelationType`, to represents any type of relation of a model.\n\n- Controller: use `TypedParams` to convert controller parameters to a typed structure\n\nIn addition to `require`ing `sorbet-rails`, you must also run\n`bundle exec rake rails_rbi:custom`, which will produce the RBI for these runtime features.\n\nDiscussion:\n[#211](https://github.com/chanzuckerberg/sorbet-rails/issues/211),\n[#214](https://github.com/chanzuckerberg/sorbet-rails/pull/214#issuecomment-546505485)\n\n## Tips \u0026 Tricks\n\n### Look for `# typed: ignore` files\n\nBecause Sorbet's initial setup tries to flag files at whichever typecheck level generates 0 errors, there may be files in your repository that are `# typed: ignore`. This is because sometimes Rails allows very dynamic code that Sorbet does not believe it can typecheck.\n\nIt is worth going through the list of files that is ignored and resolve them (and auto upgrade the types of other files; see [initial setup](#initial-setup) above). Usually this will make many more files able to be typechecked.\n\n### Overriding generated signatures if needed\n\n`sorbet-rails` relies on Rails reflection to generate signatures. There are features this gem doesn't support yet such as [attribute custom types](https://github.com/chanzuckerberg/sorbet-rails/issues/16). The gem also doesn't know the signature of any methods you have overridden. However, it is possible to override the signatures that `sorbet-rails` generates.\n\nFor example, here is how to override the signature for a method in a model:\n\n```ruby\n# -- app/models/model_name.rbi --\n\n# typed: strong\nclass ModelName\n  sig { returns(T::Hash[...]) }\n  def field_name; end\n\n  sig { params(obj: T::Hash[....]).void }\n  def field_name=(obj); end\nend\n```\n\n### Replace `Rails.application.routes.url_helpers`\n\nWhen using url helpers like _url or _path methods outside of a controller,\nwe usually have to add `include Rails.application.routes.url_helpers` in the class.\nHowever, Sorbet does not allow code that\n[includes dynamic module](https://sorbet.org/docs/error-reference#4002).\nSorbet Rails provides a drop-in replacement module for the dynamic url_helpers module, called `GeneratedUrlHelpers`.\nFollowing code change should resolve the sorbet type-check error:\n\n```ruby\nclass MyClass\n-  include Rails.application.routes.url_helpers\n+  include GeneratedUrlHelpers\nend\n```\n\n### `find`, `first` and `last`\n\nThese 3 methods can either return a single nilable record or an array of records. Sorbet does not allow us to define multiple signatures for a function ([except stdlib](https://github.com/chanzuckerberg/sorbet-rails/issues/18)). It doesn't support defining one function signature that has varying returning value depending on the input parameter type. We opt to define the most commonly used signature for these methods, and monkey-patch new functions for the secondary use case.\n\nIn short:\n- Use `find`, `first` and `last` to fetch a single record.\n- Use `find_n`, `first_n`, `last_n` to fetch an array of records.\n\n### `find_by_\u003cattributes\u003e`, `\u003cattribute\u003e_changed?`, etc.\n\nRails supports dynamic methods based on attribute names, such as `find_by_\u003cattribute\u003e`, `\u003cattribute\u003e_changed?`, etc. They all have static counterparts. Instead of generating all possible dynamic methods that Rails support, we recommend to use of the static version of these methods instead (also recommended by RuboCop).\n\nFollowing are the list of attribute dynamic methods and their static counterparts. The static methods have proper signatures:\n- `find_by_\u003cattributes\u003e` -\u003e `find_by(\u003cattributes\u003e)`\n- `find_by_\u003cattributes\u003e!` -\u003e `find_by!(\u003cattributes\u003e)`\n- `\u003cattribute\u003e_changed?` -\u003e `attribute_changed?(\u003cattribute\u003e)`\n- `\u003cattribute\u003e_was` -\u003e `attribute_was(\u003cattribute\u003e)`\n- `saved_change_to_\u003cattribute\u003e?` -\u003e `saved_change_to_attribute?(\u003cattribute\u003e)`\n- `\u003cattribute\u003e_before_type_cast` -\u003e `read_attribute_before_type_cast(\u003cattribute\u003e)`\n- `will_save_change_to_\u003cattribute\u003e?` -\u003e `will_save_change_to_attribute?(\u003cattribute\u003e)`\n\n### `after_commit` and other callbacks\n\nConsider converting `after_commit` callbacks to use instance method functions. Sorbet doesn't support binding an optional block with a different context. Because of this, when using a callback with a custom block, the block is evaluated in the wrong context (Class-level context). Refer to [this page](https://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html) for a full list of callbacks available in Rails.\n\nBefore:\n```ruby\nafter_commit do ... end\n```\nAfter:\n```ruby\nafter_commit :after_commit\ndef after_commit\n  ...\nend\n```\n\nIf you wanted to make these changes using [Codemod](https://github.com/facebook/codemod), try these commands:\n```shell\n# from methods like after_commit do \u003c...\u003e end\n❯ codemod -d app/models/ --extensions rb \\\n  '(\\s*)(before|after)_(validation|save|create|commit|find|initialize|destroy) do' \\\n  '\\1\\2_\\3 :\\2_\\3\\n\\1def \\2_\\3'\n\n# from methods like after_commit { \u003c...\u003e }\n❯ codemod -d app/models/ --extensions rb \\\n  '(\\s*)(before|after)_(validation|save|create|commit|find|initialize|destroy) \\{ (.*) \\}' \\\n  '\\1\\2_\\3 :\\2_\\3\\n\\1def \\2_\\3\\n\\1\\1\\4\\n\\1end'\n```\nNote that Codemod's preview may show that the indentation is off, but it works.\n\n### `unscoped` with a block\n\nThe [`unscoped` method](https://apidock.com/rails/ActiveRecord/Scoping/Default/ClassMethods/unscoped) returns a `Relation` when no block is provided. When a block is provided, `unscoped` calls the block and returns its result, which could be any type.\n\n`sorbet-rails` chooses to define `unscoped` as returning a `Relation` because it's more common and more useful. If you want to use a block, either override the `unscoped` definition, or replace:\n```ruby\nModel.unscoped do … end\n```\nwith:\n```ruby\nModel.unscoped.scoping do … end\n```\n\n### `select` with a block\n\nThe [`select` method](https://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select) in Rails has two modes: it can be given a list of symbols, in which case rails will only return the given columns from the database, or it can be given a block, in which case it acts like [`Enumerable.select`](https://ruby-doc.org/core-2.6.4/Enumerable.html) and returns an array.\n\nWe have chosen to support `select` with a block as the primary use case, since it is a more prevalent use of this method. We provide `select_columns` as an alternative if you want to select a list of columns.\n\n```\nModel.select { |record| record.id \u003e 1} # valid, returns an array\nModel.select_columns(:id, :name) # valid, returns an association\n\nModel.select(:id, :name) # sorbet error, use `select_column` instead\n```\n### `flatten` an array of relation\n\nWhen you call `flatten` on an array of ActiveRecord::Relation, sorbet [doesn't recognize](https://github.com/sorbet/sorbet/issues/2767) that it will flatten the relation and return an array of model. The work around is to call `to_a` on the relation first.\n\n```ruby\n# doesn't work\narr = [Model.recent, Model.old].flatten # T::Array[Model::ActiveRecord_Relation]\n\n# work around\narr = [Model.recent, Model.old].map(\u0026:to_a).flatten # T::Array[Model]\n```\n\n`flat_map` has a similar issue.\n\n```ruby\nfoo.bars.flat_map { |b| b.scope } # T::Array[T.untyped]\n\nfoo.bars.flat_map { |b| b.scope.to_a } # T::Array[Scope]\n```\n\n### Avoid `and_call_original` in rspecs\n\nIf you run into the following issue when running rspec, it's likely because you're using `expect(:method_name).and_call_original` to mock a method in RSpec. We've found the double mock doesn't interact well with Sorbet's sig wrapper and caused flaky spec. The spec should be rewritten to expect the outcome of the method instead. (It still works with `expect(:method_name)` and `expect(:method_name).and_return(...)`\n\n```\n     RuntimeError:\n       `sig` not present for method `:method_name` but you're trying to run it anyways. This should\n       only be executed if you used `alias_method` to grab a handle to a method after `sig`ing it, but\n       that clearly isn't what you are doing. Maybe look to see if an exception was thrown in your `sig`\n       lambda or somehow else your `sig` wasn't actually applied to the method. Contact #dev-productivity\n       if you're really stuck.\n```\n\n### `where.missing` does not exist on `ActiveRecord::Relation`  ###\n\nThe [`where` method](https://apidock.com/rails/ActiveRecord/QueryMethods/where) in Rails has two modes: it can be passed no arguments where it will return an `ActiveRecord::QueryMethods::WhereChain` object, or when given arguments, returns an `ActiveRecord::Relation` object. By default we've opted to support `where` with arguments and have provided a `where_missing` method to avoid conflicts during static typing.\n\n```\nWizard.where.missing(:wand) # sorbet error, use `where_missing` instead\n\nWizard.where_missing(:wand) # valid, returns a relation\n```\n\n**Note:** `where.missing` / `where_missing` are only available in Rails 6.1 or above\n\n## Extending RBI Generation logic\n\n### Extending Model Generation Task with Custom Plugins\n\n`sorbet-rails` support a customizable plugin system that you can use to generate additional RBI for each model. This will be useful to generate RBI for methods dynamically added by gems or private concerns. If you write plugins for public gems, please feel free to contribute it to this repo.\n\n#### Defining a Custom `ModelPlugin`\n\nA custom plugin should be a subclass of `SorbetRails::ModelPlugins::Base`. Each plugin would implement a `generate(root)` method that generate additional rbi for the model.\n\nAt a high level, here is the structure of a plugin:\n```ruby\n# -- lib/my_custom_plugin.rb\nclass MyCustomPlugin \u003c SorbetRails::ModelPlugins::Base\n  sig { override.params(root: Parlour::RbiGenerator::Namespace).void }\n  def generate(root)\n    # TODO: implement the generation logic\n    # You can use @model_class and @available_classes here\n  end\nend\n```\nDuring the generation phase, the system will create a new instance of the plugin, with the `model_class` to be generated and a set of all `available_classes` (available models) detected in the Rails App.\n\nWe use [Parlour gem](https://github.com/AaronC81/parlour) to generate the RBI for a model. Please check out [Parlour wiki](https://github.com/AaronC81/parlour/wiki/Using-and-creating-RBI-objects) for guide to add RBI, eg create a new module, class, or method in the generated file.\n\nAt a high level, you'd usually want to create a model-scoped module for your methods, and include or extend it in the base model class. The generation logic usually looks like this:\n```ruby\n  def generate(root)\n    # Make sure this is only run for relevant class\n    return unless @model_class.include?(CustomModule)\n\n    custom_module_name = self.model_module_name(\"CustomModule\")\n    custom_module_rbi = root.create_module(custom_module_name)\n\n    # here we re-create the model class!\n    model_class_rbi = root.create_class(self.model_class_name)\n    model_class_rbi.create_extend(custom_module_name)\n\n    # then create custom methods, constants, etc. for this module.\n    custom_module_rbi.create_method(...)\n\n    # this is allowed but not recommended, because it limit the ability to override the method.\n    model_class_rbi.create_method(...)\n  end\n```\nNotice that we re-create `model_class_rbi` here. Parlour's [ConflictResolver](https://github.com/AaronC81/parlour/wiki/Internals#overall-flow) will merge the classes or modules with the same name together to generate 1 beautiful RBI file. It'll also flag and skip if any method is created multiple times with conflict signatures. Check-out useful predefined module names \u0026 helper methods in [model_utils](https://github.com/chanzuckerberg/sorbet-rails/blob/master/lib/sorbet-rails/model_utils.rb).\n\nIt is also allowed to put methods into a model class directly. However, it is not recommended because it'll be harder to override the method. `sorbet` will enforce that the overriding method match the signature generated. It also makes the generated RBI file less modularized.\n\nHowever, sometimes this is required to make `sorbet` recognize the signature. This is the case for class methods added by `ActiveRecord::Concerns`. Because `ActiveSupport::Concern` class methods will be inserted to the class directly, you need to also put the sig in the model class rbi directly.\n\nIt is also recommended to check if the generated methods are detected by `sorbet` as a gem method (in `sorbet/rbi/gem/gem_name.rbi`) or hidden methods (in `sorbet/rbi/hidden-definitions/hidden.rbi`). If so, you may need to re-run `srb rbi hidden-definition` or put method in the model class directly.\n\nCheck out the [plugins](https://github.com/chanzuckerberg/sorbet-rails/tree/master/lib/sorbet-rails/model_plugins) written for `sorbet-rails`'s own model RBI generation logic for examples.\n\n#### Registering new plugins\nYou can register your plugins in an initializer:\n```ruby\n# -- config/initializers/sorbet_rails.rb\nSorbetRails::ModelRbiFormatter.register_plugin(MyCustomPlugin)\n```\n\n#### Enabling built-in plugins\n\nsorbet-rails comes with a handful of gem plugins that can be enabled in an initializer. You can pass enabled gem plugins to `config.enabled_gem_plugins`, like so:\n\n```ruby\n# -- config/initializers/sorbet_rails.rb\nSorbetRails.configure do |config|\n  config.enabled_gem_plugins = [\n    :kaminari\n  ]\nend\n```\n\nThese are the currently-supported gems and their symbolized names:\n\n| Gem          | Symbol         |\n|--------------|----------------|\n| [ElasticSearch]| `:elastic_search` |\n| [FriendlyId] | `:friendly_id` |\n| [Kaminari]   | `:kaminari`    |\n| [PgSearch]   | `:pg_search`   |\n| [Shrine]     | `:shrine`      |\n| [active_flag]| `:active_flag` |\n| [Paperclip]  | `:paperclip` |\n| [AttrJson]  | `:attr_json` |\n| [FlagShihTzu]  | `:flag_shih_tzu` |\n| [AASM]  | `:aasm` |\n| [Money-Rails]  | `:money_rails` |\n\nYou can also configure the core model plugins if needed. The default plugins are defined in the [config](https://github.com/chanzuckerberg/sorbet-rails/blob/master/lib/sorbet-rails/config.rb). For the full list of plugin symbols, check out [here](https://github.com/chanzuckerberg/sorbet-rails/blob/master/lib/sorbet-rails/model_plugins/plugins.rb).\n\n\n[Kaminari]: https://github.com/kaminari/kaminari\n[PgSearch]: https://github.com/Casecommons/pg_search\n[FriendlyId]: https://github.com/norman/friendly_id\n[ElasticSearch]: https://github.com/elastic/elasticsearch-rails\n[Shrine]: https://github.com/shrinerb/shrine\n[active_flag]: https://github.com/kenn/active_flag\n[Paperclip]: https://github.com/thoughtbot/paperclip\n[AttrJson]: https://github.com/jrochkind/attr_json\n[FlagShihTzu]: https://github.com/pboling/flag_shih_tzu\n[AASM]: https://github.com/aasm/aasm\n[Money-Rails]: https://github.com/RubyMoney/money-rails\n\n### Customize Generation Class\n\nFor mailer and job rbi generation, you can customize the logic by\nsetting the generation class in the config:\n\n```ruby\nSorbetRails.configure do |config|\n  config.job_generator_class = CustomJobRbiGenerator\n  config.mailer_generator_class = CustomMailerRbiGenerator\nend\n```\n\nThe custom generator can subclass the [provided generators](lib/bundled_rbi/customizabel_rbi_formatter.rbi) and override the populate_rbi method. For example:\n\n```ruby\nclass CustomJobRbiGenerator \u003c SorbetRails::JobRbiFormatter\n  def populate_rbi\n    rbi_generator.root.add_comment(\"== Custom Generator ==\")\n    super\n  end\nend\n```\n\n## Contributing\n\nContributions and ideas are welcome! Please see [our contributing guide](CONTRIBUTING.md) and don't hesitate to open an issue or send a pull request to improve the functionality of this gem.\n\nThis project adheres to the Contributor Covenant [code of conduct](https://github.com/chanzuckerberg/.github/tree/master/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to opensource@chanzuckerberg.com.\n\n## License\n\n[MIT](https://github.com/chanzuckerberg/sorbet-rails/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffigma%2Fsorbet-rails","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffigma%2Fsorbet-rails","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffigma%2Fsorbet-rails/lists"}