Module: JS

Defined in:
lib/js.rb,
lib/js/version.rb,
lib/js/require_remote.rb,
lib/js/require_remote/evaluator.rb,
lib/js/require_remote/url_resolver.rb,
ext/js/js-core.c

Overview

The JS module provides a way to interact with JavaScript from Ruby.

Example

require 'js'
JS.eval("return 1 + 2") # => 3
JS.global[:document].write("Hello, world!")
div = JS.global[:document].createElement("div")
div[:innerText] = "click me"
body = JS.global[:document][:body]
if body[:classList].contains?("main")
  body.appendChild(div)
end
div.addEventListener("click") do |event|
  puts event          # => # [object MouseEvent]
  puts event[:detail] # => 1
  div[:innerText] = "clicked!"
end

If you are using ruby.wasm without stdlib you will not have addEventListener and other specialized functions defined. You can still acomplish many of the same things using call instead.

Example

require 'js'
JS.eval("return 1 + 2") # => 3
JS.global[:document].call(:write, "Hello, world!")
div = JS.global[:document].call(:createElement, "div")
div[:innerText] = "click me"
if body[:classList].call(:contains, "main") == JS::True
  body.appendChild(div)
end
div.call(:addEventListener, "click") do |event|
  puts event          # => # [object MouseEvent]
  puts event[:detail] # => 1
  div[:innerText] = "clicked!"
end

Defined Under Namespace

Classes: Error, Object, PromiseScheduler, RequireRemote

Constant Summary collapse

Undefined =
JS.eval("return undefined")
Null =
JS.eval("return null")
True =

A boolean value in JavaScript is always a JS::Object instance from Ruby’s point of view. If we use the boolean value returned by a JavaScript function as the condition for an if expression in Ruby, the if expression will always be true.

Bad Example

searchParams = JS.global[:URLSearchParams].new(JS.global[:location][:search])
if searchParams.has('phrase')
  # Always pass through here.
  ...
else
  ...
end

Therefore, the JS::True constant is used to determine if the JavaScript function return value is true or false.

Good Example

if searchParams.has('phrase') == JS::True
  ...
end
JS.eval("return true;")
False =
JS.eval("return false;")
VERSION =
"2.8.1"

Class Method Summary collapse

Class Method Details

.__async(future, &block) ⇒ Object



117
118
119
120
121
122
123
124
125
# File 'lib/js.rb', line 117

def self.__async(future, &block)
  Fiber
    .new do
      future.resolve block.call
    rescue => e
      future.reject JS::Object.wrap(e)
    end
    .transfer
end

.__call_async_method(recv, method_name, future, *args) ⇒ Object



113
114
115
# File 'lib/js.rb', line 113

def self.__call_async_method(recv, method_name, future, *args)
  self.__async(future) { recv.send(method_name.to_s, *args) }
end

.__eval_async_rb(rb_code, future) ⇒ Object



107
108
109
110
111
# File 'lib/js.rb', line 107

def self.__eval_async_rb(rb_code, future)
  self.__async(future) do
    JS::Object.wrap(Kernel.eval(rb_code.to_s, TOPLEVEL_BINDING, "eval_async"))
  end
end

.eval(code) ⇒ JS::Object

Evaluates the given JavaScript code, returning the result as a JS::Object.

p JS.eval("return 1 + 1").to_i                             # => 2
p JS.eval("return new Object()").is_a?(JS.global[:Object]) # => true

Returns:



99
100
101
102
103
104
105
106
# File 'ext/js/js-core.c', line 99

static VALUE _rb_js_eval_js(VALUE _, VALUE code_str) {
  rb_js_abi_host_string_t abi_str;
  rstring_to_abi_string(code_str, &abi_str);
  rb_js_abi_host_js_abi_result_t ret;
  rb_js_abi_host_eval_js(&abi_str, &ret);
  raise_js_error_if_failure(&ret);
  return jsvalue_s_new(ret.val.success);
}

.globalJS::Object

Returns globalThis JavaScript object.

p JS.global
p JS.global[:document]

Returns:



181
182
183
# File 'ext/js/js-core.c', line 181

static VALUE _rb_js_global_this(VALUE _) {
  return jsvalue_s_new(rb_js_abi_host_global_this());
}

.is_a?(js_obj, js_class) ⇒ Boolean

Returns true if js_class is the instance of

<i>js_obj</i>, otherwise returns <code>false</code>.
Comparison is done using the <code>instanceof</code> in JavaScript.

 p JS.is_a?(JS.global, JS.global[:Object]) #=> true
 p JS.is_a?(JS.global, Object)             #=> false

Returns:



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'ext/js/js-core.c', line 149

static VALUE _rb_js_is_kind_of(int argc, VALUE *argv, VALUE self) {

  if (argc == 1) {
    // If the second argument is not given, behaves as `Module#is_a?`.
    return rb_obj_is_kind_of(self, argv[0]);
  }

  if (argc != 2) {
    rb_raise(rb_eArgError, "wrong number of arguments (given %d, expected 2)",
             argc);
  }

  VALUE obj = argv[0];
  VALUE klass = argv[1];
  if (!IS_JSVALUE(obj)) {
    return Qfalse;
  }
  struct jsvalue *val = DATA_PTR(obj);
  VALUE js_klass_v = _rb_js_try_convert(self, klass);
  struct jsvalue *js_klass = DATA_PTR(js_klass_v);
  return RBOOL(rb_js_abi_host_instance_of(val->abi, js_klass->abi));
}

.promise_schedulerObject



101
102
103
# File 'lib/js.rb', line 101

def self.promise_scheduler
  @promise_scheduler
end

.try_convert(obj) ⇒ JS::Object?

Try to convert the given object to a JS::Object using to_js

method. Returns <code>nil</code> if the object cannot be converted.

 p JS.try_convert(1)                           # => JS::Object
 p JS.try_convert("foo")                       # => JS::Object
 p JS.try_convert(Object.new)                  # => nil
 p JS.try_convert(JS::Object.wrap(Object.new)) # => JS::Object

Returns:



128
129
130
131
132
133
134
135
136
# File 'ext/js/js-core.c', line 128

VALUE _rb_js_try_convert(VALUE klass, VALUE obj) {
  if (_rb_js_is_js(klass, obj)) {
    return obj;
  } else if (rb_respond_to(obj, i_to_js)) {
    return rb_funcallv(obj, i_to_js, 0, NULL);
  } else {
    return Qnil;
  }
}