# TITLE:
#
# BasicObject
#
# SUMMARY:
#
# BasicObject provides an abstract base class with
# essentially no predefined methods.
#
# COPYRIGHT:
#
# Copyright (c) 2005 Thomas Sawyer, Jim Weirich
#
# LICENSE:
#
# Ruby License
#
# This module is free software. You may use, modify, and/or redistribute this
# software under the same terms as Ruby.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.
#
# AUTHORS:
#
# - Jim Weirich
# - Thomas Sawyer
#
# HISTORY:
#
# Thanks to Jim Weirich for BlankSlate Copyright (c) 2004 by Jim Weirich,
# on which BasicObject is based.
#
# TODOs:
#
# - TODO Might be nice it there were a factory method to alter which methods
# were excluded. But probably too dangerous.
require 'facets/kernel/object'
require 'facets/kernel/super'
# = BasicObject
#
# BasicObject provides an abstract base class with no predefined
# methods, except for respond_to?, any method starting in
# \_\_ (two underscore, like \_\_id__) as well as
# any method starting with instance_.
#
# BasicObject is useful as a base class when writing classes that
# depend upon method_missing (e.g. dynamic proxies).
#
# The patterns used to reserve methods are:
#
# /^__/, /^instance/, /^object/, /\?$/, /^\W$/,
# 'initialize', 'initialize_copy', 'inspect', 'dup', 'clone', 'null', 'as'
#
# By default these are the reserved methods:
#
# == __id__ __self__ __send__ as clone dup eql? equal? frozen?
# initialize inspect instance_eval instance_of? instance_variable_get
# instance_variable_set instance_variables is_a? kind_of? nil? null object_class
# respond_to? tainted?
#
# In practice only 'as', 'clone', 'dup' and 'null' have much chance of name clash.
# So be especially aware of these four. All the rest either begin with a '__',
# end in a '?' mark or start with the word 'instance' or 'object'.
#
# The special method #object_self allows access to the underlying object via a
# specialized Functor-style class access via as(Object). This binds the
# actual self to the subsequently called methods of Object instancea methods.
# So even though a method may no longer be defined for BasicObject it can still
# be called via this interface.
#
# class A < BasicObject
# end
#
# a.object_self.class #=> A
# a.class #=> NoMethodError
#
# Note that #object_self used to be called __self__. Also provided is #object_class.
class BasicObject
# Returns the Self functor class, which can then be used to
# call Kernel/Object methods on the current object.
def object_self
@__object_self__ ||= As.new(self, ::Object)
end
alias :__self__ :object_self # deprecate
#--
# The Self class allows one to get access the hidden Object/Kernel methods.
# It is essentially a specialized Functor which binds an Object/Kernel
# method to the current object for the current call.
#class Self < self
# def initialize(obj, as=nil)
# @obj = obj
# @as = as || ::Object
# end
# def method_missing(meth, *args, &blk)
# @as.instance_method(meth).bind(@obj).call(*args, &blk)
# end
#end
# This method is like #__self__, but allows any ancestor
# to act on behalf of self, not just Object.
#
#def __as__( ancestor )
# Self.new( self, ancestor )
#end
#alias_method :as, :__as__
# Shadow some other important methods.
#alias_method :__eval__, :eval
#alias_method :__method__, :method
#++
# Methods not to get rid of as they are either too important, or
# they are not likely to get in the way (such as methods ending in '?').
#
# In Ruby 1.9 BasicObject has only these methods:
# [ /^__/, "funcall", "send", "respond_to?", "equal?", "==", "object_id" ]
#
# NOTE The absolute bare minimum is EXCLUDE = /^(__|instance_eval$)/.
# But in most cases you'll want a few extra methods like #dup too.
#
#--
# TODO In the future it might be nice to allow some selectability
# in these via a factory method.
#++
EXCLUDE = [
/^__/, /^instance_/, /^object_/, /\?$/, /^\W$/,
'initialize', 'initialize_copy', 'inspect', 'dup', 'clone', 'null', 'as'
]
# Undef unwanted method as long as it doesn't match anything in the EXCLUDE list.
def self.hide(name)
#if instance_methods.include?(name.to_s) and name !~ EXCLUDE #/^(#{EXCLUDE.join('|')})/
#if name !~ EXCLUDE and
case name
when *EXCLUDE
# do nothing
else
#if ( public_instance_methods.include?(name.to_s) or
# private_instance_methods.include?(name.to_s) or
# protected_instance_methods.include?(name.to_s)
# )
undef_method name rescue nil
#end
end
end
# remove all methods
public_instance_methods(true).each { |m| hide(m) }
private_instance_methods(true).each { |m| hide(m) }
protected_instance_methods(true).each { |m| hide(m) }
#--
# If the method isn't defined and has the form '__method__'
# then try sending 'method' to self-as-Object. Remember
# this will not have any effect if you override it!
#def method_missing( sym, *args, &blk )
# if md = /^__(.+)__$/.match( sym.to_s )
# as(Object).__send__( md[1], *args, &blk )
# else
# super
# end
#end
#++
end
# Since Ruby is very dynamic, methods added to the ancestors of
# BasicObject after BasicObject is defined will show up in the
# list of available BasicObject methods. We handle this by defining
# hooks in Object, Kernel and Module that will hide any defined.
module Kernel
class << self
madded = method(:method_added)
define_method(:method_added) do |name|
r = madded.call(name)
return r if self != Kernel
BasicObject.hide(name)
r
end
end
end
# See Kernel callback.
class Object
class << self
madded = method(:method_added)
define_method(:method_added) do |name|
r = madded.call(name)
return r if self != Object
BasicObject.hide(name)
r
end
end
end
# Also, modules included into Object need to be scanned and have their
# instance methods removed from blank slate. In theory, modules
# included into Kernel would have to be removed as well, but a
# "feature" of Ruby prevents late includes into modules from being
# exposed in the first place.
=begin
class Module
if method_defined?(:append_features)
mappended = method(:append_features)
else
mappended = nil
end
define_method(:append_features) do |mod|
r = mappended.call(mod) if mappended
return r if mod != Object
public_instance_methods(true).each { |name| BasicObject.hide(name) }
private_instance_methods(true).each { |name| BasicObject.hide(name) }
public_instance_methods(true).each { |name| BasicObject.hide(name) }
r
end
end
=end