Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In ActiveRecordColumns persisted mode, remove T.nilable from reflected sigs #1937

Merged
merged 1 commit into from
Jul 4, 2024

Conversation

alex-tan
Copy link
Contributor

Motivation

We use types inheriting from ActiveModel::Type::Value where the deserialize sig says it returns T.nilable([custom type]). However when using the new dsl persisted mode, we can assume the type won't be nilable.

Implementation

Apply as_non_nilable_type to getter_type and setter_type only in persisted mode and when the column is not nullable.

Tests

I've added tests.

@alex-tan alex-tan requested a review from a team as a code owner June 20, 2024 22:34
# It's possible that when ActiveModel::Type::Value is used, the signature being reflected on in
# ActiveModelTypeHelper.type_for(type_value) may say the type can be nilable. However, if the type is
# persisted and the column is not nullable, we can assume it's not nilable.
[as_non_nilable_type(getter_type), as_non_nilable_type(setter_type)]
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wary of adding this overhead to every type by default to cover for an edge case. I'd like to understand why changing the signature of deserialize and other methods is not an acceptable solution.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure thing. So we use custom types pretty widely both for id columns and as nilable foreign keys. I'm not sure if there's a way around it for the use case where the type can be used both for nullable and non-nullable columns:

sig { params(value: T.nilable(Numeric)).returns(T.nilable(::CustomType))}
def deserialize(value)
  CustomType.new(value) if value
end

I could look at moving the call into just the branch of type_for_activerecord_value that covers this case. I think it would just mean needing to pass in @column_type_option and column into that method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(I guess just column since the other is an instance variable)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I pushed a commit showing what that might look like

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry this took so long we were away. This makes sense to me but I'd like another set of eyes before squashing the commits and merging.

Copy link
Contributor

@KaanOzkan KaanOzkan left a comment

Choose a reason for hiding this comment

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

Could you also amend the first commit so that it doesn't include the now outdated logic.

sig { params(column_type: T.untyped).returns(String) }
def type_for_activerecord_value(column_type)
sig { params(column_type: T.untyped, null: T::Boolean).returns(String) }
def type_for_activerecord_value(column_type, null:)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
def type_for_activerecord_value(column_type, null:)
def type_for_activerecord_value(column_type, column_nullability:)

Something like this will be better.

# It's possible that when ActiveModel::Type::Value is used, the signature being reflected on in
# ActiveModelTypeHelper.type_for(type_value) may say the type can be nilable. However, if the type is
# persisted and the column is not nullable, we can assume it's not nilable.
return as_non_nilable_type(base_type) if @column_type_option.persisted? && !null
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you've also contributed the EncryptedAttributeType case in line 136, this could affect that too right? So we should return non nilable types if possible in that case too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes, thanks for pointing that out 👍

@KaanOzkan KaanOzkan merged commit ee9d273 into Shopify:main Jul 4, 2024
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants