Cloning a module and isolating it from the clone

E

Earle Clubb

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
 
7

7stud --

Earle said:
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)
 
7

7stud --

Earle said:
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.
 
D

Daniel Lucraft

Earle said:
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
 
7

7stud --

Daniel said:
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
 
E

Earle Clubb

7stud said:
Daniel said:
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
 
7

7stud --

7stud said:
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
 
7

7stud --

7stud said:
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.
 
D

Daniel Lucraft

7stud said:
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.
2) 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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,150
Latest member
MakersCBDReviews
Top