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'sinitialize
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