Undefine Object#send method to call JavaScript send method
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.
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?
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.
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.
how about inheriting BasicObject instead of Object?
I think it's an interesting idea. I'll try to see how it's implemented.
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.