Home > Software engineering >  How create Ruby Class with same object id
How create Ruby Class with same object id

Time:07-12

I need to create a class where if the attribute value is the same it does not generate a new object id, example:

result:

described_class.new('01201201202')

<PixKey:0x00007eff5eab1ff8 @key="01201201202">

if i run it again with the same value it should keep the same object id

0x00007eff5eab1ff8

is similar behavior with the symbol

test:

describe '#==' do
  let(:cpf)   { described_class.new('01201201202') }

  it 'verifies the key equality' do
    expect(cpf).to eq described_class.new('01201201202')
  end
 end

Running the test shows an error, because the obejct id changes:

expected: #<PixKey:0x00007eff5eab1ff8 @key="01201201202">
got: #<PixKey:0x00007eff5eab2070 @key="01201201202">

Class:

class PixKey
  def init(key)
    @key = key
  end
end

CodePudding user response:

The other answers are fine, but they are a little more verbose than needed and they use class variables, which I find to be a confusing concept because of how they are shared among various classes.

class PixKey
  @instances = {}
  def self.new(id)
    @instances[id] ||= super(id)
  end
  def initialize(id)
    @key = id
  end
end

p PixKey.new(1)
p PixKey.new(2)
p PixKey.new(2)
p PixKey.new(1)

CodePudding user response:

Create a class method to create instances and have it look up a hash.

class PixKey
   @@instances = {}
   def PixKey.create(id)
       if not @@instances.has_key?(id) 
           @@instances[id] = PixKey.new(id)
       end
       return @@instances[id]
   end
   def initialize(id)
     @key = id
   end
end

a = PixKey.new(123)
b = PixKey.new(123)
c = PixKey.create(123)
d = PixKey.create(123)

puts a
puts b
puts c
puts d

Output:

#<PixKey:0x000000010bc39900>
#<PixKey:0x000000010bc38078>
#<PixKey:0x000000010bc33eb0>
#<PixKey:0x000000010bc33eb0>

Notice the last two instances created with the PixKey.create(id) method return the same instance.

CodePudding user response:

Note that Ruby's new method is just a method on Class and can be overridden like any other. The docs describe the default implementation.

Calls allocate to create a new object of class's class, then invokes that object's initialize method, passing it args. This is the method that ends up getting called whenever an object is constructed using .new.

So, if you want to keep the .new syntax and still get the same objects back, we can override new on the class and call super. This is exactly what OscarRyz' answer does, just with .new and super rather than a separate helper function.

class PixKey
   @@instances = {}
   def PixKey.new(id)
       if not @@instances.has_key?(id)
           @@instances[id] = super(id)
       end
       return @@instances[id]
   end
   def initialize(id)
     @key = id
   end
end

a = PixKey.new(123)
b = PixKey.new(123)

puts a
puts b

CodePudding user response:

Running the test shows an error, because the object id changes

Not quite. It shows an error because the objects are not equal. And the error message prints both objects including their id. But the object id is not what's causing the test to fail.

I need to create a class where if the attribute value is the same it does not generate a new object id

That would probably work, but you're likely approaching the problem from the wrong side. In Ruby, equality doesn't mean object identity. Two objects can be equal without being the same object, e.g.

a = 'foo'
b = 'foo'

a.object_id == b.object_id
#=> false

a == b
#=> true

There's no need to tinker with object ids to get your test passing. You just have to implement a custom == method, e.g.:

class PixKey
  attr_reader :key

  def initialize(key)   # <- not "init"
    @key = key
  end

  def ==(other)
    self.class == other.class && self.key == other.key
  end
end

The == method checks if both objects have the same class (i.e. if both are PixKey instances) and if their key's are equal.

This gives:

a = PixKey.new('01201201202')
b = PixKey.new('01201201202')

a == b
#=> true
  • Related