Skip to content

Commit d5da762

Browse files
Merge pull request #531 from ledsun/improve_errer_mesage_for_property
Improve errer mesage for property
2 parents d90b881 + ad4ba8a commit d5da762

File tree

2 files changed

+51
-16
lines changed

2 files changed

+51
-16
lines changed

packages/gems/js/lib/js.rb

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -173,16 +173,13 @@ def method_missing(sym, *args, &block)
173173
if sym_str.end_with?("?")
174174
# When a JS method is called with a ? suffix, it is treated as a predicate method,
175175
# and the return value is converted to a Ruby boolean value automatically.
176-
result = self.call(sym_str[0..-2].to_sym, *args, &block)
177-
176+
result = invoke_js_method(sym_str[0..-2].to_sym, *args, &block)
178177
# Type coerce the result to boolean type
179178
# to match the true/false determination in JavaScript's if statement.
180-
JS.global.Boolean(result) == JS::True
181-
elsif self[sym].typeof == "function"
182-
self.call(sym, *args, &block)
183-
else
184-
super
179+
return JS.global.Boolean(result) == JS::True
185180
end
181+
182+
invoke_js_method(sym, *args, &block)
186183
end
187184

188185
# Check if a JavaScript method exists
@@ -195,10 +192,10 @@ def respond_to_missing?(sym, include_private)
195192
self[sym].typeof == "function"
196193
end
197194

198-
# Call the receiver (a JavaScript function) with `undefined` as its receiver context.
195+
# Call the receiver (a JavaScript function) with `undefined` as its receiver context.
199196
# This method is similar to JS::Object#call, but it is used to call a function that is not
200197
# a method of an object.
201-
#
198+
#
202199
# floor = JS.global[:Math][:floor]
203200
# floor.apply(3.14) # => 3
204201
# JS.global[:Promise].new do |resolve, reject|
@@ -239,6 +236,24 @@ def await
239236
promise = JS.global[:Promise].resolve(self)
240237
JS.promise_scheduler.await(promise)
241238
end
239+
240+
private
241+
242+
# Invoke a JavaScript method
243+
# If the property of JavaScritp object does not exist, raise a `NoMethodError`.
244+
# If the property exists but is not a function, raise a `TypeError`.
245+
def invoke_js_method(sym, *args, &block)
246+
return self.call(sym, *args, &block) if self[sym].typeof == "function"
247+
248+
# Check to see if a non-functional property exists.
249+
if JS.global[:Reflect].call(:has, self, sym.to_s) == JS::True
250+
raise TypeError,
251+
"`#{sym}` is not a function. To reference a property, use `[:#{sym}]` syntax instead."
252+
end
253+
254+
raise NoMethodError,
255+
"undefined method `#{sym}' for an instance of JS::Object"
256+
end
242257
end
243258

244259
# A wrapper class for JavaScript Error to allow the Error to be thrown in Ruby.

packages/npm-packages/ruby-wasm-wasi/test/unit/test_object.rb

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -310,13 +310,6 @@ def test_method_missing_with_block
310310
assert_true block_called
311311
end
312312

313-
def test_method_missing_with_undefined_method
314-
object = JS.eval(<<~JS)
315-
return { foo() { return true; } };
316-
JS
317-
assert_raise(NoMethodError) { object.bar }
318-
end
319-
320313
def test_method_missing_with_?
321314
object = JS.eval(<<~JS)
322315
return {
@@ -344,6 +337,33 @@ def test_method_missing_with_?
344337
assert_false object.return_empty_string?
345338
end
346339

340+
def test_method_missing_with_property
341+
object = JS.eval(<<~JS)
342+
return { property: 42 };
343+
JS
344+
345+
e = assert_raise(TypeError) { object.property }
346+
assert_equal "`property` is not a function. To reference a property, use `[:property]` syntax instead.",
347+
e.message
348+
349+
e = assert_raise(TypeError) { object.property? }
350+
assert_equal "`property` is not a function. To reference a property, use `[:property]` syntax instead.",
351+
e.message
352+
end
353+
354+
def test_method_missing_with_undefined_method
355+
object = JS.eval(<<~JS)
356+
return { foo() { return true; } };
357+
JS
358+
e = assert_raise(NoMethodError) { object.bar }
359+
assert_equal "undefined method `bar' for an instance of JS::Object",
360+
e.message
361+
362+
e = assert_raise(NoMethodError) { object.bar? }
363+
assert_equal "undefined method `bar' for an instance of JS::Object",
364+
e.message
365+
end
366+
347367
def test_respond_to_missing?
348368
object = JS.eval(<<~JS)
349369
return { foo() { return true; } };

0 commit comments

Comments
 (0)