nodo 1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +128 -0
- data/lib/nodo/client.rb +38 -0
- data/lib/nodo/constant.rb +13 -0
- data/lib/nodo/core.rb +209 -0
- data/lib/nodo/dependency.rb +13 -0
- data/lib/nodo/errors.rb +49 -0
- data/lib/nodo/function.rb +13 -0
- data/lib/nodo/railtie.rb +8 -0
- data/lib/nodo/script.rb +13 -0
- data/lib/nodo/version.rb +3 -0
- data/lib/nodo.rb +24 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8f997b2c506f2cec9bfd235c68cb0db93fcb103fa7f9b40f03740ca27b59ffb4
|
4
|
+
data.tar.gz: 8a1ba6ae691ee2a992b2af5a127c43c70178cc403134db04a08f9597dd8588b3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 77c7a96627569da1b4093eb0fbfb6ab94c0c7dae4e425b019d52e06183f098daac57b88f68924fbe117afc1a0cf8be3aec143fd8e6935c839ebd573dde5afd70
|
7
|
+
data.tar.gz: 8057351d0968d8bc357af3926b805f82ce6e05949ee54efb9df40293ba4faf0ce8d337550318de0114c7f9dc2df05958ea481dc35aa2981a0d95501c9cb18c64
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021 Matthias Grosser
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
[](http://badge.fury.io/rb/nodo)
|
2
|
+
|
3
|
+
# Nōdo – call Node.js from Ruby
|
4
|
+
|
5
|
+
`Nodo` provides a Ruby environment to interact with JavaScript running inside a Node process.
|
6
|
+
|
7
|
+
ノード means "node" in Japanese.
|
8
|
+
|
9
|
+
## Why Nodo?
|
10
|
+
|
11
|
+
Nodo will dispatch all JS function calls to a single long-running Node process.
|
12
|
+
|
13
|
+
JavaScript code is run in a namespaced environment, where you can access your initialized
|
14
|
+
JS objects during sequential function calls without having to re-initialize them.
|
15
|
+
|
16
|
+
IPC is done via unix sockets, greatly improving performance over classic process/eval solutions.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
In your Gemfile:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'nodo'
|
24
|
+
```
|
25
|
+
|
26
|
+
### Node.js
|
27
|
+
|
28
|
+
Nodo requires a working installation of Node.js.
|
29
|
+
|
30
|
+
If the executable is located in your `PATH`, no configuration is required. Otherwise, the path to to binary can be set using:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
Nodo.binary = '/usr/local/bin/node'
|
34
|
+
```
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
In Nodo, you define JS functions as you would define Ruby methods:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class Foo < Nodo::Core
|
42
|
+
|
43
|
+
function :say_hi, <<~JS
|
44
|
+
(name) => {
|
45
|
+
return `Hello ${name}!`;
|
46
|
+
}
|
47
|
+
JS
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
foo = Foo.new
|
52
|
+
foo.say_hi('Nodo')
|
53
|
+
=> "Hello Nodo!"
|
54
|
+
```
|
55
|
+
|
56
|
+
### Using npm modules
|
57
|
+
|
58
|
+
Install your modules to `node_modules`:
|
59
|
+
|
60
|
+
```shell
|
61
|
+
$ yarn add uuid
|
62
|
+
```
|
63
|
+
|
64
|
+
Then `require` your dependencies:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
class Bar < Nodo::Core
|
68
|
+
require :uuid
|
69
|
+
|
70
|
+
function :v4, <<~JS
|
71
|
+
() => {
|
72
|
+
return uuid.v4();
|
73
|
+
}
|
74
|
+
JS
|
75
|
+
end
|
76
|
+
|
77
|
+
bar = Bar.new
|
78
|
+
bar.v4 => "b305f5c4-db9a-4504-b0c3-4e097a5ec8b9"
|
79
|
+
```
|
80
|
+
|
81
|
+
### Aliasing requires
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
class FooBar < Nodo::Core
|
85
|
+
require commonjs: '@rollup/plugin-commonjs'
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
### Setting NODE_PATH
|
90
|
+
|
91
|
+
By default, `./node_modules` is used as the `NODE_PATH`.
|
92
|
+
|
93
|
+
To set a custom path:
|
94
|
+
```ruby
|
95
|
+
Nodo.modules_root = 'path/to/node_modules'
|
96
|
+
```
|
97
|
+
|
98
|
+
For Rails applications, it will be set to `vendor/node_modules`.
|
99
|
+
To use the Rails 6 default of putting `node_modules` to `RAILS_ROOT`:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
# config/initializers/nodo.rb
|
103
|
+
Nodo.modules_root = Rails.root.join('node_modules')
|
104
|
+
```
|
105
|
+
|
106
|
+
### Defining JS constants
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
class BarFoo < Nodo::Core
|
110
|
+
const :HELLO, "World"
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
### Execute some custom JS during initialization
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
class BarFoo < Nodo::Core
|
118
|
+
|
119
|
+
script <<~JS
|
120
|
+
// some custom JS
|
121
|
+
// to be executed during initialization
|
122
|
+
JS
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
### Inheritance
|
127
|
+
|
128
|
+
Subclasses will inherit functions, constants, dependencies and scripts from their superclasses, while only functions can be overwritten.
|
data/lib/nodo/client.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Nodo
|
4
|
+
class Client < Net::HTTP
|
5
|
+
UNIX_REGEXP = /\Aunix:\/\//i
|
6
|
+
|
7
|
+
def initialize(address, port = nil)
|
8
|
+
super(address, port)
|
9
|
+
case address
|
10
|
+
when UNIX_REGEXP
|
11
|
+
@socket_type = 'unix'
|
12
|
+
@socket_path = address.sub(UNIX_REGEXP, '')
|
13
|
+
# Host header is required for HTTP/1.1
|
14
|
+
@address = 'localhost'
|
15
|
+
@port = 80
|
16
|
+
else
|
17
|
+
@socket_type = 'inet'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def connect
|
22
|
+
if @socket_type == 'unix'
|
23
|
+
connect_unix
|
24
|
+
else
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def connect_unix
|
30
|
+
s = Timeout.timeout(@open_timeout) { UNIXSocket.open(@socket_path) }
|
31
|
+
@socket = Net::BufferedIO.new(s)
|
32
|
+
@socket.read_timeout = @read_timeout
|
33
|
+
@socket.continue_timeout = @continue_timeout
|
34
|
+
@socket.debug_output = @debug_output
|
35
|
+
on_connect
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/nodo/core.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
module Nodo
|
2
|
+
class Core
|
3
|
+
SOCKET_NAME = 'nodo.sock'
|
4
|
+
DEFINE_METHOD = '__nodo_define_class__'
|
5
|
+
TIMEOUT = 5
|
6
|
+
ARRAY_CLASS_ATTRIBUTES = %i[dependencies constants scripts].freeze
|
7
|
+
HASH_CLASS_ATTRIBUTES = %i[functions].freeze
|
8
|
+
CLASS_ATTRIBUTES = (ARRAY_CLASS_ATTRIBUTES + HASH_CLASS_ATTRIBUTES).freeze
|
9
|
+
|
10
|
+
@@node_pid = nil
|
11
|
+
@@tmpdir = nil
|
12
|
+
@@mutex = Mutex.new
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
attr_accessor :class_defined
|
17
|
+
|
18
|
+
def inherited(subclass)
|
19
|
+
CLASS_ATTRIBUTES.each do |attr|
|
20
|
+
subclass.send "#{attr}=", send(attr).dup
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def instance
|
25
|
+
@instance ||= new
|
26
|
+
end
|
27
|
+
|
28
|
+
def class_defined?
|
29
|
+
!!class_defined
|
30
|
+
end
|
31
|
+
|
32
|
+
def clsid
|
33
|
+
name || "Class:0x#{object_id.to_s(0x10)}"
|
34
|
+
end
|
35
|
+
|
36
|
+
CLASS_ATTRIBUTES.each do |attr|
|
37
|
+
define_method "#{attr}=" do |value|
|
38
|
+
instance_variable_set :"@#{attr}", value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
ARRAY_CLASS_ATTRIBUTES.each do |attr|
|
43
|
+
define_method "#{attr}" do
|
44
|
+
instance_variable_get(:"@#{attr}") || instance_variable_set(:"@#{attr}", [])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
HASH_CLASS_ATTRIBUTES.each do |attr|
|
49
|
+
define_method "#{attr}" do
|
50
|
+
instance_variable_get(:"@#{attr}") || instance_variable_set(:"@#{attr}", {})
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def require(*mods)
|
55
|
+
deps = mods.last.is_a?(Hash) ? mods.pop : {}
|
56
|
+
mods = mods.map { |m| [m, m] }.to_h
|
57
|
+
self.dependencies = dependencies + mods.merge(deps).map { |name, package| Dependency.new(name, package) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def function(name, code)
|
61
|
+
self.functions = functions.merge(name => Function.new(name, code, caller.first))
|
62
|
+
define_method(name) { |*args| call_js_method(name, args) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def const(name, value)
|
66
|
+
self.constants = constants + [Constant.new(name, value)]
|
67
|
+
end
|
68
|
+
|
69
|
+
def script(code)
|
70
|
+
self.scripts = scripts + [Script.new(code)]
|
71
|
+
end
|
72
|
+
|
73
|
+
def generate_core_code
|
74
|
+
<<~JS
|
75
|
+
global.nodo = require(#{nodo_js});
|
76
|
+
|
77
|
+
const socket = process.argv[1];
|
78
|
+
if (!socket) {
|
79
|
+
process.stderr.write('Socket path is required\\n');
|
80
|
+
process.exit(1);
|
81
|
+
}
|
82
|
+
|
83
|
+
const shutdown = () => {
|
84
|
+
nodo.core.close(() => { process.exit(0) });
|
85
|
+
};
|
86
|
+
|
87
|
+
process.on('SIGINT', shutdown);
|
88
|
+
process.on('SIGTERM', shutdown);
|
89
|
+
|
90
|
+
nodo.core.run(socket);
|
91
|
+
JS
|
92
|
+
end
|
93
|
+
|
94
|
+
def generate_class_code
|
95
|
+
<<~JS
|
96
|
+
(() => {
|
97
|
+
const __nodo_log = nodo.log;
|
98
|
+
const __nodo_klass__ = {};
|
99
|
+
#{dependencies.map(&:to_js).join}
|
100
|
+
#{constants.map(&:to_js).join}
|
101
|
+
#{functions.values.map(&:to_js).join}
|
102
|
+
#{scripts.map(&:to_js).join}
|
103
|
+
return __nodo_klass__;
|
104
|
+
})()
|
105
|
+
JS
|
106
|
+
end
|
107
|
+
|
108
|
+
protected
|
109
|
+
|
110
|
+
def finalize(pid, tmpdir)
|
111
|
+
proc do
|
112
|
+
Process.kill(:SIGTERM, pid)
|
113
|
+
Process.wait(pid)
|
114
|
+
FileUtils.remove_entry(tmpdir) if File.directory?(tmpdir)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def nodo_js
|
121
|
+
Pathname.new(__FILE__).dirname.join('nodo.js').to_s.to_json
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def initialize
|
126
|
+
@@mutex.synchronize do
|
127
|
+
ensure_process_is_spawned
|
128
|
+
wait_for_socket
|
129
|
+
ensure_class_is_defined
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def node_pid
|
134
|
+
@@node_pid
|
135
|
+
end
|
136
|
+
|
137
|
+
def tmpdir
|
138
|
+
@@tmpdir
|
139
|
+
end
|
140
|
+
|
141
|
+
def socket_path
|
142
|
+
tmpdir && tmpdir.join(SOCKET_NAME)
|
143
|
+
end
|
144
|
+
|
145
|
+
def clsid
|
146
|
+
self.class.clsid
|
147
|
+
end
|
148
|
+
|
149
|
+
def ensure_process_is_spawned
|
150
|
+
return if node_pid
|
151
|
+
spawn_process
|
152
|
+
end
|
153
|
+
|
154
|
+
def ensure_class_is_defined
|
155
|
+
return if self.class.class_defined?
|
156
|
+
call_js_method(DEFINE_METHOD, self.class.generate_class_code)
|
157
|
+
self.class.class_defined = true
|
158
|
+
# rescue => e
|
159
|
+
# raise Error, e.message
|
160
|
+
end
|
161
|
+
|
162
|
+
def spawn_process
|
163
|
+
@@tmpdir = Pathname.new(Dir.mktmpdir('nodo'))
|
164
|
+
env = Nodo.env.merge('NODE_PATH' => Nodo.modules_root.to_s)
|
165
|
+
@@node_pid = Process.spawn(env, Nodo.binary, '-e', self.class.generate_core_code, '--', socket_path.to_s)
|
166
|
+
ObjectSpace.define_finalizer(self, self.class.send(:finalize, node_pid, tmpdir))
|
167
|
+
end
|
168
|
+
|
169
|
+
def wait_for_socket
|
170
|
+
start = Time.now
|
171
|
+
until socket_path.exist?
|
172
|
+
raise TimeoutError, "socket #{socket_path} not found" if Time.now - start > TIMEOUT
|
173
|
+
sleep(0.2)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def call_js_method(method, args)
|
178
|
+
raise CallError, 'Node process not ready' unless node_pid
|
179
|
+
raise CallError, "Class #{clsid} not defined" unless self.class.class_defined? || method == DEFINE_METHOD
|
180
|
+
function = self.class.functions[method]
|
181
|
+
raise NameError, "undefined function `#{method}' for #{self.class}" unless function || method == DEFINE_METHOD
|
182
|
+
request = Net::HTTP::Post.new("/#{clsid}/#{method}", 'Content-Type': 'application/json')
|
183
|
+
request.body = JSON.dump(args)
|
184
|
+
client = Client.new("unix://#{socket_path}")
|
185
|
+
response = client.request(request)
|
186
|
+
if response.is_a?(Net::HTTPOK)
|
187
|
+
parse_response(response)
|
188
|
+
else
|
189
|
+
handle_error(response, function)
|
190
|
+
end
|
191
|
+
rescue Errno::EPIPE, IOError
|
192
|
+
# TODO: restart or something? If this happens the process is completely broken
|
193
|
+
raise Error, 'Node process failed'
|
194
|
+
end
|
195
|
+
|
196
|
+
def handle_error(response, function)
|
197
|
+
if response.body
|
198
|
+
result = parse_response(response)
|
199
|
+
raise JavaScriptError.new(result['error'], function) if result.is_a?(Hash) && result.key?('error')
|
200
|
+
end
|
201
|
+
raise CallError, "Node returned #{response.code}"
|
202
|
+
end
|
203
|
+
|
204
|
+
def parse_response(response)
|
205
|
+
JSON.parse(response.body.force_encoding('UTF-8'))
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
data/lib/nodo/errors.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Nodo
|
2
|
+
class Error < StandardError; end
|
3
|
+
class TimeoutError < Error; end
|
4
|
+
class CallError < Error; end
|
5
|
+
|
6
|
+
class JavaScriptError < Error
|
7
|
+
attr_reader :attributes
|
8
|
+
|
9
|
+
def initialize(attributes = {}, function = nil)
|
10
|
+
@attributes = attributes || {}
|
11
|
+
if backtrace = generate_backtrace(attributes['stack'])
|
12
|
+
backtrace.unshift function.source_location if function && function.source_location
|
13
|
+
set_backtrace backtrace
|
14
|
+
end
|
15
|
+
@message = generate_message
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
@message
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# "filename:lineNo: in `method''' or “filename:lineNo.''
|
25
|
+
|
26
|
+
def generate_backtrace(stack)
|
27
|
+
backtrace = []
|
28
|
+
if stack and lines = stack.split("\n")
|
29
|
+
lines.shift
|
30
|
+
lines.each do |line|
|
31
|
+
if match = line.match(/\A *at (?<call>.+) \((?<src>.*):(?<line>\d+):(?<column>\d+)\)/)
|
32
|
+
backtrace << "#{match[:src]}:#{match[:line]}:in `#{match[:call]}'"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
backtrace unless backtrace.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def generate_message
|
40
|
+
message = "#{attributes['message'] || 'Unknown error'}"
|
41
|
+
if loc = attributes['loc']
|
42
|
+
message << loc.inject(' in') { |s, (key, value)| s << " #{key}: #{value}" }
|
43
|
+
end
|
44
|
+
message
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class DependencyError < JavaScriptError; end
|
49
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Nodo
|
2
|
+
class Function
|
3
|
+
attr_reader :name, :code, :source_location
|
4
|
+
|
5
|
+
def initialize(name, code, source_location)
|
6
|
+
@name, @code, @source_location = name, code, source_location
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_js
|
10
|
+
"const #{name} = __nodo_klass__.#{name} = (#{code});\n"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/nodo/railtie.rb
ADDED
data/lib/nodo/script.rb
ADDED
data/lib/nodo/version.rb
ADDED
data/lib/nodo.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'json'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'tmpdir'
|
5
|
+
|
6
|
+
module Nodo
|
7
|
+
class << self
|
8
|
+
attr_accessor :modules_root, :env, :binary
|
9
|
+
end
|
10
|
+
self.modules_root = './node_modules'
|
11
|
+
self.env = {}
|
12
|
+
self.binary = 'node'
|
13
|
+
end
|
14
|
+
|
15
|
+
require_relative 'nodo/version'
|
16
|
+
require_relative 'nodo/errors'
|
17
|
+
require_relative 'nodo/dependency'
|
18
|
+
require_relative 'nodo/function'
|
19
|
+
require_relative 'nodo/script'
|
20
|
+
require_relative 'nodo/constant'
|
21
|
+
require_relative 'nodo/client'
|
22
|
+
require_relative 'nodo/core'
|
23
|
+
|
24
|
+
require_relative 'nodo/railtie' if defined?(Rails)
|
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nodo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matthias Grosser
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-10-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
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'
|
69
|
+
description: Fast Ruby bridge to run JavaScript inside a Node process
|
70
|
+
email:
|
71
|
+
- mtgrosser@gmx.net
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- LICENSE
|
77
|
+
- README.md
|
78
|
+
- lib/nodo.rb
|
79
|
+
- lib/nodo/client.rb
|
80
|
+
- lib/nodo/constant.rb
|
81
|
+
- lib/nodo/core.rb
|
82
|
+
- lib/nodo/dependency.rb
|
83
|
+
- lib/nodo/errors.rb
|
84
|
+
- lib/nodo/function.rb
|
85
|
+
- lib/nodo/railtie.rb
|
86
|
+
- lib/nodo/script.rb
|
87
|
+
- lib/nodo/version.rb
|
88
|
+
homepage: https://github.com/mtgrosser/nodo
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 2.3.0
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubygems_version: 3.0.3
|
108
|
+
signing_key:
|
109
|
+
specification_version: 4
|
110
|
+
summary: Call Node.js from Ruby
|
111
|
+
test_files: []
|