Introducing Socket Firewall: Free, Proactive Protection for Your Software Supply Chain.Learn More
Socket
Book a DemoInstallSign in
Socket

rails-on-sorbet

Package Overview
Dependencies
Maintainers
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rails-on-sorbet

bundlerRubygems
Version
0.2.5
Version published
Maintainers
2
Created
Source

Rails::On::Sorbet

This gem is a Rails extension that enhances sorbet support in Rails.

Installation

Install the gem and add to the application's Gemfile by executing:

bundle add rails-on-sorbet

If bundler is not being used to manage dependencies, install the gem by executing:

gem install rails-on-sorbet

It is recommended to disable strict typechecking for this gem's RBI files generated by tapioca.

This can be done by adding these lines to sorbet/tapioca/config.yml:

gem:
  typed_overrides:
    rails-on-sorbet: "false"

Usage

This gem adds additional signatures to Rails types and builtin Ruby types that make using sorbet easier in Rails projects.

It also adds some new utility types and modifies certain Rails DSLs to make it possible to strictly type methods defined through metaprogramming.

Timelike

Rails and Ruby lack a unified type for representing datetime. We have Time, DateTime and ActiveSupport::TimeWithZone which basically cover really similar use cases but have no common ancestor. This makes working with time incredibly frustrating.

We introduced a new utility type called Timelike to alleviate this burden.

Definition:

Timelike = T.type_alias { T.any(Time, DateTime, ActiveSupport::TimeWithZone) }

Map

Rails introduced two hashlike types: ActionController::Parameters, ActiveSupport::HashWithIndifferentAccess. For the most part they behave like Hash but they don't inherit from it. This causes a lot of issues in sorbet.

Because of that we introduced the Map interface. It defines all methods that are common to Hash, ActionController::Parameters and ActiveSupport::HashWithIndifferentAccess.

Right now there is a bug in sorbet that causes the typechecker to hang and crash when you include a generic interface/module in Hash, Array, Set, Range etc. So we couldn't implement Map directly in Hash :(

Instead, we introduced a casting function.

Example:

# Use the `Map` type in a signature
#
#: (Map[String, Integer]) -> void
def foo(m)
  m["foo"] #=> Integer?
end

hash = { "foo" => 4 } #: Hash[String, Integer]
m = Map(hash) #=> Map[String, Integer]
foo(m) # OK

hash = { "foo" => 4 }.with_indifferent_access # => ActiveSupport::HashWithIndifferentAccess
m = Map(hash) #=> Map[String, untyped]
foo(m) # OK

params = ActionController::Parameters.new # => ActionController::Parameters
m = Map(params) #=> Map[String, untyped]
foo(m) # OK

ActiveRecord::Base::alias_association

This gem adds a new method called alias_association on ActiveRecord classes. It lets you define aliases for getters and setters of belongs_to and has_one associations. There is also a tapioca compiler that makes sorbet aware of these aliases.

Example:

  class Foo < ApplicationRecord
    belongs_to :user
    alias_association :owner, :user
  end

  f = Foo.last
  f.owner == f.user #=> true

Rails::On::Sorbet::CurrentAttributes

This is a subclass of ActiveSupport::CurrentAttributes with tapioca/sorbet support.

New optional keyword arguments have been added to attribute:

  • type: the sorbet type of the getter/setter
  • doc: a docstring whose content will be used to generate a comment above the rbi signature created by tapioca

Example:

class Current < Rails::On::Sorbet::CurrentAttributes
  attribute :session_counter, type: T.nilable(Integer), doc: <<~DOC
    A counter that gets incremented when a new session is created.
  DOC
end

The tapioca compiler will generate an RBI file like this:

# typed: true

# DO NOT EDIT MANUALLY
# This is an autogenerated file for dynamic methods in `Current`.
# Please instead update this file by running `bin/tapioca dsl Current`.

class Current
  include RailsOnSorbetCurrentAttributeMethods
  extend RailsOnSorbetCurrentAttributeMethods

  module RailsOnSorbetCurrentAttributeMethods
    # A counter that gets incremented when a new session is created.
    sig { returns(T.nilable(::Integer)) }
    def session_counter; end

    # A counter that gets incremented when a new session is created.
    sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) }
    def session_counter=(value); end
  end
end

ActiveRecord::Base::serialize

Tapioca now has the ability to generate strictly typed and accurate getters and setters for Rails serializers.

New optional keyword arguments have been added to ActiveRecord::Base::serialize:

  • return_type: the sorbet type returned by the getter
  • setter_type: the sorbet type of the parameter of the setter
  • doc: a docstring whose content will be used to generate a comment above the rbi signature created by tapioca

If no return_type or setter_type is given in the arguments, tapioca will try to call return_type and setter_type on the coder given to the serializer.

Example:

module IntegerCoder
  class << self
    def return_type = Integer

    #: (String?) -> Integer?
    def load(val)
      val&.to_i
    end

    #: (Integer?) -> String?
    def dump(val)
      val&.to_s
    end
  end
end

class Foo < ActiveRecord::Base
  serialize :bar, coder: IntegerCoder
  serialize :baz, coder: YAML, return_type: T::Hash[String, String], doc: <<~DOC
    Additional BAR data for finalizing orders.
  DOC
end

The tapioca compiler will generate the following RBI file

# typed: true

# DO NOT EDIT MANUALLY
# This is an autogenerated file for dynamic methods in `Foo`.
# Please instead update this file by running `bin/tapioca dsl Foo`.

class Foo
  sig { returns(T.nilable(Integer)) }
  def bar; end

  sig { params(value: T.nilable(Integer)).returns(T.nilable(Integer)) }
  def bar=(value); end

  # Additional BAR data for finalizing orders.
  sig { returns(T.nilable(T::Hash[::String, ::String])) }
  def baz; end

  # Additional BAR data for finalizing orders.
  sig { params(value: T.nilable(T::Hash[::String, ::String])).returns(T.nilable(T::Hash[::String, ::String])) }
  def baz=(value); end
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/espago/rails-on-sorbet.

FAQs

Package last updated on 18 Sep 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts