From: Nobuyoshi Nakada Date: 2008-08-20T11:13:17+09:00 Subject: [ruby-core:18349] [Feature:1.9] autoload with a block Hi, At Mon, 18 Aug 2008 10:35:01 +0900, Nobuyoshi Nakada wrote in [ruby-talk:311603]: > At Fri, 15 Aug 2008 15:57:21 +0900, > David Masover wrote in [ruby-talk:311365]: > > autoload :Foo do > > require 'foo' > > Foo.autoload :Bar ... > > end > > > > In that simplest form, it would at least centralize all my loading stuff in > > one place. In a more meta form, I could run through some search paths I'm > > likely to use (in my own app, for instance), and generate an autoload scheme > > based on that. > > I don't think it makes things simpler, but it sounds > interesting. You can file it in the ITS, if you want. > Although the OP doesn't seem to intend to request this feature, I move it to ruby-core since it feels interesting. Index: load.c =================================================================== --- load.c (revision 18653) +++ load.c (working copy) @@ -618,10 +618,29 @@ ruby_init_ext(const char *name, void (*i static VALUE -rb_mod_autoload(VALUE mod, VALUE sym, VALUE file) +rb_mod_autoload(int argc, VALUE *argv, VALUE mod) { - ID id = rb_to_id(sym); - - Check_SafeStr(file); - rb_autoload(mod, id, RSTRING_PTR(file)); + extern VALUE rb_autoload_proc(VALUE, ID, VALUE, VALUE); + ID id; + VALUE block = rb_block_given_p() ? rb_block_proc() : Qnil; + VALUE arg = Qnil; + + switch (argc) { + case 2: + arg = argv[1]; + break; + case 1: + if (!NIL_P(block)) break; + default: + rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc); + } + id = rb_to_id(argv[0]); + if (NIL_P(block)) { + VALUE file = argv[1]; + Check_SafeStr(file); + rb_autoload(mod, id, RSTRING_PTR(file)); + } + else { + rb_autoload_proc(mod, id, block, arg); + } return Qnil; } @@ -649,5 +668,5 @@ rb_mod_autoload_p(VALUE mod, VALUE sym) static VALUE -rb_f_autoload(VALUE obj, VALUE sym, VALUE file) +rb_f_autoload(int argc, VALUE *argv, VALUE obj) { VALUE klass = rb_vm_cbase(); @@ -655,5 +674,5 @@ rb_f_autoload(VALUE obj, VALUE sym, VALU rb_raise(rb_eTypeError, "Can not set autoload on singleton class"); } - return rb_mod_autoload(klass, sym, file); + return rb_mod_autoload(argc, argv, klass); } @@ -693,7 +712,7 @@ Init_load() rb_define_global_function("load", rb_f_load, -1); rb_define_global_function("require", rb_f_require, 1); - rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2); + rb_define_method(rb_cModule, "autoload", rb_mod_autoload, -1); rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1); - rb_define_global_function("autoload", rb_f_autoload, 2); + rb_define_global_function("autoload", rb_f_autoload, -1); rb_define_global_function("autoload?", rb_f_autoload_p, 1); Index: variable.c =================================================================== --- variable.c (revision 18653) +++ variable.c (working copy) @@ -1322,16 +1322,12 @@ check_autoload_table(VALUE av) } -void -rb_autoload(VALUE mod, ID id, const char *file) +typedef VALUE rb_autoload_func(VALUE, ID, VALUE, VALUE); + +static void +autoload_callback(VALUE mod, ID id, rb_autoload_func *func, VALUE arg1, VALUE arg2) { - VALUE av, fn; + VALUE av; struct st_table *tbl; - - if (!rb_is_const_id(id)) { - rb_raise(rb_eNameError, "autoload must be constant name: %s", rb_id2name(id)); - } - if (!file || !*file) { - rb_raise(rb_eArgError, "empty file name"); - } + NODE *n; if ((tbl = RCLASS_IV_TBL(mod)) && st_lookup(tbl, id, &av) && av != Qundef) @@ -1348,8 +1344,61 @@ rb_autoload(VALUE mod, ID id, const char DATA_PTR(av) = tbl = st_init_numtable(); } + + n = rb_node_newnode(NODE_MEMO, (VALUE)func, arg1, arg2); + st_insert(tbl, id, (st_data_t)n); +} + +static VALUE +autoload_load(VALUE klass, ID id, VALUE file, VALUE safe) +{ + return rb_require_safe(file, (int)safe); +} + +static void +check_autoload_id(ID id) +{ + if (!rb_is_const_id(id)) { + rb_raise(rb_eNameError, "autoload must be constant name: %s", rb_id2name(id)); + } +} + +void +rb_autoload(VALUE mod, ID id, const char *file) +{ + VALUE fn; + + check_autoload_id(id); + if (!file || !*file) { + rb_raise(rb_eArgError, "empty file name"); + } + fn = rb_str_new2(file); FL_UNSET(fn, FL_TAINT); OBJ_FREEZE(fn); - st_insert(tbl, id, (st_data_t)rb_node_newnode(NODE_MEMO, fn, rb_safe_level(), 0)); + + autoload_callback(mod, id, autoload_load, fn, (VALUE)rb_safe_level()); +} + +void +rb_autoload_callback(VALUE mod, ID id, rb_autoload_func *func, VALUE arg1, VALUE arg2) +{ + check_autoload_id(id); + autoload_callback(mod, id, func, arg1, arg2); +} + +static VALUE +autoload_proc_call(VALUE mod, ID id, VALUE proc, VALUE arg) +{ + VALUE args[3]; + args[0] = mod; + args[1] = ID2SYM(id); + args[2] = arg; + return rb_proc_call_with_block(proc, 3, args, Qnil); +} + +void +rb_autoload_proc(VALUE mod, ID id, VALUE proc, VALUE arg) +{ + rb_autoload_callback(mod, id, autoload_proc_call, proc, arg); } @@ -1382,25 +1431,34 @@ VALUE rb_autoload_load(VALUE klass, ID id) { - VALUE file; NODE *load = autoload_delete(klass, id); - if (!load || !(file = load->nd_lit)) { + if (!load) { return Qfalse; } - return rb_require_safe(file, load->nd_nth); + return load->u1.cfunc(klass, id, load->u2.value, load->u3.value); } static VALUE -autoload_file(VALUE mod, ID id) +autoload_p(VALUE mod, ID id) { VALUE val, file; struct st_table *tbl; - st_data_t load; + st_data_t data; + NODE *load; - if (!st_lookup(RCLASS_IV_TBL(mod), autoload, &val) || - !(tbl = check_autoload_table(val)) || !st_lookup(tbl, id, &load)) { + if (!st_lookup(RCLASS_IV_TBL(mod), autoload, &data) || + !(tbl = check_autoload_table(val = (VALUE)data)) || + !st_lookup(tbl, id, &data)) { return Qnil; } - file = ((NODE *)load)->nd_lit; + load = (NODE *)data; + if (load->u1.cfunc == autoload_proc_call) { + /* rb_obj_is_proc() */ + return load->u2.value; + } + if (load->u1.cfunc != autoload_load) { + return Qtrue; + } + file = load->u2.value; Check_Type(file, T_STRING); if (!RSTRING_PTR(file) || !*RSTRING_PTR(file)) { @@ -1433,5 +1491,5 @@ rb_autoload_p(VALUE mod, ID id) return Qnil; } - return autoload_file(mod, id); + return autoload_p(mod, id); } @@ -1630,5 +1688,5 @@ rb_const_defined_0(VALUE klass, ID id, i while (tmp) { if (RCLASS_IV_TBL(tmp) && st_lookup(RCLASS_IV_TBL(tmp), id, &value)) { - if (value == Qundef && NIL_P(autoload_file(klass, id))) + if (value == Qundef && NIL_P(autoload_p(klass, id))) return Qfalse; return Qtrue; -- Nobu Nakada