如何在 Ruby 中将每个键值对创建为新对象时将哈希值分配给对象

How to assign hash values to an object while creating each key-value pair as a new object in Ruby

我有一个人对象,它从 web 服务调用(大约 400 个键值对)中获取有关此人的大量信息。散列中的每个项目都有些不同,并且在显示给用户之前必须进行不同的处理(例如时间戳值转换、nils 处理或以其他方式转换的文本)等,并且键本身在其他地方也很重要,必须'translated' 来自固定映射列表。

目前在 Rails 视图中直接访问属性的散列,然后在助手中清理 return 值,如

<%= clean_up(person.attributes["some_strangly_formatted_name"]) %>

<%= show_timestamp(person.attributes["some_nonhuman_time"]) %>

我试图弄清楚如何将这些值中的每一个动态地分配给人物对象本身,同时还将每个属性创建为不同 class(例如属性)的自己的对象将相关行为移到那里,因为辅助方法的数量和复杂性正在失控。

理想情况下,我可以调用 person.some_timestamp,它会 return 人类可读时间或 person.name 和 return 人名字符串

我试过按此处所示分配变量,但似乎必须在对象上预先定义该属性,我试图避免 400 attr_accessors 并尝试适应添加新属性的可能性 Set Attribute Dynamically of Ruby Object Having a hash, set object properties in Ruby DRY way to assign hash values to an object

无效的方法看起来像

  def process_attributes(attributes)
    attributes.each {|k,v| public_send("#{k}=", v)}
  end

我怎样才能实现这个目标?更一般地说,这种策略是否有意义?

编辑

我开始重复下面的答案,但它让我开始思考 "there's usually a built-in way to do this in Ruby/Rails",我会查看 SimpleDelgator,如果有人想尝试使用它来创建像 PersonAttributor < SimpleDelgator 这样的模型并在其中进行转换,我会赞成它。


不确定这是否是您想要的方向,但您可以使用 JSON 序列化做一些事情。如果您碰巧使用 Postgres,则可以使用 JASONB 列类型并将数据存储为 JSON ,这样可以直接查询。但是普通的序列化可能会像这样发生:

person.attributes = {'one' => 'something', 'two' => 'some_other_thing'}.to_json
=> "{\"one\":\"something\",\"two\":\"some_other_thing\"}"
person.save

JSON(person.attributes)
=> {"one"=>"something", "two"=>"some_other_thing"}

JSON(person.attributes)["one"]
=> "something"

这是一个非常简单的即兴示例。它会将所有 key/value 对放入一个列中,而不会创建大量 attr_accessors。您可以在哈希首次分配给该人时进行清理。

这不包括您将这些属性移动到它们自己的 类 中的想法。如果您希望即时创建这些对象,我不确定这样做能为您节省什么。如果您希望它们可以作为方法访问,您可以以某种方式使用 OpenStruct,这是一个:

在你的 person 模型中你可以有一个方法:

class Person < ApplicationRecord
...
def attr_object
  OpenStruct.new(self.attributes)
end
...

然后您将获得一个对象,其中所有键作为方法,您的值作为这些方法的 return...

os_obj = person.attr_object
=> #<OpenStruct one='something', two='something_else'....>

您现在有了一个对象,它为您提供方法 oneone=。您可以使用以下方法将所有转换应用于该对象...

os_obj.one
=> 'something'
os_obj.one = 'something new'
=> 'something new'

您可以创建一个继承自 Person 并创建 OpenStruct 对象的模型。它还可以通过将 OpenStruct 对象序列化为 JSON:

来存储更新的 key/value 对
person.attr_store = os_obj.to_h.to_json
"{\"one\":\"something new\"....}"

new_obj = Openstruct.new(JSON(person.attr_store))
=> #<OpenStruct one='something_new', two='something_else'....>

希望对您有所帮助。