Project

General

Profile

Actions

Feature #21387

open

Proposal to add Data#[]

Added by ybiquitous (Masafumi Koba) 4 days ago. Updated 1 day ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:122350]

Description

Proposal

I propose to add a new instance method #[] to the Data class, similar to Struct#[].

If writing the method signature in RBS, it would be like this:

class Data
  def []: (name: String | Symbol) -> untyped
end

Requirements:

  • Data#[] accepts a member name as a Symbol or String, e.g., data[:id] == data["id"].
  • Data#[] returns a value associated with the given name, e.g., data[:id] == data.id.
  • Data#[] raises NameError if the given name is not one of the members, e.g., data[:invalid].

Note: Please assume that data = Data.define(:id).new(id: 100) is given in the examples above.

Motivation

In Active Support Core Extensions of Rails, I found a use case that Data#[] would be helpful with Enumerable#pluck.

Please look at this example:

# data_test.rb
require "bundler/inline"

gemfile do
  source "https://rubygems.org"
  gem "activesupport", "8.0.2"
end

require "active_support/core_ext/enumerable"

StructPerson = Struct.new(:name, :age)
DataPerson = Data.define(:name, :age)

struct_people = [
  StructPerson.new("Bob", 25),
]
puts "Struct:"
puts "=> #{struct_people.pluck(:name)}"

data_people = [
  DataPerson.new("Charlie", 35),
]
puts "Data:"
begin
  puts "=> #{data_people.pluck(:name)}"
rescue => e
  puts e.detailed_message(syntax_suggest: true)
end

Running this script outputs below:

$ ruby data_test.rb
Struct:
=> ["Bob"]
Data:
undefined method '[]' for an instance of DataPerson (NoMethodError)

      map { |element| element[key] }
                             ^^^^^

Note: This output resulted on ruby 3.4.4 (2025-05-14 revision a38531fd3f) +PRISM [arm64-darwin24].

The error reason is that the Enumerable#pluck extension expects all the elements in the array to respond to [].

See also the pluck code here:
https://github.com/rails/rails/blob/v8.0.2/activesupport/lib/active_support/core_ext/enumerable.rb#L145-L152

If Data#[] was implemented as follows,

# data_patch.rb
class Data
  def [](name)
    unless members.include?(name.to_sym)
      raise NameError, "no member '#{name}' in data"
    end
    public_send(name)
  end
end

The script is successful:

$ ruby -r ./data_patch.rb data_test.rb
Struct:
=> ["Bob"]
Data:
=> ["Charlie"]

Ref:

Although Enumerable#pluck is just an example, I guess that there would be other cases where Data objects should respond to [].

Reasoning

From the long discussion in #16122 that introduced Data, I understand that Data#[] was rejected in #16122#note-28 because [] was like Enumerable.

I also agree with rejecting Data#each since Data is not an Enumerable, but [] seems acceptable to me.

Reasons:

  • [] is sometimes used for non-container objects, such as ActiveRecord::AttributeMethods#[].
  • Considering Data#to_h is provided, it seems reasonable that we could access a Data member's value through [].
  • From the similarity between Struct and Data, some people might expect Hash-like accessing via [].
  • Unless [] is provided, we have to call public_send or convert to a hash via to_h when we want to get member values with names. It'd be inefficient and easy to make mistakes.

Let me show an example:

Person = Data.define(:name, :age)
charlie = Person.new("Charlie", 35)

member_names_from_user_input = ["age", "hash"] # "hash" is a malicious input.

# Using #public_send
member_names_from_user_input.map { charlie.public_send(it) }
#=> [35, -1363726049241242821]

# Using #to_h and Hash#fetch with string keys
member_names_from_user_input.map { charlie.to_h.fetch(it) }
#=> key not found: "age" (KeyError)

# Using #to_h and Hash#fetch with symbol keys
member_names_from_user_input.map { charlie.to_h.fetch(it.to_sym) }
#=> key not found: :hash (KeyError)

# Using #[]
member_names_from_user_input.map { charlie[it] }
#=> no member 'hash' in data (NameError)

The last example is most elegant for the same goal. That's why I propose to add Data#[].

However, there might be downsides that I have overlooked, so I'd appreciate it if you could let me know.

Thanks.

Actions

Also available in: Atom PDF

Like0
Like1Like0Like0Like1Like0