【问题标题】:Merging two ruby objects合并两个红宝石对象
【发布时间】:2016-03-05 07:57:11
【问题描述】:

问题:Ruby(和/或 Rails)中是否有一种简洁的方法可以将两个对象合并在一起?

具体来说,我试图找出类似于 jQuery 的 $.extend() 方法的东西,而您传入​​的第一个对象的属性将被第二个对象覆盖。

我正在使用 Rails 3.2+ 中的无表模型。当表单提交发生时,提交的参数用于动态填充用户对象。该用户对象使用 Ruby 的 PStore 类在页面请求之间持久化,将对象编组为平面文件,以便将来轻松检索。

相关代码:

module Itc
  class User
    include ActiveModel::Validations
    include ActiveModel::Conversion
    include ActionView::Helpers
    extend ActiveModel::Naming

    def set_properties( properties = {} )
      properties.each { |k, v|
        class_eval("attr_reader :#{k.to_sym}")
        self.instance_variable_set("@#{k}", v)
      } unless properties.nil?
    end
  end
end

用户对象的创建过程如下:

user = Itc.User.new( params[:user] )
user.save()

上面的save()方法不是ActiveRecord的save方法,而是我写的通过PStore做持久化的方法。

如果我加载了一个用户对象,并且我有一个表单提交,我想做这样的事情:

merged = existingUserObject.merge(User.new(params[:user])

并将merged 的结果设为用户对象,仅更新表单提交中更改的属性。

如果有人对总体上更好的方法有任何想法,我会全力以赴。

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    Hash#merge 不是您要找的吗? http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-merge。看来你可以走了

    merged = existingUserObject.merge(params[:user])
    

    我认为您不需要创建一个全新的 User 对象,因为可能这就是 existingUserObject 并且您只想覆盖一些属性。

    【讨论】:

    • 这应该是答案,因为它不需要任何额外的类创建,并且哈希合并功能已经融入 Ruby ?
    • 是的,我在谷歌上搜索“相当于 jquery 扩展的 ruby​​”,但从未注意到这一点,因为它不是选定的答案;-(
    【解决方案2】:

    通过捎带哈希的行为来做到这一点。创建一个为 new() 方法的参数获取哈希的类,然后创建一个 to_h 方法,该方法接受一个对象并从实例的当前状态生成一个哈希:

    class Foo
      def initialize(params={})
        @a = params[:a]
        @b = params[:b]
      end
    
      def to_h
        {
          a: @a,
          b: @b
        }
      end
    end
    
    instance_a = Foo.new(a: 1, b:2)
    instance_b = Foo.new(a: 1, b:3)
    
    instance_c = Foo.new(instance_a.to_h.merge(instance_b.to_h))
    

    将其转储到 IRB:

    irb(main):001:0> class Foo
    irb(main):002:1>   def initialize(params={})
    irb(main):003:2>     @a = params[:a]
    irb(main):004:2>     @b = params[:b]
    irb(main):005:2>   end
    irb(main):006:1> 
    irb(main):007:1*   def to_h
    irb(main):008:2>     {
    irb(main):009:3*       a: @a,
    irb(main):010:3*       b: @b
    irb(main):011:3>     }
    irb(main):012:2>   end
    irb(main):013:1> end
    nil
    irb(main):014:0> 
    irb(main):015:0* instance_a = Foo.new(a: 1, b:2)
    #<Foo:0x1009cfd00
        @a = 1,
        @b = 2
    >
    irb(main):016:0> instance_b = Foo.new(a: 1, b:3)
    #<Foo:0x1009ead08
        @a = 1,
        @b = 3
    >
    irb(main):017:0> 
    irb(main):018:0* instance_c = Foo.new(instance_a.to_h.merge(instance_b.to_h))
    #<Foo:0x100a06c60
        @a = 1,
        @b = 3
    >
    

    【讨论】:

    • 锡人,捎带散列的合并行为是要走的路。唯一需要注意的是我需要动态设置类属性和属性 getter/setter,所以我的初始化方法如下所示:def initialize( properties = {} ) properties.each { |k, v| class_eval("attr_reader :#{k.to_sym}") self.instance_variable_set("@#{k}", v) } unless properties.nil? end
    • 如果您事先不知道您的实例值将是什么,这会起作用。就个人而言,我更喜欢对我的对象包含的内容进行更多控制,但这就是 Ruby 的美妙之处,如果我们将其编码为完全动态的,我很乐意效劳。您可能还想为新对象考虑对 Hash 进行子类化。 Hash 有一些不错的特性,您可以自动继承,而不必担心创建自己的 to_h
    【解决方案3】:

    这就是我如何用我的模型之一实现类似的事情

      # merge other_config.attrs into self.attrs, only for nil attrs
      def merge!(other_object)
        return if other_object.nil? || other_object.class != self.class
        self.assign_attributes(self.attributes.slice ('id').merge(other_object.attributes.slice!('id')){|key, oldval, newval|
          oldval.nil? ? newval: oldval
        })
      end
    

    【讨论】:

      猜你喜欢
      • 2019-03-18
      • 1970-01-01
      • 2015-08-06
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-09
      相关资源
      最近更新 更多