Permalink
Browse files

Metakoans.

  • Loading branch information...
1 parent 2a2d36e commit 60fc76c2dee5be9c36e013e9a1e5899e029ef3ee @mattwildig committed Mar 7, 2012
Showing with 450 additions and 0 deletions.
  1. +3 −0 67_metakoans/.gitignore
  2. +6 −0 67_metakoans/extconf.rb
  3. +122 −0 67_metakoans/knowledge.c
  4. +24 −0 67_metakoans/knowledge.rb
  5. +295 −0 67_metakoans/metakoans.rb
View
@@ -0,0 +1,3 @@
+*.o
+*.bundle
+Makefile
View
@@ -0,0 +1,6 @@
+require 'mkmf'
+
+$warnflags.gsub! /-Wdeclaration-after-statement/, '' if $warnflags
+$CFLAGS << " -std=c99"
+
+create_makefile 'knowledge'
View
@@ -0,0 +1,122 @@
+#include "ruby.h"
+
+static ID meth_name_id;
+static ID to_s_id;
+
+static VALUE evalulator;
+static ID evalualte_id;
+
+static VALUE current_method(){
+ VALUE sym = rb_funcall(rb_cObject, meth_name_id, 0);
+ return rb_funcall(sym, to_s_id, 0);
+}
+
+static char* ivar_name() {
+ VALUE name = current_method();
+ char last = RSTRING_PTR(name)[RSTRING_LEN(name)-1];
+ char* ivar;
+ int strip = 0;
+ size_t len;
+
+ if(last == '=' || last == '?'){
+ strip = 1;
+ }
+
+ len = RSTRING_LEN(name) + 2 - strip;
+ ivar = malloc(len);
+ memcpy(ivar + 1, RSTRING_PTR(name), len - 2);
+ ivar[0] = '@';
+ ivar[len - 1] = 0;
+
+ return ivar;
+}
+
+static VALUE do_block(VALUE yielded, VALUE proc, int argc, VALUE argv[]) {
+ return rb_proc_call(proc, rb_ary_new());
+}
+
+static VALUE get(VALUE self){
+ char* ivar = ivar_name();
+ VALUE val = Qnil;
+
+ if (rb_ivar_defined(self, rb_intern(ivar))) {
+ val = rb_iv_get(self, ivar);
+ }
+ else {
+ VALUE class = RBASIC(self)->klass; //need to check singletons / metaclasses etc, so rb_obj_class is no good
+ while (val == Qnil && class != rb_cObject) {
+ //if it's an ICLASS, we need to check the original
+ VALUE check_class = (RBASIC(class)->flags & T_ICLASS) ? RBASIC(class)->klass : class;
+ val = rb_iv_get(check_class, ivar +1);
+ class = RCLASS_SUPER(class);
+ }
+
+ if (rb_obj_is_proc(val)) {
+ val = rb_funcall(evalulator, evalualte_id, 2, self, val);
+ }
+ }
+
+ free(ivar);
+ return val;
+}
+
+static VALUE set(VALUE self, VALUE new_val){
+ char* ivar = ivar_name();
+ rb_iv_set(self, ivar, new_val);
+
+ free(ivar);
+ return Qnil;
+}
+
+static VALUE attribute(VALUE, VALUE);
+
+static int hash_entry(VALUE key, VALUE value, VALUE mod){
+ attribute(mod, key);
+ rb_iv_set(mod, StringValuePtr(key), value);
+ return ST_CONTINUE;
+}
+
+static VALUE attribute(VALUE self, VALUE name){
+
+ VALUE tmp = rb_check_hash_type(name);
+ if (!NIL_P(tmp)) {
+ rb_hash_foreach(tmp, hash_entry, self);
+ return Qnil;
+ }
+
+ char* c_name = StringValuePtr(name);
+ size_t name_len = RSTRING_LEN(name);
+ char* query = malloc(name_len + 2);
+ memcpy(query, c_name, name_len);
+ query[name_len + 1] = 0;
+
+ rb_define_method(self, c_name, get, 0);
+ query[name_len] = '?';
+ rb_define_method(self, query, get, 0);
+ query[name_len] = '=';
+ rb_define_method(self, query, set, 1);
+
+ free(query);
+
+ //set default
+ VALUE default_val = Qnil;
+ if (rb_block_given_p()) {
+ default_val = rb_block_proc();
+ }
+ rb_iv_set(self, c_name, default_val);
+
+ return Qnil;
+}
+
+void Init_knowledge() {
+ meth_name_id = rb_intern("__method__");
+ to_s_id = rb_intern("to_s");
+
+ //the only way to evaluate a block in another context is in Ruby...
+ evalulator = rb_class_new_instance(0, NULL, rb_cObject);
+ VALUE eval_method = rb_str_new2("def eval_in_context(context, proc); context.instance_eval &proc;end");
+ rb_obj_instance_eval(1, &eval_method, evalulator);
+ evalualte_id = rb_intern("eval_in_context");
+
+ rb_define_method(rb_cModule, "attribute", attribute, 1);
+}
View
@@ -0,0 +1,24 @@
+class Module
+ def attribute name, &blk
+
+ name, default = name.is_a?(Hash) ? name.to_a.first : [name, nil]
+
+ ivar = "@#{name}"
+
+ define_method name do
+ if instance_variables.include? ivar.to_sym #symbols in 1.9
+ instance_variable_get ivar
+ else
+ blk ? instance_eval(&blk) : default
+ end
+ end
+
+ define_method "#{name}?" do
+ !!__send__(name)
+ end
+
+ define_method "#{name}=" do |val|
+ instance_variable_set ivar, val
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 60fc76c

Please sign in to comment.