Skip to content

Instantly share code, notes, and snippets.

@apeiros
Last active December 15, 2015 04:19
Show Gist options
  • Save apeiros/acca4c51a4ebca5cfa8e to your computer and use it in GitHub Desktop.
Save apeiros/acca4c51a4ebca5cfa8e to your computer and use it in GitHub Desktop.
require 'securerandom'
class BasicObject
def self.singleton_class_of(obj)
(class <<obj; self; end)
end
def self.basic_object?(obj)
class_of(obj) == ::BasicObject
end
def self.object_responds_to?(obj, method_name, priv=false)
sclass = singleton_class_of(obj)
sclass.method_defined?(method_name) && (priv || !sclass.private_method_defined?(method_name))
end
def self.inspect_object(obj)
obj.inspect
rescue ::NoMethodError
if ::BasicObject.basic_object? obj
"#<BasicObject:0x%014x>" % (obj.__id__<<1)
else
::Object.instance_method(:inspect).bind(obj).call
end
end
def self.get_instance_variable_of(obj, name)
obj.instance_variable_get(name)
rescue ::NoMethodError
if ::BasicObject.basic_object? obj
raise ::NameError, "`#{name}' is not allowed as an instance variable name" unless name =~ /\A@[A-Za-z_]\w*\z/
::BasicObject.instance_method(:instance_eval).bind(obj).call(name.to_s) # not adding another if just to check whether instance_eval had been removed
else
::Object.instance_method(:instance_variable_get).bind(obj).call(name)
end
end
def self.set_instance_variable_of(obj, name, value)
obj.instance_variable_set(name)
rescue ::NoMethodError
if ::BasicObject.basic_object? obj
raise ::NameError, "`#{name}' is not allowed as an instance variable name" unless name =~ /\A@[A-Za-z_]\w*\z/
uuid = ::SecureRandom.uuid
::Thread.current[uuid] = value
obj.instance_eval("#{name} = ::Thread.current['#{uuid}']")
::Thread.current[uuid] = nil # no way to delete? odd
else
::Object.instance_method(:instance_variable_set).bind(obj).call(name, value)
end
end
def self.instance_variables_of(obj)
::Object.instance_method(:instance_variables).bind(obj).call(*args, &block)
rescue TypeError
raise "Sorry Dave, I can't do that" # no way to implement that in pure ruby :(
end
def self.class_of(obj)
::Object.instance_method(:class).bind(obj).call
rescue ::TypeError
::BasicObject
end
def self.send_to(obj, method_name, *args, &block)
::BasicObject.instance_method(:__send__).bind(obj).call(method_name, *args, &block)
end
def self.instance_eval_in(obj, *args, &block)
::BasicObject.instance_method(:instance_eval).bind(obj).call(*args, &block)
end
def self.instance_exec_in(obj, *args, &block)
::BasicObject.instance_method(:instance_exec).bind(obj).call(*args, &block)
end
end
__END__
For 2.0:
define_method(:instance_variables, ::Kernel.instance_method(:instance_variables))
define_method(:instance_variable_set, ::Kernel.instance_method(:instance_variable_set))
InstanceVariablesMethod = instance_method(:instance_variables)
InstanceVariableSetMethod = instance_method(:instance_variable_set)
undef_method :instance_variables
undef_method :instance_variable_set
def instance_variables_of(obj)
obj.instance_variables
rescue ::NoMethodError
if ::BasicObject.basic_object? obj
raise NameError: "`#{name}' is not allowed as an instance variable name" unless name =~ /\A@[A-Za-z_]\w*\z/
::BasicObject::InstanceVariablesMethod.bind(obj).call
else
::Object.instance_method(:instance_variable_get).bind(obj).call
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment