-
Notifications
You must be signed in to change notification settings - Fork 41
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
Annotate Active Support string extensions #185
Conversation
rbi/annotations/activesupport.rbi
Outdated
sig { params(separator: T.nilable(String), preserve_case: T.untyped, locale: T.nilable(Symbol)).returns(T.self_type) } | ||
def parameterize(separator: "-", preserve_case: false, locale: nil); end | ||
|
||
sig { params(count: T.nilable(Integer), locale: T.nilable(Symbol)).returns(T.self_type) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like pluralize method does a reassignment here to satisfy usages like 'ley'.pluralize(:es)
so this won't work https://sorbet.run/#%23%20typed%3A%20true%0Aclass%20String%0A%20%20extend%20T%3A%3ASig%0A%20%20sig%20%7B%20params%28count%3A%20T.nilable%28Integer%29%2C%20locale%3A%20T.nilable%28Symbol%29%29.returns%28T.self_type%29%20%7D%0A%20%20def%20pluralize%28count%20%3D%20nil%2C%20locale%20%3D%20%3Aen%29%0A%20%20%20%20%22a%22%0A%20%20end%0Aend%0A%0A'len'.pluralize%28%3Aes%29
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doh! Thanks, will fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you so much! Just a few comments.
def html_safe; end | ||
|
||
sig { params(capitalize: T.untyped, keep_id_suffix: T.untyped).returns(T.self_type) } | ||
def humanize(capitalize: true, keep_id_suffix: false); end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
capitalize
looks like a boolean, is there a reason it's T.untyped
here? https://github.com/rails/rails/blob/7-1-stable/activesupport/lib/active_support/core_ext/string/inflections.rb#L262
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Always a tough balance to strike when adding types to untyped code :)
I opted to err on the side of flexibility here since any truthy value will work here but happy to change to boolean if you want a more strict type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not familiar with its usages so if you think being flexible is good for cases where an object is supplied I'm okay with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My $0.02: We should enforce booleans, as it improves the clarity of the API.
It's easy enough for callers to toss in a !!x
if they need.
def first(limit = 1); end | ||
|
||
sig { params(separate_class_name_and_id_with_underscore: T.untyped).returns(T.self_type) } | ||
def foreign_key(separate_class_name_and_id_with_underscore = true); end |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I gave this a really quick view, will look in more detail later, but here are my preliminary comments
Thanks @paracycle! Addressed your comments so far :) |
Rebased and resolved conflicts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @bdewater, thanks for putting these together!
I'm just rebasing and making some small tweaks, and I'll merge this shortly.
rbi/annotations/activesupport.rbi
Outdated
sig { returns(Module) } | ||
def constantize; end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constantize
can return anything, because anything can be assigned to constants:
ABC = 123
"ABC".constantize # => 123
sig { returns(Module) } | |
def constantize; end | |
sig { returns(T.anything) } | |
def constantize; end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, nice catch.
rbi/annotations/activesupport.rbi
Outdated
sig { params(separator: T.nilable(String), preserve_case: T.untyped, locale: T.nilable(Symbol)).returns(String) } | ||
def parameterize(separator: "-", preserve_case: false, locale: nil); end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The separator
can't be nil:
irb(main):014> "abc def ghi".parameterize
=> "abc-def-ghi"
irb(main):015> "abc def ghi".parameterize(separator: nil)
/Users/alex/.gem/ruby/3.3.0/gems/activesupport-7.0.8/lib/active_support/inflector/transliterate.rb:126:in `gsub!': no implicit conversion of nil into String (TypeError)
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
sig { params(separator: T.nilable(String), preserve_case: T.untyped, locale: T.nilable(Symbol)).returns(String) } | |
def parameterize(separator: "-", preserve_case: false, locale: nil); end | |
sig { params(separator: String, preserve_case: T.untyped, locale: T.nilable(Symbol)).returns(String) } | |
def parameterize(separator: "-", preserve_case: false, locale: nil); end |
sig { params(count: T.nilable(T.any(Integer, Symbol)), locale: T.nilable(Symbol)).returns(String) } | ||
def pluralize(count = nil, locale = :en); end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woah this is a weird sig (the implementation itself is strange, because the count
can be the locale
if you only pass 1 arg).
rbi/annotations/activesupport.rbi
Outdated
sig { params(locale: T.nilable(Symbol)).returns(String) } | ||
def singularize(locale = :en); end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The locale
can't be nil
:
irb(main):023> "cars".singularize
=> "car"
irb(main):024> "cars".singularize(nil)
/Users/alex/.gem/ruby/3.3.0/gems/i18n-1.14.1/lib/i18n/locale/fallbacks.rb:61:in `[]': nil is not a valid locale (I18n::InvalidLocale)
old_raise.call(*args, **kwargs)
^^^^^^^^^^^^^^^
sig { params(locale: T.nilable(Symbol)).returns(String) } | |
def singularize(locale = :en); end | |
sig { params(locale: T.nilable(Symbol)).returns(String) } | |
def singularize(locale = :en); end |
rbi/annotations/activesupport.rbi
Outdated
sig { params(patterns: T.nilable(T.any(Regexp, String))).returns(String) } | ||
def remove(*patterns); end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't be nil
:
"abc".remove(nil)
/Users/alex/.gem/ruby/3.3.0/gems/activesupport-7.0.8/lib/active_support/core_ext/string/filters.rb:42:in `gsub!': wrong argument type nil (expected Regexp) (TypeError)
gsub! pattern, ""
^^^^^^^^^^^
sig { params(patterns: T.nilable(T.any(Regexp, String))).returns(String) } | |
def remove(*patterns); end | |
sig { params(patterns: T.any(Regexp, String)).returns(String) } | |
def remove(*patterns); end |
Likewise for #remove!
rbi/annotations/activesupport.rbi
Outdated
sig { returns(T.nilable(Date)) } | ||
def to_date; end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method raises instead of returning nil
:
irb(main):027> "abc".to_date
/Users/alex/.gem/ruby/3.3.0/gems/activesupport-7.0.8/lib/active_support/core_ext/string/conversions.rb:48:in `parse': invalid date (Date::Error)
::Date.parse(self, false) unless blank?
^^^^^^^^^^^
sig { returns(T.nilable(Date)) } | |
def to_date; end | |
sig { returns(Date) } | |
def to_date; end |
rbi/annotations/activesupport.rbi
Outdated
sig { returns(T.nilable(DateTime)) } | ||
def to_datetime; end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method raises instead of returning nil
:
irb(main):028> "abc".to_datetime
/Users/alex/.gem/ruby/3.3.0/gems/activesupport-7.0.8/lib/active_support/core_ext/string/conversions.rb:58:in `parse': invalid date (Date::Error)
::DateTime.parse(self, false) unless blank?
^^^^^^^^^^^
sig { returns(T.nilable(DateTime)) } | |
def to_datetime; end | |
sig { returns(Date) } | |
def to_datetime; end |
sig { params(form: T.nilable(Symbol)).returns(T.nilable(Time)) } | ||
def to_time(form = :local); end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interestingly, this one does return nil
on error, unlike #to_date
and #to_datetime
.
"abc".to_time # => nil
def html_safe; end | ||
|
||
sig { params(capitalize: T.untyped, keep_id_suffix: T.untyped).returns(T.self_type) } | ||
def humanize(capitalize: true, keep_id_suffix: false); end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My $0.02: We should enforce booleans, as it improves the clarity of the API.
It's easy enough for callers to toss in a !!x
if they need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd selfishly like it if we returned a union e.g.T.any(Date, Date::Error)
to signify the exception possibility but I can imagine dealing with this would be unpopular among Ruby devs.
@KaanOzkan do you mean you'd (in theory) like Rails to return an error rather than raise? Or is this actually the valid Sorbet way to write that a method may raise? |
Yes the method will have the return instead of raise to make that work which makes what I said even less feasible. Example: https://blog.jez.io/union-types-checked-exceptions/#:~:text=Here%E2%80%99s%20how%20we%E2%80%99d%20write%20that%20in%20Sorbet%3A |
Thanks for getting it over the finish line @amomchilov ! |
Completes String extensions for #137
Type of Change
Changes