首页 > 解决方案 > 如何实现getter setter方法来更改动态方法中的键值?

问题描述

读取一个 csv 格式的文件,并以文件名动态构造一个新的类。所以如果csv是persons.csv,ruby类应该是person,如果是places.csv,ruby类应该是places还创建用于读取和显示“csv”文件中的每个值以及csv文件第一行中的值的方法将作为函数的名称。构造一个对象数组并将每个对象与 csv 文件的行相关联。例如 csv 文件的内容可以是 name,age,city Chris,23,NYC Matt,23,SFO

我已经找到了解决这个问题的方法,并且代码可以有效地解决这个问题,我只在更新正在访问的新键值时遇到问题。我还想更新如下值:

p k.call 'name'
p k.call 'age'
p k.call 'city'
k.name = 'XYZ' # updating new key values
k.age = 25  # updating new key values
p k.call 'name'
p k.call 'age'

我在运行时收到错误:

undefined method `name' for #<Input:0x00007fb6c20d75c0>

有人可以建议,在这种情况下实现getter setter方法的正确方法是什么?

require 'csv'

class ReadCsv
  attr_accessor :arr
  def initialize(source_name)
    @klass = Class.new
    class_name = File.basename(source_name, ".csv").capitalize
    Object.const_set(class_name, @klass)
    csv_read(source_name)
    method_def
  end

  def csv_read(source_name)
    @arr = CSV.read(source_name).transpose
  end

  def method_def
    @klass_obj = @klass.new
    arr.each { |method_name, *a| @klass_obj.define_singleton_method(method_name.to_s) { a } }
  end

  def universal_attr_accessor(entity, attr_name) #change added
    definition_method_name = "define_#{entity.is_a?(Object) ? 'singleton_' : ''}method".to_sym

    entity.send(definition_method_name, "#{attr_name}=".to_sym) do |value|
      instance_variable_set("@#{attr_name}", value)
    end

    entity.send(definition_method_name, attr_name.to_sym) do
      instance_variable_get("@#{attr_name}")
    end
  end

  def call(method_name)
    @klass_obj.send(method_name)
    universal_attr_accessor(@klass_obj, method_name)
  end

end

k = ReadCsv.new('Input.csv')
p k.call 'name' 
p k.call 'age'  
p k.call 'city' . # until here code works just fine
k.name = 'XYZ' # I am not able to assign new key value for this dynamic method called at runtime.
k.age = 25
p k.call 'name'
p k.call 'age'

预期结果 :

k = ReadCsv.new('Input.csv')
p k.call 'name'  # ['Chris','Matt']
p k.call 'age'  #  [23,23]
p k.call 'city' . # ['NYC','SFO']
k.name = 'XYZ' 
k.age = 25
p k.call 'name' # ['Chris','Matt','XYZ']
p k.call 'age'  # [23,23,25]

标签: ruby

解决方案


根据@Amadan 的评论,这是您要实现的目标的准系统解决方案。它使用OpenStruct而不是自定义类。

persons =
  CSV.parse(<<~ROWS, headers: true, header_converters: :symbol)
    name,department,salary
    Bob,Engineering,1000
    Jane,Sales,2000
    John,Management,5000
  ROWS
persons =
  persons.map(&:to_h).map(&OpenStruct.method(:new))

导致:

#⇒ [#<OpenStruct name="Bob", department="Engineering", salary="1000">,
#   #<OpenStruct name="Jane", department="Sales", salary="2000">,
#   #<OpenStruct name="John", department="Management", salary="5000">]

和:

persons.first.name
#⇒ "Bob"
persons.first.name = "Mary"
#⇒ "Mary"
persons.first.name
#⇒ "Mary"

推荐阅读