Reason #167 •
June 16th, 2026
Brackets are methods
In Ruby, executing array[i] or hash[key] is actually syntactic sugar for array.[](i) and hash.[](key). In the same way, array[i] = value is syntactic sugar for array.[]=(i, value). This means that we can easily define our own [] and []= methods on custom classes to provide similar behavior.
Let's use it to implement a case-insensitive hash to handle HTTP headers:
Ruby
class Headers
def initialize(initial_headers = {})
@data = initial_headers.transform_keys(&:downcase)
end
def [](key)
@data[key.downcase]
end
def []=(key, value)
@data[key.downcase] = value
end
end
headers = Headers.new("Content-Type" => "application/json")
headers["Content-Type"] # => "application/json"
headers["content-type"] # => "application/json"
headers["Accept"] = "text/html"
headers["accept"] # => "text/html"
headers["authorization"] = "Bearer token"
headers["Authorization"] # => "Bearer token"
Of course, we can also define brackets as class methods, a pattern commonly used to provide a more concise and "literal-like" syntax for constructing objects:
Ruby
class Headers
def self.[](initial_headers = {})
new(initial_headers)
end
end
headers = Headers["Content-Type" => "application/json"]
headers["Content-Type"] # => "application/json"
headers["content-type"] # => "application/json"
Concise, straightforward and absolutely lovely ❤️
History
Bracket access as method dispatch has been part of Ruby since its inception.