ruby-await-node 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 450ba09014e24cfd92d646e03dbdaa4b3bc4f867
4
+ data.tar.gz: f9b9a15aa7d56aedd26af82650f574fc53b9e947
5
+ SHA512:
6
+ metadata.gz: 0cb5c3440f8bfeca49eeab6021b8aeb1efc82450cf2039c4c700921ee6fc09624f99742372b0cc8c3f4d2d25f845d9831838655026d87e9d31d6928bdeb0fdc8
7
+ data.tar.gz: c2fd46e29a35ec2bcad25b6bb3a5e9ac0e4611266bea5a51837477c9038312f2207910562fed723260826a32f741725739257efd783270e75f4fe179840211f8
@@ -0,0 +1,16 @@
1
+ version: 2
2
+ template:
3
+ docker: &docker
4
+ - image: circleci/ruby:2.4.5-node
5
+ jobs:
6
+ test:
7
+ docker: *docker
8
+ steps:
9
+ - checkout
10
+ - run: bundle
11
+ - run: bundle exec rspec spec/
12
+ workflows:
13
+ version: 2
14
+ test:
15
+ jobs:
16
+ - test
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ tags
2
+ .byebug_history
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.2.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mavenlink-js.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ruby-await-node (1.2.2)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ byebug (10.0.2)
10
+ diff-lcs (1.3)
11
+ rspec (3.8.0)
12
+ rspec-core (~> 3.8.0)
13
+ rspec-expectations (~> 3.8.0)
14
+ rspec-mocks (~> 3.8.0)
15
+ rspec-core (3.8.2)
16
+ rspec-support (~> 3.8.0)
17
+ rspec-expectations (3.8.4)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.8.0)
20
+ rspec-mocks (3.8.1)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.8.0)
23
+ rspec-support (3.8.2)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ byebug
30
+ rspec
31
+ ruby-await-node!
32
+
33
+ BUNDLED WITH
34
+ 1.16.1
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Mavenlink, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # Introduction
2
+
3
+ This was inspired by https://github.com/mavenlink/alaska
4
+ Unfortunately, alaska was designed to run every new script in a new
5
+ sandbox, thus things like require won't work. So we don't have access to
6
+ all the power offered by nodejs
7
+
8
+ Out of our need to call javascript in a rapid fashion from javascript
9
+ with minimal setup, we decided to rewrite alaska into ruby-await-node
10
+ with the goal of efficiently execute sophisticated javascript code from
11
+ ruby.
12
+
13
+ The key to the performance is to launch a nodejs webserver, then use
14
+ such webserver to dangerously eval any given javascript part.
15
+
16
+ It's risky, we know. But with great power come great responsibility. You
17
+ should be using this if you want to leverage the expertise of javascript
18
+ where ruby fall short
19
+
20
+ # TODO For ruby-await-node
21
+
22
+ - [x] Execute any javascript from ruby
23
+ - [x] Pass all test
24
+ - [ ] Execute await/async method
25
+ - [ ] Use zeromq instead of http webserver to design away port conflicting
26
+
27
+ # Getting Started
28
+
29
+ ```ruby
30
+ gem 'ruby-await-node', '~> 1.0'
31
+ ```
32
+
33
+ Or if you want to run the latest version
34
+
35
+ ```ruby
36
+ gem 'ruby-await-node', :git => 'git@github.com:remitano/ruby-await-node.git'
37
+ ```
38
+
39
+ Then to invoke some javascript
40
+
41
+ ```ruby
42
+ runtime = RubyAwaitNode::Runtime.new(debug: true)
43
+ context = RubyAwaitNode::Context.new(runtime)
44
+ entry_path = File.expand_path("entry.js", __dir__)
45
+ context.eval("global.actor = require(#{entry_path.to_json})")
46
+ result = context.eval("actor.method()")
47
+ ```
48
+
49
+ Your entry.js may look like this:
50
+ ```javascript
51
+ const moment = require("moment")
52
+ module.exports = {
53
+ method: function() {
54
+ return something;
55
+ },
56
+
57
+ asyncMethod: async function() {
58
+ await operation;
59
+ return something;
60
+ }
61
+ }
62
+ ```
63
+
64
+ Under the hood ruby-await-node will automatically wait for async method
65
+ to complete
66
+
67
+ ## Examples
68
+
69
+ ### Load a javascript file
70
+ ```
71
+ context.load(File.expand_path("entry.js", __dir__))
72
+ ```
73
+
74
+ *Warning*: always pass the absolute path
75
+ ### Simple execution
76
+ ```
77
+ context.eval("1.0 + 2.0")
78
+ context.eval("global.a = 1.0")
79
+ ```
80
+
81
+ ### Calling function with arguments
82
+ ```
83
+ context.call("(function(b) { return global.a * b })", 5)
84
+ ```
85
+
86
+ This will be equivalent to execute this in nodejs
87
+ ```
88
+ (function(b) { return a * b }).apply(this, [5])
89
+ ```
90
+
91
+ ### Other examples
92
+ Look into spec/ for other potential examples
@@ -0,0 +1,71 @@
1
+ require 'net/http'
2
+ require 'socket'
3
+ require 'tempfile'
4
+ require 'json'
5
+ require_relative "exception"
6
+
7
+ module RubyAwaitNode
8
+ class Context
9
+ # runtime is an instance of RubyAwaitNode
10
+ # src is the js code to be eval()'d in the nodejs context
11
+ def initialize(runtime, src = "", options = {})
12
+ @runtime = runtime
13
+
14
+ # compile context source, in most cases
15
+ # this is something like the CoffeeScript compiler
16
+ # or the SASS compiler
17
+ eval(src)
18
+ end
19
+
20
+ def eval(src)
21
+ if /\S/ =~ src #IMPORTANT! /\S/ =~ "()" => 0
22
+ exec(src)
23
+ end
24
+ end
25
+
26
+ def exec(src)
27
+ return "" unless src.length > 0
28
+
29
+ src = src.encode('UTF-8', :undef => :replace, :replace => '')
30
+ src = compile_source(src)
31
+
32
+ # src is either an empty object
33
+ # OR a valid JSON string in the form
34
+ # ['ok', 'result-of-coffeescript-or-sass-compiler']
35
+ # OR if an error occured
36
+ # ['err', 'some sort of error to be presented to the developer as a sprockets error']
37
+ #
38
+ status, value = src.empty? ? [] : ::JSON.parse(src, create_additions: false)
39
+ if status == "ok"
40
+ value
41
+ elsif value =~ /SyntaxError:/
42
+ raise RuntimeError, value
43
+ else
44
+ raise ProgramError, value
45
+ end
46
+ end
47
+
48
+ def call(identifier, *args)
49
+ eval "#{identifier}.apply(this, #{::JSON.generate(args)})"
50
+ end
51
+
52
+ private
53
+
54
+ def compile_source(contents)
55
+ sock = Net::BufferedIO.new(@runtime.provision_socket)
56
+ request = Net::HTTP::Post.new("/")
57
+ request['Connection'] = 'close'
58
+ request['Content-Type'] = 'application/x-www-form-urlencoded'
59
+ request.body = contents
60
+ request.exec(sock, "1.1", "/")
61
+
62
+ begin
63
+ response = Net::HTTPResponse.read_new(sock)
64
+ end while response.kind_of?(Net::HTTPContinue)
65
+
66
+ response.reading_body(sock, request.response_body_permitted?) { }
67
+ sock.close
68
+ response.body
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,4 @@
1
+ module RubyAwaitNode
2
+ class RuntimeError < StandardError; end
3
+ class ProgramError < StandardError; end
4
+ end
@@ -0,0 +1,105 @@
1
+ require 'tempfile'
2
+
3
+ require_relative 'context'
4
+
5
+ module RubyAwaitNode
6
+ class Runtime
7
+ attr_accessor :debug, :nodejs_cmd, :port, :pid, :semaphore
8
+
9
+ def initialize(opts = {})
10
+ srand
11
+
12
+ @debug = opts[:debug]
13
+ @nodejs_cmd = "node"
14
+ @pid = nil
15
+ @semaphore = Mutex.new
16
+ end
17
+
18
+ def name
19
+ "RubyAwaitNode"
20
+ end
21
+
22
+ def available?
23
+ ENV["PATH"].split(":").detect do |path|
24
+ %w{
25
+ nodejs
26
+ node
27
+ }.detect do |node|
28
+ File.exist?(File.join(path, node)) || File.symlink?(File.join(path, node))
29
+ end
30
+ end
31
+ end
32
+
33
+ def deprecated?
34
+ false
35
+ end
36
+
37
+ def context_class
38
+ RubyAwaitNode::Context
39
+ end
40
+
41
+ #NOTE: this should be thread-safe
42
+ def provision_socket
43
+ ensure_startup unless @pid
44
+
45
+ wait_socket = nil
46
+ checks = 0
47
+ max_retries = 12
48
+
49
+ while checks < max_retries
50
+ begin
51
+ checks += 1
52
+ wait_socket = UNIXSocket.new(@port)
53
+ break
54
+ rescue Errno::ENOENT, Errno::ECONNREFUSED, Errno::ENOTDIR
55
+ wait_socket = nil
56
+ sleep 0.5
57
+ end
58
+ end
59
+
60
+ if checks >= max_retries
61
+ ensure_shutdown
62
+ raise RuntimeError, "unable to connect to ruby-await-node.js server"
63
+ end
64
+
65
+ wait_socket
66
+ end
67
+
68
+ private
69
+
70
+ def ensure_startup
71
+ @semaphore.synchronize {
72
+ return if @pid
73
+
74
+ @port = begin
75
+ tmpfile = Tempfile.new("ruby-await-node")
76
+ path = tmpfile.path
77
+ tmpfile.close
78
+ tmpfile.unlink
79
+ path
80
+ end
81
+
82
+ ruby_await_node_js_path = File.join(File.dirname(File.expand_path(__FILE__)), '../../ruby-await-node.js')
83
+ command_options = [ruby_await_node_js_path, "--debug #{!!@debug}"] # --other --command-line --options --go --here
84
+
85
+ @initialize_pid = Process.pid
86
+ @pid = Process.spawn({"PORT" => @port.to_s}, @nodejs_cmd, *command_options, {:err => :out})
87
+
88
+ at_exit do
89
+ ensure_shutdown
90
+ end
91
+ }
92
+ end
93
+
94
+ def ensure_shutdown
95
+ return unless @pid
96
+ return unless Process.pid == @initialize_pid
97
+
98
+ Process.kill("TERM", @pid) rescue Errno::ECHILD
99
+ Process.wait(@pid) rescue Errno::ECHILD
100
+
101
+ @port = nil
102
+ @pid = nil
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,3 @@
1
+ module RubyAwaitNode
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'ruby-await-node/version'
2
+ require 'ruby-await-node/runtime'
@@ -0,0 +1,18 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'ruby-await-node/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'ruby-await-node'
7
+ s.version = RubyAwaitNode::VERSION
8
+ s.summary = "Efficiently execute js from ruby by keeping a long run nodejs process"
9
+ s.description = "Efficiently execute js from ruby by keeping a long run nodejs process"
10
+ s.authors = ["Phuong Nguyen", "Jon Bardin", "Stephen Grider", "Ville Lautanala", "Giovanni Bonetti"]
11
+ s.email = 'phuongnd08@gmail.com'
12
+ s.files = `git ls-files -z`.split("\x0")
13
+ s.homepage = "https://github.com/remitano/ruby-await-node"
14
+ s.license = 'MIT'
15
+
16
+ s.add_development_dependency 'rspec'
17
+ s.add_development_dependency 'byebug'
18
+ end
@@ -0,0 +1,69 @@
1
+ const http = require("http");
2
+
3
+ const debugOpt = "--debug true";
4
+ let debug = false;
5
+
6
+ process.argv.forEach((val, index, array) => {
7
+ if (debugOpt === val) {
8
+ debug = true;
9
+ }
10
+ });
11
+
12
+ const webPrint = (res, respBody) => {
13
+ res.writeHead(200, { Connection: "keep-alive" });
14
+ res.write(respBody);
15
+ res.end();
16
+ };
17
+
18
+ const webPrintResult = (res, result) => {
19
+ webPrint(res, JSON.stringify(["ok", result]));
20
+ };
21
+
22
+ const webPrintError = (res, error) => {
23
+ webPrint(res, JSON.stringify(["err", error.toString()]));
24
+ };
25
+
26
+ function isPromise(value) {
27
+ return Boolean(value && typeof value.then === "function");
28
+ }
29
+
30
+ const server = http.createServer((req, res) => {
31
+ let contents = "";
32
+
33
+ req.on("data", (dataIn) => {
34
+ contents += dataIn;
35
+ });
36
+
37
+ req.on("end", () => {
38
+ try {
39
+ const result = eval(contents);
40
+ if (typeof result == "undefined" && result !== null) {
41
+ webPrintResult(res, null);
42
+ } else if (isPromise(result)) {
43
+ result
44
+ .then((value) => {
45
+ webPrintResult(res, value);
46
+ })
47
+ .catch((err) => {
48
+ webPrintError(res, err);
49
+ });
50
+ } else {
51
+ try {
52
+ webPrintResult(res, result);
53
+ } catch (err) {
54
+ webPrintError(res, err);
55
+ }
56
+ }
57
+ } catch (err) {
58
+ webPrintError(res, err);
59
+ }
60
+ });
61
+ });
62
+
63
+ const port = process.env.PORT || 3001;
64
+ server.listen(port);
65
+
66
+ if (debug) {
67
+ console.log("Listening on port:", port);
68
+ console.error("RubyAwaitNode.js started. Piping coffee to warmer climates.");
69
+ }
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ruby-await-node'
4
+
5
+ ruby_await_node_runtime = RubyAwaitNode::Runtime.new(:debug => false)
6
+ context = RubyAwaitNode::Context.new(ruby_await_node_runtime)
7
+ context.eval("global.a = 2.0")
8
+
9
+ fork_count = 4
10
+
11
+ pids = (0...fork_count).map do |i|
12
+ fork do
13
+ sleep i * 0.1 # simulate different process exit at different time to expose premature shutdown of shared nodejs process
14
+ result = context.call("(function(b) { return (global.a * b); })", i)
15
+ exit result #use the exit code to communicate the calculation result
16
+ end
17
+ end
18
+
19
+ exit_statuses = pids.map { |pid| Process.wait2(pid) }
20
+
21
+ results = []
22
+
23
+ exit_statuses.each do |pid, status|
24
+ results << status.exitstatus
25
+ end
26
+
27
+ # http://www.wolframalpha.com/input/?i=3rd+trianglur+number
28
+ sum = results.compact.inject(0) { |result, element| result + element }
29
+
30
+ if (sum) == 12
31
+ exit 0
32
+ else
33
+ exit 1
34
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyAwaitNode do
4
+ def return_42
5
+ "(function() { var f = 42; return 42;})()"
6
+ end
7
+
8
+ def await_return_42
9
+ "(async function() { var f = 42; await new Promise(resolve => setTimeout(resolve, 1000)); return 42;})()"
10
+ end
11
+
12
+ def create_ruby_await_node_runtime
13
+ RubyAwaitNode::Runtime.new(:debug => false)
14
+ end
15
+
16
+ def create_ruby_await_node_context
17
+ RubyAwaitNode::Context.new(create_ruby_await_node_runtime)
18
+ end
19
+
20
+ context "simple javascript" do
21
+ it "returns desired value" do
22
+ js_result = create_ruby_await_node_context.eval(return_42)
23
+ expect(js_result).to eq 42
24
+ end
25
+ end
26
+
27
+ context "await javascript" do
28
+ it "return desired value" do
29
+ js_result = create_ruby_await_node_context.eval(await_return_42)
30
+ expect(js_result).to eq 42
31
+ end
32
+ end
33
+
34
+ it "raises an RubyAwaitNode::ProgramError on error" do
35
+ expect {
36
+ create_ruby_await_node_context.eval("(function() { throw new Error('foo\\nbar', 0, 'test.js'); })()")
37
+ }.to raise_error(RubyAwaitNode::ProgramError)
38
+ end
39
+
40
+ it "requires js to be in a self-executing function" do
41
+ js_result = -1
42
+
43
+ expect {
44
+ js_result = create_ruby_await_node_context.eval("return true;")
45
+ }.to raise_error(RubyAwaitNode::RuntimeError)
46
+
47
+ expect(js_result).to eq(-1)
48
+ end
49
+
50
+ it "safely allows subsequent #eval calls after runtime error" do
51
+ js_result = create_ruby_await_node_context.eval(return_42)
52
+ expect(js_result).to eq 42
53
+
54
+ expect {
55
+ js_result = create_ruby_await_node_context.eval("return true;")
56
+ }.to raise_error(RubyAwaitNode::RuntimeError)
57
+
58
+ js_result = create_ruby_await_node_context.eval(return_42)
59
+ expect(js_result).to eq 42
60
+ end
61
+
62
+ it "should be thread safe when sharing context between threads" do
63
+ thread_count = 128
64
+ threads = []
65
+ semaphore = Mutex.new
66
+ results = []
67
+
68
+ context = create_ruby_await_node_context
69
+ context.eval("global.a = 1.0;")
70
+
71
+ thread_count.times { |index|
72
+ threads << Thread.new {
73
+ result = context.call("(function(b) { return (global.a * b); })", index)
74
+ semaphore.synchronize {
75
+ results << result
76
+ }
77
+ }
78
+ }
79
+
80
+ threads.each { |t| t.join }
81
+
82
+ sum = results.inject(0) { |result, element| result + element }
83
+
84
+ expect(sum).to eq(8128) # http://www.wolframalpha.com/input/?i=127th+trianglur+number
85
+ end
86
+
87
+ it "should be thread safe when creating new contexts in threads" do
88
+ thread_count = 128
89
+ threads = []
90
+ semaphore = Mutex.new
91
+ results = []
92
+
93
+ thread_count.times { |index|
94
+ threads << Thread.new {
95
+ context = create_ruby_await_node_context
96
+ context.eval("global.a = 2.0;") # this asserts that the context is shared
97
+ result = context.call("(function(b) { return (a * b); })", index)
98
+ semaphore.synchronize {
99
+ results << result
100
+ }
101
+ }
102
+ }
103
+
104
+ threads.each { |t| t.join }
105
+
106
+ sum = results.inject(0) { |result, element| result + element }
107
+
108
+ expect(sum).to eq(8128 * 2) # http://www.wolframalpha.com/input/?i=127th+trianglur+number
109
+ end
110
+
111
+ it "should not break if exception occurs in thread" do
112
+ context_a = create_ruby_await_node_context
113
+
114
+ g_err_a = nil
115
+ g_err_b = nil
116
+
117
+ make_program_error_thread = Thread.new {
118
+ begin
119
+ _b = context_a.call("(function() { asd() })")
120
+ rescue => err_a
121
+ g_err_a = err_a
122
+ end
123
+ }
124
+
125
+ context_b = create_ruby_await_node_context
126
+
127
+ begin
128
+ _c = context_b.call("(function() { asd) })")
129
+ rescue => err_b
130
+ g_err_b = err_b
131
+ end
132
+
133
+ make_program_error_thread.join
134
+
135
+ expect(g_err_a).to be_kind_of(RubyAwaitNode::ProgramError)
136
+ expect(g_err_b).to be_kind_of(RubyAwaitNode::RuntimeError)
137
+ end
138
+
139
+ it "should work when the context is shared between forked processes" do
140
+ fork_test_harness_pid = Process.spawn("bundle exec ruby spec/fork_test_harness.rb")
141
+ exit_pid, exit_status = Process.wait2(fork_test_harness_pid)
142
+
143
+ expect(exit_pid).to eq(fork_test_harness_pid)
144
+ expect(exit_status).to eq(0)
145
+ end
146
+ end
@@ -0,0 +1,2 @@
1
+ require 'byebug'
2
+ require 'ruby-await-node'
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-await-node
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Phuong Nguyen
8
+ - Jon Bardin
9
+ - Stephen Grider
10
+ - Ville Lautanala
11
+ - Giovanni Bonetti
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+ date: 2019-08-11 00:00:00.000000000 Z
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: rspec
19
+ requirement: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: byebug
33
+ requirement: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ description: Efficiently execute js from ruby by keeping a long run nodejs process
46
+ email: phuongnd08@gmail.com
47
+ executables: []
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - ".circleci/config.yml"
52
+ - ".gitignore"
53
+ - ".travis.yml"
54
+ - Gemfile
55
+ - Gemfile.lock
56
+ - LICENSE
57
+ - README.md
58
+ - lib/ruby-await-node.rb
59
+ - lib/ruby-await-node/context.rb
60
+ - lib/ruby-await-node/exception.rb
61
+ - lib/ruby-await-node/runtime.rb
62
+ - lib/ruby-await-node/version.rb
63
+ - ruby-await-node.gemspec
64
+ - ruby-await-node.js
65
+ - spec/fork_test_harness.rb
66
+ - spec/ruby_await_node_spec.rb
67
+ - spec/spec_helper.rb
68
+ homepage: https://github.com/remitano/ruby-await-node
69
+ licenses:
70
+ - MIT
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 2.4.5.2
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Efficiently execute js from ruby by keeping a long run nodejs process
92
+ test_files: []