Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Interact with JS Object directly without having to manually wrap with Native #2637

Open
AndyObtiva opened this issue Feb 4, 2024 · 1 comment
Labels

Comments

@AndyObtiva
Copy link

AndyObtiva commented Feb 4, 2024

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

I get a JS object response object from an HTTP Ajax library I'm using (jquery-opal).

If I call response.body, I get a JS object, and if I try to dig into its data the simple way any developer might attempt by simply calling response.body.data, Opal crashes telling me Uncaught TypeError: response.$body(...).$data is not a function

This is extremely discouraging to an Opal Newbie.

I realize I could wrap response.body with Native and then call data like Native(response.body).data, but that is not intuitive, and having to worry about low-level details like that while I am solving business problems with Opal is distracting and can be a big waste of time.

In JRuby, I can interact with any Java object directly from Ruby without having to wrap anything explicitly. response.body.data does work in JRuby. It's a shame it doesn't work in Opal out of the box given that JS is a closer language to Ruby with both being dynamically typed, unlike Java, which is statically typed. JRuby also handles edge cases like Ruby implementing a method that is already implemented in Java. The developer could still dig into the Java version of a method with special syntax (java_send) or inherit/override Java method with Ruby behavior.

I realize there might be performance implications for supporting this, but that concern can be resolved by following the 80/20 rule:

  • Support an Opal configuration option for interacting with JS objects using Native manually if extreme performance optimizations are needed
  • Make the Opal default being able to simply and intuitively interact with JS objects from Opal Ruby through direct invocation syntax without having to wrap anything with Native explicitly or manually.

This is a major design flaw in Opal that might have been 50% responsible for the lack of adoption of Opal over the last decade. If this was handled already, the selling pitch for Opal Ruby would have been a lot stronger to a point where it's a no-brainer and nobody could say no. Ideally, we don't want to give people any excuses or reasons to see Opal Ruby in the Frontend as less convenient to use than straight JS.

Describe the solution you'd like
A clear and concise description of what you want to happen.

Allow the ability to interact with JavaScript objects directly from Ruby without using Native explicitly or manually, just like JRuby allows interaction with Java objects directly from Ruby.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

For now, I am able to resolve this issue by wrapping objects with Native manually or sometimes adding the wrapping functionality to an Opal library gem directly when possible to save the user from having to do the wrapping themselves.

Additional context
Add any other context or screenshots about the feature request here.

You can learn from how JRuby handles calling Java objects from Ruby:
https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby

@hmdne
Copy link
Member

hmdne commented Feb 12, 2024

The main idea I have regarding this issue, which I hope I will be able to implement, is as follows:

  • For Opal 2.0, deprecate the backtick operator (except if called with a flag) - this is mostly done.
  • Rename the JS library to Opal::Raw. This way we ensure everyone knows that we are dealing with "Raw" values. And we free the JS namespace (already worked on in [compat] Rename JS to Opal::Raw #2524; namely I also want JS free for a future Ruby-WASM compatibility library but I won't be writing about this in this post)
  • Since those two interfaces are basically the only ways to get raw values, we basically ensure that each raw value returned from a library is a library bug.

Furthermore:

  • Provide an alternative wrapping access library as Opal::JS. This will be a next generation of what Native currently is.
    • This library would - just like Native - have two purposes:
      • Direct access to JavaScript world of types, automatically converting between Ruby and JS semantics.
        • So, for instance an Opal proc would always transparently wrap/unwrap the values
        • For importing, we would have something like: jquery = Opal::JS.import("jquery") which would compile into something like import _tmp from "jquery"; var jquery = Opal::JS.wrap(_tmp) (similarly, there would be a raw call like Opal::Raw.import("jquery"))
      • Provide a way to create Ruby-idiomatic bindings from scratch, just like Opal::Browser uses Native

I have thought of many other names, but after all, I believe that we should make everything Opal-specific go under the Opal namespace (as described in #2604). Raw gives us a way to clearly say: don't use this interface unless you know what you are doing (use JS instead). It makes the programmer choose JS over Raw and not let's say JSWrap over JS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants