Cloning a module and isolating it from the clone

I’m trying to find a way to create a copy of a module and then sever all
ties to the original. I’ve tried clone and dup, but if anything (e.g
class variable) changes in one then it also changes in the other. The
result I’m trying to achieve is the same as if I had copied the module
source code and renamed the module. Now I have two independent modules
with identical functionality. Any ideas on how to do this without
manually copying code?

Thanks,
Earle

Earle C. wrote:

I’m trying to find a way to create a copy of a module and then sever all
ties to the original. I’ve tried clone and dup, but if anything (e.g
class variable) changes in one then it also changes in the other. The
result I’m trying to achieve is the same as if I had copied the module
source code and renamed the module. Now I have two independent modules
with identical functionality. Any ideas on how to do this without
manually copying code?

Thanks,
Earle

module A
end

B = A.clone

A.const_set(“MyConstant”, 20)
p A.constants
p B.constants
puts A::MyConstant
puts B::MyConstant

–output:–
[“MyConstant”]
[]
20
r1test.rb:10: uninitialized constant B::MyConstant (NameError)

new_meth = %q{
def A.sayhi
puts ‘hi’
end
}

A.module_eval(new_meth)
A.sayhi
B.sayhi

–output:–
hi
r1test.rb:19: undefined method `sayhi’ for B:Module (NoMethodError)

7stud – wrote:

Earle C. wrote:

I’ve tried clone and dup, but if anything (e.g
class variable) changes in one then it also changes in the other.

Whoops. I see what you mean.

Did you find a solution?

Earle C. wrote:

I’ve tried clone and dup, but if anything (e.g
class variable) changes in one then it also changes in the other.

Whoops. I see what you mean.

Earle C. wrote:

The
result I’m trying to achieve is the same as if I had copied the module
source code and renamed the module.

I think stud number 7 was onto something. How about this:

CommonDef = %q{
def self.hello
puts “hello from #{self}”
end
}

module A
module_eval CommonDef
end

module B
module_eval CommonDef
end

A.hello
B.hello

module B
def self.hello
puts “different hello”
end
end

A.hello
B.hello

===>

hello from A
hello from B
hello from A
different hello


Seems to work right.

best,
Dan

Daniel L. wrote:

Earle C. wrote:

The
result I’m trying to achieve is the same as if I had copied the module
source code and renamed the module.

I think stud number 7 was onto something. How about this:

CommonDef = %q{
def self.hello
puts “hello from #{self}”
end
}

module A
module_eval CommonDef
end

module B
module_eval CommonDef
end

A.hello
B.hello

module B
def self.hello
puts “different hello”
end
end

A.hello
B.hello

===>

hello from A
hello from B
hello from A
different hello


Seems to work right.

best,
Dan

As far as I can tell, your code is no different than this:

module A
def self.hello
puts “hello from #{self}”
end
end

module B
def self.hello
puts “hello from #{self}”
end
end

A.hello
B.hello

module B
def self.hello
puts “different hello”
end
end

A.hello
B.hello

  1. You never cloned A. All you did was create two separate modules
    which have similar methods.

  2. Then you manually overwrote the method in B to do something else.

I don’t see how that addresses anything the op’s question. The problem
has to do with the fact that clone makes a shallow copy. In other
words, clone copies references to objects, so you end up with two
references to the same object. Therefore, a cloned object has
references to the same values as the original object.

However, I can’t figure out a way to make a ‘deep copy’ where the object
that a reference refers to is also copied. I tried deleting all the
class variables in the cloned module and then adding them back in with
new values–but they still refer to the same objects as the original
module, which is puzzling.

module A
@@num = 10
@@arr = [1, 2, 3]

def A.get_num
@@num
end

def A.set_num(x)
@@num = x
end
end

puts A.get_num

new_meth = %q{
def B.remove(arr)
arr.each do |name|
remove_class_variable(name.to_sym)
end
end
}

B = A.clone
B.module_eval(new_meth)

class_vars = B.class_variables
B.remove(class_vars)

puts “class variables in B:”
p B.class_variables

class_vars.each do |name|
B.module_eval("#{name} = 0")
end

B.set_num(“hello”)
puts ‘After cloning:’
puts A.get_num
puts B.get_num

A.set_num(“goodbye”)
puts A.get_num
puts B.get_num

–output:–
10
class variables in B:
[]
After cloning:
hello
hello
goodbye
goodbye

The Ruby Way says there is a hack to make deep copies using Marshal, but
it doesn’t work for me:

module A
@@num = 10
@@arr = [1, 2, 3]

def A.get_num
@@num
end

def A.set_num(x)
@@num = x
end
end

puts A.get_num

B = Marshal.load(Marshal.dump(A))
B.set_num(30)
puts “After deep copying:”
puts A.get_num
puts B.get_num

–output:–
10
After deep copying:
30
30

7stud – wrote:

However, I can’t figure out a way to make a ‘deep copy’ where the object
that a reference refers to is also copied. I tried deleting all the
class variables in the cloned module and then adding them back in with
new values–but they still refer to the same objects as the original
module, which is puzzling.

module A
@@num = 10
@@arr = [1, 2, 3]

def A.get_num
@@num
end

def A.set_num(x)
@@num = x
end
end

puts A.get_num

new_meth = %q{
def B.remove(arr)
arr.each do |name|
remove_class_variable(name.to_sym)
end
end
}

B = A.clone
B.module_eval(new_meth)

class_vars = B.class_variables
B.remove(class_vars)

puts “class variables in B:”
p B.class_variables

class_vars.each do |name|
B.module_eval("#{name} = 0")
end

B.set_num(“hello”)
puts ‘After cloning:’
puts A.get_num
puts B.get_num

A.set_num(“goodbye”)
puts A.get_num
puts B.get_num

–output:–
10
class variables in B:
[]
After cloning:
hello
hello
goodbye
goodbye

I see now why that doesn’t work. Even though the code changes the class
variables in B at the module scope, inside B’s methods the class
variables still refer to the same objects in A. This demonstrates that:

test = %q{
def B.get_my_num
@@num #this is the @@num in B at module scope that was erased and
replaced
end
}

A.set_num(35.2)
B.module_eval(test)
puts B.get_num #retrieves the @@num inside get_num which is still a
reference to objects in A
puts B.get_my_num

–output:–
35.2
0

7stud – wrote:

Daniel L. wrote:

Earle C. wrote:

The
result I’m trying to achieve is the same as if I had copied the module
source code and renamed the module.

I think stud number 7 was onto something. How about this:

CommonDef = %q{
def self.hello
puts “hello from #{self}”
end
}

module A
module_eval CommonDef
end

module B
module_eval CommonDef
end

A.hello
B.hello

module B
def self.hello
puts “different hello”
end
end

A.hello
B.hello

===>

hello from A
hello from B
hello from A
different hello


Seems to work right.

best,
Dan

As far as I can tell, your code is no different than this:

module A
def self.hello
puts “hello from #{self}”
end
end

module B
def self.hello
puts “hello from #{self}”
end
end

A.hello
B.hello

module B
def self.hello
puts “different hello”
end
end

A.hello
B.hello

  1. You never cloned A. All you did was create two separate modules
    which have similar methods.

  2. Then you manually overwrote the method in B to do something else.

I don’t see how that addresses anything the op’s question. The problem
has to do with the fact that clone makes a shallow copy. In other
words, clone copies references to objects, so you end up with two
references to the same object. Therefore, a cloned object has
references to the same values as the original object.

However, I can’t figure out a way to make a ‘deep copy’ where the object
that a reference refers to is also copied. I tried deleting all the
class variables in the cloned module and then adding them back in with
new values–but they still refer to the same objects as the original
module, which is puzzling.

module A
@@num = 10
@@arr = [1, 2, 3]

def A.get_num
@@num
end

def A.set_num(x)
@@num = x
end
end

puts A.get_num

new_meth = %q{
def B.remove(arr)
arr.each do |name|
remove_class_variable(name.to_sym)
end
end
}

B = A.clone
B.module_eval(new_meth)

class_vars = B.class_variables
B.remove(class_vars)

puts “class variables in B:”
p B.class_variables

class_vars.each do |name|
B.module_eval("#{name} = 0")
end

B.set_num(“hello”)
puts ‘After cloning:’
puts A.get_num
puts B.get_num

A.set_num(“goodbye”)
puts A.get_num
puts B.get_num

–output:–
10
class variables in B:
[]
After cloning:
hello
hello
goodbye
goodbye

The Ruby Way says there is a hack to make deep copies using Marshal, but
it doesn’t work for me:

module A
@@num = 10
@@arr = [1, 2, 3]

def A.get_num
@@num
end

def A.set_num(x)
@@num = x
end
end

puts A.get_num

B = Marshal.load(Marshal.dump(A))
B.set_num(30)
puts “After deep copying:”
puts A.get_num
puts B.get_num

–output:–
10
After deep copying:
30
30

I’m trying a different approach than I was initially, but I get the same
result. Instead of cloning the module, I’m generating each copy on the
fly.

So instead of this:

module A
@@str = ‘hello’

def self.str
@@str
end

def self.str=(s)
@@str = s
end

class Test
def self.bye
puts “#{self}: bye”
end
end
end

B = A.clone

puts “A: #{A.str}”
puts “B: #{B.str}”

B.str = ‘test’

puts “A: #{A.str}”
puts “B: #{B.str}”

— output —
A: hello
B: hello
A: test
B: test

which causes @@str in module B to change if I change it in module A, I’m
now doing this:


def define_module
Module.new do
@@str = ‘hello’
def self.str
@@str
end
def self.str=(s)
@@str = s
end
const_set(:Test, Class.new do
def self.bye
puts “#{self}: bye”
end
end)
end
end

A = define_module
B = define_module

puts “A: #{A.str}”
puts “B: #{B.str}”

B.str = ‘test’

puts “A: #{A.str}”
puts “B: #{B.str}”

— output —
A: hello
B: hello
A: test
B: test

Somehow the class variables are still shared between the modules.

Earle

7stud – wrote:

I see now why that doesn’t work. Even though the code changes the class
variables in B at the module scope, inside B’s methods the class
variables still refer to the same objects in A. This demonstrates that:

test = %q{
def B.get_my_num
@@num #this is the @@num in B at module scope that was erased and
replaced
end
}

A.set_num(35.2)
B.module_eval(test)
puts B.get_num #retrieves the @@num inside get_num which is still a
reference to objects in A
puts B.get_my_num

–output:–
35.2
0

Or, maybe what is really happening is that module B has references to
the methods in module A. So, when you call B.get_num, you are really
calling A.get_num, which then returns A’s class variable @@num.

7stud – wrote:

  1. You never cloned A. All you did was create two separate modules
    which have similar methods.

Identical methods in fact. Earle said that he wanted identical
implementations with only one piece of source code. That’s the first
part of my post where A and B have an identical definition but are
completely distinct modules.

  1. Then you manually overwrote the method in B to do something else.

This was just to demonstrate that in this implementation, you can change
something in B without it affecting A. Which it didn’t. Class variable
changes don’t affect the other module either.

I’m not sure what other requirements than these Earle had.

best,
Dan

“7” == 7stud ← [email protected]> writes:

7> Or, maybe what is really happening is that module B has references to
7> the methods in module A. So, when you call B.get_num, you are really
7> calling A.get_num, which then returns A’s class variable @@num.

It’s a bug for both 1.8 and 1.9. Try to send it to ruby-bug.

Guy Decoux