Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
$VERBOSE = nil
require File.expand_path('../rooby', __FILE__)
Person = Rooby::Class.new 'Person' do
define :initialize do |name|
@name = name
end
define :name do
@name
end
end
p Person.new('Alice').name # "Alice"
p Person.new('Bob').name # "Bob"
JSON = Rooby::Module.new 'JSON' do
define :to_json do
'{"hello": "world"}'
end
end
Sum = Rooby::Module.new 'Sum' do
define :wat do |a, b|
a + b
end
end
Product = Rooby::Module.new 'Product' do
define :wat do |a, b|
a * b
end
end
Log = Rooby::Module.new 'Log' do
define :wat do |a, b|
Math.log(a) / Math.log(b)
end
end
Foo = Rooby::Class.new 'Foo' do
include JSON
include Sum
metaclass.define :a_singleton_method do
[name, :this_is_cool]
end
define :hello do
:hello_there
end
define :wat do |a, b|
a ** b
end
define :method_missing do |sym, *args|
[:missing, sym, args]
end
end
Bar = Rooby::Class.new 'Bar', Foo
p Foo.ancestors # [Foo, Sum, JSON, Kernel]
p Foo.instance_methods.sort # [:extend, :hello, :inspect, :is_a?, :method, :method_missing, :methods,
# :respond_to?, :to_json, :to_s, :wat]
p Foo.a_singleton_method # ["Foo", :this_is_cool]
p Bar.a_singleton_method # ["Bar", :this_is_cool]
p Foo.new.is_a?(Foo) # true
p Foo.new.is_a?(Bar) # false
p Bar.new.is_a?(Foo) # true
p Bar.new.is_a?(Bar) # true
p Bar.new.is_a?(JSON) # true
p Foo.new.method(:hello) # <Method: <Foo:5af2d6dd>#hello>
p Foo.new.no_method(42) # [:missing, :no_method, [42]]
p Foo.new.hello # :hello_there
p Foo.new.to_json # "{\"hello\": \"world\"}"
p Foo.new.wat(3,4) # 81
Foo.prepend(Product)
p Foo.ancestors # [Product, Foo, Sum, JSON, Kernel]
p Foo.new.wat(3,4) # 12
o = Foo.new
p o.wat(3,4) # 12
o.extend(Log)
p o.wat(3,4) # 0.7924812503605781
# A simplified version of Ruby's object system, implemented in Ruby. This only
# supports a subset of Ruby, but is intended to accurately model inheritance
# and method lookup in as little code as possible, for ease of understanding by
# Ruby programmers.
#
# Classes implemented:
#
# * Object
# * Module < Object
# * Class < Module
# * Kernel, a module included in all Classes
# * UnboundMethod
# * Method
#
# This model implements the main features of Ruby's inheritance system:
#
# * Instance method inheritance with #include
# * Instance method inheritance with #prepend
# * Instance and singleton method inheritance through subclassing
# * Metaclasses, singleton methods and #extend
#
# It does diverge from Ruby on some edge cases. This has the effect of keeping
# the implementation simple while fixing what I consider to be bugs in the
# language ;)
#
# * The double-inclusion problem does not exist in this implementation
# * Class does not violate Liskov -- classes can be passed to #include
# * The superclass of a Class can be a Module
# * Metaclasses can be instantiated and subclassed
#
# Basically, a Class is just a Module that can be instantiated and that will
# inherit the singleton methods of its superclass.
module Rooby
class Object
RESERVED = [:instance_eval, :instance_exec, :==]
(instance_methods - RESERVED).each { |m| undef_method m }
attr_reader :class, :object_id
def initialize(klass = nil)
@class = klass
@object_id = rand(2**32)
end
def metaclass
@meta ||= Class.new(self, self.class)
end
def __send__(method_name, *args)
if method = metaclass.instance_method(method_name)
method.bind(self).call(*args)
elsif method = metaclass.instance_method(:method_missing)
method.bind(self).call(method_name, *args)
else
raise NoMethodError, "Undefined method `#{method_name}' for #{self}"
end
end
alias :method_missing :__send__
end
class Module < Object
def initialize(name = nil, &block)
@name = name
@methods = {}
@includes = []
@prepends = []
instance_eval(&block) if block_given?
end
def ===(object)
object.metaclass.ancestors.include?(self)
end
def ancestors(list = [])
@includes.each { |m| m.ancestors(list) }
list.unshift(self) unless list.include?(self)
@prepends.each { |m| m.ancestors(list) }
list
end
def define(method_name, &block)
name = method_name.to_sym
@methods[name] = UnboundMethod.new(self, name, block)
end
def include(mixin)
@includes << mixin
end
def prepend(mixin)
@prepends << mixin
end
def instance_method(method_name, include_ancestors = true)
if include_ancestors
ancestor = ancestors.find { |a| a.instance_methods(false).include?(method_name) }
ancestor && ancestor.instance_method(method_name, false)
else
@methods[method_name]
end
end
def instance_methods(include_ancestors = true, list = [])
if include_ancestors
@includes.each { |m| m.instance_methods(true, list) }
@prepends.each { |m| m.instance_methods(true, list) }
end
@methods.each_key { |m| list << m unless list.include?(m) }
list
end
def name
case @name
when String then @name
else "<#{Class === self ? 'Class' : 'Module'}:#{@name}>"
end
end
alias :inspect :name
end
class UnboundMethod < Object
attr_reader :name
def initialize(_module, name, block)
@module = _module
@name = name
@block = block
end
def bind(receiver)
Method.new(self, receiver, @block)
end
def inspect
"<UnboundMethod: #{@module}##{name}>"
end
end
class Method < Object
def initialize(unbound, receiver, block)
@unbound = unbound
@receiver = receiver
@block = block
end
def call(*args)
@receiver.instance_exec(*args, &@block)
end
def inspect
"<Method: #{@receiver}##{@unbound.name}>"
end
end
Kernel = Module.new 'Kernel' do
define :extend do |mixin|
metaclass.include(mixin)
end
define :inspect do
"<#{self.class.name}:#{object_id.to_s 16}>"
end
define :is_a? do |type|
type === self
end
define :method do |method_name|
metaclass.instance_method(method_name).bind(self)
end
define :methods do
metaclass.instance_methods
end
define :respond_to? do |method_name|
metaclass.instance_method(method_name) ? true : false
end
define :to_s do
inspect
end
end
class Class < Module
attr_reader :superclass
def initialize(name = nil, superclass = nil, &block)
super(name, &block)
@superclass = superclass
if superclass
@includes.unshift(superclass)
metaclass.include(superclass.metaclass)
end
@includes.unshift(Kernel)
end
def new(*args)
object = Object.new(self)
object.__send__(:initialize, *args) if object.respond_to?(:initialize)
object
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.