runjs 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +94 -0
- data/Rakefile +32 -0
- data/lib/runjs.rb +62 -0
- data/lib/runjs/encoding.rb +24 -0
- data/lib/runjs/error.rb +59 -0
- data/lib/runjs/os.rb +80 -0
- data/lib/runjs/runtime.rb +65 -0
- data/lib/runjs/runtimes/java_script_core.rb +8 -0
- data/lib/runjs/runtimes/jscript.rb +36 -0
- data/lib/runjs/runtimes/node.rb +7 -0
- data/lib/runjs/runtimes/spider_monkey.rb +8 -0
- data/lib/runjs/runtimes/the_ruby_racer.rb +54 -0
- data/lib/runjs/runtimes/the_ruby_rhino.rb +16 -0
- data/lib/runjs/runtimes/v8.rb +11 -0
- data/lib/runjs/support/runner.js +78 -0
- data/lib/runjs/system_runtime.rb +46 -0
- data/lib/runjs/vendor/json2.js +486 -0
- data/runjs.gemspec +32 -0
- data/test/coffeescript_test.rb +39 -0
- data/test/encoding_test.rb +35 -0
- data/test/execjs_test.rb +415 -0
- data/test/javascript_error_test.rb +150 -0
- data/test/javascript_test.rb +93 -0
- data/test/select_runtime_test.rb +103 -0
- data/test/test_helper.rb +17 -0
- metadata +137 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8dfa257866a324091b0aa01a4e628ad4adce24c9
|
4
|
+
data.tar.gz: 8ffafee810cb03f6da1738c056a9f3c78594986e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 883f31c273ca2e28d6078eaa972bbbbab42bf47ef92a4822d4fa9f4963c4cd3a9dd7c11eb52593e1c2a9d443a8b7b7c153be96def9b85a44e55eec09caa4db68
|
7
|
+
data.tar.gz: f72abb482bdd5ebe8095c6d18184f0d866bfca7fcdc9ff4ddce358555fa216fcdc80cb28fc012011a8fe8f60ccafcfef4bfb73dbd4097f4a8cd42136c26f941b
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 AS Harbitz
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# RunJS
|
2
|
+
|
3
|
+
With RunJS you can run JavaScript code from Ruby.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
require 'runjs'
|
7
|
+
puts RunJS.run('return "Hello World";')
|
8
|
+
```
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
```bash
|
13
|
+
$ gem install runjs
|
14
|
+
```
|
15
|
+
|
16
|
+
RunJS depends on the `json` library. Starting with Ruby version 1.9 this is included, but on Ruby 1.8 you have to install the `json` gem. If this causes any problems, install the `json_pure` gem instead.
|
17
|
+
|
18
|
+
```bash
|
19
|
+
$ gem install json || gem install json_pure
|
20
|
+
```
|
21
|
+
|
22
|
+
## JavaScript Runtimes
|
23
|
+
|
24
|
+
#### Supported runtimes
|
25
|
+
|
26
|
+
* [TheRubyRacer](https://github.com/cowboyd/therubyracer)
|
27
|
+
* [JavaScriptCore](http://trac.webkit.org/wiki/JavaScriptCore) included with OS X
|
28
|
+
* [V8](http://code.google.com/p/v8/)
|
29
|
+
* [Node](http://nodejs.org)
|
30
|
+
* [JScript](http://msdn.microsoft.com/en-us/library/9bbdkx3k.aspx) on Windows
|
31
|
+
|
32
|
+
RunJS will automatically select the first available runtime from the list above.
|
33
|
+
|
34
|
+
#### Deprecated runtimes
|
35
|
+
|
36
|
+
* [TheRubyRhino](https://github.com/cowboyd/therubyrhino)
|
37
|
+
* [SpiderMonkey](http://www.mozilla.org/js/spidermonkey)
|
38
|
+
|
39
|
+
To use TheRubyRhino or SpiderMonkey you have to set the runtime manually.
|
40
|
+
|
41
|
+
#### Set the runtime
|
42
|
+
|
43
|
+
You can control which runtime RunJS uses in your Ruby code.
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
RunJS.runtime = RunJS::Node
|
47
|
+
```
|
48
|
+
|
49
|
+
Or with the RUNJS_RUNTIME environment variable.
|
50
|
+
|
51
|
+
```bash
|
52
|
+
$ export RUNJS_RUNTIME=Node
|
53
|
+
```
|
54
|
+
|
55
|
+
<!-- ## API -->
|
56
|
+
|
57
|
+
## Examples
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
require 'runjs'
|
61
|
+
|
62
|
+
RunJS.run('return 2 + 2;') # => 4
|
63
|
+
RunJS.call('Math.sqrt', 25) # => 5
|
64
|
+
RunJS.apply('Array.prototype.slice', '"cat"') # => ["c", "a", "t"]
|
65
|
+
```
|
66
|
+
|
67
|
+
#### Compiling CoffeeScript
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
require 'runjs'
|
71
|
+
require 'open-uri'
|
72
|
+
|
73
|
+
url = 'http://jashkenas.github.com/coffee-script/extras/coffee-script.js'
|
74
|
+
compiler = open(url).read
|
75
|
+
|
76
|
+
coffee = 'alert yes'
|
77
|
+
options = { header: true, bare: true }
|
78
|
+
RunJS.context(compiler).apply('CoffeeScript.compile', 'CoffeeScript', coffee, options)
|
79
|
+
|
80
|
+
# => // Generated by CoffeeScript 1.6.3
|
81
|
+
# => alert(true);
|
82
|
+
```
|
83
|
+
|
84
|
+
<!-- ## RunJS vs. ExecJS -->
|
85
|
+
|
86
|
+
## Credits
|
87
|
+
|
88
|
+
Thanks to [Sam Stephenson](https://github.com/sstephenson), [Joshua Peek](https://github.com/josh) and the other contributors to [ExecJS](https://github.com/sstephenson/execjs). Although none of the code is directly copied, the API and code structure is almost identical. While coding, the ExecJS code was consulted all the time, and RunJS ended up like a rewrite of ExecJS.
|
89
|
+
|
90
|
+
## License
|
91
|
+
|
92
|
+
Copyright (c) 2013 AS Harbitz.
|
93
|
+
|
94
|
+
RunJS is released under the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
|
2
|
+
|
3
|
+
require 'runjs'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
desc 'Run tests for all the runtimes'
|
7
|
+
task :test do
|
8
|
+
RunJS::RUNTIMES.each { |runtime| run_tests(runtime) }
|
9
|
+
end
|
10
|
+
|
11
|
+
RunJS::RUNTIMES.each do |runtime|
|
12
|
+
desc "Run tests for #{runtime.class_name}"
|
13
|
+
task "test:#{runtime.class_name.downcase}" do
|
14
|
+
run_tests(runtime)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Rake::TestTask.new 'test:runtime' do |test|
|
19
|
+
test.libs << 'test'
|
20
|
+
test.pattern = 'test/*_test.rb'
|
21
|
+
test.warning = true
|
22
|
+
end
|
23
|
+
Rake::Task['test:runtime'].clear_comments
|
24
|
+
Rake::Task['test:runtime'].comment = 'Run tests for the default runtime'
|
25
|
+
|
26
|
+
def run_tests(runtime)
|
27
|
+
ENV['RUNJS_RUNTIME'] = runtime.class_name
|
28
|
+
Rake::Task['test:runtime'].execute
|
29
|
+
rescue SignalException
|
30
|
+
raise unless RUBY_PLATFORM == 'java'
|
31
|
+
rescue
|
32
|
+
end
|
data/lib/runjs.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'runjs/encoding'
|
2
|
+
require 'runjs/error'
|
3
|
+
require 'runjs/os'
|
4
|
+
require 'runjs/runtime'
|
5
|
+
require 'runjs/system_runtime'
|
6
|
+
|
7
|
+
require 'runjs/runtimes/java_script_core'
|
8
|
+
require 'runjs/runtimes/jscript'
|
9
|
+
require 'runjs/runtimes/node'
|
10
|
+
require 'runjs/runtimes/spider_monkey'
|
11
|
+
require 'runjs/runtimes/the_ruby_racer'
|
12
|
+
require 'runjs/runtimes/the_ruby_rhino'
|
13
|
+
require 'runjs/runtimes/v8'
|
14
|
+
|
15
|
+
module RunJS
|
16
|
+
|
17
|
+
VERSION = '0.1.0'
|
18
|
+
|
19
|
+
RUNTIMES = [TheRubyRacer, JavaScriptCore, V8, D8, Node, JScript,
|
20
|
+
TheRubyRhino, SpiderMonkey]
|
21
|
+
|
22
|
+
def self.runtime=(runtime)
|
23
|
+
raise RuntimeUnavailable, runtime unless runtime.available?
|
24
|
+
@runtime = runtime
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.runtime
|
28
|
+
@runtime ||= from_environment(ENV['RUNJS_RUNTIME']) ||
|
29
|
+
RUNTIMES.reject(&:deprecated?).find(&:available?) ||
|
30
|
+
raise(RuntimeUnavailable)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.context(js)
|
34
|
+
runtime.new.context(js)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.run(js)
|
38
|
+
runtime.new.run(js)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.call(function, *args)
|
42
|
+
runtime.new.call(function, *args)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.apply(function, this, *args)
|
46
|
+
runtime.new.apply(function, this, *args)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.eval(js)
|
50
|
+
runtime.new.eval(js)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def self.from_environment(name)
|
56
|
+
name = (name || '').sub(/^RunJS::/, '')
|
57
|
+
self.runtime = const_get(name) unless name.empty?
|
58
|
+
end
|
59
|
+
|
60
|
+
private_class_method(:from_environment)
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RunJS
|
2
|
+
|
3
|
+
module Encoding
|
4
|
+
|
5
|
+
if ''.respond_to?(:encode)
|
6
|
+
|
7
|
+
def encode(text, to, from = nil)
|
8
|
+
text.encode(to, from)
|
9
|
+
end
|
10
|
+
|
11
|
+
else
|
12
|
+
|
13
|
+
require 'iconv'
|
14
|
+
|
15
|
+
def encode(text, to, from = nil)
|
16
|
+
return text unless from
|
17
|
+
Iconv.conv(to, from, text)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/lib/runjs/error.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module RunJS
|
2
|
+
|
3
|
+
class Error < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class CompileError < Error
|
7
|
+
|
8
|
+
attr_reader :source
|
9
|
+
|
10
|
+
def initialize(message, source)
|
11
|
+
@source = source
|
12
|
+
super(message)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
class JavaScriptError < Error
|
18
|
+
|
19
|
+
attr_reader :error
|
20
|
+
attr_reader :source
|
21
|
+
|
22
|
+
def initialize(error, source)
|
23
|
+
@error = error
|
24
|
+
@source = source
|
25
|
+
super(get_message)
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](key)
|
29
|
+
@error[key].nil? ? @error[key.to_s] : @error[key]
|
30
|
+
rescue
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def get_message
|
37
|
+
return '' if @error.respond_to?(:empty?) && @error.empty?
|
38
|
+
message = [self['name'], self['message']].compact
|
39
|
+
message.delete('')
|
40
|
+
message.empty? ? @error.to_s : message.join(': ')
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class RuntimeUnavailable < Error
|
46
|
+
|
47
|
+
def initialize(runtime = nil)
|
48
|
+
if runtime
|
49
|
+
super('Could not find the runtime: ' + runtime.class_name)
|
50
|
+
else
|
51
|
+
super('Could not find a JavaScript runtime. ' +
|
52
|
+
"The supported runtimes are:\n" +
|
53
|
+
RUNTIMES.reject(&:deprecated?).map(&:class_name).join(', '))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/lib/runjs/os.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'rbconfig'
|
3
|
+
require 'shellwords'
|
4
|
+
|
5
|
+
module RunJS
|
6
|
+
|
7
|
+
module OS
|
8
|
+
|
9
|
+
def self.which(cmd)
|
10
|
+
cmd += extension(cmd)
|
11
|
+
return cmd if executable?(cmd)
|
12
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
13
|
+
path = File.join(path, cmd)
|
14
|
+
return path if executable?(path)
|
15
|
+
end
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.write_tempfile(text, &block)
|
20
|
+
file = Tempfile.new(['runjs', '.js'])
|
21
|
+
file.binmode # Required on Windows when writing UTF-16
|
22
|
+
file.write(text)
|
23
|
+
file.close
|
24
|
+
yield file.path
|
25
|
+
ensure
|
26
|
+
file.close!
|
27
|
+
end
|
28
|
+
|
29
|
+
if RUBY_VERSION < '1.9' || RUBY_PLATFORM == 'java'
|
30
|
+
|
31
|
+
extend Encoding
|
32
|
+
|
33
|
+
def self.popen(cmd, options = {})
|
34
|
+
cmd = shell_escape(cmd) << ' 2>&1'
|
35
|
+
result = IO.popen(cmd) { |io| io.read }
|
36
|
+
encode(result, 'UTF-8', options[:external_encoding])
|
37
|
+
end
|
38
|
+
|
39
|
+
else
|
40
|
+
|
41
|
+
def self.popen(cmd, options = {})
|
42
|
+
cmd = cmd.dup.push({ :err => [:child, :out] }) # Unsupported by JRuby
|
43
|
+
options[:internal_encoding] ||= 'UTF-8'
|
44
|
+
options[:external_encoding] ||= 'UTF-8'
|
45
|
+
IO.popen(cmd, options) { |io| io.read }
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.success?
|
51
|
+
$?.success?
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def self.extension(cmd)
|
57
|
+
(windows? && File.extname(cmd).empty?) ? '.exe' : ''
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.windows?
|
61
|
+
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.executable?(cmd)
|
65
|
+
File.file?(cmd) && File.executable?(cmd)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.shell_escape(cmd)
|
69
|
+
if windows?
|
70
|
+
cmd.map { |arg| '"' + arg.gsub('"', '""') + '"' }.join(' ')
|
71
|
+
else
|
72
|
+
Shellwords.join(cmd)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private_class_method(:extension, :windows?, :executable?, :shell_escape)
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
begin
|
2
|
+
require 'json'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'json'
|
6
|
+
end
|
7
|
+
|
8
|
+
module RunJS
|
9
|
+
|
10
|
+
class Runtime
|
11
|
+
|
12
|
+
def self.deprecated?
|
13
|
+
@deprecated ||= false
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.available?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.class_name
|
21
|
+
name.split('::').last
|
22
|
+
end
|
23
|
+
|
24
|
+
def context(js)
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(js)
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(function, *args)
|
33
|
+
apply(function, 'this', *args)
|
34
|
+
end
|
35
|
+
|
36
|
+
def apply(function, this, *args)
|
37
|
+
this = 'null' if this.nil?
|
38
|
+
args = args.to_json
|
39
|
+
run("return #{function}.apply(#{this}, #{args});")
|
40
|
+
end
|
41
|
+
|
42
|
+
def eval(js)
|
43
|
+
js = js.to_json
|
44
|
+
run("return eval(#{js});")
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
include Encoding
|
50
|
+
|
51
|
+
RUNNER = File.read(File.expand_path('../support/runner.js', __FILE__))
|
52
|
+
|
53
|
+
def merge_runner(js)
|
54
|
+
RUNNER % js
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_json(result, js)
|
58
|
+
result, ok = JSON.parse(result)
|
59
|
+
raise JavaScriptError.new(result, js) unless ok
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|