红宝石克隆对象

时间:2018-10-12 22:32:16

标签: ruby class clone dup

我需要克隆一个现有对象并更改该克隆对象。 问题是我的更改更改了原始对象。 这是代码:

require "httparty"

class Http
  attr_accessor :options
  attr_accessor :rescue_response
  include HTTParty
  def initialize(options)
    options[:path] = '/' if options[:path].nil? == true
    options[:verify] = false
    self.options = options

    self.rescue_response = {
      :code => 500
    }
  end

  def get
    self.class.get(self.options[:path], self.options)
  end

  def post
    self.class.post(self.options[:path], self.options)
  end

  def put
    self.class.put(self.options[:path], self.options)
  end

  def delete
    self.class.put(self.options[:path], self.options)
  end

end

场景:

test = Http.new({})

test2 = test

test2.options[:path] = "www"

p test2
p test

输出:

#<Http:0x00007fbc958c5bc8 @options={:path=>"www", :verify=>false}, @rescue_response={:code=>500}>
#<Http:0x00007fbc958c5bc8 @options={:path=>"www", :verify=>false}, @rescue_response={:code=>500}>

有没有办法解决这个问题?

2 个答案:

答案 0 :(得分:2)

您想要.clone还是.dup

test2 = test.clone

但是根据您的目的,但是在这种情况下,您可能想要.clone 参见What's the difference between Ruby's dup and clone methods?

主要区别在于.clone还会复制对象单例方法和冻结状态。

在旁注中,您也可以更改

options[:path] = '/' if options[:path].nil? # you don't need "== true" 

答案 1 :(得分:2)

您甚至不需要在这里克隆,只需要创建一个新实例即可。

就在这里:

test = Http.new({})
test2 = test

您没有Http的两个实例,您只有一个。您只有两个变量指向同一个实例。

您可以改为将其更改为此,这样就不会有问题。

test = Http.new({})
test2 = Http.new({})

但是,如果您使用共享的options参数,那就是遇到问题的地方:

options = { path: nil }
test = Http.new(options)

# options has been mutated, which may be undesirable
puts options[:path] # => "/"

为避免这种“副作用”,您可以将initialize方法更改为使用选项的克隆:

def initialize(options)
  options = options.clone
  # ... do other stuff
end

您还可以使用splat运算符,它有点神秘,但可能更惯用:

def initialize(**options)
  # do stuff with options, no need to clone
end

然后您将像这样调用构造函数:

options = { path: nil }
test = Http.new(**options)
puts test.options[:path] # => "/"

# the original hasn't been mutated
puts options[:path] # => nil