first commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
MRuby::Gem::Specification.new('mruby-class-ext') do |spec|
|
||||
spec.license = 'MIT'
|
||||
spec.author = 'mruby developers'
|
||||
spec.summary = 'class/module extension'
|
||||
end
|
||||
@@ -0,0 +1,89 @@
|
||||
class Module
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# mod < other -> true, false, or nil
|
||||
#
|
||||
# Returns true if `mod` is a subclass of `other`. Returns
|
||||
# <code>nil</code> if there's no relationship between the two.
|
||||
# (Think of the relationship in terms of the class definition:
|
||||
# "class A < B" implies "A < B".)
|
||||
#
|
||||
def <(other)
|
||||
if self.equal?(other)
|
||||
false
|
||||
else
|
||||
self <= other
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# mod <= other -> true, false, or nil
|
||||
#
|
||||
# Returns true if `mod` is a subclass of `other` or
|
||||
# is the same as `other`. Returns
|
||||
# <code>nil</code> if there's no relationship between the two.
|
||||
# (Think of the relationship in terms of the class definition:
|
||||
# "class A < B" implies "A < B".)
|
||||
def <=(other)
|
||||
raise TypeError, 'compared with non class/module' unless other.is_a?(Module)
|
||||
if self.ancestors.include?(other)
|
||||
return true
|
||||
elsif other.ancestors.include?(self)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# mod > other -> true, false, or nil
|
||||
#
|
||||
# Returns true if `mod` is an ancestor of `other`. Returns
|
||||
# <code>nil</code> if there's no relationship between the two.
|
||||
# (Think of the relationship in terms of the class definition:
|
||||
# "class A < B" implies "B > A".)
|
||||
#
|
||||
def >(other)
|
||||
if self.equal?(other)
|
||||
false
|
||||
else
|
||||
self >= other
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# mod >= other -> true, false, or nil
|
||||
#
|
||||
# Returns true if `mod` is an ancestor of `other`, or the
|
||||
# two modules are the same. Returns
|
||||
# <code>nil</code> if there's no relationship between the two.
|
||||
# (Think of the relationship in terms of the class definition:
|
||||
# "class A < B" implies "B > A".)
|
||||
#
|
||||
def >=(other)
|
||||
raise TypeError, 'compared with non class/module' unless other.is_a?(Module)
|
||||
return other < self
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# module <=> other_module -> -1, 0, +1, or nil
|
||||
#
|
||||
# Comparison---Returns -1, 0, +1 or nil depending on whether `module`
|
||||
# includes `other_module`, they are the same, or if `module` is included by
|
||||
# `other_module`.
|
||||
#
|
||||
# Returns `nil` if `module` has no relationship with `other_module`, if
|
||||
# `other_module` is not a module, or if the two values are incomparable.
|
||||
#
|
||||
def <=>(other)
|
||||
return 0 if self.equal?(other)
|
||||
return nil unless other.is_a?(Module)
|
||||
cmp = self < other
|
||||
return -1 if cmp
|
||||
return 1 unless cmp.nil?
|
||||
return nil
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "mruby.h"
|
||||
#include "mruby/class.h"
|
||||
#include "mruby/string.h"
|
||||
|
||||
static mrb_value
|
||||
mrb_mod_name(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self));
|
||||
if (mrb_string_p(name)) {
|
||||
MRB_SET_FROZEN_FLAG(mrb_basic_ptr(name));
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_mod_singleton_class_p(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
return mrb_bool_value(mrb_sclass_p(self));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* module_exec(arg...) {|var...| block } -> obj
|
||||
* class_exec(arg...) {|var...| block } -> obj
|
||||
*
|
||||
* Evaluates the given block in the context of the
|
||||
* class/module. The method defined in the block will belong
|
||||
* to the receiver. Any arguments passed to the method will be
|
||||
* passed to the block. This can be used if the block needs to
|
||||
* access instance variables.
|
||||
*
|
||||
* class Thing
|
||||
* end
|
||||
* Thing.class_exec{
|
||||
* def hello() "Hello there!" end
|
||||
* }
|
||||
* puts Thing.new.hello()
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
mrb_mod_module_exec(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
const mrb_value *argv;
|
||||
mrb_int argc;
|
||||
mrb_value blk;
|
||||
struct RClass *c;
|
||||
|
||||
mrb_get_args(mrb, "*&!", &argv, &argc, &blk);
|
||||
|
||||
c = mrb_class_ptr(self);
|
||||
if (mrb->c->ci->acc < 0) {
|
||||
return mrb_yield_with_class(mrb, blk, argc, argv, self, c);
|
||||
}
|
||||
mrb->c->ci->target_class = c;
|
||||
return mrb_yield_cont(mrb, blk, self, argc, argv);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_mruby_class_ext_gem_init(mrb_state *mrb)
|
||||
{
|
||||
struct RClass *mod = mrb->module_class;
|
||||
|
||||
mrb_define_method(mrb, mod, "name", mrb_mod_name, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, mod, "singleton_class?", mrb_mod_singleton_class_p, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, mod, "module_exec", mrb_mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK());
|
||||
mrb_define_method(mrb, mod, "class_exec", mrb_mod_module_exec, MRB_ARGS_ANY()|MRB_ARGS_BLOCK());
|
||||
}
|
||||
|
||||
void
|
||||
mrb_mruby_class_ext_gem_final(mrb_state *mrb)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
assert 'Module#<' do
|
||||
a = Class.new
|
||||
b = Class.new(a)
|
||||
c = Class.new(a)
|
||||
d = Module.new
|
||||
e = Class.new { include d }
|
||||
f = Module.new { include d }
|
||||
|
||||
# compare class to class
|
||||
assert_true b < a
|
||||
assert_false b < b
|
||||
assert_false a < b
|
||||
assert_nil c < b
|
||||
|
||||
# compare class to module
|
||||
assert_true e < d
|
||||
assert_false d < e
|
||||
assert_nil a < d
|
||||
|
||||
# compare module to module
|
||||
assert_true f < d
|
||||
assert_false f < f
|
||||
assert_false d < f
|
||||
|
||||
assert_raise(TypeError) { a < Object.new }
|
||||
end
|
||||
|
||||
assert 'Module#<=' do
|
||||
a = Class.new
|
||||
b = Class.new(a)
|
||||
c = Class.new(a)
|
||||
d = Module.new
|
||||
e = Class.new { include d }
|
||||
f = Module.new { include d }
|
||||
|
||||
# compare class to class
|
||||
assert_true b <= a
|
||||
assert_true b <= b
|
||||
assert_false a <= b
|
||||
assert_nil c <= b
|
||||
|
||||
# compare class to module
|
||||
assert_true e <= d
|
||||
assert_false d <= e
|
||||
assert_nil a <= d
|
||||
|
||||
# compare module to module
|
||||
assert_true f <= d
|
||||
assert_true f <= f
|
||||
assert_false d <= f
|
||||
|
||||
assert_raise(TypeError) { a <= Object.new }
|
||||
end
|
||||
|
||||
assert 'Module#name' do
|
||||
module Outer
|
||||
class Inner; end
|
||||
const_set :SetInner, Class.new
|
||||
end
|
||||
|
||||
assert_equal 'Outer', Outer.name
|
||||
assert_equal 'Outer::Inner', Outer::Inner.name
|
||||
assert_equal 'Outer::SetInner', Outer::SetInner.name
|
||||
|
||||
outer = Module.new do
|
||||
const_set :SetInner, Class.new
|
||||
end
|
||||
Object.const_set :SetOuter, outer
|
||||
|
||||
assert_equal 'SetOuter', SetOuter.name
|
||||
assert_equal 'SetOuter::SetInner', SetOuter::SetInner.name
|
||||
|
||||
mod = Module.new
|
||||
cls = Class.new
|
||||
|
||||
assert_nil mod.name
|
||||
assert_nil cls.name
|
||||
end
|
||||
|
||||
assert 'Module#singleton_class?' do
|
||||
mod = Module.new
|
||||
cls = Class.new
|
||||
scl = (class <<cls; self; end)
|
||||
|
||||
assert_false mod.singleton_class?
|
||||
assert_false cls.singleton_class?
|
||||
assert_true scl.singleton_class?
|
||||
end
|
||||
|
||||
assert 'Module#module_eval' do
|
||||
mod = Module.new
|
||||
mod.class_exec(1,2,3) do |a,b,c|
|
||||
assert_equal([1,2,3], [a,b,c])
|
||||
def hi
|
||||
"hi"
|
||||
end
|
||||
end
|
||||
cls = Class.new
|
||||
cls.class_exec(42) do |x|
|
||||
assert_equal(42, x)
|
||||
include mod
|
||||
def hello
|
||||
"hello"
|
||||
end
|
||||
end
|
||||
obj = cls.new
|
||||
assert_equal("hi", obj.hi)
|
||||
assert_equal("hello", obj.hello)
|
||||
end
|
||||
Reference in New Issue
Block a user