@@ -125,7 +125,20 @@ def self.__async(future, &block)
125125 end
126126end
127127
128- class JS ::Object
128+ # Inherit BasicObject to prevent define coventional menthods. #Override the `Object#send` to give priority to `send` method of JavaScript.
129+ #
130+ # This is to make it easier to use JavaScript Objects with `send` method such as `WebSocket` and `XMLHttpRequest`.
131+ # The JavaScript method call short-hand in `JS::Object` is implemented using `method_missing`.
132+ # If JS::Object inherits from Object, the `send` method defined in Ruby will take precedence over the JavaScript `send` method.
133+ # If you want to call the JavaScript `send` method, you must use the `call` method as follows:
134+ #
135+ # ws = JS.global[:WebSocket].new("ws://example.com")
136+ # ws.call(:send, ["Hello, world! from Ruby"])
137+ #
138+ # This inheritation allows you to call the JavaScript `send` method with the following syntax:
139+ #
140+ # ws.send("Hello, world! from Ruby")
141+ class JS ::Object < BasicObject
129142 # Create a JavaScript object with the new method
130143 #
131144 # The below examples show typical usage in Ruby
@@ -141,16 +154,16 @@ class JS::Object
141154 #
142155 def new ( *args , &block )
143156 args = args + [ block ] if block
144- JS . global [ :Reflect ] . construct ( self , args . to_js )
157+ :: JS . global [ :Reflect ] . construct ( self , args . to_js )
145158 end
146159
147160 # Converts +self+ to an Array:
148161 #
149162 # JS.eval("return [1, 2, 3]").to_a.map(&:to_i) # => [1, 2, 3]
150163 # JS.global[:document].querySelectorAll("p").to_a # => [[object HTMLParagraphElement], ...
151164 def to_a
152- as_array = JS . global [ :Array ] . from ( self )
153- Array . new ( as_array [ :length ] . to_i ) { as_array [ _1 ] }
165+ as_array = :: JS . global [ :Array ] . from ( self )
166+ :: Array . new ( as_array [ :length ] . to_i ) { as_array [ _1 ] }
154167 end
155168
156169 # Provide a shorthand form for JS::Object#call
@@ -176,7 +189,7 @@ def method_missing(sym, *args, &block)
176189 result = invoke_js_method ( sym_str [ 0 ..-2 ] . to_sym , *args , &block )
177190 # Type coerce the result to boolean type
178191 # to match the true/false determination in JavaScript's if statement.
179- return JS . global . Boolean ( result ) == JS ::True
192+ return :: JS . global . Boolean ( result ) == :: JS ::True
180193 end
181194
182195 invoke_js_method ( sym , *args , &block )
@@ -186,7 +199,6 @@ def method_missing(sym, *args, &block)
186199 #
187200 # See JS::Object#method_missing for details.
188201 def respond_to_missing? ( sym , include_private )
189- return true if super
190202 sym_str = sym . to_s
191203 sym = sym_str [ 0 ..-2 ] . to_sym if sym_str . end_with? ( "?" )
192204 self [ sym ] . typeof == "function"
@@ -203,7 +215,7 @@ def respond_to_missing?(sym, include_private)
203215 # end.await # => 42
204216 def apply ( *args , &block )
205217 args = args + [ block ] if block
206- JS . global [ :Reflect ] . call ( :apply , self , JS ::Undefined , args . to_js )
218+ :: JS . global [ :Reflect ] . call ( :apply , self , :: JS ::Undefined , args . to_js )
207219 end
208220
209221 # Await a JavaScript Promise like `await` in JavaScript.
@@ -233,8 +245,17 @@ def apply(*args, &block)
233245 # JS.eval("return new Promise((ok, err) => err(new Error())").await # => raises JS::Error
234246 def await
235247 # Promise.resolve wrap a value or flattens promise-like object and its thenable chain
236- promise = JS . global [ :Promise ] . resolve ( self )
237- JS . promise_scheduler . await ( promise )
248+ promise = ::JS . global [ :Promise ] . resolve ( self )
249+ ::JS . promise_scheduler . await ( promise )
250+ end
251+
252+ # The `respond_to?` method is only used in unit tests.
253+ # There is little need to define it here.
254+ # However, methods suffixed with `?` do not conflict with JavaScript methods.
255+ # As there are no disadvantages, we will define the `respond_to?` method here
256+ # in the same way as the `nil?` and `is_a?` methods, prioritizing convenience.
257+ [ :nil? , :is_a? , :raise , :respond_to? ] . each do |method |
258+ define_method ( method , ::Object . instance_method ( method ) )
238259 end
239260
240261 private
@@ -246,12 +267,12 @@ def invoke_js_method(sym, *args, &block)
246267 return self . call ( sym , *args , &block ) if self [ sym ] . typeof == "function"
247268
248269 # 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 ,
270+ if :: JS . global [ :Reflect ] . call ( :has , self , sym . to_s ) == :: JS ::True
271+ raise :: TypeError ,
251272 "`#{ sym } ` is not a function. To reference a property, use `[:#{ sym } ]` syntax instead."
252273 end
253274
254- raise NoMethodError ,
275+ raise :: NoMethodError ,
255276 "undefined method `#{ sym } ' for an instance of JS::Object"
256277 end
257278end
0 commit comments