# Bugs in Facets Cloneable?

**URL:** <https://www.ruby-forum.com/t/bugs-in-facets-cloneable/126833>\
**Category:** Ruby\
**Created:** [January 22, 2008, 5:10pm UTC](https://www.ruby-forum.com/t/bugs-in-facets-cloneable/126833 "2008-01-22T17:10:43Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![sevensoft](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f9ae1b/32.png) [@sevensoft](https://www.ruby-forum.com/u/sevensoft)\
**Post date:** [January 22, 2008, 5:10pm UTC](https://www.ruby-forum.com/t/bugs-in-facets-cloneable/126833/1 "2008-01-22T17:10:43Z")

</div>

I was just looking at the Cloneable module from the Facets’ library,  
because it’s useful, and noticed a couple of possible bugs. Is my  
assessment of this correct?

module Cloneable  
def clone  
sibling = self.class.new  
instance\_variables.each do |ivar|  
value = self.instance\_variable\_get(ivar)  
sibling.instance\_variable\_set(ivar, value.dup) #rake\_dup)  
end  
sibling  
end  
alias\_method :dup, :clone  
end

Question 1: Shouldn’t #clone use self.class.allocate instead of  
self.class.new? Better yet, shouldn’t the superclass’s dup be called  
somehow?

Question 2: Shouldn’t #clone and #dup be implemented differently, so  
that  
#clone can freeze the newly created object?

–Ken

---

<div class="post-metadata">

**Author:** ![sevensoft](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f9ae1b/32.png) [@sevensoft](https://www.ruby-forum.com/u/sevensoft)\
**Post date:** [January 22, 2008, 11:15pm UTC](https://www.ruby-forum.com/t/bugs-in-facets-cloneable/126833/2 "2008-01-22T23:15:46Z")

</div>

On Jan 22, 11:09 am, Ken B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> end
> 
> ```
> 
> #clone can freeze the newly created object?  
> Hmmm… some good questions. This bit of code came from Rake a long  
> time ago. I never really analysed it, and just accepted it at face  
> value. I suspect the thing to do these days is to use #allocate and  
> #initialize\_copy instead.

I point out, if anyone is wondering, the difference between this and  
the built-in clone/dup is that the above dups the instance vars.

The difference between the two being that #dup carries over the  
tainted state while #clone carries over the tainted and frozen states  
(correct?). Since we are duplicating the instance vars here, there is  
no reason to consider taintedness. But frozen should still apply  
(yes?).

Could anyone else with a bit more assuredness about Ruby’s behavior  
confirm this or offer the proper perspective if it is wrong?

Thanks,  
T.

---

<div class="post-metadata">

**Author:** ![sevensoft](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f9ae1b/32.png) [@sevensoft](https://www.ruby-forum.com/u/sevensoft)\
**Post date:** [January 23, 2008, 1:40am UTC](https://www.ruby-forum.com/t/bugs-in-facets-cloneable/126833/3 "2008-01-23T01:40:30Z")

</div>

On Tue, 22 Jan 2008 17:14:55 -0500, Trans wrote:

> > ```
> > sibling.instance_variable_set(ivar, value.dup) #rake_dup)
> > 
> > ```
> > 
> > Question 2: Shouldn’t #clone and #dup be implemented differently, so  
> > The difference between the two being that #dup carries over the tainted  
> > state while #clone carries over the tainted and frozen states  
> > (correct?). Since we are duplicating the instance vars here, there is no  
> > reason to consider taintedness. But frozen should still apply (yes?).

Basically, I think it should be possible for an object to encapsulate  
(and hide) member variables that should be copied (not shared) when the  
enclosing object is copied. Thus, a semi-deep copying version of #clone  
and #dup are in order. That seems to be what Cloneable is about.

Thanks for pointing out #initialize\_copy. I didn’t know it existed. Now  
I  
see it in PickAxe, but I think it’s role has been greatly trivialized.  
Pickaxe only mentions it with regard to C extensions, empathetically  
says  
that it’s only for C extensions, and completely overlooks the idea that  
an object might be hiding other kinds of information that should be  
copied, not shared between duplicates.

Might I suggest that instead of overriding #dup and #clone at all,  
Cloneable should just provide the following implementation for  
#initialize\_copy, then we can make #dup and #clone will both behave  
properly by virtue of the fact that they descend from Object#dup and  
Object#clone:

module Cloneable  
def initialize\_copy sibling  
#first duplicate my superclass’ state. Note that if it’s duplicating  
#instance variables, this will be overwritten, but this is important  
#because we could be dealing with a C extension with state hidden  
from  
#the Ruby interpreter  
super

```
#we want to know if we're being dup'ed or clone'd, because we want 

```

to  
#preserve the state of our internals the same way our state is being  
#preserved.  
#(If we can’t figure it out, we’ll just use #dup)  
operation=caller.find{|x| x !~ /`initialize_copy'/}. match(/`(dup|clone)’/)[1] or :dup

```
sibling.instance_variables.each do |ivar|
  value = sibling.instance_variable_get(ivar)

  #set my instance variable to be a #dup or #clone
  #or my sibling, depending on what's happening to me right now
  instance_variable_set(ivar, value.send(operation))
end

```

end  
end

This will make the following test case work:

require ‘test/unit’

class Foo  
include Cloneable  
def initialize  
@bar=[]  
end  
def bar\_id  
@bar.object\_id  
end  
end

class TestCloneable \< Test::Unit::TestCase  
def test\_dup  
a=Foo.new  
b=a.dup  
assert\_not\_equal a.bar\_id,b.bar\_id

```
a.taint
b=a.dup
assert b.tainted?, "b should be tainted"

a.freeze
b=a.dup
assert !b.frozen?, "b should not be frozen"

```

end  
def test\_clone  
a=Foo.new  
b=a.clone  
assert\_not\_equal a.bar\_id,b.bar\_id

```
a.taint
b=a.dup
assert b.tainted?, "b should be tainted"

a.freeze
b=a.clone
assert b.frozen?, "b should be frozen"

```

end  
end

---

<div class="post-metadata">

**Author:** ![sevensoft](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f9ae1b/32.png) [@sevensoft](https://www.ruby-forum.com/u/sevensoft)\
**Post date:** [January 23, 2008, 11:08pm UTC](https://www.ruby-forum.com/t/bugs-in-facets-cloneable/126833/4 "2008-01-23T23:08:41Z")

</div>

On Jan 22, 7:39 pm, Ken B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > > ```
> > > value = self.instance_variable_get(ivar)
> > > 
> > > ```
> 
> Thanks for pointing out #initialize\_copy. I didn’t know it existed. Now I  
> Object#clone:
> 
> module Cloneable  
> def initialize\_copy sibling  
> #first duplicate my superclass’ state. Note that if it’s duplicating  
> #instance variables, this will be overwritten, but this is important  
> #because we could be dealing with a C extension with state hidden from  
> #the Ruby interpreter  
> super

Nice. Makes sense to me. I will work on this a bit and incorporate –  
with due credit.

Thanks Ken,  
T.

---

<div class="post-metadata">

**Author:** ![sevensoft](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f9ae1b/32.png) [@sevensoft](https://www.ruby-forum.com/u/sevensoft)\
**Post date:** [February 6, 2008, 3:53pm UTC](https://www.ruby-forum.com/t/bugs-in-facets-cloneable/126833/5 "2008-02-06T15:53:14Z")

</div>

On Jan 22, 7:39 pm, Ken B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> copied, not shared between duplicates.  
> #instance variables, this will be overwritten, but this is important
> 
> ```
> sibling.instance_variables.each do |ivar|
> value = sibling.instance_variable_get(ivar)
> 
> #set my instance variable to be a #dup or #clone
> #or my sibling, depending on what's happening to me right now
> instance_variable_set(ivar, value.send(operation))
> end
> 
> ```
> 
> end  
> end

Thanks for this Ken. I’ve adopted this code as given --I could not  
think of better way to determine if dup or clone was being used. If  
anyone knows of a more robust way to determine dup vs. clone please  
let me know.

Thanks,  
T.

---

<div class="post-metadata">

**Author:** ![sevensoft](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/f9ae1b/32.png) [@sevensoft](https://www.ruby-forum.com/u/sevensoft)\
**Post date:** [February 6, 2008, 4:14pm UTC](https://www.ruby-forum.com/t/bugs-in-facets-cloneable/126833/6 "2008-02-06T16:14:29Z")

</div>

On Feb 6, 2008, at 8:52 AM, Trans wrote:

> > Thanks for pointing out #initialize\_copy. I didn’t know it existed.  
> > Now I  
> > see it in PickAxe, but I think it’s role has been greatly  
> > trivialized.  
> > Pickaxe only mentions it with regard to C extensions,  
> > empathetically says  
> > that it’s only for C extensions, and completely overlooks the idea  
> > that  
> > an object might be hiding other kinds of information that should be  
> > copied, not shared between duplicates.

Yes, that was an oversight, and one that I’ll fix in the new edition.

Dave
