humid 0.2.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +168 -75
- data/lib/humid/controller_runtime.rb +1 -1
- data/lib/humid/log_subscriber.rb +1 -1
- data/lib/humid/version.rb +2 -2
- data/lib/humid.rb +64 -79
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1b62f6d6c3c7ffac0ba1c609965742a6a09dafebe0ebb9fc05cbae3c9200299e
|
|
4
|
+
data.tar.gz: 56557384d6768ada9b8fa9e9403720b5e75f03a579e39483892afcb631cb04a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4465a746beab25d302782cc71b8a70cd7fe827dc6ce3058e6720056c121bb86357733a9d31a7c266ceee6a53f938b174fec1eef111dbc634aa6699ffccb30ac9
|
|
7
|
+
data.tar.gz: c5d8a1c61b84b49a14a78c7d198af09871bbdf9cebc8c9bbd1d4b32c2cdd95ac7de2bad06214f80ec12a8507158b8bb95a592eff313e51e454dcd768e5a2cdc1
|
data/README.md
CHANGED
|
@@ -2,9 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
Humid is a
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Humid is a set of helper functions for using `mini_racer` for Server Side
|
|
6
|
+
Rendering (SSR). **There are only 2 pure public functions and a `configure` to set
|
|
7
|
+
default args**. `mini_racer` does the heavy lifting, Humid just provides a few
|
|
8
|
+
conveniences.
|
|
9
|
+
|
|
10
|
+
While it was built with React in mind, it can work with any JS function that
|
|
11
|
+
returns an HTML string.
|
|
12
|
+
|
|
13
|
+
## Design
|
|
14
|
+
|
|
15
|
+
Humid is designed for the common case where all data is gathered before
|
|
16
|
+
rendering. Your application fetches everything needed, passes it as props, and
|
|
17
|
+
Humid returns the rendered HTML in a single synchronous call. It does not
|
|
18
|
+
support streaming or async data fetching during render.
|
|
8
19
|
|
|
9
20
|
## Caution
|
|
10
21
|
|
|
@@ -26,12 +37,14 @@ For source-map support, also add
|
|
|
26
37
|
yarn add source-map-support
|
|
27
38
|
```
|
|
28
39
|
|
|
29
|
-
|
|
30
40
|
## Configuration
|
|
31
41
|
|
|
32
|
-
Add an initializer to configure
|
|
42
|
+
Add an initializer to configure the default options for `Humid.render`. These
|
|
43
|
+
are overridable on `Humid.render`.
|
|
33
44
|
|
|
34
45
|
```ruby
|
|
46
|
+
# app/initializers/humid.rb
|
|
47
|
+
|
|
35
48
|
Humid.configure do |config|
|
|
36
49
|
# Path to your build file located in `app/assets/builds/`. You should use a
|
|
37
50
|
# separate build apart from your `application.js`.
|
|
@@ -55,41 +68,70 @@ Humid.configure do |config|
|
|
|
55
68
|
# the respective logger levels on the ruby side.
|
|
56
69
|
#
|
|
57
70
|
# Defaults to `nil`
|
|
58
|
-
config.logger = Rails.env.
|
|
59
|
-
|
|
60
|
-
# Options passed to mini_racer.
|
|
61
|
-
#
|
|
62
|
-
# Defaults to empty `{}`.
|
|
63
|
-
config.context_options = {
|
|
64
|
-
timeout: 1000,
|
|
65
|
-
ensure_gc_after_idle: 2000
|
|
66
|
-
}
|
|
71
|
+
config.logger = Rails.env.local? ? Rails.logger : nil
|
|
67
72
|
end
|
|
68
73
|
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
|
|
74
|
+
# if Rails.env.local?
|
|
75
|
+
# # Use single_threaded mode for Spring and other forked envs.
|
|
76
|
+
# MiniRacer::Platform.set_flags! :single_threaded
|
|
77
|
+
# ctx = MiniRacer::Context.new(timeout: 100, ensure_gc_after_idle: 2000)
|
|
78
|
+
# MINI_RACER_CONTEXT = Humid.prepare(ctx)
|
|
79
|
+
# end
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
### Create the MiniRacer Context.
|
|
86
|
+
|
|
87
|
+
On local development or test environments, uncomment the below.
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
if Rails.env.local?
|
|
72
91
|
# Use single_threaded mode for Spring and other forked envs.
|
|
73
92
|
MiniRacer::Platform.set_flags! :single_threaded
|
|
74
|
-
|
|
93
|
+
ctx = MiniRacer::Context.new(timeout: 100, ensure_gc_after_idle: 2000)
|
|
94
|
+
MINI_RACER_CONTEXT = Humid.prepare(ctx)
|
|
75
95
|
end
|
|
76
96
|
```
|
|
77
97
|
|
|
78
|
-
|
|
98
|
+
On production, keep in mind that `mini_racer` is **thread safe, but not fork
|
|
99
|
+
safe**. When using with web servers that employ forking, create a
|
|
100
|
+
`MINI_RACER_CONTEXT` with options of your choosing **on worker boot. There
|
|
101
|
+
should be no context created on the master process.**
|
|
79
102
|
|
|
80
|
-
|
|
81
|
-
workers ENV.fetch("WEB_CONCURRENCY") { 1 }
|
|
103
|
+
For example with puma:
|
|
82
104
|
|
|
105
|
+
```ruby
|
|
106
|
+
# config/puma.rb
|
|
83
107
|
on_worker_boot do
|
|
84
|
-
|
|
108
|
+
ctx = MiniRacer::Context.new(timeout: 100, ensure_gc_after_idle: 2000)
|
|
109
|
+
MINI_RACER_CONTEXT = Humid.prepare(ctx)
|
|
85
110
|
end
|
|
86
111
|
|
|
87
112
|
on_worker_shutdown do
|
|
88
|
-
|
|
113
|
+
MINI_RACER_CONTEXT.dispose
|
|
89
114
|
end
|
|
90
115
|
```
|
|
91
116
|
|
|
117
|
+
### Prepare the context with `Humid.prepare`
|
|
118
|
+
|
|
119
|
+
`Humid.prepare` will prepare the context's environment by [removing
|
|
120
|
+
functions](#functions-not-available), delegate `console.log` and friends to
|
|
121
|
+
your logger, load the SSR js bundle, and add the render function.
|
|
122
|
+
|
|
123
|
+
You can also override config options per-context:
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
MINI_RACER_CONTEXT = Humid.prepare(
|
|
127
|
+
MiniRacer::Context.new(timeout: 1000),
|
|
128
|
+
application_path: Rails.root.join("other_bundle.js"),
|
|
129
|
+
logger: nil
|
|
130
|
+
)
|
|
131
|
+
```
|
|
132
|
+
|
|
92
133
|
If you'd like support for source map support, you will need to
|
|
134
|
+
|
|
93
135
|
1. Add the following to your entry file, e.g, `server_rendering.js`.
|
|
94
136
|
2. set `config.source_map_path`.
|
|
95
137
|
|
|
@@ -103,9 +145,40 @@ require("source-map-support").install({
|
|
|
103
145
|
}
|
|
104
146
|
});
|
|
105
147
|
```
|
|
106
|
-
A [sample] webpack.config is available for reference.
|
|
107
148
|
|
|
108
|
-
|
|
149
|
+
See the [sample server_rendering.tsx](./sample/server_rendering.tsx) to see how
|
|
150
|
+
it is integrated.
|
|
151
|
+
|
|
152
|
+
### Add a renderer and call `Humid.render`
|
|
153
|
+
|
|
154
|
+
In your entry file, e.g, `server_rendering.js`, pass your HTML render function
|
|
155
|
+
to `setHumidRenderer`. There is no need to require the function.
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
// Set a factory function that will create a new instance of our app
|
|
159
|
+
// for each request.
|
|
160
|
+
setHumidRenderer((json) => {
|
|
161
|
+
const initialState = JSON.parse(json)
|
|
162
|
+
|
|
163
|
+
return ReactDOMServer.renderToString(
|
|
164
|
+
<Application initialPage={initialState}/>
|
|
165
|
+
)
|
|
166
|
+
})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
And finally call `render` from ERB.
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
<%= Humid.render(MINI_RACER_CONTEXT, json).html_safe %>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Instrumentation is included:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
Completed 200 OK in 14ms (Views: 0.2ms | Humid SSR: 11.0ms | ActiveRecord: 2.7ms)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## The prepared `mini_racer` environment.
|
|
109
182
|
|
|
110
183
|
### Functions not available
|
|
111
184
|
|
|
@@ -144,55 +217,10 @@ The formatter receives `(level, message, *rest)` where:
|
|
|
144
217
|
- `message` — the first argument passed to `console.log/info/warn/error`
|
|
145
218
|
- `rest` — any additional arguments (objects come through as Ruby hashes/arrays)
|
|
146
219
|
|
|
147
|
-
The default formatter
|
|
220
|
+
The default formatter returns `message` unchanged.
|
|
148
221
|
|
|
149
|
-
##
|
|
222
|
+
## Server-side libraries that detect node.js envs.
|
|
150
223
|
|
|
151
|
-
In your entry file, e.g, `server_rendering.js`, pass your HTML render function
|
|
152
|
-
to `setHumidRenderer`. There is no need to require the function.
|
|
153
|
-
|
|
154
|
-
```javascript
|
|
155
|
-
// Set a factory function that will create a new instance of our app
|
|
156
|
-
// for each request.
|
|
157
|
-
setHumidRenderer((json) => {
|
|
158
|
-
const initialState = JSON.parse(json)
|
|
159
|
-
|
|
160
|
-
return ReactDOMServer.renderToString(
|
|
161
|
-
<Application initialPage={initialState}/>
|
|
162
|
-
)
|
|
163
|
-
})
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
And finally call `render` from ERB.
|
|
167
|
-
|
|
168
|
-
```ruby
|
|
169
|
-
<%= Humid.render(initial_state).html_safe %>
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Instrumentation is included:
|
|
173
|
-
|
|
174
|
-
```
|
|
175
|
-
Completed 200 OK in 14ms (Views: 0.2ms | Humid SSR: 11.0ms | ActiveRecord: 2.7ms)
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### Puma
|
|
179
|
-
|
|
180
|
-
`mini_racer` is thread safe, but not fork safe. To use with web servers that
|
|
181
|
-
employ forking, use `Humid.create_context` only on forked processes. On
|
|
182
|
-
production, There should be no context created on the master process.
|
|
183
|
-
|
|
184
|
-
```ruby
|
|
185
|
-
# Puma
|
|
186
|
-
on_worker_boot do
|
|
187
|
-
Humid.create_context
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
on_worker_shutdown do
|
|
191
|
-
Humid.dispose
|
|
192
|
-
end
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Server-side libraries that detect node.js envs.
|
|
196
224
|
You may need webpacker to create aliases for server friendly libraries that can
|
|
197
225
|
not detect the `mini_racer` environment. For example, in `webpack.config.js`.
|
|
198
226
|
|
|
@@ -210,7 +238,7 @@ not detect the `mini_racer` environment. For example, in `webpack.config.js`.
|
|
|
210
238
|
[Vue has a resource][vue_ssr] on how to write universal code. Below
|
|
211
239
|
are a few highlights that are important to keep in mind.
|
|
212
240
|
|
|
213
|
-
|
|
241
|
+
## State
|
|
214
242
|
|
|
215
243
|
Humid uses a single context across multiple request. To avoid state pollution, we
|
|
216
244
|
provide a factory function to `setHumidRenderer` that builds a new app instance on
|
|
@@ -219,9 +247,9 @@ every call.
|
|
|
219
247
|
This provides better isolation, but as it is still a shared context, polluting
|
|
220
248
|
`global` is still possible. Be careful of modifying `global` in your code.
|
|
221
249
|
|
|
222
|
-
|
|
250
|
+
## Missing browser APIs
|
|
223
251
|
|
|
224
|
-
|
|
252
|
+
Some libraries that depend on browser APIs will fail in the
|
|
225
253
|
`mini_racer` environment because of missing browser APIs. Account for this by
|
|
226
254
|
moving the `require` to `useEffect` in your component.
|
|
227
255
|
|
|
@@ -232,6 +260,71 @@ moving the `require` to `useEffect` in your component.
|
|
|
232
260
|
}, [])
|
|
233
261
|
```
|
|
234
262
|
|
|
263
|
+
## Polyfills
|
|
264
|
+
|
|
265
|
+
React SSR may import node.js dependencies that you need to polyfill for. See
|
|
266
|
+
a sample esbuild [build script](./sample/bulid_ssr.js) and a [shim.js](./sample/shim.js)
|
|
267
|
+
to get around these issues.
|
|
268
|
+
|
|
269
|
+
## Testing
|
|
270
|
+
|
|
271
|
+
When running in test environments that also forks, you may need to set up new mini_racer
|
|
272
|
+
contexts for each parallel worker. For example:
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
ActiveSupport.on_load(:action_dispatch_integration_test) do
|
|
276
|
+
include ActionView::Helpers::TranslationHelper
|
|
277
|
+
include Devise::Test::IntegrationHelpers
|
|
278
|
+
|
|
279
|
+
parallelize_setup do
|
|
280
|
+
MINI_RACER_CONTEXT.dispose if defined?(MINI_RACER_CONTEXT)
|
|
281
|
+
ctx = MiniRacer::Context.new(timeout: 1000, ensure_gc_after_idle: 2000)
|
|
282
|
+
Object.send(:remove_const, :MINI_RACER_CONTEXT) if defined?(MINI_RACER_CONTEXT)
|
|
283
|
+
Object.const_set(:MINI_RACER_CONTEXT, Humid.prepare(ctx))
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
parallelize_teardown do
|
|
287
|
+
MINI_RACER_CONTEXT.dispose if defined?(MINI_RACER_CONTEXT)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Telemetry
|
|
293
|
+
|
|
294
|
+
The `MiniRacer::Context` gives you access to V8 heap statistics for monitoring
|
|
295
|
+
memory usage over time.
|
|
296
|
+
|
|
297
|
+
```ruby
|
|
298
|
+
MINI_RACER_CONTEXT.heap_stats
|
|
299
|
+
# {:total_heap_size=>3100672,
|
|
300
|
+
# :total_heap_size_executable=>4194304,
|
|
301
|
+
# :total_physical_size=>1280640,
|
|
302
|
+
# :total_available_size=>1501560832,
|
|
303
|
+
# :used_heap_size=>1205376,
|
|
304
|
+
# :heap_size_limit=>1501560832,
|
|
305
|
+
# ...}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
You can combine humid's instrumentation and OpenTelemetry to track heap growth
|
|
309
|
+
per worker:
|
|
310
|
+
|
|
311
|
+
```ruby
|
|
312
|
+
meter = OpenTelemetry.meter_provider.meter("humid")
|
|
313
|
+
render_histogram = meter.create_histogram("humid.render.duration", unit: "ms", description: "SSR render duration")
|
|
314
|
+
heap_gauge = meter.create_gauge("humid.heap.used_bytes", unit: "By", description: "V8 heap used bytes")
|
|
315
|
+
|
|
316
|
+
ActiveSupport::Notifications.subscribe("render.humid") do |event|
|
|
317
|
+
stats = MINI_RACER_CONTEXT.heap_stats
|
|
318
|
+
attributes = { "worker.pid" => Process.pid.to_s }
|
|
319
|
+
|
|
320
|
+
render_histogram.record(event.duration, attributes: attributes)
|
|
321
|
+
heap_gauge.record(stats[:used_heap_size], attributes: attributes)
|
|
322
|
+
end
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
A steadily climbing `used_heap_size` across requests indicates a memory leak in
|
|
326
|
+
your JavaScript bundle.
|
|
327
|
+
|
|
235
328
|
## Contributing
|
|
236
329
|
|
|
237
330
|
Please see [CONTRIBUTING.md](/CONTRIBUTING.md).
|
data/lib/humid/log_subscriber.rb
CHANGED
data/lib/humid/version.rb
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
VERSION = "0.
|
|
1
|
+
module Humid
|
|
2
|
+
VERSION = "0.5.0".freeze
|
|
3
3
|
end
|
data/lib/humid.rb
CHANGED
|
@@ -6,108 +6,93 @@ require "humid/log_subscriber"
|
|
|
6
6
|
require "humid/controller_runtime"
|
|
7
7
|
require "humid/version"
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
@@context = nil
|
|
11
|
-
|
|
9
|
+
module Humid
|
|
12
10
|
class RenderError < StandardError
|
|
13
11
|
end
|
|
14
12
|
|
|
15
13
|
class FileNotFound < StandardError
|
|
16
14
|
end
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
|
|
16
|
+
mattr_accessor :config
|
|
19
17
|
|
|
20
18
|
self.config = ActiveSupport::OrderedOptions.new.merge({
|
|
21
19
|
raise_render_errors: true,
|
|
22
|
-
context_options: {},
|
|
23
20
|
log_formatter: proc { |_level, message, *_rest| message },
|
|
24
21
|
})
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def remove_functions
|
|
32
|
-
<<~JS
|
|
33
|
-
delete this.setTimeout;
|
|
34
|
-
delete this.setInterval;
|
|
35
|
-
delete this.clearTimeout;
|
|
36
|
-
delete this.clearInterval;
|
|
37
|
-
delete this.setImmediate;
|
|
38
|
-
delete this.clearImmediate;
|
|
39
|
-
JS
|
|
40
|
-
end
|
|
23
|
+
extend self
|
|
24
|
+
|
|
25
|
+
def configure
|
|
26
|
+
yield self.config
|
|
27
|
+
end
|
|
41
28
|
|
|
42
|
-
|
|
43
|
-
|
|
29
|
+
def prepare(ctx, options = {})
|
|
30
|
+
effective_config = config.merge(options)
|
|
31
|
+
logger = effective_config.logger
|
|
32
|
+
log_formatter = effective_config.log_formatter
|
|
33
|
+
|
|
34
|
+
if logger
|
|
35
|
+
fmt = log_formatter || proc { |_level, message, *_rest| message }
|
|
36
|
+
ctx.attach("console.log", proc { |*args| logger.debug(fmt.call(:debug, *args)) })
|
|
37
|
+
ctx.attach("console.info", proc { |*args| logger.info(fmt.call(:info, *args)) })
|
|
38
|
+
ctx.attach("console.error", proc { |*args| logger.error(fmt.call(:error, *args)) })
|
|
39
|
+
ctx.attach("console.warn", proc { |*args| logger.warn(fmt.call(:warn, *args)) })
|
|
44
40
|
end
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
__renderer = fn;
|
|
51
|
-
}
|
|
52
|
-
JS
|
|
53
|
-
end
|
|
42
|
+
js = ""
|
|
43
|
+
js << remove_functions
|
|
44
|
+
js << renderer
|
|
45
|
+
ctx.eval(js)
|
|
54
46
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
end
|
|
47
|
+
source_path = effective_config.application_path
|
|
48
|
+
map_path = effective_config.source_map_path
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
@@context.dispose
|
|
62
|
-
@@context = nil
|
|
63
|
-
end
|
|
50
|
+
if map_path
|
|
51
|
+
ctx.attach("readSourceMap", proc { File.read(map_path) })
|
|
64
52
|
end
|
|
65
53
|
|
|
66
|
-
|
|
67
|
-
|
|
54
|
+
filename = File.basename(source_path.to_s)
|
|
55
|
+
ctx.eval(File.read(source_path), filename: filename)
|
|
68
56
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
ctx.attach("console.log", proc { |*args| logger.debug(fmt.call(:debug, *args)) })
|
|
72
|
-
ctx.attach("console.info", proc { |*args| logger.info(fmt.call(:info, *args)) })
|
|
73
|
-
ctx.attach("console.error", proc { |*args| logger.error(fmt.call(:error, *args)) })
|
|
74
|
-
ctx.attach("console.warn", proc { |*args| logger.warn(fmt.call(:warn, *args)) })
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
js = ""
|
|
78
|
-
js << remove_functions
|
|
79
|
-
js << renderer
|
|
80
|
-
ctx.eval(js)
|
|
81
|
-
|
|
82
|
-
source_path = config.application_path
|
|
83
|
-
map_path = config.source_map_path
|
|
57
|
+
ctx
|
|
58
|
+
end
|
|
84
59
|
|
|
85
|
-
|
|
86
|
-
|
|
60
|
+
def render(ctx, *args)
|
|
61
|
+
ActiveSupport::Notifications.instrument("render.humid") do
|
|
62
|
+
ctx.call("__renderer", *args)
|
|
63
|
+
rescue MiniRacer::RuntimeError => e
|
|
64
|
+
message = ([e.message] + e.backtrace.filter { |x| x.starts_with? "JavaScript" }).join("\n")
|
|
65
|
+
render_error = Humid::RenderError.new(message)
|
|
66
|
+
|
|
67
|
+
if config.raise_render_errors
|
|
68
|
+
raise render_error
|
|
69
|
+
else
|
|
70
|
+
config.logger.error(render_error.inspect)
|
|
71
|
+
""
|
|
87
72
|
end
|
|
88
|
-
|
|
89
|
-
filename = File.basename(source_path.to_s)
|
|
90
|
-
@@current_filename = filename
|
|
91
|
-
ctx.eval(File.read(source_path), filename: filename)
|
|
92
|
-
|
|
93
|
-
@@context = ctx
|
|
94
73
|
end
|
|
74
|
+
end
|
|
95
75
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def remove_functions
|
|
79
|
+
<<~JS
|
|
80
|
+
delete this.setTimeout;
|
|
81
|
+
delete this.setInterval;
|
|
82
|
+
delete this.clearTimeout;
|
|
83
|
+
delete this.clearInterval;
|
|
84
|
+
delete this.setImmediate;
|
|
85
|
+
delete this.clearImmediate;
|
|
86
|
+
JS
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def renderer
|
|
90
|
+
<<~JS
|
|
91
|
+
var __renderer;
|
|
92
|
+
function setHumidRenderer(fn) {
|
|
93
|
+
__renderer = fn;
|
|
94
|
+
}
|
|
95
|
+
JS
|
|
111
96
|
end
|
|
112
97
|
end
|
|
113
98
|
|