Encapsulating instance data

Happy Holidays!

It always concerned me that instance variables of a class propagate to
its subclasses and that instance variables of a module propagate to
classes which include it.

This lack of private variables caused me great unrest until I learned
a little more ruby and then realized private variables can be
implemented in ruby itself!

What do you think? Criticism is welcome; this one of my first
non-hello-world ruby scripts so be nice :slight_smile: And thanks to the irc folks
who patiently answered my questions.

···

#----------------------------------

synopsis

#----------------------------------

require ‘wrap’

module A
def a_f
@x = 11
end

def a_x
@x
end
end

module B
def b_f
@x = 22
end

def b_x
@x
end
end

class C
include Wrap(A)
include Wrap(B)
end

c = C.new
c.a_f
c.b_f
puts "A’s @x is #{c.a_x}"
puts “B’s @x is #{c.b_x}”

=> A’s @x is 11

=> B’s @x is 22

class P
def initialize
@x = 33
end

def p_x
@x
end
end

class Q < Wrap P
def initialize
super()
@x = 44
end

def q_x
@x
end
end

q = Q.new
puts "P’s @x is #{q.p_x}"
puts “Q’s @x is #{q.q_x}”

=> P’s @x is 33

=> Q’s @x is 44

#----------------------------------

wrap.rb

privatize data using delegation to an anonymous class.

#----------------------------------

def Wrap mod
if mod.class == Module
wrap_module(mod)
elsif mod.class == Class
wrap_class(mod)
else
raise "expected Class or Module for Wrap: got #{mod.class}"
end
end

def wrap_module mod
wrap = Module.new

classname = "Private__class#{wrap.object_id}"
instance = “@__private#{wrap.object_id}”

wrap.const_set(classname.intern, Class.new{ include mod })

mod.constants.each do |name|
wrap.const_set(name.intern, mod.const_get(name.intern))
end

create = Proc.new do |visibility, method|
next if method =~ /^__/
wrap.class_eval <<-end_eval
def #{method}(*args, &block)
#{instance} ||= #{classname}.new
#{instance}.send(:#{method}, *args, &block)
end
end_eval
wrap.send(visibility, method)
end

mod.public_instance_methods(true).each{|m|create.call(:public,m)}
mod.protected_instance_methods(true).each{|m|create.call(:protected,m)}
mod.private_instance_methods(true).each{|m|create.call(:protected,m)}

wrap
end

def wrap_class aclass
wrap = Class.new

instance = “@__private#{wrap.object_id}”

aclass.constants.each do |name|
wrap.const_set(name.intern, aclass.const_get(name.intern))
end

aclass.singleton_methods(true).each do |method|
wrap.class_eval <<-end_eval
def self.#{method}(*args, &block)
#{aclass.name}.#{method}(*args, &block)
end
end_eval
end

wrap.class_eval <<-end_eval
def initialize(*args, &block)
#{instance} = #{aclass.name}.new(*args, &block)
end
end_eval

create = Proc.new do |visibility, method|
next if method == "initialize"
next if method =~ /^__/
wrap.class_eval <<-end_eval
def #{method}(*args, &block)
#{instance}.send(:#{method}, *args, &block)
end
end_eval
wrap.send(visibility, method)
end

aclass.ancestors.each do |c|
break if c == Object
c.public_instance_methods(false).each{|m|create.call(:public,m)}
c.protected_instance_methods(false).each{|m|create.call(:protected,m)}
c.private_instance_methods(false).each{|m|create.call(:private,m)}
end

wrap
end

#----------------------------------

It’s both like and unlike delegation. With delegation, you pass an
object explicitly and the dual-object relationship is part of the
design.

With wrapping there is no object explicitly passed and the delegation
is hidden; there are still two objects but you pretend one of them
doesn’t exist. But essentially it’s just a notational convenience for
a certain form of delegation.

I see some specialized things would make it faster, for example direct
evals of public methods (no send) and re-evaling after the first
method call to remove the nil test for module methods.

Anyway, all of this may be too silly. It’s really an anecdote of
sorts. I was surprised that ruby was flexible enough that I could
seemingly add (or simulate) a new language construct from inside the
language itself.

-Jeff (quix)

Hi –

Happy Holidays!

It always concerned me that instance variables of a class propagate to
its subclasses and that instance variables of a module propagate to
classes which include it.

Looking ahead to your code: you’re not actually dealing here with
instance variables of classes or modules (i.e., instance variables of
Class or Module objects), but rather with instances variables of any
old object. That could play a role… I wonder whether class/module
instance variables would fit the bill in some cases.

Also, it’s not exactly that instance variables propagate: it’s that
classes and modules define instance methods, and then position
themselves in the method lookup chain. So the instance variables
don’t really propagate, any more than the local variables used by
those methods do. The methods stay where they are, defined as they
are, and the object comes looking for them.

This lack of private variables caused me great unrest until I learned
a little more ruby and then realized private variables can be
implemented in ruby itself!

What do you think? Criticism is welcome; this one of my first
non-hello-world ruby scripts so be nice :slight_smile: And thanks to the irc folks
who patiently answered my questions.

You’ve unleashed… a monster! :slight_smile: Actually I think it’s an
interesting experiment, but I don’t think it’s really useable, for
reasons I’ll try to explain.

On the method definition end, you’re redefining the very concept of
“instance variable”; meaning, instead of having it be per instance,
you’re making it per instance per module, which is categorically
different from per instance.

For example, in your code:

puts “A’s @x is #{c.a_x}”
puts “B’s @x is #{c.b_x}”

=> A’s @x is 11

=> B’s @x is 22

the concept of “A’s @x” doesn’t really make sense. It’s c’s @x, c
being the instance whose instance variable @x is.

It may sound simple-minded, but I’d tend to say: if, in your method
definitions, you need to keep track of two bits of state information
for your object, just use two different instance variables.

On the object end – method calls, as in your examples – there’s no
knowledge of the splitting up of @x, but there’s no advantage to it
either. Objects can do these kinds of things already, using multiple
instance variables (or whatever).

There’s also the matter of assigning this much importance to the
question of where (which class or module) a method was defined in. It
shouldn’t be this important, because it can (or should be able to)
change without users of the object knowing or caring. Once you start
scoping these variables by where you defined their methods, I think
reorganizing your modules and classes would be incredibly hard or even
impossible.

What sorts of problems are you addressing here? It would be
interesting to see and play around with an example or particular
case.

David

···

On Tue, 2 Sep 2003, Jeff Mitchell wrote:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav