public
Created

  • Download Gist
monkeygems.c
C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
/*
* WARNING: This code has a few oversights, and is not really safe to use for production,
* etc. If you want to play with it in dev, though, it won't eat your children or anything.
*
* In particular, it doesn't necessarily choose the correct option if multiple items on the
* load path match the same require path. That wouldn't be hard, but I didn't get around to it.
*/
 
#include "ruby.h"
#include <string.h>
#include <stdio.h>
 
static long cached = 0;
static struct st_table *mapped_load_path_items;
static struct st_table *require_fullpaths;
 
VALUE monkeygems_require(VALUE obj, VALUE fname)
{
int i, j;
long load_path_item_size;
char *glob;
st_data_t data;
VALUE load_path, files;
char *entryFullpath;
char *entryShortpath;
char *stringName;
load_path = rb_gv_get("$:");
 
if (TYPE(fname) == T_STRING) {
stringName = RSTRING_PTR(fname);
} else {
stringName = RSTRING_PTR(rb_funcall(fname, rb_intern("to_s"), 0));
}
 
if (RARRAY_LEN(load_path) > cached) {
printf("*> Rebuilding cache: %ld -> %ld\n", cached, RARRAY_LEN(load_path));
cached = RARRAY_LEN(load_path);
for (i = 0; i < RARRAY_LEN(load_path); i++) {
VALUE load_path_item = RARRAY_PTR(load_path)[i];
if (TYPE(load_path_item) != T_STRING)
load_path_item = rb_funcall(load_path_item, rb_intern("to_s"), 0);
data = 0;
st_lookup(mapped_load_path_items, (st_data_t)RSTRING_PTR(load_path_item), &data);
if (data) continue;
st_insert(mapped_load_path_items, (st_data_t)RSTRING_PTR(load_path_item), (st_data_t)1);
if (RSTRING_PTR(load_path_item)[0] != '/') continue;
load_path_item_size = RSTRING_LEN(load_path_item);
 
glob = malloc(RSTRING_LEN(load_path_item) + 16);
strcpy(glob, RSTRING_PTR(load_path_item));
strcpy(glob + RSTRING_LEN(load_path_item), "/**/*.rb");
files = rb_funcall(rb_cDir, rb_intern("glob"), 1, rb_str_new2(glob));
free(glob);
 
for (j = 0; j < RARRAY_LEN(files) ; j++) {
entryFullpath = strdup(RSTRING_PTR(RARRAY_PTR(files)[j]));
entryShortpath = malloc(strlen(entryFullpath));
strcpy(entryShortpath, entryFullpath + load_path_item_size + 1);
entryShortpath[strlen(entryShortpath) - 3] = 0;
st_insert(require_fullpaths, (st_data_t)entryShortpath, (st_data_t)entryFullpath);
}
}
}
 
data = 0;
if (st_lookup(require_fullpaths, (st_data_t)stringName, &data)) {
return rb_f_require(obj, rb_str_new2((char *)data));
} else {
return rb_f_require(obj, fname);
}
}
 
void Init_monkeygems()
{
mapped_load_path_items = st_init_numtable();
require_fullpaths = st_init_strtable();
/*
* Bundler resets require to `gem_original_require` to undo
* rubygems' overwriting of require.
*
* So here we overwrite gem_original_require with our own extra
* logic that we would like preserved after bundler takes over.
*
* *cue Inception theme*
*/
rb_define_method(rb_mKernel, "gem_original_require", monkeygems_require, 1);
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.