Skip to content

Instantly share code, notes, and snippets.

@nashby

nashby/1.md Secret

Created April 12, 2021 06:50
Show Gist options
  • Save nashby/8cb78164132bf1bf49fcf39834ee30de to your computer and use it in GitHub Desktop.
Save nashby/8cb78164132bf1bf49fcf39834ee30de to your computer and use it in GitHub Desktop.

Ruby 3.0 changes how methods of subsclassed core classes work.

First of all you most likely shouldn’t inherit from Ruby’s core classes. But if you still want to do it, it's better to know what was changed in Ruby 3.0.

Before Ruby 3.0 return values of core classes methods like e.g String#upcase or Array#rotate were inconsistent. Here what I mean:

class Foo < Array
end

foo = Foo.new
foo.rotate.class # => Array
class Bar < String
end

bar = Bar.new
bar.upcase.class # => Bar

As you can see in the first example Foo#rotate returns the instance of Array class and Bar#upcase returns the instance of Bar class. Personally, I'd prefer if Ruby would always return an instance of subclass but if you check this discussion it becomes pretty clear that it's hard to make it work properly for all cases and it's better to always return an instance of the original class.

So that's what was done in Ruby 3.0! You can check these Pull Requests here and here with the changes. Now all String and Array methods always return instances of the original class, not the subclass.

The way I learned about this change was failing Enumerize build. Enumerize is a gem for enumerated attributes with I18n and ActiveRecord/Mongoid/MongoMapper/Sequel support. And a class that powers enumerated value is a subclass of String core class. So before you could just write something like

user.role.upcase # => 'ADMIN'

where the role is an enumerated field, you would get upcased value of Enumerize::Value instance but with Ruby 3.0 changes it becomes String instance. It means you can't use any Enumerize methods on that value anymore. It's not a big deal in terms of Enumerize usage since in most cases you don't apply any String's methods on it but would be good to make it not depend on the fact that Enumerize::Value is a subclass of Ruby's String.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment