Box brackets in Ruby class declarations
posted by Ayush Newatia
27 December, 2024
If you’ve ever used Rails, you’ll almost certainly have created a database migration and seen the below syntax in the class declaration:
class CreateUserTable < ActiveRecord::Migration[7.1]
def change
# ...
end
end
I’d never understood how the [7.1]
in the superclass definition worked, and never really questioned it to be honest. I thought it was a fancy Ruby language feature that I didn’t need to bother myself with at the moment.
While looking through the code for async-rest
, I came across the same syntax.
I’m attempting to build a client for Paddle to use in Scattergun using async-rest
, so I needed to understand what that syntax was actually doing.
It’s such a typically elegant mechanism. The box brackets aren’t a language feature, but a class level method which returns a class.
class SomeClass
def self.[]
return SomeOtherClass
end
end
Going back to the Active Record example:
class CreateUserTable < ActiveRecord::Migration[7.1]
def change
# ...
end
end
The above is functionally equivalent to:
class CreateUserTable < ActiveRecord::Migration::Compatibility::V7_1
def change
# ...
end
end
Run ActiveRecord::Migration[7.1]
in a Rails console and see that it returns a class. It’s just like calling a method!
Here’s the method definition: https://github.com/rails/rails/blob/af9ecc7a35dcec5c744353d9494d92709f6e1381/activerecord/lib/active_record/migration.rb#L630.
In the case of async-rest
, it’s a really elegant way to set the representation format (JSON, form etc.) for a resource. The method definition shows how it injects the wrapper class into a new class definition and then returns that.
That enables us to write code like:
module Paddle
class PaymentMethod < Async::REST::Representation[Async::REST::Wrapper::JSON]
# ...
end
end
It’s makes things so clear to a reader!