ruby.wasm icon indicating copy to clipboard operation
ruby.wasm copied to clipboard

Undefine Object#send method to call JavaScript send method

Open ledsun opened this issue 1 year ago • 3 comments

Background

The following ruby.wasm code will cause an error.

ws = JS.global[:WebSocket].new("ws://localhost:9292")
ws[:onopen] = -> (event) {
  ws.send("Hello")
}
Error: /bundle/gems/js-2.6.2/lib/js.rb:184:in `method_missing': undefined method `Hello' for an instance of JS::Object (NoMethodError)

This error is caused by the Object#send method being called instead of the send method of the JavaScript WebSocket object.

Currently, we can use the send method by using the call method as follows.

ws.call(:send, ["Hello"])

Goal

Enable to call the JavaScript send method with the following syntax:

ws.send("Hello, world! from Ruby")

Solution

1st approch

Undef Object#send method. But, this is too much change to enable short-hand methods.

2nd approch

Overirde Object#send method. Then, it checks whether the JavaScript object has a send method and changes its behavior.

ledsun avatar Aug 18 '24 23:08 ledsun

Given that method_missing API is just a shorthand of JS::Object#call, it might be too much to undefine such conventional methods. Could you collect some case studies on how other similar method_missing users (e.g. ActiveRecord, PyCall.rb, etc...) address this kind of name conflict?

kateinoigakukun avatar Aug 19 '24 00:08 kateinoigakukun

I have collected case studies.

In ActiveRecord, the send column is defined as a method using define_attribute_methods. In other words, the Object#send method is overridden.

In PyCall, if there is a name conflict with the send method, the Object#send method is given priority. To call the send method of a Python object, use PyColl.getattr as follows.

require 'pycall'
client = PyCall.import_module 'http.client'
conn = client.HTTPConnection.new('www.example.com')
PyCall.getattr(conn, 'send').("GET / HTTP/1.0\r\n\r\n".b)

I like the way Rails does it. So I defined the JS::Object#send method.

ledsun avatar Aug 24 '24 05:08 ledsun

Sorry for my late response. I have been thinking about this, how about inheriting BasicObject instead of Object? If we do that, we don't need to care about most of conventional methods (not only for send) except for a few like __send__. It slightly breaks API compatibility but shouldn't be a big deal considering its use cases.

kateinoigakukun avatar Aug 29 '24 03:08 kateinoigakukun

how about inheriting BasicObject instead of Object?

I think it's an interesting idea. I'll try to see how it's implemented.

ledsun avatar Aug 30 '24 00:08 ledsun

It looks like it can be successfully implemented by inheriting the BasicObject. I will create a new pull request with a new implementation.

I close this pull request.

ledsun avatar Sep 07 '24 13:09 ledsun