isorun 0.1.6.pre-x86_64-linux → 0.1.8-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a2a3dd9a75e0acd0f6dc8339db3028a8deed4020970268488f9d0f06d503aa5
4
- data.tar.gz: 3193e958415a77908d2462b667c4a2eb347909cc60684bc0d3915cf91569db2b
3
+ metadata.gz: 3b7fbac84c4db38f693ab09b2e841b9c8ef840cfeb50d212d1123d147e6bc8ff
4
+ data.tar.gz: bfa169c65285f23465e5016eb8b0e5a8700cad33e966ecaaa8e7bf101ed15bb2
5
5
  SHA512:
6
- metadata.gz: 598eadb1ecd04196250b5b94024cd3f05b95f3bebe0a3a8924e3822ca132a9003fd5cc7c979680a7e7cac35449df6feb57fec2bfefdfa8522ab58adcf78c69fa
7
- data.tar.gz: 16b57543336c4ee2e30e1b99d7d404519fe6a64ca2a725694d5e3c7267507c536e8a5c4d44aad3670e54526bfa9901e7a3eeecc08b3780e62a4e12ac106123fe
6
+ metadata.gz: 426b9460bfda7af56298427b522808a0816506799895ea4f6adf57836ad259622e056b040d860e4c568a7a01d749496d65255aa45014fe02276b3ba27e32f7c6
7
+ data.tar.gz: 85d72159f954e240c8d68d1c415e5f07e751df504a23310f4305eba9f47c5d90d44b61ed42c92c536fe3b2b40d0ea85e9deba2e4b9811bf150dc67ed83453712
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require "rake/testtask"
6
6
  require "rake/extensiontask"
7
7
  require "rb_sys"
8
8
 
9
- cross_rubies = %w[3.1.0 3.0.0 2.7.0]
9
+ cross_rubies = %w[3.2.0 3.1.0 3.0.0 2.7.0]
10
10
  cross_platforms = %w[
11
11
  arm64-darwin
12
12
  x86_64-darwin
@@ -72,7 +72,7 @@ begin
72
72
 
73
73
  YARD::Rake::YardocTask.new
74
74
 
75
- task docs: :environment do
75
+ task :docs do
76
76
  `yard server --reload`
77
77
  end
78
78
  rescue LoadError
@@ -2,10 +2,27 @@
2
2
 
3
3
  module Isorun
4
4
  module AppHelper
5
+ # The isorun_app helper is the most convenient way to server-side render
6
+ # a JavaScript application, including state extraction and automatic
7
+ # rehydration. The helper tries to render the application and will also
8
+ # inject the client-side code immediately after the rendered result.
9
+ #
10
+ # @example Renders a JavaScript application
11
+ # <html>
12
+ # <body>
13
+ # <%= isorun_app("my_app") %>
14
+ # </body>
15
+ # </html>
16
+ #
17
+ # @param id [String] An ID representing both, the asset bundle, and by
18
+ # convention, the target node (e.g. `<div id="my_app">`)
19
+ # @return [String]
5
20
  def isorun_app(id) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
6
- module_path = Isorun.configuration.module_resolver.call(id)
21
+ ActiveSupport::Notifications.instrument "start.render.isorun", { ts: Time.current }
7
22
 
8
- ssr_html = Isorun::Context.create do |context|
23
+ module_path = Isorun.config.module_resolver.call(id)
24
+
25
+ ssr_html = Isorun::Context.create(receiver: Isorun.config.receiver) do |context|
9
26
  render_context = { environment: Rails.env.to_s }
10
27
  render_function = context.import.from(module_path)
11
28
 
@@ -14,11 +31,15 @@ module Isorun
14
31
  "does not have a server entrypoint. Please " \
15
32
  "check if an asset with filename " + "
16
33
  `#{id}-server.js` exists.")
34
+ return ""
17
35
  end
18
36
 
19
- Isorun.with_receiver(Isorun.configuration.receiver) do
20
- render_function.call_without_gvl(render_context)
21
- end
37
+ html = render_function.call(render_context)
38
+
39
+ ActiveSupport::Notifications.instrument "finish.render.isorun", { ts: Time.current }
40
+ ActiveSupport::Notifications.instrument "stats.isorun", Isorun.stats
41
+
42
+ html
22
43
  end
23
44
 
24
45
  html = if ssr_html.present?
@@ -1,26 +1,26 @@
1
1
  [package]
2
2
  name = "isorun"
3
- version = "0.1.6"
3
+ version = "0.1.8"
4
4
  edition = "2021"
5
5
  authors = ["Hannes Moser <box@hannesmoser.at>"]
6
6
  homepage = "https://github.com/eliias/isorun"
7
7
  repository = "https://github.com/eliias/isorun"
8
8
 
9
9
  [dependencies]
10
- deno_console = "0.80.0"
11
- deno_core = "0.162.0"
12
- deno_fetch = "0.103.0"
13
- deno_runtime = "0.88.0"
14
- deno_url = "0.80.0"
15
- deno_web = "0.111.0"
16
- deno_webidl = "0.80.0"
10
+ deno_console = "0.83.0"
11
+ deno_core = "0.165.0"
12
+ deno_fetch = "0.107.0"
13
+ deno_runtime = "0.91.0"
14
+ deno_url = "0.83.0"
15
+ deno_web = "0.114.0"
16
+ deno_webidl = "0.83.0"
17
17
  lazy_static = "1.4.0"
18
- magnus = { git = "https://github.com/eliias/magnus", rev = "1833ad96919b8891c1b5d62a22f0fc2b94f5961a" } # waiting for release with full rb-sys backend
18
+ magnus = { git = "https://github.com/eliias/magnus", rev = "5220756ca0ab4f2bc32d38310c26f7dda2be9bd6" }
19
19
  tokio = { version = "1.21.1", features = ["full"] }
20
- v8 = "0.58.0"
20
+ v8 = "0.60.0"
21
21
 
22
22
  [dev-dependencies]
23
- magnus = { git = "https://github.com/eliias/magnus", rev = "1833ad96919b8891c1b5d62a22f0fc2b94f5961a", features = ["embed"] } # waiting for release with full rb-sys backend
23
+ magnus = { git = "https://github.com/eliias/magnus", rev = "5220756ca0ab4f2bc32d38310c26f7dda2be9bd6", features = ["embed"] }
24
24
 
25
25
  [lib]
26
26
  name = "isorun"
@@ -2,7 +2,8 @@ use crate::isorun;
2
2
  use crate::js::module::Module;
3
3
  use crate::js::worker::WORKER;
4
4
  use deno_core::JsRealm;
5
- use magnus::Error;
5
+ use magnus::block::Proc;
6
+ use magnus::{exception, Error};
6
7
  use std::cell::RefCell;
7
8
  use std::rc::Rc;
8
9
 
@@ -18,10 +19,17 @@ impl Context {
18
19
  .with(|worker| worker.create_realm())
19
20
  .map(|realm| Context(Rc::new(RefCell::from(realm))))
20
21
  .map_err(|_error| {
21
- Error::runtime_error("cannot create JavaScript context")
22
+ Error::new(
23
+ exception::runtime_error(),
24
+ "cannot create JavaScript context",
25
+ )
22
26
  })
23
27
  }
24
28
 
29
+ pub(crate) fn set_receiver(&self, receiver: Option<Proc>) {
30
+ WORKER.with(|worker| worker.ruby_receiver.replace(receiver));
31
+ }
32
+
25
33
  pub(crate) fn load(
26
34
  &self,
27
35
  path: String,
@@ -36,11 +44,14 @@ impl Context {
36
44
  })
37
45
  .map(|module| isorun::Module(RefCell::from(module)))
38
46
  .map_err(|error| {
39
- Error::runtime_error(format!(
40
- "cannot load module: `{}`: {}",
41
- path.as_str(),
42
- error
43
- ))
47
+ Error::new(
48
+ exception::runtime_error(),
49
+ format!(
50
+ "cannot load module: `{}`: {}",
51
+ path.as_str(),
52
+ error
53
+ ),
54
+ )
44
55
  })
45
56
  }
46
57
  }
@@ -1,8 +1,9 @@
1
1
  use crate::js;
2
2
  use crate::js::worker::WORKER;
3
- use magnus::Error;
3
+ use magnus::{exception, Error};
4
4
  use std::cell::RefCell;
5
5
  use std::ops::Deref;
6
+ use v8::{Global, Value};
6
7
 
7
8
  #[magnus::wrap(class = "Isorun::Function")]
8
9
  pub(crate) struct Function(pub(crate) RefCell<js::module_item::Function>);
@@ -15,46 +16,25 @@ impl Function {
15
16
  &self,
16
17
  args: &[magnus::Value],
17
18
  ) -> Result<magnus::Value, Error> {
18
- let args = WORKER.with(|worker| {
19
+ WORKER.with(|worker| {
19
20
  let func = self.0.borrow();
20
21
  let realm = func.realm.borrow();
21
22
  let realm = realm.deref();
22
23
 
23
- let mut v8_args = vec![];
24
- for arg in args {
25
- let v8_arg = worker.to_v8(realm, *arg).unwrap();
26
- v8_args.push(v8_arg);
27
- }
28
- v8_args
29
- });
24
+ let v8_args: Vec<Global<Value>> = args
25
+ .iter()
26
+ .map(|arg| worker.to_v8(realm, *arg).unwrap())
27
+ .collect();
30
28
 
31
- self.0.borrow().call(args.as_slice()).map_err(|error| {
32
- Error::runtime_error(format!("cannot call function: {}", error))
33
- })
34
- }
29
+ let result =
30
+ self.0.borrow().call(v8_args.as_slice()).map_err(|error| {
31
+ Error::new(
32
+ exception::runtime_error(),
33
+ format!("cannot call function: {}", error),
34
+ )
35
+ });
35
36
 
36
- pub(crate) fn call_without_gvl(
37
- &self,
38
- args: &[magnus::Value],
39
- ) -> Result<magnus::Value, Error> {
40
- let args = WORKER.with(|worker| {
41
- let func = self.0.borrow();
42
- let realm = func.realm.borrow();
43
- let realm = realm.deref();
44
-
45
- let mut v8_args = vec![];
46
- for arg in args {
47
- let v8_arg = worker.to_v8(realm, *arg).unwrap();
48
- v8_args.push(v8_arg);
49
- }
50
- v8_args
51
- });
52
-
53
- self.0
54
- .borrow()
55
- .call_without_gvl(args.as_slice())
56
- .map_err(|error| {
57
- Error::runtime_error(format!("cannot call function: {}", error))
58
- })
37
+ result
38
+ })
59
39
  }
60
40
  }
@@ -1,4 +1,3 @@
1
- pub(crate) mod configure;
2
1
  pub(crate) mod context;
3
2
  pub(crate) mod function;
4
3
  pub(crate) mod module;
@@ -1,6 +1,6 @@
1
1
  use crate::isorun::function::Function;
2
2
  use crate::js;
3
- use magnus::Error;
3
+ use magnus::{exception, Error};
4
4
  use std::cell::RefCell;
5
5
 
6
6
  #[magnus::wrap(class = "Isorun::Module")]
@@ -19,9 +19,10 @@ impl Module {
19
19
  export_name: String,
20
20
  ) -> Result<magnus::Value, Error> {
21
21
  let module = self.0.borrow();
22
- let module_item = module
23
- .import(export_name.as_str())
24
- .map_err(|error| Error::runtime_error(format!("{}", error)))?;
22
+ let module_item =
23
+ module.import(export_name.as_str()).map_err(|error| {
24
+ Error::new(exception::runtime_error(), format!("{}", error))
25
+ })?;
25
26
 
26
27
  match module_item {
27
28
  js::module_item::ModuleItem::Value(v) => Ok(v.to_ruby().unwrap()),
@@ -1,3 +1,4 @@
1
+ use crate::js::worker::WORKER;
1
2
  use deno_core::error::AnyError;
2
3
  use magnus::r_hash::ForEach;
3
4
  use magnus::value::{Qfalse, Qtrue};
@@ -5,12 +6,14 @@ use magnus::{
5
6
  Integer, RArray, RFloat, RHash, RString, RStruct, Symbol, Value, QFALSE,
6
7
  QNIL, QTRUE,
7
8
  };
8
- use v8::{Array, GetPropertyNamesArgs, HandleScope, Local, Object};
9
+ use std::collections::HashMap;
10
+ use v8::{Array, GetPropertyNamesArgs, Global, HandleScope, Local, Object};
9
11
 
10
12
  pub fn convert_v8_to_ruby(
11
- value: Local<v8::Value>,
13
+ value: &Global<v8::Value>,
12
14
  scope: &mut HandleScope,
13
15
  ) -> Result<Value, AnyError> {
16
+ let value = Local::new(scope, value);
14
17
  if value.is_null() {
15
18
  return Ok(Value::from(QNIL));
16
19
  }
@@ -36,8 +39,7 @@ pub fn convert_v8_to_ruby(
36
39
  }
37
40
 
38
41
  if value.is_string() {
39
- let raw = value.to_rust_string_lossy(scope);
40
- return Ok(Value::from(RString::from(raw)));
42
+ return Ok(Value::from(value.to_rust_string_lossy(scope)));
41
43
  }
42
44
 
43
45
  if value.is_array() {
@@ -46,7 +48,8 @@ pub fn convert_v8_to_ruby(
46
48
  let r_arr = RArray::with_capacity(length as usize);
47
49
  for i in 0..length {
48
50
  let raw = arr.get_index(scope, i).unwrap();
49
- let val = convert_v8_to_ruby(raw, scope).unwrap();
51
+ let global_raw = Global::<v8::Value>::new(scope, raw);
52
+ let val = convert_v8_to_ruby(&global_raw, scope).unwrap();
50
53
  r_arr.push(val).expect("cannot add item to array");
51
54
  }
52
55
  return Ok(Value::from(r_arr));
@@ -61,9 +64,11 @@ pub fn convert_v8_to_ruby(
61
64
  let r_hash = RHash::new();
62
65
  for i in 0..length {
63
66
  let raw_key = properties.get_index(scope, i).unwrap();
67
+ let global_raw_key = Global::<v8::Value>::new(scope, raw_key);
64
68
  let raw_val = obj.get(scope, raw_key).unwrap();
65
- let key = convert_v8_to_ruby(raw_key, scope).unwrap();
66
- let val = convert_v8_to_ruby(raw_val, scope).unwrap();
69
+ let global_raw_val = Global::<v8::Value>::new(scope, raw_val);
70
+ let key = convert_v8_to_ruby(&global_raw_key, scope).unwrap();
71
+ let val = convert_v8_to_ruby(&global_raw_val, scope).unwrap();
67
72
  r_hash.aset(key, val).expect("cannot set item to hash");
68
73
  }
69
74
  return Ok(Value::from(r_hash));
@@ -154,3 +159,67 @@ pub fn convert_ruby_to_v8<'s>(
154
159
 
155
160
  Ok(v8::null(scope).into())
156
161
  }
162
+
163
+ pub(crate) fn low_memory_notification() {
164
+ WORKER.with(|worker| {
165
+ let mut worker = worker.worker.borrow_mut();
166
+ worker.js_runtime.v8_isolate().low_memory_notification();
167
+ });
168
+ }
169
+
170
+ pub(crate) fn stats() -> RHash {
171
+ WORKER.with(|worker| {
172
+ let mut worker = worker.worker.borrow_mut();
173
+
174
+ let heap_stats = &mut Default::default();
175
+ worker
176
+ .js_runtime
177
+ .v8_isolate()
178
+ .get_heap_statistics(heap_stats);
179
+
180
+ let current_thread = std::thread::current();
181
+ let thread = HashMap::from([(
182
+ "thread_id",
183
+ format!("{:?}", current_thread.id()),
184
+ )]);
185
+
186
+ let heap = HashMap::from([
187
+ ("external_memory", heap_stats.external_memory()),
188
+ ("heap_size_limit", heap_stats.heap_size_limit()),
189
+ ("malloced_memory", heap_stats.malloced_memory()),
190
+ (
191
+ "number_of_detached_contexts",
192
+ heap_stats.number_of_detached_contexts(),
193
+ ),
194
+ (
195
+ "number_of_native_contexts",
196
+ heap_stats.number_of_native_contexts(),
197
+ ),
198
+ ("peak_malloced_memory", heap_stats.peak_malloced_memory()),
199
+ ("total_available_size", heap_stats.total_available_size()),
200
+ (
201
+ "total_global_handles_size",
202
+ heap_stats.total_global_handles_size(),
203
+ ),
204
+ ("total_heap_size", heap_stats.total_heap_size()),
205
+ (
206
+ "total_heap_size_executable",
207
+ heap_stats.total_heap_size_executable(),
208
+ ),
209
+ ("total_physical_size", heap_stats.total_physical_size()),
210
+ (
211
+ "used_global_handles_size",
212
+ heap_stats.used_global_handles_size(),
213
+ ),
214
+ ("used_heap_size", heap_stats.used_heap_size()),
215
+ ]);
216
+
217
+ let h = RHash::new();
218
+ h.aset("current_thread", RHash::from_iter(thread))
219
+ .expect("cannot set stats for current_thread");
220
+ h.aset("heap", RHash::from_iter(heap))
221
+ .expect("cannot set stats for heap");
222
+
223
+ h
224
+ })
225
+ }
@@ -1,6 +1,5 @@
1
1
  use crate::js::worker::WORKER;
2
2
  use deno_core::JsRealm;
3
- use magnus::gvl::without_gvl;
4
3
  use std::cell::RefCell;
5
4
  use std::ops::Deref;
6
5
  use std::rc::Rc;
@@ -31,30 +30,6 @@ impl Function {
31
30
  .block_on(worker.call(realm, &self.binding, args))
32
31
  })
33
32
  }
34
-
35
- pub(crate) fn call_without_gvl(
36
- &self,
37
- args: &[Global<v8::Value>],
38
- ) -> Result<magnus::Value, magnus::Error> {
39
- WORKER.with(|worker| {
40
- let realm = self.realm.borrow();
41
- let realm = realm.deref();
42
- let result = without_gvl(
43
- |gvl_context| {
44
- worker.ruby_context.replace(Some(gvl_context));
45
- let result = worker
46
- .runtime
47
- // we block here instead of the worker, due to a refcell issue
48
- // when borrowing within an await
49
- .block_on(worker.call(realm, &self.binding, args));
50
- worker.ruby_context.replace(None);
51
- result
52
- },
53
- None::<fn()>,
54
- );
55
- result.0.unwrap()
56
- })
57
- }
58
33
  }
59
34
 
60
35
  pub(crate) struct Value {
@@ -8,7 +8,7 @@ use deno_runtime::worker::{MainWorker, WorkerOptions};
8
8
  use deno_runtime::BootstrapOptions;
9
9
  use deno_web::BlobStore;
10
10
  use magnus::block::Proc;
11
- use magnus::gvl::GVLContext;
11
+ use magnus::gvl::{without_gvl, GVLContext};
12
12
  use magnus::{Error, Value};
13
13
  use std::borrow::BorrowMut;
14
14
  use std::cell::RefCell;
@@ -19,7 +19,7 @@ use std::rc::Rc;
19
19
  use std::string::ToString;
20
20
  use std::sync::Arc;
21
21
  use tokio::runtime::Runtime;
22
- use v8::{Global, Local};
22
+ use v8::{Global, HandleScope, Local};
23
23
 
24
24
  fn get_error_class_name(e: &AnyError) -> &'static str {
25
25
  deno_runtime::errors::get_error_class_name(e).unwrap_or("Error")
@@ -84,9 +84,23 @@ impl Worker {
84
84
  local_args.push(local_arg);
85
85
  }
86
86
  let receiver = v8::undefined(scope.borrow_mut());
87
- let promise = callee
88
- .call(&mut scope, receiver.into(), local_args.as_slice())
89
- .unwrap();
87
+ let (promise, _cancel) = without_gvl(
88
+ |ctx| {
89
+ self.ruby_context.replace(Some(ctx));
90
+ let result = callee
91
+ .call(
92
+ &mut scope,
93
+ receiver.into(),
94
+ local_args.as_slice(),
95
+ )
96
+ .unwrap();
97
+ self.ruby_context.replace(None);
98
+ result
99
+ },
100
+ None::<fn()>,
101
+ );
102
+
103
+ let promise = promise.unwrap();
90
104
  Global::<v8::Value>::new(&mut scope, promise)
91
105
  };
92
106
 
@@ -107,7 +121,6 @@ impl Worker {
107
121
  ) -> Option<Value> {
108
122
  let mut worker = self.worker.borrow_mut();
109
123
  let mut scope = realm.handle_scope(worker.js_runtime.v8_isolate());
110
- let value = Local::new(&mut scope, value);
111
124
  let result = convert_v8_to_ruby(value, &mut scope);
112
125
 
113
126
  match result {
@@ -129,20 +142,57 @@ impl Worker {
129
142
  Some(value)
130
143
  }
131
144
 
132
- fn send(&self, value: Value) -> Result<Value, Error> {
133
- // we need to deref the receiver as mut, as it is behind an Option
134
- if let (Some(ctx), Some(rec)) = (
145
+ fn send<'a>(
146
+ &self,
147
+ scope: &mut HandleScope,
148
+ value: serde_v8::Value<'a>,
149
+ ) -> Result<serde_v8::Value<'a>, AnyError> {
150
+ if let None = self.ruby_receiver.borrow_mut().as_mut() {
151
+ return Err(AnyError::msg(
152
+ "Cannot send to ruby. The ruby receiver is missing, please \
153
+ initialize and set one before calling into Ruby?",
154
+ ));
155
+ }
156
+
157
+ // we can send with and without gvl, if no ruby context is present, we
158
+ // assume that GVL is held
159
+ if let (None, Some(rec)) = (
135
160
  self.ruby_context.borrow_mut().as_mut(),
136
161
  self.ruby_receiver.borrow_mut().as_mut(),
137
162
  ) {
163
+ let mut scope = scope; // fixme: can't figure out how to forward the borrow
164
+ let value = value.v8_value;
165
+ let value = Global::<v8::Value>::new(&mut scope, value);
166
+ let value = convert_v8_to_ruby(&value, &mut scope).unwrap();
167
+ let args: (Value,) = (value,);
168
+ return rec
169
+ .call::<(Value,), Value>(args)
170
+ .map_err(|err| AnyError::msg(format!("{:?}", err)))
171
+ .and_then(|value| convert_ruby_to_v8(value, &mut scope))
172
+ .map(|value| from_v8(&mut scope, value).unwrap());
173
+ }
174
+
175
+ // we need to deref the receiver as mut, as it is behind an Option
176
+ // TODO: make sure all operations on Ruby data happen when GVL is held
177
+ if let Some(ctx) = self.ruby_context.borrow_mut().as_mut() {
178
+ let mut scope = scope; // fixme: can't figure out how to forward the borrow
138
179
  ctx.with_gvl(|| {
139
- let args: (Value,) = (value,);
140
- rec.call::<(Value,), Value>(args)
141
- })?
180
+ if let Some(rec) = self.ruby_receiver.borrow_mut().as_mut() {
181
+ let value = value.v8_value;
182
+ let value = Global::<v8::Value>::new(&mut scope, value);
183
+ let value = convert_v8_to_ruby(&value, &mut scope)?;
184
+ let args: (Value,) = (value,);
185
+ rec.call::<(Value,), Value>(args)
186
+ .map_err(|err| AnyError::msg(format!("{:?}", err)))
187
+ } else {
188
+ Err(AnyError::msg("cannot access ruby receiver"))
189
+ }
190
+ })
191
+ .map_err(|err| AnyError::msg(format!("{:?}", err)))?
192
+ .and_then(|value| convert_ruby_to_v8(value, &mut scope))
193
+ .map(|value| from_v8(&mut scope, value).unwrap())
142
194
  } else {
143
- Err(Error::runtime_error(
144
- "Cannot send to ruby. Is the ruby receiver and context initialized and set?",
145
- ))
195
+ Err(AnyError::msg("this should never happen"))
146
196
  }
147
197
  }
148
198
  }
@@ -179,6 +229,7 @@ impl Default for Worker {
179
229
  inspect: false,
180
230
  },
181
231
  extensions: std::mem::take(&mut extensions),
232
+ extensions_with_js: Default::default(),
182
233
  startup_snapshot: None,
183
234
  unsafely_ignore_certificate_errors: None,
184
235
  root_cert_store: None,
@@ -200,6 +251,7 @@ impl Default for Worker {
200
251
  shared_array_buffer_store: None,
201
252
  compiled_wasm_module_store: None,
202
253
  stdio: Default::default(),
254
+ should_wait_for_inspector_session: false,
203
255
  };
204
256
 
205
257
  // todo: we don't use the main module at all, but it could be used as an
@@ -254,17 +306,5 @@ fn op_send_to_ruby<'a>(
254
306
  scope: &mut v8::HandleScope,
255
307
  data: serde_v8::Value<'a>,
256
308
  ) -> Result<serde_v8::Value<'a>, AnyError> {
257
- let value = convert_v8_to_ruby(data.v8_value, scope)?;
258
-
259
- WORKER.with(|worker| {
260
- worker
261
- .send(value)
262
- .map(|v| {
263
- let v = convert_ruby_to_v8(v, scope).unwrap();
264
- from_v8(scope, v).unwrap()
265
- })
266
- .map_err(|error| {
267
- AnyError::msg(format!("failed to send to ruby: {}", error))
268
- })
269
- })
309
+ WORKER.with(|worker| worker.send(scope, data))
270
310
  }
@@ -1,5 +1,6 @@
1
+ use self::isorun::utils::stats;
1
2
  use crate::isorun::context::Context;
2
- use isorun::configure::set_receiver;
3
+ use crate::isorun::utils::low_memory_notification;
3
4
  use isorun::function::Function;
4
5
  use isorun::module::Module;
5
6
  use magnus::{define_module, function, method, Error, Module as M, Object};
@@ -11,8 +12,14 @@ mod js;
11
12
  fn init() -> Result<(), Error> {
12
13
  let root = define_module("Isorun").expect("cannot define module: Isorun");
13
14
 
14
- root.define_module_function("receiver=", function!(set_receiver, 1))
15
- .expect("cannot define module function: receiver=");
15
+ root.define_module_function("stats", function!(stats, 0))
16
+ .expect("cannot define module function: stats");
17
+
18
+ root.define_module_function(
19
+ "low_memory_notification",
20
+ function!(low_memory_notification, 0),
21
+ )
22
+ .expect("cannot define module function: low_memory_notification");
16
23
 
17
24
  let context = root
18
25
  .define_class("Context", Default::default())
@@ -22,7 +29,10 @@ fn init() -> Result<(), Error> {
22
29
  .expect("cannot define singelton method: new");
23
30
  context
24
31
  .define_method("load", method!(Context::load, 1))
25
- .expect("cannot load module");
32
+ .expect("cannot define method: load");
33
+ context
34
+ .define_method("receiver=", method!(Context::set_receiver, 1))
35
+ .expect("cannot define method: receiver=");
26
36
 
27
37
  let module = root
28
38
  .define_class("Module", Default::default())
@@ -40,12 +50,6 @@ fn init() -> Result<(), Error> {
40
50
  function
41
51
  .define_method("call", method!(Function::call, -1))
42
52
  .expect("cannot define method: call");
43
- function
44
- .define_method(
45
- "call_without_gvl",
46
- method!(Function::call_without_gvl, -1),
47
- )
48
- .expect("cannot define method: call_without_gvl");
49
53
 
50
54
  Ok(())
51
55
  }
Binary file
Binary file
Binary file
@@ -10,7 +10,10 @@ module Isorun
10
10
  @export_names = export_names
11
11
  end
12
12
 
13
+ # Specify the module to import from
13
14
  def from(module_path)
15
+ module_path = module_path.to_s if module_path.is_a? Pathname
16
+
14
17
  mod = load(module_path)
15
18
  imports = export_names.map { |export_name| mod.import(export_name) }
16
19
  return imports.first if imports.size == 1
@@ -23,7 +26,7 @@ module Isorun
23
26
  CACHE_KEY = "isorun_module_path_mtime"
24
27
  private_constant :CACHE_KEY
25
28
 
26
- def load(module_path)
29
+ def load(module_path) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
27
30
  return context.load(module_path) if Rails.env.production?
28
31
 
29
32
  key = module_path.parameterize
@@ -41,10 +44,14 @@ module Isorun
41
44
  # map to URI scheme to allow adding a timestamp to bust module cache
42
45
  module_path = "file://#{module_path}?t=#{mtime}"
43
46
 
44
- return context.load(module_path) if cache_miss
47
+ if cache_miss
48
+ ActiveSupport::Notifications.instrument "load.module.isorun", { path: module_path }
49
+ return context.load(module_path)
50
+ end
45
51
 
46
52
  Rails.cache.write("#{CACHE_KEY}:#{key}", mtime) if prev_mtime < mtime
47
53
 
54
+ ActiveSupport::Notifications.instrument "load.module.isorun", { path: module_path }
48
55
  context.load(module_path)
49
56
  end
50
57
  end
@@ -52,11 +59,30 @@ module Isorun
52
59
  private_constant :Import
53
60
 
54
61
  class << self
55
- def create(&block)
62
+ # Creates a new context and yields the context as the first argument to
63
+ # the block.
64
+ #
65
+ # @example Creates a new context, imports the default as function
66
+ # result = Isorun::Context.create do |context|
67
+ # func = context.import.from(module_path)
68
+ # func.call("Hello, World!")
69
+ # end
70
+ #
71
+ # @yield [context] The newly created JavaScript context
72
+ # @yieldparam [Isorun::Context] context
73
+ # @yieldreturn [Object, nil] An optional return value from the execution context
74
+ def create(options = default_options, &block)
56
75
  raise "[Isorun::Context] block missing when creating context" unless block
57
76
 
58
77
  context = Isorun::Context.new
59
- yield(context)
78
+
79
+ context.receiver = options[:receiver] if options[:receiver].present?
80
+
81
+ result = yield(context)
82
+
83
+ context.receiver = nil if options[:receiver].present?
84
+
85
+ result
60
86
  end
61
87
 
62
88
  # @!method new()
@@ -66,19 +92,37 @@ module Isorun
66
92
 
67
93
  def default_options
68
94
  {
69
- receiver: Isorun.configuration.receiver
95
+ receiver: nil
70
96
  }
71
97
  end
72
98
  end
73
99
 
100
+ # Specify items you want to import from the module. If none is specified,
101
+ # the default export is taken.
102
+ #
103
+ # @example Import default export
104
+ # result = Isorun::Context.create do |context|
105
+ # item = context.import.from(module_path)
106
+ # end
107
+ #
108
+ # @example Import default export explicitly
109
+ # result = Isorun::Context.create do |context|
110
+ # item = context.import(:default).from(module_path)
111
+ # end
112
+ #
113
+ # @example Import various exports
114
+ # result = Isorun::Context.create do |context|
115
+ # hello, world = context.import(:hello, :world).from(module_path)
116
+ # end
74
117
  def import(*export_names)
75
118
  export_names = [*export_names].map(&:to_s)
76
119
  export_names = [:default.to_s] if export_names.empty?
77
120
  Import.new(self, export_names)
78
121
  end
79
- end
80
122
 
81
- # @!method receiver=(receiver)
82
- # @param receiver [Proc]
83
- # @return [Isorun::Context] the newly created context
123
+ # @!method receiver=(receiver)
124
+ #
125
+ # @param receiver [Proc, nil]
126
+ # @return [Isorun::Context] the newly created context
127
+ end
84
128
  end
data/lib/isorun/engine.rb CHANGED
@@ -12,7 +12,6 @@ module Isorun
12
12
 
13
13
  initializer "isorun.helpers", before: :load_config_initializers do
14
14
  ActiveSupport.on_load(:action_controller_base) do
15
- Rails.logger.debug Isorun::Engine.helpers
16
15
  helper Isorun::Engine.helpers
17
16
  end
18
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Isorun
4
- VERSION = "0.1.6.pre"
4
+ VERSION = "0.1.8"
5
5
  end
data/lib/isorun.rb CHANGED
@@ -6,7 +6,7 @@ ENV["ISORUN_NATIVE_GEM_PATH"] = File.expand_path("..", __dir__)
6
6
 
7
7
  # load native extension
8
8
  begin
9
- ruby_version = /(\d+\.\d+)/.match(::RUBY_VERSION)
9
+ ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
10
10
  require "isorun/#{ruby_version}/isorun"
11
11
  rescue LoadError
12
12
  require "isorun/isorun"
@@ -23,11 +23,4 @@ module Isorun
23
23
  extend ActiveSupport::Autoload
24
24
 
25
25
  class Error < StandardError; end
26
-
27
- def self.with_receiver(receiver)
28
- self.receiver = receiver
29
- result = yield
30
- self.receiver = nil
31
- result
32
- end
33
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isorun
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6.pre
4
+ version: 0.1.8
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Hannes Moser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-10 00:00:00.000000000 Z
11
+ date: 2023-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: 0.9.46
55
+ - !ruby/object:Gem::Dependency
56
+ name: rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake-compiler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,20 @@ dependencies:
80
94
  - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: rspec-rails
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -94,10 +122,27 @@ dependencies:
94
122
  - - ">="
95
123
  - !ruby/object:Gem::Version
96
124
  version: '0'
97
- description: "Import ECMAScript modules into Ruby and use values and functions like
98
- \nJavaScript is part of Ruby. Enables easy to set up server-side rendering for\nmodern
99
- frontend stacks.\n\nIsorun embeds v8 into Ruby via a native extension built with
100
- Rust and\ndeno_core.\n"
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.22.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.22.0
139
+ description: |
140
+ Import ECMAScript modules into Ruby and use values and functions like
141
+ JavaScript is part of Ruby. Enables easy to set up server-side rendering for
142
+ modern frontend stacks.
143
+
144
+ Isorun embeds v8 into Ruby via a native extension built with Rust and
145
+ deno_core.
101
146
  email:
102
147
  - box@hannesmoser.at
103
148
  executables: []
@@ -112,7 +157,6 @@ files:
112
157
  - ext/isorun/Cargo.toml
113
158
  - ext/isorun/extconf.rb
114
159
  - ext/isorun/src/call.js
115
- - ext/isorun/src/isorun/configure.rs
116
160
  - ext/isorun/src/isorun/context.rs
117
161
  - ext/isorun/src/isorun/function.rs
118
162
  - ext/isorun/src/isorun/mod.rs
@@ -161,9 +205,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
161
205
  version: 3.2.dev
162
206
  required_rubygems_version: !ruby/object:Gem::Requirement
163
207
  requirements:
164
- - - ">"
208
+ - - ">="
165
209
  - !ruby/object:Gem::Version
166
- version: 1.3.1
210
+ version: '0'
167
211
  requirements: []
168
212
  rubygems_version: 3.3.22
169
213
  signing_key:
@@ -1,9 +0,0 @@
1
- use crate::js::worker::WORKER;
2
- use magnus::block::Proc;
3
-
4
- pub(crate) fn set_receiver(receiver: Option<Proc>) {
5
- WORKER.with(|worker| match receiver {
6
- None => worker.ruby_receiver.replace(None),
7
- Some(r) => worker.ruby_receiver.replace(Some(r)),
8
- });
9
- }