|
198 | 198 | if ($hasOwn.call(base._scope, id)) { |
199 | 199 | module = base._scope[id]; |
200 | 200 |
|
201 | | - if (!module._mod$ && module !== RubyObject) { |
| 201 | + if (!module.__mod__ && module !== RubyObject) { |
202 | 202 | throw Opal.TypeError.$new(id + " is not a module") |
203 | 203 | } |
204 | 204 | } |
|
212 | 212 | return module; |
213 | 213 | }; |
214 | 214 |
|
| 215 | + /* |
| 216 | + * Internal function to create a new module instance. This simply sets up |
| 217 | + * the prototype hierarchy and method tables. |
| 218 | + */ |
215 | 219 | function boot_module() { |
216 | | - |
217 | 220 | var mtor = function() {}; |
218 | 221 | mtor.prototype = RubyModule.constructor.prototype; |
219 | 222 |
|
|
230 | 233 | module.__inc__ = []; |
231 | 234 | module.__parent = RubyModule; |
232 | 235 | module._proto = {}; |
233 | | - module._mod$ = true; |
| 236 | + module.__mod__ = true; |
234 | 237 | module.__dep__ = []; |
235 | 238 |
|
236 | 239 | return module; |
|
279 | 282 | return klass; |
280 | 283 | }; |
281 | 284 |
|
282 | | - var bridge_class = function(name, constructor) { |
283 | | - var klass = boot_class(RubyObject, constructor), idx, length, mid; |
284 | | - |
285 | | - bridged_classes.push(klass); |
286 | | - |
287 | | - var table = RubyObject._proto, methods = RubyObject._methods; |
288 | | - |
289 | | - for (idx = 0, len = methods.length; idx < len; idx++) { |
290 | | - mid = methods[idx]; |
291 | | - constructor.prototype[mid] = table[mid]; |
292 | | - } |
| 285 | + /* |
| 286 | + * For performance, some core ruby classes are toll-free bridged to their |
| 287 | + * native javascript counterparts (e.g. a ruby Array is a javascript Array). |
| 288 | + * |
| 289 | + * This method is used to setup a native constructor (e.g. Array), to have |
| 290 | + * its prototype act like a normal ruby class. Firstly, a new ruby class is |
| 291 | + * created using the native constructor so that its prototype is set as the |
| 292 | + * target for th new class. Note: all bridged classes are set to inherit |
| 293 | + * from Object. |
| 294 | + * |
| 295 | + * Bridged classes are tracked in `bridged_classes` array so that methods |
| 296 | + * defined on Object can be "donated" to all bridged classes. This allows |
| 297 | + * us to fake the inheritance of a native prototype from our Object |
| 298 | + * prototype. |
| 299 | + * |
| 300 | + * Example: |
| 301 | + * |
| 302 | + * bridge_class("Proc", Function); |
| 303 | + * |
| 304 | + * @param [String] name the name of the ruby class to create |
| 305 | + * @param [Function] constructor native javascript constructor to use |
| 306 | + * @return [Class] returns new ruby class |
| 307 | + */ |
| 308 | + function bridge_class(name, constructor) { |
| 309 | + var klass = boot_class(RubyObject, constructor); |
293 | 310 |
|
294 | 311 | klass._name = name; |
| 312 | + |
295 | 313 | create_scope(Opal, klass, name); |
| 314 | + bridged_classes.push(klass); |
296 | 315 |
|
297 | 316 | return klass; |
298 | 317 | }; |
299 | 318 |
|
300 | | - Opal.puts = function(a) { console.log(a); }; |
301 | | - |
| 319 | + /* |
| 320 | + * Methods stubs are used to facilitate method_missing in opal. A stub is a |
| 321 | + * placeholder function which just calls `method_missing` on the receiver. |
| 322 | + * If no method with the given name is actually defined on an object, then it |
| 323 | + * is obvious to say that the stub will be called instead, and then in turn |
| 324 | + * method_missing will be called. |
| 325 | + * |
| 326 | + * When a file in ruby gets compiled to javascript, it includes a call to |
| 327 | + * this function which adds stubs for every method name in the compiled file. |
| 328 | + * It should then be safe to assume that method_missing will work for any |
| 329 | + * method call detected. |
| 330 | + * |
| 331 | + * Method stubs are added to the BasicObject prototype, which every other |
| 332 | + * ruby object inherits, so all objects should handle method missing. A stub |
| 333 | + * is only added if the given property name (method name) is not already |
| 334 | + * defined. |
| 335 | + * |
| 336 | + * Note: all ruby methods have a `$` prefix in javascript, so all stubs will |
| 337 | + * have this prefix as well (to make this method more performant). |
| 338 | + * |
| 339 | + * Opal.add_stubs(["$foo", "$bar", "$baz="]); |
| 340 | + * |
| 341 | + * All stub functions will have a private `rb_stub` property set to true so |
| 342 | + * that other internal methods can detect if a method is just a stub or not. |
| 343 | + * `Kernel#respond_to?` uses this property to detect a methods presence. |
| 344 | + * |
| 345 | + * @param [Array] stubs an array of method stubs to add |
| 346 | + */ |
302 | 347 | Opal.add_stubs = function(stubs) { |
303 | 348 | for (var i = 0, length = stubs.length; i < length; i++) { |
304 | 349 | var stub = stubs[i]; |
|
310 | 355 | } |
311 | 356 | }; |
312 | 357 |
|
| 358 | + /* |
| 359 | + * Actuall add a method_missing stub function to the given prototype for the |
| 360 | + * given name. |
| 361 | + * |
| 362 | + * @param [Prototype] prototype the target prototype |
| 363 | + * @param [String] stub stub name to add (e.g. "$foo") |
| 364 | + */ |
313 | 365 | function add_stub_for(prototype, stub) { |
314 | 366 | function method_missing_stub() { |
| 367 | + // Copy any given block onto the method_missing dispatcher |
315 | 368 | this.$method_missing._p = method_missing_stub._p; |
| 369 | + |
| 370 | + // Set block property to null ready for the next call (stop false-positives) |
316 | 371 | method_missing_stub._p = null; |
317 | 372 |
|
| 373 | + // call method missing with correct args (remove '$' prefix on method name) |
318 | 374 | return this.$method_missing.apply(this, [stub.slice(1)].concat($slice.call(arguments))); |
319 | 375 | } |
320 | 376 |
|
321 | 377 | method_missing_stub.rb_stub = true; |
322 | 378 | prototype[stub] = method_missing_stub; |
323 | 379 | } |
324 | 380 |
|
| 381 | + // Expose for other parts of Opal to use |
325 | 382 | Opal.add_stub_for = add_stub_for; |
326 | 383 |
|
327 | 384 | // Const missing dispatcher |
|
393 | 450 | }; |
394 | 451 |
|
395 | 452 | /* |
396 | | - Used to return as an expression. Sometimes, we can't simply return from |
397 | | - a javascript function as if we were a method, as the return is used as |
398 | | - an expression, or even inside a block which must "return" to the outer |
399 | | - method. This helper simply throws an error which is then caught by the |
400 | | - method. This approach is expensive, so it is only used when absolutely |
401 | | - needed. |
| 453 | + * Used to return as an expression. Sometimes, we can't simply return from |
| 454 | + * a javascript function as if we were a method, as the return is used as |
| 455 | + * an expression, or even inside a block which must "return" to the outer |
| 456 | + * method. This helper simply throws an error which is then caught by the |
| 457 | + * method. This approach is expensive, so it is only used when absolutely |
| 458 | + * needed. |
402 | 459 | */ |
403 | 460 | Opal.$return = function(val) { |
404 | 461 | Opal.returner.$v = val; |
|
0 commit comments