Private variables

Why I always write stupid things ? See the effect of attr_accessor with
private variable

pigeon% cat b.rb
#!./ruby
class A
attr_accessor :_n
def initialize
@a = 12
@_m = [1,2]
@_n = [3,4]
end
def a
puts "a : #{@a.inspect} - _m : #{@_m.inspect} - _n : #{@_n.inspect}"
end
end

class B < A
attr_accessor :_m
def initialize
@_m = [2,4]
@_n = 6
super
end
def a
puts "a : #{@a.inspect} - _m : #{@_m.inspect} - _n : #{@_n.inspect}"
super
end
end

b = B.new
b.a
b._m = 24
b._n = 36
b.a
pigeon%

pigeon% b.rb

···

a : 12 - _m : [2, 4] - _n : 6
a : 12 - _m : [1, 2] - _n : [3, 4]
a : 12 - _m : 24 - _n : 6
a : 12 - _m : [1, 2] - _n : 36
pigeon%

Guy Decoux

This is exactly what I would expect to happen. I’m not sure why I’d
want to use a private variable and them make it visible with
attr_accessor. Would there be any bug that could arise from doing this?

Paul

···

On Wed, Sep 11, 2002 at 10:31:06PM +0900, ts wrote:

pigeon% b.rb
a : 12 - _m : [2, 4] - _n : 6
a : 12 - _m : [1, 2] - _n : [3, 4]
a : 12 - _m : 24 - _n : 6
a : 12 - _m : [1, 2] - _n : 36
pigeon%

Hi,

···

In message “private variables” on 02/09/11, ts decoux@moulon.inra.fr writes:

Why I always write stupid things ? See the effect of attr_accessor with
private variable

Do you mean you’ve implemented private instance variable, even if it’s
incomplete? If so, show me the code please.

						matz.

Hi,

···

In message “private variables” on 02/09/11, ts decoux@moulon.inra.fr writes:

Why I always write stupid things ? See the effect of attr_accessor with
private variable

Oh, I forget to mention that private instance variables should not be
exported by attr_accessor etc., because of the fact these variables
are private. Publicly exported private instance variable is
contradicting existence.

						matz.

pigeon% b.rb
a : 12 - _m : [2, 4] - _n : 6
a : 12 - _m : [1, 2] - _n : [3, 4]
a : 12 - _m : 24 - _n : 6
a : 12 - _m : [1, 2] - _n : 36
pigeon%

Hi Guy, I got the following:

···

a : 12 - _m : [1, 2] - _n : [3, 4]
a : 12 - _m : [1, 2] - _n : [3, 4]
a : 12 - _m : 24 - _n : 36
a : 12 - _m : 24 - _n : 36

This is on WinXP with the WindowsInstaller 1.72-4 release.

I’m still new to ruby, but the output I have looks correct.

Rob

attr_accessor. Would there be any bug that could arise from doing this?

perhaps hard to understand what do the code in this case.

Guy Decoux

Do you mean you've implemented private instance variable, even if it's
incomplete? If so, show me the code please.

Well, here a quick diff (*WARNING* old 1.6.7 : you can see it with
NODE_CVASGN)

To understand what it do

pigeon% cat b.rb
#!./ruby
class A
   def initialize
      @a = 12
      @_m = [1,2]
   end
end

class B < A
   def initialize
      @_m = [2,4]
      @_n = 6
      super
   end
end

Marshal.dump(B.new, File.new("aa", "w"))
pigeon%

pigeon% b.rb
pigeon%

pigeon% cat c.rb
#!/usr/bin/ruby
class A
end
class B < A
   def b
      puts "a = #{@a.inspect} - _m = #{@_m.inspect} - _n = #{@_n.inspect}"
   end
end

Marshal.load(IO.readlines("aa", nil)[0]).b
pigeon%

pigeon% c.rb
a = 12 - _m = {B=>[2, 4], A=>[1, 2]} - _n = {B=>6}
pigeon%

Now the problems :

  1) you need to have 2 hash access for a private variable, rather than one
     for a public variable

  2) I use

#define ID_PRIVATE 0x07

  which is used for ID_JUNK in 1.7.*

For 1.7 I see only a possibility

#define ID_JUNK 0x00
#define ID_PRIVATE 0x07

but this means that *all* bits are used and it will not possible to add
new types after this

Guy Decoux

diff -u ruby-1.6.7/env.h ruby-1.6.m/env.h
--- ruby-1.6.7/env.h 2001-03-21 09:04:11.000000000 +0100
+++ ruby-1.6.m/env.h 2002-09-07 15:55:19.000000000 +0200
@@ -20,6 +20,7 @@
     ID last_func;
     VALUE last_class;
     VALUE cbase;
+ VALUE curr_class;
     struct FRAME *prev;
     struct FRAME *tmp;
     char *file;
diff -u ruby-1.6.7/eval.c ruby-1.6.m/eval.c
--- ruby-1.6.7/eval.c 2002-02-27 05:50:29.000000000 +0100
+++ ruby-1.6.m/eval.c 2002-09-11 14:17:29.000000000 +0200
@@ -110,6 +110,8 @@
static VALUE rb_cUnboundMethod;
static VALUE umethod_bind _((VALUE, VALUE));
static VALUE rb_mod_define_method _((int, VALUE*, VALUE));
+static VALUE remove_private_variable __((VALUE, VALUE));
+static VALUE rb_current_class __((void));

static int scope_vmode;
#define SCOPE_PUBLIC 0
@@ -500,18 +502,19 @@
static struct FRAME *top_frame;
static struct SCOPE *top_scope;

-#define PUSH_FRAME() { \
- struct FRAME _frame; \
- _frame.prev = ruby_frame; \
- _frame.tmp = 0; \
- _frame.file = ruby_sourcefile; \
- _frame.line = ruby_sourceline; \
- _frame.iter = ruby_iter->iter; \
- _frame.cbase = ruby_frame->cbase; \
- _frame.argc = 0; \
- _frame.argv = 0; \
- _frame.flags = FRAME_ALLOCA; \
- ruby_frame = &_frame; \
+#define PUSH_FRAME() { \
+ struct FRAME _frame; \
+ _frame.prev = ruby_frame; \
+ _frame.tmp = 0; \
+ _frame.file = ruby_sourcefile; \
+ _frame.line = ruby_sourceline; \
+ _frame.iter = ruby_iter->iter; \
+ _frame.cbase = ruby_frame->cbase; \
+ _frame.curr_class = ruby_frame->curr_class; \
+ _frame.argc = 0; \
+ _frame.argv = 0; \
+ _frame.flags = FRAME_ALLOCA; \
+ ruby_frame = &_frame;

#define POP_FRAME() \
     ruby_sourcefile = _frame.file; \
@@ -1026,6 +1029,7 @@
   rb_call_inits();
   ruby_class = rb_cObject;
   ruby_frame->self = ruby_top_self;
+ ruby_frame->curr_class = rb_cObject;
   top_cref = rb_node_newnode(NODE_CREF,rb_cObject,0,0);
   ruby_cref = top_cref;
   ruby_frame->cbase = (VALUE)ruby_cref;
@@ -1266,6 +1270,7 @@
     ruby_top_self = rb_obj_clone(ruby_top_self);
     rb_extend_object(ruby_top_self, ruby_wrapper);
     PUSH_FRAME();
+ ruby_frame->curr_class = ruby_class;
     ruby_frame->last_func = 0;
     ruby_frame->last_class = 0;
     ruby_frame->self = self;
@@ -2708,7 +2713,12 @@

       case NODE_IASGN:
   result = rb_eval(self, node->nd_value);
- rb_ivar_set(self, node->nd_vid, result);
+ if (rb_is_instance_id(node->nd_vid)) {
+ rb_ivar_set(self, node->nd_vid, result);
+ }
+ else {
+ rb_pvar_set(self, rb_current_class(), node->nd_vid, result);
+ }
   break;

       case NODE_CDECL:
@@ -2732,7 +2742,11 @@

       case NODE_CVASGN:
   result = rb_eval(self, node->nd_value);
- rb_cvar_set(ruby_cbase, node->nd_vid, result);
+ if (!FL_TEST(ruby_cbase, FL_SINGLETON)) {
+ rb_cvar_set(ruby_cbase, node->nd_vid, result);
+ break;
+ }
+ rb_cvar_set(rb_iv_get(ruby_cbase, "__attached__"), node->nd_vid, result);
   break;

       case NODE_LVAR:
@@ -2751,7 +2765,12 @@
   break;

       case NODE_IVAR:
- result = rb_ivar_get(self, node->nd_vid);
+ if (rb_is_instance_id(node->nd_vid)) {
+ result = rb_ivar_get(self, node->nd_vid);
+ }
+ else {
+ result = rb_pvar_get(self, rb_current_class(), node->nd_vid);
+ }
   break;

       case NODE_CONST:
@@ -2941,7 +2960,13 @@
   if (ruby_frame->argc != 1)
       rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)",
          ruby_frame->argc);
- result = rb_ivar_set(self, node->nd_vid, ruby_frame->argv[0]);
+ if (rb_is_instance_id(node->nd_vid)) {
+ result = rb_ivar_set(self, node->nd_vid, ruby_frame->argv[0]);
+ }
+ else {
+ result = rb_pvar_set(self, rb_current_class(), node->nd_vid,
+ ruby_frame->argv[0]);
+ }
   break;

       case NODE_DEFN:
@@ -3242,6 +3267,8 @@
     PUSH_SCOPE();
     PUSH_VARS();

+ ruby_frame->curr_class = ruby_class;

···

+
     if (node->nd_tbl) {
   VALUE *vars = TMP_ALLOC(node->nd_tbl[0]+1);
   *vars++ = (VALUE)node;
@@ -3570,6 +3597,7 @@
   ruby_dyna_vars = block->dyna_vars;
     }
     ruby_class = klass?klass:block->klass;
+ ruby_frame->curr_class = ruby_class;
     if (!klass) self = block->self;
     node = block->body;

@@ -3826,7 +3854,11 @@
   break;

       case NODE_CVASGN:
- rb_cvar_set(ruby_cbase, lhs->nd_vid, val);
+ if (!FL_TEST(ruby_cbase, FL_SINGLETON)) {
+ rb_cvar_set(ruby_cbase, lhs->nd_vid, val);
+ break;
+ }
+ rb_cvar_set(rb_iv_get(ruby_cbase, "__attached__"), lhs->nd_vid, val);
   break;

       case NODE_MASGN:
@@ -4362,6 +4394,9 @@
     ruby_frame->self = recv;
     ruby_frame->argc = argc;
     ruby_frame->argv = argv;
+ if (nd_type(body) != NODE_CFUNC) {
+ ruby_frame->curr_class = klass;
+ }

     switch (nd_type(body)) {
       case NODE_CFUNC:
@@ -4902,6 +4937,7 @@
     if (TYPE(ruby_class) == T_ICLASS) {
   ruby_class = RBASIC(ruby_class)->klass;
     }
+ ruby_frame->curr_class = ruby_class;
     PUSH_TAG(PROT_NONE);
     if ((state = EXEC_TAG()) == 0) {
   NODE *node;
@@ -5043,6 +5079,7 @@
     ruby_frame->last_class = _frame.prev->last_class;
     ruby_frame->argc = _frame.prev->argc;
     ruby_frame->argv = _frame.prev->argv;
+ ruby_frame->curr_class = ruby_class;
     if (ruby_cbase != under) {
   ruby_frame->cbase = (VALUE)rb_node_newnode(NODE_CREF,under,0,ruby_frame->cbase);
     }
@@ -5248,6 +5285,7 @@
     ruby_frame->last_class = 0;
     ruby_frame->self = self;
     ruby_frame->cbase = (VALUE)rb_node_newnode(NODE_CREF,ruby_class,0,0);
+ ruby_frame->curr_class = ruby_class;
     PUSH_SCOPE();
     /* default visibility is private at loading toplevel */
     SCOPE_SET(SCOPE_PRIVATE);
@@ -6005,6 +6043,8 @@
     rb_define_method(rb_mKernel, "send", rb_f_send, -1);
     rb_define_method(rb_mKernel, "__send__", rb_f_send, -1);
     rb_define_method(rb_mKernel, "instance_eval", rb_obj_instance_eval, -1);
+ rb_define_private_method(rb_mKernel, "remove_private_variable",
+ remove_private_variable, 1);

     rb_define_private_method(rb_cModule, "append_features", rb_mod_append_features, 1);
     rb_define_private_method(rb_cModule, "extend_object", rb_mod_extend_object, 1);
@@ -9091,3 +9131,16 @@
   tt = tt->prev;
     }
}
+
+static VALUE
+rb_current_class()
+{
+ return ruby_frame->curr_class;
+}
+
+static VALUE
+remove_private_variable(obj, name)
+ VALUE obj, name;
+{
+ return rb_obj_remove_private_variable(obj, rb_current_class(), name);
+}
diff -u ruby-1.6.7/gc.c ruby-1.6.m/gc.c
--- ruby-1.6.7/gc.c 2002-02-13 10:02:15.000000000 +0100
+++ ruby-1.6.m/gc.c 2002-09-07 16:11:11.000000000 +0200
@@ -911,6 +911,7 @@
{
     mark_locations_array(frame->argv, frame->argc);
     rb_gc_mark(frame->cbase);
+ rb_gc_mark(frame->curr_class);
}

#ifdef __GNUC__
diff -u ruby-1.6.7/intern.h ruby-1.6.m/intern.h
--- ruby-1.6.7/intern.h 2002-02-27 05:50:30.000000000 +0100
+++ ruby-1.6.m/intern.h 2002-09-11 12:12:50.000000000 +0200
@@ -267,6 +267,7 @@
void rb_parser_while_loop _((int, int));
int rb_is_const_id _((ID));
int rb_is_instance_id _((ID));
+int rb_is_private_id _((ID));
int rb_is_class_id _((ID));
VALUE rb_backref_get _((void));
void rb_backref_set _((VALUE));
@@ -370,6 +371,11 @@
void rb_mark_generic_ivar _((VALUE));
void rb_mark_generic_ivar_tbl _((void));
void rb_free_generic_ivar _((VALUE));
+VALUE rb_pvar_get _((VALUE, VALUE, ID));
+VALUE rb_pvar_set _((VALUE, VALUE, ID, VALUE));
+VALUE rb_pvar_defined _((VALUE, VALUE, ID));
+VALUE rb_obj_private_variables _((VALUE, VALUE));
+VALUE rb_obj_remove_private_variable _((VALUE, VALUE, VALUE));
VALUE rb_ivar_get _((VALUE, ID));
VALUE rb_ivar_set _((VALUE, ID, VALUE));
VALUE rb_ivar_defined _((VALUE, ID));
diff -u ruby-1.6.7/marshal.c ruby-1.6.m/marshal.c
--- ruby-1.6.7/marshal.c 2002-02-28 07:52:47.000000000 +0100
+++ ruby-1.6.m/marshal.c 2002-09-08 13:50:33.000000000 +0200
@@ -755,19 +755,41 @@
     return v;
}

+struct obj_id {
+ VALUE obj;
+ ID id;
+};
+
+static VALUE
+r_i_pvar(ary, st)
+ VALUE ary;
+ struct obj_id *st;
+{
+ rb_pvar_set(st->obj, RARRAY(ary)->ptr[0], st->id, RARRAY(ary)->ptr[1]);
+ return Qnil;
+}
+
static void
r_ivar(obj, arg)
     VALUE obj;
     struct load_arg *arg;
{
     long len;
+ struct obj_id st;

     len = r_long(arg);
     if (len > 0) {
   while (len--) {
       ID id = r_symbol(arg);
       VALUE val = r_object(arg);
- rb_ivar_set(obj, id, val);
+ if (rb_is_private_id(id)) {
+ st.obj = obj;
+ st.id = id;
+ rb_iterate(rb_each, val, r_i_pvar, (VALUE)&st);
+ }
+ else {
+ rb_ivar_set(obj, id, val);
+ }
   }
     }
}
diff -u ruby-1.6.7/object.c ruby-1.6.m/object.c
--- ruby-1.6.7/object.c 2002-02-04 09:09:10.000000000 +0100
+++ ruby-1.6.m/object.c 2002-09-11 12:14:15.000000000 +0200
@@ -1165,7 +1165,7 @@
     rb_define_method(rb_mKernel, "instance_variables", rb_obj_instance_variables, 0);
     rb_define_private_method(rb_mKernel, "remove_instance_variable",
            rb_obj_remove_instance_variable, 1);
-
+ rb_define_method(rb_mKernel, "private_variables", rb_obj_private_variables, 1);
     rb_define_method(rb_mKernel, "instance_of?", rb_obj_is_instance_of, 1);
     rb_define_method(rb_mKernel, "kind_of?", rb_obj_is_kind_of, 1);
     rb_define_method(rb_mKernel, "is_a?", rb_obj_is_kind_of, 1);
diff -u ruby-1.6.7/parse.y ruby-1.6.m/parse.y
--- ruby-1.6.7/parse.y 2002-02-20 05:28:51.000000000 +0100
+++ ruby-1.6.m/parse.y 2002-09-08 12:25:58.000000000 +0200
@@ -22,12 +22,14 @@

#define ID_SCOPE_SHIFT 3
#define ID_SCOPE_MASK 0x07
+
#define ID_LOCAL 0x01
#define ID_INSTANCE 0x02
#define ID_GLOBAL 0x03
#define ID_ATTRSET 0x04
#define ID_CONST 0x05
#define ID_CLASS 0x06
+#define ID_PRIVATE 0x07

#define is_notop_id(id) ((id)>LAST_TOKEN)
#define is_local_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL)
@@ -36,6 +38,7 @@
#define is_attrset_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_ATTRSET)
#define is_const_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_CONST)
#define is_class_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_CLASS)
+#define is_private_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_PRIVATE)

NODE *ruby_eval_tree_begin = 0;
NODE *ruby_eval_tree = 0;
@@ -673,7 +676,7 @@
           if ($2 == tOROP) {
         $<node>3->nd_value = $4;
         $$ = NEW_OP_ASGN_OR(gettable($1), $<node>3);
- if (is_instance_id($1)) {
+ if (is_instance_id($1) || is_private_id($1)) {
             $$->nd_aid = $1;
         }
           }
@@ -4182,7 +4185,7 @@
     else if (is_global_id(id)) {
   return NEW_GVAR(id);
     }
- else if (is_instance_id(id)) {
+ else if (is_instance_id(id) || is_private_id(id)) {
   return NEW_IVAR(id);
     }
     else if (is_const_id(id)) {
@@ -4237,7 +4240,7 @@
     else if (is_global_id(id)) {
   return NEW_GASGN(id, val);
     }
- else if (is_instance_id(id)) {
+ else if (is_instance_id(id) || is_private_id(id)) {
   return NEW_IASGN(id, val);
     }
     else if (is_const_id(id)) {
@@ -4955,6 +4958,8 @@
       case '@':
   if (name[1] == '@')
       id |= ID_CLASS;
+ else if (name[1] == '_')
+ id |= ID_PRIVATE;
   else
       id |= ID_INSTANCE;
   break;
@@ -5065,6 +5070,14 @@
     return Qfalse;
}

+int
+rb_is_private_id(id)
+ ID id;
+{
+ if (is_private_id(id)) return Qtrue;
+ return Qfalse;
+}
+
static void
special_local_set(c, val)
     char c;
diff -u ruby-1.6.7/ruby.h ruby-1.6.m/ruby.h
--- ruby-1.6.7/ruby.h 2002-02-26 14:08:17.000000000 +0100
+++ ruby-1.6.m/ruby.h 2002-09-08 14:00:04.000000000 +0200
@@ -466,6 +466,8 @@
VALUE rb_gv_get _((const char*));
VALUE rb_iv_get _((VALUE, const char*));
VALUE rb_iv_set _((VALUE, const char*, VALUE));
+VALUE rb_pv_get _((VALUE, VALUE, const char*));
+VALUE rb_pv_set _((VALUE, VALUE, const char*, VALUE));

VALUE rb_equal _((VALUE,VALUE));

diff -u ruby-1.6.7/variable.c ruby-1.6.m/variable.c
--- ruby-1.6.7/variable.c 2002-02-19 05:48:04.000000000 +0100
+++ ruby-1.6.m/variable.c 2002-09-11 12:12:17.000000000 +0200
@@ -885,6 +885,7 @@
     VALUE clone, obj;
{
     st_table *tbl;
+ VALUE val;

     if (!generic_iv_tbl) return;
     if (st_lookup(generic_iv_tbl, obj, &tbl)) {
@@ -892,6 +893,295 @@
     }
}

+static void
+check_valid_class(klass)
+ VALUE klass;
+{
+ switch (TYPE(klass)) {
+ case T_CLASS:
+ case T_ICLASS:
+ case T_MODULE:
+ case T_TRUE:
+ case T_FALSE:
+ case T_NIL:
+ break;
+ default:
+ rb_raise(rb_eArgError, "expected a class object");
+ }
+}
+
+static VALUE
+private_ivar_get(obj, klass, id)
+ VALUE obj, klass;
+ ID id;
+{
+ st_table *tbl;
+ VALUE val;
+
+ if (!generic_iv_tbl) return Qnil;
+ if (!st_lookup(generic_iv_tbl, obj, &tbl)) return Qnil;
+ if (st_lookup(tbl, id, &val) && TYPE(val) == T_HASH &&
+ st_lookup(RHASH(val)->tbl, klass, &val)) {
+ return val;
+ }
+ return Qnil;
+}
+
+VALUE
+rb_pvar_get(obj, klass, id)
+ VALUE klass, obj;
+ ID id;
+{
+ VALUE val;
+
+ check_valid_class(klass);
+ if (!rb_is_private_id(id)) {
+ rb_raise(rb_eNameError, "`%s' is not a private variable",
+ rb_id2name(id));
+ }
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (ROBJECT(obj)->iv_tbl &&
+ st_lookup(ROBJECT(obj)->iv_tbl, id, &val) &&
+ TYPE(val) == T_HASH && st_lookup(RHASH(val)->tbl, klass, &val)) {
+ return val;
+ }
+ break;
+ default:
+ if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) {
+ return private_ivar_get(obj, klass, id);
+ }
+ break;
+ }
+ if (ruby_verbose) {
+ rb_warning("private variable %s not initialized", rb_id2name(id));
+ }
+ return Qnil;
+}
+
+static void
+private_ivar_set(obj, klass, id, val)
+ VALUE obj, klass;
+ ID id;
+ VALUE val;
+{
+ st_table *tbl;
+ VALUE stbl;
+
+ if (rb_special_const_p(obj)) {
+ special_generic_ivar = 1;
+ }
+ if (!generic_iv_tbl) {
+ generic_iv_tbl = st_init_numtable();
+ }
+
+ if (!st_lookup(generic_iv_tbl, obj, &tbl)) {
+ FL_SET(obj, FL_EXIVAR);
+ tbl = st_init_numtable();
+ st_add_direct(generic_iv_tbl, obj, tbl);
+ }
+ if (!st_lookup(tbl, id, &stbl)) {
+ stbl = rb_hash_new();
+ st_add_direct(tbl, id, stbl);
+ }
+ if (TYPE(stbl) != T_HASH) {
+ rb_raise(rb_eNameError, "`%s' is not a private variable",
+ rb_id2name(id));
+ }
+ rb_hash_aset(stbl, klass, val);
+}
+
+VALUE
+rb_pvar_set(obj, klass, id, val)
+ VALUE klass, obj;
+ ID id;
+ VALUE val;
+{
+ VALUE stbl;
+
+ if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
+ if (OBJ_FROZEN(obj)) rb_error_frozen("object");
+
+ check_valid_class(klass);
+ if (!rb_is_private_id(id)) {
+ rb_raise(rb_eNameError, "`%s' is not a private variable",
+ rb_id2name(id));
+ }
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (!ROBJECT(obj)->iv_tbl) ROBJECT(obj)->iv_tbl = st_init_numtable();
+ if (!st_lookup(ROBJECT(obj)->iv_tbl, id, &stbl)) {
+ stbl = rb_hash_new();
+ st_add_direct(ROBJECT(obj)->iv_tbl, id, stbl);
+ }
+ if (TYPE(stbl) != T_HASH) {
+ rb_raise(rb_eNameError, "`%s' is not a private variable",
+ rb_id2name(id));
+ }
+ rb_hash_aset(stbl, klass, val);
+ break;
+ default:
+ private_ivar_set(obj, id, val);
+ break;
+ }
+ return val;
+}
+
+static VALUE
+private_ivar_defined(obj, klass, id)
+ VALUE obj, klass;
+ ID id;
+{
+ st_table *tbl;
+ VALUE val;
+
+ if (!generic_iv_tbl) return Qfalse;
+ if (st_lookup(generic_iv_tbl, obj, &tbl) && st_lookup(tbl, id, &val) &&
+ TYPE(val) == T_HASH && st_lookup(RHASH(val)->tbl, klass, 0)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+VALUE
+rb_pvar_defined(obj, klass, id)
+ VALUE obj, klass;
+ ID id;
+{
+ VALUE stbl;
+
+ check_valid_class(klass);
+ if (!rb_is_private_id(id)) {
+ rb_raise(rb_eNameError, "`%s' is not a private variable",
+ rb_id2name(id));
+ }
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (ROBJECT(obj)->iv_tbl &&
+ st_lookup(ROBJECT(obj)->iv_tbl, id, &stbl) &&
+ TYPE(stbl) == T_HASH && st_lookup(RHASH(stbl)->tbl, klass, 0))
+ return Qtrue;
+ break;
+ default:
+ if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) {
+ return private_ivar_defined(obj, klass, id);
+ }
+ break;
+ }
+ return Qfalse;
+}
+
+struct ary_class {
+ VALUE ary;
+ VALUE klass;
+};
+
+static int
+pvar_i(key, val, st)
+ ID key;
+ VALUE val;
+ struct ary_class *st;
+{
+ if (rb_is_private_id(key) && TYPE(val) == T_HASH &&
+ st_lookup(RHASH(val)->tbl, st->klass, 0)) {
+ rb_ary_push(st->ary, rb_str_new2(rb_id2name(key)));
+ }
+ return ST_CONTINUE;
+}
+
+VALUE
+rb_obj_private_variables(obj, klass)
+ VALUE obj, klass;
+{
+ VALUE ary;
+ struct ary_class st;
+
+ check_valid_class(klass);
+ ary = rb_ary_new();
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (ROBJECT(obj)->iv_tbl) {
+ st.ary = ary;
+ st.klass = klass;
+ st_foreach(ROBJECT(obj)->iv_tbl, pvar_i, &st);
+ }
+ break;
+ default:
+ if (!generic_iv_tbl) break;
+ if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj)) {
+ st_table *tbl;
+
+ if (st_lookup(generic_iv_tbl, obj, &tbl)) {
+ st.ary = ary;
+ st.klass = klass;
+ st_foreach(tbl, pvar_i, &st);
+ }
+ }
+ break;
+ }
+ return ary;
+}
+
+static VALUE
+generic_pvar_remove(obj, klass, id)
+ VALUE obj, klass;
+ ID id;
+{
+ st_table *tbl;
+ VALUE val, stbl;
+
+ if (!generic_iv_tbl) return Qnil;
+ if (!st_lookup(generic_iv_tbl, obj, &tbl)) return Qnil;
+ if (!st_lookup(tbl, id, &stbl)) return Qnil;
+ if (TYPE(stbl) != T_HASH) return Qnil;
+ val = RHASH(stbl)->ifnone;
+ st_delete(RHASH(stbl)->tbl, &klass, &val);
+ return val;
+}
+
+VALUE
+rb_obj_remove_private_variable(obj, klass, name)
+ VALUE obj, klass, name;
+{
+ VALUE val = Qnil;
+ ID id = rb_to_id(name);
+
+ if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
+ if (OBJ_FROZEN(obj)) rb_error_frozen("object");
+ if (!rb_is_private_id(id)) {
+ rb_raise(rb_eNameError, "`%s' is not a private variable",
+ rb_id2name(id));
+ }
+ check_valid_class(klass);
+
+ switch (TYPE(obj)) {
+ case T_OBJECT:
+ case T_CLASS:
+ case T_MODULE:
+ if (ROBJECT(obj)->iv_tbl &&
+ st_lookup(ROBJECT(obj)->iv_tbl, id, &val) &&
+ TYPE(val) == T_HASH) {
+ st_delete(RHASH(val)->tbl, &klass, &val);
+ }
+ break;
+ default:
+ if (FL_TEST(obj, FL_EXIVAR) || rb_special_const_p(obj))
+ return generic_pvar_remove(obj, klass, id);
+ break;
+ }
+ return val;
+}
+
VALUE
rb_ivar_get(obj, id)
     VALUE obj;
@@ -899,6 +1189,9 @@
{
     VALUE val;

+ if (rb_is_private_id(id)) {
+ return rb_pvar_get(obj, 1, id);
+ }
     switch (TYPE(obj)) {
       case T_OBJECT:
       case T_CLASS:
@@ -926,6 +1219,9 @@
     if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)
   rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
     if (OBJ_FROZEN(obj)) rb_error_frozen("object");
+ if (rb_is_private_id(id)) {
+ return rb_pvar_set(obj, 1, id, val);
+ }
     switch (TYPE(obj)) {
       case T_OBJECT:
       case T_CLASS:
@@ -945,6 +1241,9 @@
     VALUE obj;
     ID id;
{
+ if (rb_is_private_id(id)) {
+ return rb_pvar_defined(obj, 1, id);
+ }
     switch (TYPE(obj)) {
       case T_OBJECT:
       case T_CLASS:
@@ -1549,3 +1848,24 @@

     return rb_ivar_set(obj, id, val);
}
+
+VALUE
+rb_pv_get(obj, klass, name)
+ VALUE obj, klass;
+ const char *name;
+{
+ ID id = rb_intern(name);
+
+ return rb_pvar_get(obj, klass, id);
+}
+
+VALUE
+rb_pv_set(obj, klass, name, val)
+ VALUE obj, klass;
+ const char *name;
+ VALUE val;
+{
+ ID id = rb_intern(name);
+
+ return rb_pvar_set(obj, klass, id, val);
+}

Yukihiro Matsumoto wrote:

Hi,

Why I always write stupid things ? See the effect of attr_accessor with
private variable

Oh, I forget to mention that private instance variables should not be
exported by attr_accessor etc., because of the fact these variables
are private. Publicly exported private instance variable is
contradicting existence.

  					matz.

I wonder why. Could you give some concrete reason for it?

I thought, being private for a variable means that it can
only be acessed by the methods of its class to avoid
name collisions with instance variables in derived classes.

Regards, Christian

···

In message “private variables” > on 02/09/11, ts decoux@moulon.inra.fr writes:

“ts” wrote in

To understand what it do

Wouldn’t the

``@_I_am a_private_variable"

thing break a lot of code?

/Christoph

Hi,

···

In message “Re: private variables” on 02/09/12, Christian Szegedy szegedy@nospam.or.uni-bonn.de writes:

Oh, I forget to mention that private instance variables should not be
exported by attr_accessor etc., because of the fact these variables
are private. Publicly exported private instance variable is
contradicting existence.

I wonder why. Could you give some concrete reason for it?

I thought, being private for a variable means that it can
only be acessed by the methods of its class to avoid
name collisions with instance variables in derived classes.

Yes. Hence it should not be exported as an attribute method, which
can be seen from everywhere. Am I missing something?

						matz.

I doubt there are many people who begin their instance variable names
with “@_”; those that do probably don’t intend for the variable to be
accessed by derived classes anyway.

Paul

···

On Thu, Sep 12, 2002 at 03:57:24AM +0900, Christoph wrote:

“ts” wrote in

To understand what it do

Wouldn’t the

``@_I_am a_private_variable"

thing break a lot of code?

Wouldn't the
``@_I_am a_private_variable"
thing break a lot of code?

Yes, because you've introduced a new type of variable but this is not a
problem with the implementation (i.e. you'll have the same problem with
any implementations)

Guy Decoux

Yukihiro Matsumoto wrote:

I thought, being private for a variable means that it can
only be acessed by the methods of its class to avoid
name collisions with instance variables in derived classes.

Yes. Hence it should not be exported as an attribute method, which
can be seen from everywhere. Am I missing something?

There is a (narrow) use here, where one might like to have a private
instance variable and share it out with attr_* methods; avoiding
namespace pollution in baseclasses that are to be subclassed alot.
That is, one wants to use private instance variable to protect one self
vertically in the inheritance tree. (I could see myself using both
attr_writer and attr_reader on private instance variables, while
attr_accessor is less likely.) Along the horizontal, I think that Ruby
provides enough protection.

Now, I don’t see losing attr_* for private instance variables a big
loss. But at the same time, I do not see it as a requirement that they
are prohibited. If I was being terribly pragmatic about it, I’d
implement private instance variables any way I could, and not really
care about attr_. If they still work with private instance variables,
keep them. If they don’t work, ignore them.
Only time I would pour energy and time on them with regards to private
instance variables, is if PoLS might be violated. For example, if the
syntax would allow you to write an attr_
that appears to expose the
variable, but it is actually some other variable that gets accessed.

My 0.02 NOK

···

In message “Re: private variables” > on 02/09/12, Christian Szegedy szegedy@nospam.or.uni-bonn.de writes:


([ Kent Dahl ]/)_ ~ [ http://www.stud.ntnu.no/~kentda/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 5. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

“Paul Brannan” wrote in

I doubt there are many people who begin their instance variable names
with “@_”; those that do probably don’t intend for the variable to be
accessed by derived classes anyway.

Are you sure? I like @_vars and never associated the use of
a _'' prefix with being private" until this discussion.

We also could use your secret'' method frame work (it simulates private methods of C++ right?) and only implement private_attributes’’ or “secret_attribute”? Here is a half baked
Ruby implementation (without your secret_method stuff).

/Christoph

···

module PrivateAttribute
def instance_variables
# needs more work
super() -
type.instance_eval {
@__private_attributes + @__shadow
}
end

class << self
 # helper methods
  def mangle(klass,sym)
    "__pa__#{klass.id}_#{sym.to_s}"
  end

  #actuall code
  private
  def append_features(klass)
    Class === klass or raise TypeError, "Expected klass .."
    super
  end

  def included(klass)
    klass.instance_eval {
      @private_attributes = []
      @__private_attributes = []
      @__shadow = []
    }
    class << klass
      attr_reader :private_attributes
      private
      def private_attribute(attr)
        # silly tests
        unless String === attr or Symbol === attr
          raise TypeError, "expected ..."
        end
        attr = attr.to_s
        class_eval <<-PRIVATE_ATTR
          def #{attr}
            @#{PrivateAttribute.mangle(self,attr)}
          end
          def set_#{attr}(x)
            @#{PrivateAttribute.mangle(self,attr)} = x
          end
        PRIVATE_ATTR
        @private_attributes << attr
        @__private_attributes << "@" + PrivateAttribute.mangle(self,attr)
      end


      def inherited(sub_klass)
        tmp = @__private_attributes
        sub_klass.instance_eval {
          @private_attributes = []
          @__private_attributes = []
          @__shadow = tmp
        }
        @private_attributes.each {|attr|
          sub_klass.class_eval <<-INHERITED
            def #{attr}
              raise NoMethodError, "privacy violation of ..."
            end
            def set_#{attr}(x)
              raise NoMethodError, "privacy violation of ..."
            end
          INHERITED
        }
        # potenieal source of conflict with other mixins
        # like singleton
        super
      end
    end
  end
end

end

class A
include PrivateAttribute
private_attribute :xxx
def initialize(x)
set_xxx x
end
end

a = A.new “a”
a.instance_eval { @me = “me” }
p a.instance_variables
p A.private_attributes

class B < A
private_attribute :yyy
def initialize(a)
set_yyy a
# super(a)
end
end

b = B.new “b”
p b.yyy
p b.instance_eval { @you = “you” }
p b.instance_variables
p B.private_attributes

class B
def initialize(a)
super a
end
end
B.new (“boom”) # NoMethodError

[“@me”]
[“xxx”]
“b”
“you”
[“@you”]
[“yyy”]
(eval):5:in set_xxx': privacy violation of ... (NoMethodError) from D:\Ruby\mswin\samples\rb58.tmp:84:in initialize’
from D:\Ruby\mswin\samples\rb58.tmp:111:in initialize' from D:\Ruby\mswin\samples\rb58.tmp:114:in new’
from D:\Ruby\mswin\samples\rb58.tmp:114

“ts” wrote in

Yes, because you’ve introduced a new type of variable but this is not a

which means one more set of scoping rules to keep mind? (Probably
not, but even the prospect makes me shudder;-)

problem with the implementation (i.e. you’ll have the same problem with
any implementations)

Well had you used a prefix convention _@var instead of @_var
the only thing that breaks is that previously illegal code would
suddenly be legal.

/Christoph

Hi,

There is a (narrow) use here, where one might like to have a private
instance variable and share it out with attr_* methods; avoiding
namespace pollution in baseclasses that are to be subclassed alot.

Possible. But, you know, attr_accessor is just a short form of:

def foo
@foo
end
def foo=(val)
@foo=val
end

So you can export a private instance variable as an attribute by hand,
but not by attr_accessor, etc.

						matz.
···

In message “Re: private variables” on 02/09/13, Kent Dahl kentda@stud.ntnu.no writes:

I’m not sure there’s much value in secret attributes/accessors. I can
see why you’d want it (if you need to call a method every time you
update a private instance varaible), but every method in the class still
has access to both the accessor and the private instance variable, I
can’t help but feeling that there is a better solution.

Paul

···

On Thu, Sep 12, 2002 at 05:58:29AM +0900, Christoph wrote:

We also could use your secret'' method frame work (it simulates private methods of C++ right?) and only implement private_attributes’’ or “secret_attribute”? Here is a half baked
Ruby implementation (without your secret_method stuff).

which means one more set of scoping rules to keep mind? (Probably
not, but even the prospect makes me shudder;-)

If I've well understood, the variable is accessible only in the class
where it was defined.

Well had you used a prefix convention _@var instead of @_var

This is an instance variable, it seems natural to have the prefix `@'

the only thing that breaks is that previously illegal code would
suddenly be legal.

pigeon% ruby -e 'def _(a) p a; end; @a = 12; _@a'
12
pigeon%

Except for obfuscation, I don't think that someone use it :slight_smile:

Guy Decoux

“Christoph” chr_news@gmx.net writes:

which means one more set of scoping rules to keep mind? (Probably
not, but even the prospect makes me shudder;-)

Actually, that raises an interesting point.

If we’re (somewhat unfortunately, imo) using ‘_’ to denote ‘localized’,
then should we be using more orthogonally?

@_a - localized instance variable

_a - localized block variable,

   _a = 1

   10.times {|_a| puts _a}

   puts _a  # => 1

If we’re going down this route (and I’m wondering if I think we
should), then we can at least make us of the options.

Dave

“Paul Brannan” wrote in

I’m not sure there’s much value in secret attributes/accessors. I can
see why you’d want it (if you need to call a method every time you

Actually I am fairly neutral about ``private_variables’’ …

update a private instance varaible), but every method in the class still
has access to both the accessor and the private instance variable, I
can’t help but feeling that there is a better solution.

Oh well my half-baked ``solution’’ is well-done nonsense anyway
(semantically it is plain wrong)

In the mean time how about using

_@I_am_a_private_instance_variable

  • this also makes Guy’s ``attr_reader problem’’ obsolete?

/Christoph