ruby-await-node 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []