jscall 1.0.1 → 1.2.0

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: 97c7ab8cd5d710cfca8c77f6e906643252b9c54cc5973709763b89aef3830dee
4
- data.tar.gz: e205fad16cc87df640b186ac99190186374f2db821a7e3d499563add9016b097
3
+ metadata.gz: dad6ca4017b93ba7f1675b3d1e3727a143d5e413f86d9f000966f7eae9306d06
4
+ data.tar.gz: e50ef13d47441f195194e4a36886b506266f87339f9d7bbf7f5b9d79dc500814
5
5
  SHA512:
6
- metadata.gz: f25ebf232e77432bb5c79f511c4fc99de78e2d7861e4d3831edcf3662e747021d5d4a207036c8a7add2fe1822c9fb8fdb7439b1e17e67627abea090c1285662c
7
- data.tar.gz: 8e02fe7a9f3d7f0d82ac70f24d33335c2dffe1261133e2dec4a1c4ff65ec9b87bb9fad8f0049dee3fb681c20e7b3f47d9a97890b45958199f1e78c0b76fd69b5
6
+ metadata.gz: 16a7885d6e0e5ef993a9f8ea43a6dc2bcfe78624f20cd10517642c00f59525b1edab1a91ab99367f962d3f63ba5a5c6705bf7cad915673613fb9ee805358baeb
7
+ data.tar.gz: 1f7d95224652882839fd9bce1d4fd539021980018ecc1cca73aed56e598a2c034b8800cdfb88cc6ffd4b09f6488867e33335e8180f7e5e81aa29663ebdc6a5d4
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Jscall
2
2
 
3
+ [![Ruby](https://github.com/csg-tokyo/jscall/actions/workflows/ruby.yml/badge.svg)](https://github.com/csg-tokyo/jscall/actions/workflows/ruby.yml)
4
+
3
5
  Jscall allows executing a program in JavaScript on node.js or a web browser.
4
6
  By default, node.js is used for the execution.
5
7
  To choose a web browser, call `Jscall.config`.
@@ -16,13 +18,17 @@ Jscall.exec '1 + 1'
16
18
  ```
17
19
 
18
20
  This returns `2`. The argument passed to `Jscall.exec` can be
19
- multipe lines. It is executed as source code written in JavaScript.
21
+ multiple lines. It is executed as source code written in JavaScript.
20
22
 
21
23
  `Jscall.exec` returns a resulting value. Numbers, character strings (and symbols), boolean values, and `nil` (and `null`)
22
- are copied when passing between Ruby and JavaScript. An array is also shallow-copied.
24
+ are copied when passing between Ruby and JavaScript. An array is shallow-copied.
23
25
  Other objects are not copied. When they are passed, a remote reference is created at the destination.
26
+ When a `Map` object is returned from JavaScript to Ruby, it is also
27
+ shallow-copied but into a `Hash` object in Ruby.
24
28
 
25
- A remote reference is a proxy object. A method call on a remote reference invokes a method on the corresponding object on the remote site. For example,
29
+ A remote reference is a local reference to a proxy object.
30
+ A method call on a remote reference invokes a method on the corresponding
31
+ object on the remote site. For example,
26
32
 
27
33
  ```
28
34
  js_obj = Jscall.exec '({ foo: (x) => x + 1, bar: 7 })'
@@ -35,11 +41,30 @@ js_obj.baz # 9
35
41
  The `foo` method is executed in JavaScript.
36
42
  Since `bar` is not a function, its value is returned to Ruby as it is.
37
43
 
38
- Setting a object property to a given value is also
44
+ Setting an object property to a given value is also
39
45
  allowed. The expression `js_obj.baz = 9` above sets
40
46
  the object property `baz` to 9.
41
47
 
42
- To call a JavaScript function from Ruby, call a mehtod on `Jscall`.
48
+ An argument to a JavaScript method is copied from Ruby to
49
+ JavaScript unless it is an object. When an argument is a Ruby object,
50
+ a proxy object is created in JavaScript. The rule is the same as the
51
+ rule for returning a value from JavaScript to Ruby. A primitive
52
+ value is copied but an object is not. An array is shallow-copied.
53
+
54
+ A `Hash` object in Ruby is also shallow-copied into JavaScript but a normal
55
+ object is created in JavaScript. Recall that a JavaScript object is
56
+ regarded as an associative array, or a hash table as in Ruby.
57
+ For example, in Ruby,
58
+
59
+ ```
60
+ obj = { a: 2, b: 3 }
61
+ ```
62
+
63
+ when this ruby object is passed to JavaScript as an argument,
64
+ a normal object `{ a: 2, b: 3 }` is created as its copy in JavaScript
65
+ and passed to a JavaScript method.
66
+
67
+ To call a JavaScript function from Ruby, call a method on `Jscall`.
43
68
  For example,
44
69
 
45
70
  ```
@@ -54,9 +79,22 @@ Jscall.foo(7) # 8
54
79
  `Jscall.foo(7)` invokes the JavaScript function with the name following `Jscall.`
55
80
  with the given argument. In this case,
56
81
  the `foo` function is executed with the argument `7`.
82
+ Arguments and a return value are passed to/from a function
83
+ as they are passed to/from a method.
84
+
85
+ `Jscall` can be used for obtaining a remote reference to access a global variable
86
+ in JavaScript. For example,
87
+
88
+ ```
89
+ Jscall.console.log('Hello')
90
+ ```
91
+
92
+ This prints `Hello` on a JavaScript console. `Jscall.console` returns a remote
93
+ reference to the value of `console` in JavaScript. Then, `.log('Hello')`
94
+ calls the `log` method on `console` in JavaScript.
57
95
 
58
96
  When a Ruby object is passed to a JavaScript function/method,
59
- it can call a method on the passed Ruby object.
97
+ you can call a method on the passed Ruby object.
60
98
 
61
99
  ```
62
100
  Jscall.exec <<CODE
@@ -72,7 +110,7 @@ created in Ruby.
72
110
  Note that you must `await` every call to Ruby object since it is
73
111
  asynchronous call.
74
112
 
75
- In JavaScript, `Ruby.exec` is availale to run a program in Ruby.
113
+ In JavaScript, `Ruby.exec` is available to run a program in Ruby.
76
114
  For example,
77
115
 
78
116
  ```
@@ -84,10 +122,19 @@ CODE
84
122
  Jscall.foo()
85
123
  ```
86
124
 
87
- `Jscall.foo()` returns the result of evaluating `RUBY_VERSION`
88
- in Ruby.
125
+ `Jscall.foo()` returns the result of evaluating given Ruby code
126
+ `RUBY_VERSION` in Ruby.
89
127
  Don't forget to `await` a call to `Ruby.exec`.
90
128
 
129
+ ### Remote references
130
+
131
+ A remote reference is implemented by a local reference to a proxy
132
+ object representing the remote object that the remote reference refers to.
133
+ When a proxy object is passed as an argument or a return value
134
+ from Ruby to JavaScript (or vice versa), the corresponding JavaScript
135
+ (or Ruby) object is passed to the destination. In other words,
136
+ a remote reference passed is converted back to a local reference.
137
+
91
138
  Remote references will be automatically reclaimed when they are no
92
139
  longer used. To reclaim them immediately, call:
93
140
 
@@ -95,6 +142,25 @@ longer used. To reclaim them immediately, call:
95
142
  Jscall.scavenge_references
96
143
  ```
97
144
 
145
+ As mentioned above, a remote reference is a local reference
146
+ to a proxy object. In Ruby,
147
+ even a proxy object provides a number of methods inherited from `Object` class,
148
+ such as `clone`, `to_s`, and `inspect`. A call to such a method is not
149
+ delegated to the corresponding JavaScript object. To invoke such a method
150
+ on a JavaScript object, call `send` on its proxy object.
151
+ For example,
152
+
153
+ ```
154
+ js_obj = Jscall.exec '({ to_s: (x, y) => x + y })'
155
+ puts js_obj.to_s(3, 4) # error
156
+ puts js_obj.send('to_s', 3, 4) # 7
157
+ ```
158
+
159
+ The `send` method invokes the JavaScript method with the name specified
160
+ by the first argument. The remaining arguments passed to `send` are passed
161
+ to that JavaScript method.
162
+
163
+
98
164
  ## DOM manipulation
99
165
 
100
166
  When JavaScript code is run on a browser, some utility methods
@@ -109,7 +175,16 @@ links `mystyle.css` in the current directory.
109
175
  - `Jscall.dom.print(msg)`
110
176
 
111
177
  This adds a `p` element to the DOM tree.
178
+ Its inner text is the character string passed as `msg`.
112
179
 
180
+ - `Jscall.dom.append_to_body(html_source)`
181
+
182
+ This inserts the given `html_source` at the end of the `body` element.
183
+ It is a shorthand for
184
+
185
+ ```
186
+ Jscall.document.body.insertAdjacentHTML('beforeend', html_source)
187
+ ```
113
188
 
114
189
  ## Variable scope
115
190
 
@@ -142,16 +217,33 @@ for loading a CommonJS module. For example,
142
217
  Jscall.exec "mime = require('./mime.js')"
143
218
  ```
144
219
 
145
- The file `./mime.js` is loaded and the module is bound to a global variable `mime`.
220
+ The file `./mime.js` is loaded and the module is bound to a global variable `mime` in JavaScript.
146
221
 
147
- You may want to call `Jscall.dyn_import` in Ruby.
222
+ You can directly call `require` on `Jscall` in Ruby.
223
+
224
+ ```
225
+ parser = Jscall.require("@babel/parser")
226
+ ast = parser.parse('const i = 3')
227
+ Jscall.console.log(ast)
228
+ ```
229
+
230
+ `require` will search `./node_modules/` for `@babel/parser`.
231
+ This is equivalent to the following JavaScript code.
232
+
233
+ ```
234
+ parser = require("@babel/parser")
235
+ ast = parser.parse('const i = 3')
236
+ console.log(ast)
237
+ ```
238
+
239
+ Dynamic importing is also available. Call `Jscall.dyn_import` in Ruby.
148
240
 
149
241
  ```
150
242
  fs = Jscall.dyn_import('fs')
151
243
  ```
152
244
 
153
245
  This executes dynamic importing in JavaScript.
154
- For node.js, the file name of the imported module should be a full path name. For a web browser, the root directory is a current working directory. So `Jscall.dyn_import('/mine.mjs')` loads the file `./mine.mjs`.
246
+ For node.js, the file name of the imported module should be a full path name. For a web browser, the root directory is the current working directory. So `Jscall.dyn_import('/mine.mjs')` loads the file `./mine.mjs`.
155
247
 
156
248
  `Jscall.dyn_import` takes the second argument. If it is given,
157
249
  a global variable in JavaScript is bound to the loaded module.
@@ -166,49 +258,147 @@ This is quite equivalent to the following JavaScript code:
166
258
  fs_module = await load('fs')
167
259
  ```
168
260
 
261
+ ## Promise
262
+
263
+ If a program attempts to pass a `Promise` object from JavaScript to Ruby,
264
+ it waits until the promise is fulfilled. Then Jscall passes
265
+ the value of that promise from JavaScript to Ruby instead of that
266
+ promise itself (or a remote reference to that promise). When that promise
267
+ is rejected, an error object is passed to Ruby
268
+ so that the error will be raised in Ruby.
269
+ This design reflects the fact that an `async` function in JavaScript
270
+ also returns a `Promise` object but this object must not be returned
271
+ to Ruby as is when that `async` function is called from Ruby.
272
+ Jscall cannot determine whether a promise should be passed as is to Ruby
273
+ or its value must be passed to Ruby after the promise is fulfilled.
274
+
275
+ When enforcing Jscall to pass a `Promise` object from JavaScript to Ruby,
276
+ `.async` must be inserted between a receiver and a method name.
277
+
278
+ ```
279
+ Jscall.exec(<<CODE)
280
+ function make_promise() {
281
+ return { a: Promise.resolve(7) }
282
+ }
283
+ CODE
284
+
285
+ obj = Jscall.make_promise
286
+ result = obj.a # 7
287
+ prom = obj.async.a # promise
288
+ prom.then(->(r) { puts r }) # 7
289
+ ```
290
+
291
+ ## Synchronous calls
292
+
293
+ You might want to avoid writing `await` when you call a method on a Ruby
294
+ object or you execute Ruby code by `Ruby.exec` from JavaScript.
295
+ For example, that call is included in library code and you might not
296
+ be able to modify the library code so that `await` will be inserted.
297
+
298
+ Jscall supports synchronous calls from JavaScript to Ruby only when
299
+ the underlying JavaScript engine is node.js on Linux.
300
+ In the mode of synchronous calls, you do not have to `await` a method call
301
+ on a Ruby object or a call to `Ruby.exec`.
302
+ It blocks until the return value comes back from Ruby.
303
+ While it blocks, all calls from Ruby to JavaScript are
304
+ synchronously processed.
305
+
306
+ To change to the mode of synchronous calls,
307
+ call `Jscall.config`:
308
+
309
+ ```
310
+ Jscall.config(sync: true)
311
+ ```
169
312
 
170
313
  ## Configuration
171
314
 
172
- To import JavaScript modules when node.js starts,
315
+ Jscall supports several configuration options.
316
+ Call `Jscall.config` with necessary options.
317
+
318
+ ### module_names:
319
+
320
+ To import JavaScript modules when node.js or a web browser starts,
173
321
 
174
322
  ```
175
- Jscall.config(module_names: [["Foo", "./foo.mjs"], ["Bar", "./bar.mjs"]], options: "--use-strict")
323
+ Jscall.config(module_names: [["Foo", "./js", "/lib/foo.mjs"], ["Bar", "./js", "/lib/bar.mjs"]])
176
324
  ```
177
325
 
178
- This specifies that `./foo.mjs` and `./bar.mjs` are impoted when node.js starts.
326
+ This specifies that `./js/lib/foo.mjs` and `./js/lib/bar.mjs` are imported
327
+ at the beginning.
179
328
  This is equivalent to the following import declarations:
180
329
 
181
330
  ```
182
- import * as "Foo" from "./foo.mjs"
183
- import * as "Bar" from "./bar.mjs"
331
+ import * as "Foo" from "./js/lib/foo.mjs"
332
+ import * as "Bar" from "./js/lib/bar.mjs"
184
333
  ```
185
334
 
186
- `'--use-strict'` is passed to node.js as a command line argument.
335
+ Note that each array element given to `module_names:` is
187
336
 
188
- `module_names:` and `options:` are optional arguments to `Jscall.config`.
337
+ ```
338
+ [<module_name> <root> <path>]
339
+ ```
189
340
 
190
- To change the name of the node command,
341
+ `<path>` must start with `/`. It is used as a part of the URL when a browser
342
+ accesses a module.
343
+ When importing a module for node.js, `<root>` and `<path>` are concatenated
344
+ to form a full path name.
345
+
346
+ `<path>` must not start with `/jscall` or `/cmd`. They are reserved for
347
+ internal use.
348
+
349
+ ### options:
350
+
351
+ To specify a command line argument passed to node.js,
191
352
 
192
353
  ```
193
- Jscall::PipeToJs.node_command = "node.exe"
354
+ Jscall.config(options: '--use-strict')
194
355
  ```
195
356
 
196
- The default command name is `"node"`.
357
+ This call specifies that
358
+ `--use-strict` is passed as a command line argument.
359
+
360
+ ### browser: and port:
197
361
 
198
362
  When running JavaScript code on a web browser,
199
363
 
200
364
  ```
201
- Jscall.config(browser: true, options: {port: 10082})
365
+ Jscall.config(browser: true, port: 10082)
202
366
  ```
203
367
 
204
- `options:` is an optional argument.
368
+ Passing `true` for `browser:` switches the execution engine to a web browser.
369
+ The default engine is node.js.
370
+ To switch the engine back to node.js, pass `false` for `browser:`.
371
+ Call `Jscall.close` to detach the current execution engine.
372
+ A new engine with a new configuration will be created.
373
+
374
+ `port:` specifies the port number of an http server. It is optional.
205
375
  The example above specifies that Ruby receives http requests
206
376
  sent to http://localhost:10082 from JavaScript on a web browser.
207
377
 
208
- Passing `false` for `browser:` to `Jscall.config` switches
209
- the execution engine to node.js.
210
- Call `Jscall.close` to detach the current execution engine.
211
- A new enigine with a new configuration will be created.
378
+
379
+ ### Misc.
380
+
381
+ To change to the mode of synchronous calls,
382
+
383
+ ```
384
+ Jscall.config(sync: true)
385
+ ```
386
+
387
+ To set all the configurations to the default ones,
388
+
389
+ ```
390
+ Jscall.config()
391
+ ```
392
+
393
+ ### Other configurations
394
+
395
+ To change the name of the node command,
396
+
397
+ ```
398
+ Jscall::PipeToJs.node_command = "node.exe"
399
+ ```
400
+
401
+ The default command name is `"node"`.
212
402
 
213
403
  To change the command for launching a web browser,
214
404
 
@@ -224,6 +414,12 @@ Jscall launches a web browser by the command like the following:
224
414
  open http://localhost:10082/jscall/jscall.html
225
415
  ```
226
416
 
417
+ Jscall generates a verbose error message if its debug level is more than 0.
418
+
419
+ ```
420
+ Jscall.debug = 1
421
+ ```
422
+
227
423
  ## Installation
228
424
 
229
425
  Add this line to your application's Gemfile:
@@ -254,7 +450,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/csg-to
254
450
 
255
451
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
256
452
 
257
- ## Acknowledgement
453
+ ## Acknowledgment
258
454
 
259
455
  The icon image for jscall was created by partly using the Ruby logo, which was obtained
260
456
  from https://www.ruby-lang.org/en/about/logo/ under CC BY-SA 2.5.
@@ -0,0 +1,56 @@
1
+ # Display a pdf file by using pdf.js
2
+
3
+ require 'jscall'
4
+
5
+ # Run a JavaScript program on a browser
6
+ Jscall.config browser: true
7
+
8
+ # Write HTML code on a blank web page.
9
+ Jscall.dom.append_to_body(<<CODE)
10
+ <h1>PDF.js 'Hello, world!' example</h1>
11
+ <canvas id="the-canvas"></canvas>
12
+ CODE
13
+
14
+ # import pdf.js
15
+ pdfjs = Jscall.dyn_import('https://mozilla.github.io/pdf.js/build/pdf.js')
16
+
17
+ # A pdf file
18
+ url = "https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf"
19
+
20
+ pdf = Jscall.exec 'window["pdfjs-dist/build/pdf"]'
21
+
22
+ pdf.GlobalWorkerOptions.workerSrc = "https://mozilla.github.io/pdf.js/build/pdf.worker.js"
23
+
24
+ loadingTask = pdf.getDocument(url)
25
+ loadingTask.async.promise.then(-> (pdf) {
26
+ puts "PDF loaded"
27
+
28
+ # Fetch the first page
29
+ pageNumber = 1;
30
+ pdf.async.getPage(pageNumber).then(-> (page) {
31
+ puts "Page loaded"
32
+
33
+ scale = 1.5;
34
+ viewport = page.getViewport({ scale: scale })
35
+
36
+ canvas = Jscall.document.getElementById("the-canvas")
37
+ context = canvas.getContext("2d")
38
+ canvas.height = viewport.height
39
+ canvas.width = viewport.width
40
+
41
+ # Render the pdf page
42
+ renderContext = {
43
+ canvasContext: context,
44
+ viewport: viewport,
45
+ }
46
+ renderTask = page.render(renderContext)
47
+ renderTask.async.promise.then(-> (r) {
48
+ # Print a message when the rendering succeeds.
49
+ puts "Page rendered #{r}"
50
+ })
51
+ })
52
+ },
53
+ -> (reason) {
54
+ # an error occurs.
55
+ puts reason
56
+ })
@@ -2,24 +2,27 @@
2
2
 
3
3
  export class HttpStream {
4
4
  constructor() {
5
- this.send_buffer = ['start']
5
+ this.recv_buffer = []
6
6
  this.send_callback = null
7
+ this.fetching = null
8
+ this.call_queue = []
9
+ this.puts('start')
7
10
  }
8
11
 
9
12
  [Symbol.asyncIterator]() {
10
13
  const http_stream = this
11
14
  return {
12
15
  next() {
13
- let msg = http_stream.send_buffer.shift()
14
- if (msg === undefined)
16
+ let next_data = http_stream.recv_buffer.shift()
17
+ if (next_data === undefined)
15
18
  return new Promise((resolve, reject) => {
16
19
  if (http_stream.send_callback === null)
17
20
  http_stream.send_callback = resolve
18
21
  else
19
22
  throw new Error('(fatal) send_callback is not null!')
20
- }).then(() => this.next())
23
+ })
21
24
  else
22
- return http_stream.do_fetch(msg)
25
+ return next_data
23
26
  }
24
27
  }
25
28
  }
@@ -40,12 +43,30 @@ export class HttpStream {
40
43
  }
41
44
 
42
45
  puts(msg) {
43
- this.send_buffer.push(msg)
44
- if (this.send_callback !== null) {
45
- const callback = this.send_callback
46
- this.send_callback = null
47
- callback()
48
- }
46
+ if (this.fetching === null)
47
+ this.fetching = this.puts0(msg)
48
+ else
49
+ this.call_queue.push(msg)
50
+ }
51
+
52
+ puts0(msg) {
53
+ return this.do_fetch(msg)
54
+ .then((data) => {
55
+ if (this.send_callback === null)
56
+ this.recv_buffer.push(data)
57
+ else {
58
+ const callback = this.send_callback
59
+ this.send_callback = null
60
+ callback(data)
61
+ }
62
+
63
+ if (this.call_queue.length > 0) {
64
+ const msg2 = this.call_queue.shift()
65
+ this.fetching = this.puts0(msg2)
66
+ }
67
+ else
68
+ this.fetching = null
69
+ })
49
70
  }
50
71
 
51
72
  setEncoding(encoding) {}
@@ -64,4 +85,8 @@ export const Jscall = new class {
64
85
  link.href = file_name
65
86
  document.head.append(link)
66
87
  }
88
+
89
+ append_to_body(html_source) {
90
+ document.body.insertAdjacentHTML('beforeend', html_source)
91
+ }
67
92
  }
@@ -8,6 +8,7 @@ module Jscall
8
8
  WEBrick::HTTPUtils::DefaultMimeTypes['mjs'] ||= "application/javascript"
9
9
 
10
10
  class Dom
11
+ # see Jscall class in browser.mjs
11
12
  def method_missing(name, *args)
12
13
  Jscall.__getpipe__.funcall(nil, "Jscall.#{name}", args)
13
14
  end
@@ -23,13 +24,27 @@ module Jscall
23
24
  end
24
25
 
25
26
  class PipeToBrowser < PipeToJs
26
- def startJS(module_names, options)
27
- port = 10081
28
- port = options[:port] if options.is_a?(Hash) && options.has_key?(:port)
29
- @pipe = FetchServer.new(port)
27
+ def startJS(module_names, config)
28
+ urls = {}
29
+ module_names.each_index do |i|
30
+ mod = module_names[i]
31
+ urls[mod[2]] = mod
32
+ end
33
+ port = config[:port] || 10081
34
+ @pipe = FetchServer.new(port, urls)
30
35
  @pipe.open
31
36
  end
32
37
 
38
+ def setup(config)
39
+ if config[:browser]
40
+ module_names = config[:module_names] || []
41
+ module_names.each_index do |i|
42
+ mod = module_names[i]
43
+ Jscall.dyn_import(mod[2], mod[0])
44
+ end
45
+ end
46
+ end
47
+
33
48
  def close
34
49
  @pipe.shutdown
35
50
  sleep(0.5)
@@ -58,7 +73,8 @@ module Jscall
58
73
  @@run_cmd = name
59
74
  end
60
75
 
61
- def initialize(port)
76
+ def initialize(port, urls)
77
+ @url_map = urls
62
78
  @send_buffer = Thread::Queue.new
63
79
  @receive_buffer = Thread::Queue.new
64
80
  @server_running = false
@@ -96,10 +112,16 @@ module Jscall
96
112
  end
97
113
 
98
114
  def read_file(req, res)
99
- if req.path.start_with?('/jscall/')
115
+ path = req.path
116
+ if path.start_with?('/jscall/')
100
117
  root = "#{__dir__}/../"
101
118
  else
102
- root = @server.config[:DocumentRoot]
119
+ value = @url_map[path]
120
+ if value.nil?
121
+ root = @server.config[:DocumentRoot]
122
+ else
123
+ root = value[1]
124
+ end
103
125
  end
104
126
  WEBrick::HTTPServlet::FileHandler.new(@server, root).service(req, res)
105
127
  end