Shopify / Shopify/tapioca

Suggestion: Treat `ActiveModel::Attribute` as non-nilable if a PresenceValidator is present

Open
#2,249 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Ruby
Stars
873
Forks
164
Avg merge
4d 27m
Merged PRs (30d)
9

Description

class Foo
  include ActiveModel::Model

  attribute :name, :string
  validates :name, presence: true
end

Under normal circumstances, Foo#name should never be nil.

Yes, it's possible to get a nil like Foo.new.name, but I'd argue that returning a type error would make sense here.

I ended up implementing this tapioca extension in my codebase:

# sorbet/tapioca/compilers/active_model_presence_validator.rb

require "active_model/attributes"
require "tapioca/dsl/compilers/active_model_attributes"

module Tapioca
  module Compilers
    module ActiveModelPresenceValidator
      def attribute_methods_for_constant
        return super unless constant.is_a?(ActiveModel::Validations::ClassMethods)

        attribute_methods = super.to_h # Convert to hash for easier manipulation

        attribute_methods.each do |method, type|
          has_unconditional_validator = constant
            .validators_on(method)
            .grep(ActiveModel::Validations::PresenceValidator)
            # Skip if any options would mean that the attribute is not always non-nilable
            .any? { |v| v.options.slice(:if, :unless, :on, :allow_nil, :allow_blank).empty? }

          next unless has_unconditional_validator

          type = as_non_nilable_type(type)
          attribute_methods[method] = type
          attribute_methods["#{method}="] = type
        end

        attribute_methods.to_a
      end
    end

    Dsl::Compilers::ActiveModelAttributes.prepend(ActiveModelPresenceValidator)
  end
end

It's hacky, but it works 😅

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with sorbet/tapioca/compilers/active_model_presence_validator.rb and the Tapioca::Dsl::Compilers::ActiveModelAttributes entry point shown in the issue. Inspect how validators and conditional options are represented, then verify generated RBI output for attributes with unconditional and conditional PresenceValidator instances. Done means unconditional presence validation produces non-nilable getter and setter types without changing conditional cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
ruby
Domain
tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.