@@ -125,7 +125,20 @@ def self.__async(future, &block)
125
125
end
126
126
end
127
127
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
129
142
# Create a JavaScript object with the new method
130
143
#
131
144
# The below examples show typical usage in Ruby
@@ -141,16 +154,16 @@ class JS::Object
141
154
#
142
155
def new ( *args , &block )
143
156
args = args + [ block ] if block
144
- JS . global [ :Reflect ] . construct ( self , args . to_js )
157
+ :: JS . global [ :Reflect ] . construct ( self , args . to_js )
145
158
end
146
159
147
160
# Converts +self+ to an Array:
148
161
#
149
162
# JS.eval("return [1, 2, 3]").to_a.map(&:to_i) # => [1, 2, 3]
150
163
# JS.global[:document].querySelectorAll("p").to_a # => [[object HTMLParagraphElement], ...
151
164
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 ] }
154
167
end
155
168
156
169
# Provide a shorthand form for JS::Object#call
@@ -176,7 +189,7 @@ def method_missing(sym, *args, &block)
176
189
result = invoke_js_method ( sym_str [ 0 ..-2 ] . to_sym , *args , &block )
177
190
# Type coerce the result to boolean type
178
191
# 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
180
193
end
181
194
182
195
invoke_js_method ( sym , *args , &block )
@@ -186,7 +199,6 @@ def method_missing(sym, *args, &block)
186
199
#
187
200
# See JS::Object#method_missing for details.
188
201
def respond_to_missing? ( sym , include_private )
189
- return true if super
190
202
sym_str = sym . to_s
191
203
sym = sym_str [ 0 ..-2 ] . to_sym if sym_str . end_with? ( "?" )
192
204
self [ sym ] . typeof == "function"
@@ -203,7 +215,7 @@ def respond_to_missing?(sym, include_private)
203
215
# end.await # => 42
204
216
def apply ( *args , &block )
205
217
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 )
207
219
end
208
220
209
221
# Await a JavaScript Promise like `await` in JavaScript.
@@ -233,8 +245,17 @@ def apply(*args, &block)
233
245
# JS.eval("return new Promise((ok, err) => err(new Error())").await # => raises JS::Error
234
246
def await
235
247
# 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 ) )
238
259
end
239
260
240
261
private
@@ -246,12 +267,12 @@ def invoke_js_method(sym, *args, &block)
246
267
return self . call ( sym , *args , &block ) if self [ sym ] . typeof == "function"
247
268
248
269
# 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 ,
251
272
"`#{ sym } ` is not a function. To reference a property, use `[:#{ sym } ]` syntax instead."
252
273
end
253
274
254
- raise NoMethodError ,
275
+ raise :: NoMethodError ,
255
276
"undefined method `#{ sym } ' for an instance of JS::Object"
256
277
end
257
278
end
0 commit comments