Skip to content

Instantly share code, notes, and snippets.

@burke
Created September 11, 2012 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save burke/3698714 to your computer and use it in GitHub Desktop.
Save burke/3698714 to your computer and use it in GitHub Desktop.
/*
* 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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment