Project

General

Profile

Actions

Feature #2366

closed

private constant

Added by mame (Yusuke Endoh) about 13 years ago. Updated over 11 years ago.

Status:
Closed
Priority:
Normal
Target version:
[ruby-dev:39685]

Description

=begin
遠藤です。

今の Ruby には、クラスが公開 API かどうかを伝える手段がドキュメント
しかありません。そのため、ERB::Compiler など、ライブラリの中の公開
でない (と思われる) inner class を外から自由に参照できてしまいます。

これを防ぐためには、匿名クラスを用いて定義すれば大分隠蔽できますが、
記述が相当煩雑になってしまいます。また、そのようにしてしまうと、
「非公開というのは承知の上で敢えて使いたい」という要求に答えにくく
なります。

そこで、定数に public/private の属性を指定できるようにするのはどう
でしょうか。

module SomeMod
class PublicInnerCls
end

 class PrivateInnerCls
 end

 # PrivateInnerCls を private にする
 private_constant :PrivateInnerCls

end

public な定数は従来どおり参照できる

p SomeMod::PublicInnerCls #=> SomeMod::PublicInnerCls

private な定数を外から参照しようとすると例外

p SomeMod::PrivateInnerCls #=> private constant (RuntimeError)

同じスコープからは参照できる (望むなら自分の足を撃てる)

p SomeMod.module_eval { PrivateInnerCls } #=> SomeMod::PrivateInnerCls
p(module SomeMod; PrivateInnerCls; end) #=> SomeMod::PrivateInnerCls

要するに、メソッドの public/private と同じ感じです。

細かい仕様は詰めていませんが、proof of concept のパッチを付けます。
1 定数ごとに 2 要素の配列を作ってしまうので、ちゃんとした実装は必要
だと思います。

どんなものでしょうか。

ちなみにこの提案のきっかけは [ruby-dev:39677] です。

diff --git a/variable.c b/variable.c
index 779a8e8..db3a81c 100644
--- a/variable.c
+++ b/variable.c
@@ -1525,6 +1525,33 @@ rb_autoload_p(VALUE mod, ID id)
return load && (file = load->nd_lit) ? file : Qnil;
}

+void
+rb_change_const_visibility(VALUE klass, ID id, VALUE ex)
+{

  • VALUE value, tmp;
  • int mod_retry = 0;
  • tmp = klass;
  • while (RTEST(tmp)) {
  • VALUE am = 0;
  • while (RCLASS_IV_TBL(tmp) && st_lookup(RCLASS_IV_TBL(tmp), (st_data_t)id, &value)) {
  •  if (value == Qundef) {
    
  •  if (am == tmp) break;
    
  •  am = tmp;
    
  •  rb_autoload_load(tmp, id);
    
  •  continue;
    
  •  }
    
  •  if (tmp == rb_cObject && klass != rb_cObject) {
    
  •  rb_warn("toplevel constant %s referenced by %s::%s",
    
  •  	rb_id2name(id), rb_class2name(klass), rb_id2name(id));
    
  •  }
    
  •  RARRAY_PTR(value)[1] = ex;
    
  •  return;
    
  • }
  • tmp = RCLASS_SUPER(tmp);
  • }
    +}

static VALUE
rb_const_get_0(VALUE klass, ID id, int exclude, int recurse)
{
@@ -1546,7 +1573,10 @@ rb_const_get_0(VALUE klass, ID id, int exclude, int recurse)
rb_warn("toplevel constant %s referenced by %s::%s",
rb_id2name(id), rb_class2name(klass), rb_id2name(id));
}

  •  return value;
    
  •  if (!RARRAY_PTR(value)[1]) {
    
  •  rb_raise(rb_eRuntimeError, "private constant");
    
  •  }
    
  •  return RARRAY_PTR(value)[0];
    
    }
    if (!recurse && klass != rb_cObject) break;
    tmp = RCLASS_SUPER(tmp);
    @@ -1798,11 +1828,13 @@ mod_av_set(VALUE klass, ID id, VALUE val, int isconst)
    void
    rb_const_set(VALUE klass, ID id, VALUE val)
    {
  • VALUE ary;
    if (NIL_P(klass)) {
    rb_raise(rb_eTypeError, "no class/module to define constant %s",
    rb_id2name(id));
    }
  • mod_av_set(klass, id, val, TRUE);
  • ary = rb_ary_new3(2, val, Qtrue);
  • mod_av_set(klass, id, ary, TRUE);
    }

void
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 0660c7d..e3e789e 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1141,7 +1141,7 @@ vm_get_ev_const(rb_thread_t *th, const rb_iseq_t *iseq,
return 1;
}
else {

  •  	    return val;
    
  •  	    return RARRAY_PTR(val)[0];
     	}
         }
     }
    

diff --git a/vm_method.c b/vm_method.c
index 557583f..40b125a 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -1038,6 +1038,30 @@ rb_mod_private_method(int argc, VALUE *argv, VALUE obj)
return obj;
}

+static void
+set_const_visibility(VALUE self, int argc, VALUE *argv, VALUE ex)
+{

  • int i;
  • extern void rb_change_const_visibility(VALUE klass, ID id, VALUE ex);
  • secure_visibility(self);
  • for (i = 0; i < argc; i++) {
  • rb_change_const_visibility(self, rb_to_id(argv[i]), ex);
  • }
  • rb_clear_cache_by_class(self);
    +}

+static VALUE
+rb_mod_public_constant(int argc, VALUE *argv, VALUE obj)
+{

  • set_const_visibility(obj, argc, argv, Qtrue);
    +}

+static VALUE
+rb_mod_private_constant(int argc, VALUE *argv, VALUE obj)
+{

  • set_const_visibility(obj, argc, argv, Qfalse);
    +}

/*

  • call-seq:
  • public
    

@@ -1250,6 +1274,8 @@ Init_eval_method(void)
rb_define_method(rb_cModule, "protected_method_defined?", rb_mod_protected_method_defined, 1);
rb_define_method(rb_cModule, "public_class_method", rb_mod_public_method, -1);
rb_define_method(rb_cModule, "private_class_method", rb_mod_private_method, -1);

  • rb_define_method(rb_cModule, "public_constant", rb_mod_public_constant, -1);

  • rb_define_method(rb_cModule, "private_constant", rb_mod_private_constant, -1);

    rb_define_singleton_method(rb_vm_top_self(), "public", top_public, -1);
    rb_define_singleton_method(rb_vm_top_self(), "private", top_private, -1);

--
Yusuke Endoh
=end


Files

const_tbl.patch (10 KB) const_tbl.patch mame (Yusuke Endoh), 11/15/2009 03:50 AM
const_entry.patch (10.1 KB) const_entry.patch mame (Yusuke Endoh), 11/15/2009 03:50 AM
private_constant.patch (3.3 KB) private_constant.patch mame (Yusuke Endoh), 11/15/2009 03:50 AM
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0