Skip to content

Commit 9bff9ae

Browse files
committed
add is_instance_of public function
1 parent 898a311 commit 9bff9ae

6 files changed

Lines changed: 106 additions & 25 deletions

File tree

docs/standard_lib.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,23 @@ is_instance MyClass -- false
148148
is_instance MyClass.__base -- false
149149
```
150150

151+
### `is_instance_of(value, class)`
152+
153+
Returns `true` if `value` is an instance of `class` or any of its parent
154+
classes, `false` otherwise. Throws an error if `value` is not a MoonScript
155+
instance. First verifies that `value` is a valid instance using `is_instance`,
156+
then walks the `__parent` chain to check if the instance's class matches or
157+
inherits from `class`.
158+
159+
```moon
160+
class Parent
161+
class Child extends Parent
162+
163+
is_instance_of Child!, Parent -- true
164+
is_instance_of Child!, Child -- true
165+
is_instance_of Parent!, Child -- false
166+
```
167+
151168
### `type(value)`
152169

153170
If `value` is an instance of a MoonScript class, then return its class object.

moon/init.lua

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ do
77
local _obj_0 = require("moonscript.util")
88
getfenv, setfenv, dump = _obj_0.getfenv, _obj_0.setfenv, _obj_0.dump
99
end
10-
local p, is_object, is_class, is_instance, type, debug, run_with_scope, bind_methods, defaultbl, extend, copy, mixin, mixin_object, mixin_table, fold
10+
local p, is_object, is_class, is_instance, is_instance_of, type, debug, run_with_scope, bind_methods, defaultbl, extend, copy, mixin, mixin_object, mixin_table, fold
1111
p = function(o, ...)
1212
print(dump(o))
1313
if select("#", ...) > 0 then
@@ -31,6 +31,20 @@ is_instance = function(value)
3131
end
3232
return false
3333
end
34+
is_instance_of = function(value, cls)
35+
if not (is_instance(value)) then
36+
error("is_instance_of: expected instance, got " .. tostring(lua.type(value)))
37+
end
38+
local mt = getmetatable(value)
39+
local check = rawget(mt, "__class")
40+
while check do
41+
if check == cls then
42+
return true
43+
end
44+
check = check.__parent
45+
end
46+
return false
47+
end
3448
type = function(value)
3549
local base_type = lua.type(value)
3650
if base_type == "table" then
@@ -180,6 +194,7 @@ return {
180194
is_object = is_object,
181195
is_class = is_class,
182196
is_instance = is_instance,
197+
is_instance_of = is_instance_of,
183198
type = type,
184199
debug = debug,
185200
run_with_scope = run_with_scope,

moon/init.moon

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ is_instance = (value) ->
2424
return mt and rawget(mt, "__index") == mt and rawget(value, "__index") != value
2525
false
2626

27+
is_instance_of = (value, cls) ->
28+
error "is_instance_of: expected instance, got #{lua.type value}" unless is_instance value
29+
mt = getmetatable value
30+
check = rawget mt, "__class"
31+
while check
32+
if check == cls
33+
return true
34+
check = check.__parent
35+
false
36+
2737
type = (value) -> -- class aware type
2838
base_type = lua.type value
2939
if base_type == "table"
@@ -143,6 +153,6 @@ fold = (items, fn)->
143153
items[1]
144154

145155
{
146-
:dump, :p, :is_object, :is_class, :is_instance, :type, :debug, :run_with_scope, :bind_methods,
156+
:dump, :p, :is_object, :is_class, :is_instance, :is_instance_of, :type, :debug, :run_with_scope, :bind_methods,
147157
:defaultbl, :extend, :copy, :mixin, :mixin_object, :mixin_table, :fold
148158
}

moonscript/util.lua

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,6 @@ local moon = {
1717
end
1818
return false
1919
end,
20-
is_a = function(thing, t)
21-
if not (type(thing) == "table") then
22-
return false
23-
end
24-
local cls = thing.__class
25-
while cls do
26-
if cls == t then
27-
return true
28-
end
29-
cls = cls.__parent
30-
end
31-
return false
32-
end,
3320
type = function(value)
3421
local base_type = type(value)
3522
if base_type == "table" then

moonscript/util.moon

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,6 @@ moon = {
1717
return mt and rawget(mt, "__index") == mt and rawget(value, "__index") != value
1818
false
1919

20-
is_a: (thing, t) ->
21-
return false unless type(thing) == "table"
22-
cls = thing.__class
23-
while cls
24-
if cls == t
25-
return true
26-
cls = cls.__parent
27-
28-
false
29-
3020
type: (value) -> -- the moonscript object class
3121
base_type = type value
3222
if base_type == "table"

spec/moon_spec.moon

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,68 @@ describe "moon", ->
245245
assert.falsy moon.is_instance Parent
246246
assert.falsy moon.is_instance Child
247247

248+
describe "is_instance_of", ->
249+
it "returns true for direct instance", ->
250+
class Hello
251+
assert.truthy moon.is_instance_of Hello!, Hello
252+
253+
it "returns true for instance of parent class", ->
254+
class Parent
255+
class Child extends Parent
256+
assert.truthy moon.is_instance_of Child!, Parent
257+
assert.truthy moon.is_instance_of Child!, Child
258+
259+
it "returns false for instance of unrelated class", ->
260+
class A
261+
class B
262+
assert.falsy moon.is_instance_of A!, B
263+
assert.falsy moon.is_instance_of B!, A
264+
265+
it "returns false for parent instance checked against child class", ->
266+
class Parent
267+
class Child extends Parent
268+
assert.falsy moon.is_instance_of Parent!, Child
269+
270+
it "errors when value is not an instance", ->
271+
class Hello
272+
assert.has_error (-> moon.is_instance_of Hello, Hello), "is_instance_of: expected instance, got table"
273+
assert.has_error (-> moon.is_instance_of Hello.__base, Hello), "is_instance_of: expected instance, got table"
274+
assert.has_error (-> moon.is_instance_of {}, Hello), "is_instance_of: expected instance, got table"
275+
assert.has_error (-> moon.is_instance_of nil, Hello), "is_instance_of: expected instance, got nil"
276+
assert.has_error (-> moon.is_instance_of 123, Hello), "is_instance_of: expected instance, got number"
277+
278+
it "errors when __base is passed as the value", ->
279+
class Parent
280+
class Child extends Parent
281+
assert.has_error (-> moon.is_instance_of Parent.__base, Parent), "is_instance_of: expected instance, got table"
282+
assert.has_error (-> moon.is_instance_of Child.__base, Child), "is_instance_of: expected instance, got table"
283+
assert.has_error (-> moon.is_instance_of Child.__base, Parent), "is_instance_of: expected instance, got table"
284+
285+
it "returns false when __base is passed as the class", ->
286+
class Parent
287+
class Child extends Parent
288+
assert.falsy moon.is_instance_of Parent!, Parent.__base
289+
assert.falsy moon.is_instance_of Child!, Child.__base
290+
assert.falsy moon.is_instance_of Child!, Parent.__base
291+
292+
it "errors when __base is on both sides", ->
293+
class Parent
294+
class Child extends Parent
295+
assert.has_error (-> moon.is_instance_of Parent.__base, Parent.__base), "is_instance_of: expected instance, got table"
296+
assert.has_error (-> moon.is_instance_of Child.__base, Child.__base), "is_instance_of: expected instance, got table"
297+
assert.has_error (-> moon.is_instance_of Child.__base, Parent.__base), "is_instance_of: expected instance, got table"
298+
assert.has_error (-> moon.is_instance_of Parent.__base, Child.__base), "is_instance_of: expected instance, got table"
299+
300+
it "works with deep inheritance chain", ->
301+
class A
302+
class B extends A
303+
class C extends B
304+
assert.truthy moon.is_instance_of C!, A
305+
assert.truthy moon.is_instance_of C!, B
306+
assert.truthy moon.is_instance_of C!, C
307+
assert.falsy moon.is_instance_of A!, B
308+
assert.falsy moon.is_instance_of A!, C
309+
248310
it "should fold", ->
249311
numbers = {4,3,5,6,7,2,3}
250312
sum = moon.fold numbers, (a,b) -> a + b

0 commit comments

Comments
 (0)