Skip to content

Instantly share code, notes, and snippets.

@matsadler
Created February 26, 2022 00:57
Show Gist options
  • Save matsadler/d4e6d595c97f06c15d7e0edab11b1e18 to your computer and use it in GitHub Desktop.
Save matsadler/d4e6d595c97f06c15d7e0edab11b1e18 to your computer and use it in GitHub Desktop.
A bad idea

BindingHash

A bad idea

Build

To build the C-extension, from within the directory containing this project, run:

ruby extconf.rb

This will generate a Makefile, extconf.h, and mkmf.log. Then run

make

This will generate example.bundle (on macOS, example.so on linux).

Run

Once the C-extension has been built, run:

ruby test.rb
#include "ruby.h"
#include "extconf.h"
VALUE local_hash_new(VALUE klass) {
VALUE argv[1] = {rb_binding_new()};
return rb_class_new_instance(1, argv, klass);
}
void Init_binding_hash()
{
VALUE klass = rb_define_class("LocalHash", rb_cObject);
rb_define_singleton_method(klass, "new", local_hash_new, 0);
}
require_relative "local_hash"
require_relative "binding_hash.so"
class BindingHash < LocalHash
def keys
super | (@binding.receiver.methods - Object.methods)
end
def key?(key)
super || @binding.receiver.respond_to?(key, true)
end
def get(key)
return @binding.receiver.send(key) if @binding.receiver.respond_to?(key, true)
super
end
end
require 'mkmf'
create_header
create_makefile 'binding_hash'
class LocalHash
include Enumerable
NONE = Object.new
private_constant :NONE
def initialize(binding)
@binding = binding
@default = nil
@default_proc = false
end
def default(key=NONE)
return @default.call(self, key) if @default_proc && key != NONE
@default
end
def default=(value)
@default_proc = false
@default = value
end
def default_proc
@default if @default_proc
end
def default_proc=(value)
pr = value.to_proc if value.respond_to?(:to_proc)
unless Proc === pr
raise TypeError, "wrong default_proc type #{value.class.name} (expected Proc)"
end
@default_proc = true
@default = pr
end
def key?(key)
@binding.local_variable_defined?(key)
end
def fetch(key, default=NONE)
if key?(key)
get(key)
elsif block_given?
warn "warning: block supersedes default value argument" if default != NONE
yield key
elsif default != NONE
default
else
raise KeyError, "key not found: #{key.inspect}"
end
end
def [](key)
fetch(key) {default}
end
def []=(key, value)
@binding.local_variable_set(key, value)
end
def keys
@binding.local_variables
end
def each
return to_enum unless block_given?
keys.each do |k|
yield k, get(k)
end
end
alias inspect to_s
private
def get(key)
@binding.local_variable_get(key)
end
end
require_relative "binding_hash"
foo = 1
bar = 2
def baz
3
end
h = BindingHash.new
p h[:foo] #=> 1
p h[:bar] #=> 2
p h[:baz] #=> 3
p h[:h] #=> BindingHash
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment