public
Last active

  • Download Gist
_test.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
$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
rooby.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
# 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

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.