therubyracer 0.5.0-x86-darwin-9 → 0.5.1-x86-darwin-9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of therubyracer might be problematic. Click here for more details.
- data/History.txt +4 -0
- data/Rakefile +7 -1
- data/lib/v8/tap.rb +8 -0
- data/lib/v8/v8.bundle +0 -0
- data/lib/v8.rb +2 -1
- data/spec/redjs/jsapi_spec.rb +405 -0
- data/therubyracer.gemspec +4 -2
- metadata +5 -2
data/History.txt
CHANGED
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ begin
|
|
9
9
|
require 'jeweler'
|
10
10
|
Jeweler::Tasks.new do |gemspec|
|
11
11
|
gemspec.name = gemspec.rubyforge_project = "therubyracer"
|
12
|
-
gemspec.version = "0.5.
|
12
|
+
gemspec.version = "0.5.1"
|
13
13
|
gemspec.summary = "Embed the V8 Javascript interpreter into Ruby"
|
14
14
|
gemspec.description = "Call javascript code and manipulate javascript objects from ruby. Call ruby code and manipulate ruby objects from javascript."
|
15
15
|
gemspec.email = "cowboyd@thefrontside.net"
|
@@ -34,6 +34,12 @@ rescue LoadError
|
|
34
34
|
puts "Rake Compiler not available. Install it with: gem install rake-compiler"
|
35
35
|
end
|
36
36
|
|
37
|
+
require 'spec/rake/spectask'
|
38
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
39
|
+
spec.libs << 'lib' << 'spec'
|
40
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
41
|
+
end
|
42
|
+
|
37
43
|
desc "Build gem"
|
38
44
|
task :gem => :build
|
39
45
|
|
data/lib/v8/tap.rb
ADDED
data/lib/v8/v8.bundle
CHANGED
Binary file
|
data/lib/v8.rb
CHANGED
@@ -2,9 +2,10 @@ $:.unshift(File.dirname(__FILE__)) unless
|
|
2
2
|
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
3
|
|
4
4
|
module V8
|
5
|
-
VERSION = '0.5.
|
5
|
+
VERSION = '0.5.1'
|
6
6
|
require 'v8/v8' #native glue
|
7
7
|
require 'v8/to'
|
8
8
|
require 'v8/context'
|
9
9
|
require 'v8/object'
|
10
|
+
require 'v8/tap'
|
10
11
|
end
|
@@ -0,0 +1,405 @@
|
|
1
|
+
|
2
|
+
require "#{File.dirname(__FILE__)}/../redjs_helper.rb"
|
3
|
+
|
4
|
+
describe "Ruby Javascript API" do
|
5
|
+
|
6
|
+
describe "Basic Evaluation" do
|
7
|
+
it "can evaluate some javascript" do
|
8
|
+
Context.eval("5 + 3")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "can pass back null to ruby" do
|
12
|
+
Context.eval("null").should be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "can pass back undefined to ruby" do
|
16
|
+
Context.eval("this.undefined").should be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can pass the empty string back to ruby" do
|
20
|
+
Context.eval("''").should == ""
|
21
|
+
end
|
22
|
+
|
23
|
+
it "can pass doubles back to ruby" do
|
24
|
+
Context.eval("2.5").should == 2.5
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can pass fixed numbers back to ruby" do
|
28
|
+
Context.eval("1").should == 1
|
29
|
+
end
|
30
|
+
|
31
|
+
it "can pass boolean values back to ruby" do
|
32
|
+
Context.eval("true").should be(true)
|
33
|
+
Context.eval("false").should be(false)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "treats nil and the empty string as the same thing when it comes to eval" do
|
37
|
+
Context.eval(nil).should == Context.eval('')
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can pass back strings to ruby" do
|
41
|
+
Context.open do |cxt|
|
42
|
+
cxt['foo'] = "Hello World"
|
43
|
+
cxt.eval("foo").should == "Hello World"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "can pass back very long strings to ruby" do
|
48
|
+
lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis faucibus, diam vel pellentesque aliquet, nisl sapien molestie eros, vitae vehicula libero massa vel neque. Phasellus tempor pharetra ipsum vel venenatis. Quisque vitae nisl vitae quam mattis pellentesque et in sapien. Sed at lectus quis eros pharetra feugiat non ac neque. Vivamus lacus eros, feugiat at volutpat at, viverra id nisl. Vivamus ac dolor eleifend libero venenatis pharetra ut iaculis arcu. Donec neque nibh, vehicula non porta a, consectetur eu erat. Sed eleifend, metus vel euismod placerat, lectus lectus sollicitudin nisl, ac elementum sem quam nec dolor. In hac habitasse platea dictumst. Proin vitae suscipit orci. Suspendisse a ipsum vel lorem tempus scelerisque et vitae neque. Proin sodales, tellus sit amet consequat cursus, odio massa ultricies enim, eu fermentum velit lectus in lacus. Quisque eu porttitor diam. Nunc felis purus, facilisis non tristique ac, pulvinar nec nulla. Duis dolor risus, egestas nec tristique ac, ullamcorper cras amet."
|
49
|
+
Context.open do |cxt|
|
50
|
+
cxt.eval("'#{lorem}'").should == lorem
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "can pass objects back to ruby" do
|
55
|
+
Context.open do |cxt|
|
56
|
+
cxt.eval("({foo: 'bar', baz: 'bang', '5': 5, embedded: {badda: 'bing'}})").tap do |object|
|
57
|
+
object.should_not be_nil
|
58
|
+
object['foo'].should == 'bar'
|
59
|
+
object['baz'].should == 'bang'
|
60
|
+
object['5'].should == 5
|
61
|
+
object['embedded'].tap do |embedded|
|
62
|
+
embedded.should_not be_nil
|
63
|
+
embedded['badda'].should == 'bing'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "unwraps ruby objects returned by embedded ruby code to maintain referential integrity" do
|
70
|
+
Object.new.tap do |o|
|
71
|
+
Context.open do |cxt|
|
72
|
+
cxt['get'] = lambda {o}
|
73
|
+
cxt.eval('get()').should be(o)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "Calling Ruby Code From Within Javascript" do
|
80
|
+
|
81
|
+
before(:each) do
|
82
|
+
@class = Class.new
|
83
|
+
@instance = @class.new
|
84
|
+
end
|
85
|
+
|
86
|
+
it "can embed a closure into a context and call it" do
|
87
|
+
Context.open do |cxt|
|
88
|
+
cxt["say"] = lambda {|word, times| word * times}
|
89
|
+
cxt.eval("say('Hello',2)").should == "HelloHello"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it "can embed a ruby object into a context and call its methods" do
|
94
|
+
class_eval do
|
95
|
+
def say_hello(to)
|
96
|
+
"Hello #{to}!"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
evaljs('o.say_hello("Gracie")').should == "Hello Gracie!"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "can call a bound ruby method" do
|
103
|
+
five = class_eval do
|
104
|
+
def initialize(lhs)
|
105
|
+
@lhs = lhs
|
106
|
+
end
|
107
|
+
def times(rhs)
|
108
|
+
@lhs * rhs
|
109
|
+
end
|
110
|
+
new(5)
|
111
|
+
end
|
112
|
+
Context.open do |cxt|
|
113
|
+
cxt['timesfive'] = five.method(:times)
|
114
|
+
cxt.eval('timesfive(3)').should == 15
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "reports ruby methods that do not exist as undefined" do
|
119
|
+
Context.open(:with => Object.new) do |cxt|
|
120
|
+
cxt.eval('this.foobar').should be_nil
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it "can call public locally defined ruby methods" do
|
125
|
+
class_eval do
|
126
|
+
def voo(str)
|
127
|
+
"voo#{str}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
evaljs("o.voo('doo')").should == "voodoo"
|
131
|
+
end
|
132
|
+
|
133
|
+
it "translates ruby naming conventions into javascript naming conventions, but you can still access them by their original names" do
|
134
|
+
class_eval do
|
135
|
+
def my_special_method(to)
|
136
|
+
"hello #{to}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
evaljs("o.mySpecialMethod('Frank')").should == "hello Frank"
|
140
|
+
evaljs("o.my_special_method('Jack')").should == "hello Jack"
|
141
|
+
end
|
142
|
+
|
143
|
+
it "hides methods not defined directly on this instance's class" do
|
144
|
+
class_eval do
|
145
|
+
def bar
|
146
|
+
end
|
147
|
+
end
|
148
|
+
evaljs("o.to_s").should be_nil
|
149
|
+
end
|
150
|
+
|
151
|
+
it "translated camel case properties are enumerated by default, but perl case are not" do
|
152
|
+
class_eval do
|
153
|
+
def foo_bar
|
154
|
+
end
|
155
|
+
|
156
|
+
def baz_bang
|
157
|
+
end
|
158
|
+
end
|
159
|
+
require 'set'
|
160
|
+
evaljs(<<-EOJS).to_set.should == Set.new(["fooBar","bazBang"])
|
161
|
+
var names = [];
|
162
|
+
for (var p in o) {
|
163
|
+
names.push(p);
|
164
|
+
}
|
165
|
+
names;
|
166
|
+
EOJS
|
167
|
+
end
|
168
|
+
|
169
|
+
it "will see a method that appears after the wrapper was first created" do
|
170
|
+
Context.open do |cxt|
|
171
|
+
cxt['o'] = @instance
|
172
|
+
class_eval do
|
173
|
+
def whiz(str)
|
174
|
+
"whiz#{str}!"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
cxt.eval("o.whiz('bang')").should == "whizbang!"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
it "treats ruby methods that have an arity of 0 as javascript properties by default" do
|
182
|
+
class_eval do
|
183
|
+
def property
|
184
|
+
"flan!"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
evaljs('o.property').should == 'flan!'
|
188
|
+
end
|
189
|
+
|
190
|
+
it "will call ruby accesssor function when setting a property from javascript" do
|
191
|
+
class_eval do
|
192
|
+
def dollars
|
193
|
+
@dollars
|
194
|
+
end
|
195
|
+
|
196
|
+
def dollars=(amount)
|
197
|
+
@dollars = amount
|
198
|
+
end
|
199
|
+
end
|
200
|
+
evaljs('o.dollars = 50')
|
201
|
+
@instance.dollars.should == 50
|
202
|
+
end
|
203
|
+
|
204
|
+
it "will accept expando properties by default for properties on ruby object that are not implemented in ruby" do
|
205
|
+
evaljs('o.five = 5; o.five').should == 5
|
206
|
+
end
|
207
|
+
|
208
|
+
it "it silently fails to replace properties which are defined on ruby objects but which are read-only" do
|
209
|
+
class_eval do
|
210
|
+
def bar
|
211
|
+
"baz"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
evaljs('o.bar = "bing"; o.bar').should == "baz"
|
215
|
+
end
|
216
|
+
|
217
|
+
def evaljs(str)
|
218
|
+
Context.open do |cxt|
|
219
|
+
cxt['puts'] = lambda {|o| puts o.inspect}
|
220
|
+
cxt['o'] = @instance
|
221
|
+
cxt.eval(str)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def class_eval(&body)
|
226
|
+
@class.class_eval &body
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
describe "Setting up the Host Environment" do
|
232
|
+
it "can eval javascript with a given ruby object as the scope." do
|
233
|
+
scope = Class.new.class_eval do
|
234
|
+
def plus(lhs, rhs)
|
235
|
+
lhs + rhs
|
236
|
+
end
|
237
|
+
|
238
|
+
def minus(lhs, rhs)
|
239
|
+
lhs - rhs
|
240
|
+
end
|
241
|
+
|
242
|
+
new
|
243
|
+
end
|
244
|
+
|
245
|
+
Context.open(:with => scope) do |cxt|
|
246
|
+
cxt.eval("plus(1,2)").should == 3
|
247
|
+
cxt.eval("minus(10, 20)").should == -10
|
248
|
+
cxt.eval("this").should be(scope)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
it "can directly embed ruby values into javascript" do
|
253
|
+
Context.open do |cxt|
|
254
|
+
cxt["bar"] = 9
|
255
|
+
cxt['foo'] = "bar"
|
256
|
+
cxt['num'] = 3.14
|
257
|
+
cxt['trU'] = true
|
258
|
+
cxt['falls'] = false
|
259
|
+
cxt.eval("bar + 10").should be(19)
|
260
|
+
cxt.eval('foo').should == "bar"
|
261
|
+
cxt.eval('num').should == 3.14
|
262
|
+
cxt.eval('trU').should be(true)
|
263
|
+
cxt.eval('falls').should be(false)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
it "extends object to allow for the arbitrary execution of javascript with any object as the scope" do
|
268
|
+
Class.new.class_eval do
|
269
|
+
|
270
|
+
def initialize
|
271
|
+
@lhs = 5
|
272
|
+
end
|
273
|
+
|
274
|
+
def timesfive(rhs)
|
275
|
+
@lhs * rhs
|
276
|
+
end
|
277
|
+
|
278
|
+
new.eval_js("timesfive(6)").should == 30
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
it "can limit the number of instructions that are executed in the context" do
|
283
|
+
pending "haven't figured out how to constrain resources in V8"
|
284
|
+
lambda {
|
285
|
+
Context.open do |cxt|
|
286
|
+
cxt.instruction_limit = 100 * 1000
|
287
|
+
timeout(1) do
|
288
|
+
cxt.eval('while (true);')
|
289
|
+
end
|
290
|
+
end
|
291
|
+
}.should raise_error(RunawayScriptError)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe "loading javascript source into the interpreter" do
|
296
|
+
|
297
|
+
it "can take an IO object in the eval method instead of a string" do
|
298
|
+
source = StringIO.new(<<-EOJS)
|
299
|
+
/*
|
300
|
+
* we want to have a fairly verbose function so that we can be assured tha
|
301
|
+
* we overflow the buffer size so that we see that the reader is chunking
|
302
|
+
* it's payload in at least several fragments.
|
303
|
+
*
|
304
|
+
* That's why we're wasting space here
|
305
|
+
*/
|
306
|
+
function five() {
|
307
|
+
return 5
|
308
|
+
}
|
309
|
+
foo = 'bar'
|
310
|
+
five();
|
311
|
+
EOJS
|
312
|
+
Context.open do |cxt|
|
313
|
+
cxt.eval(source, "StringIO").should == 5
|
314
|
+
cxt['foo'].should == "bar"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
it "can load a file into the runtime" do
|
319
|
+
mock(:JavascriptSourceFile).tap do |file|
|
320
|
+
File.should_receive(:open).with("path/to/mysource.js").and_yield(file)
|
321
|
+
Context.open do |cxt|
|
322
|
+
cxt.should_receive(:evaluate).with(file, "path/to/mysource.js", 1)
|
323
|
+
cxt.load("path/to/mysource.js")
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
describe "A Javascript Object Reflected Into Ruby" do
|
331
|
+
|
332
|
+
before(:each) do
|
333
|
+
@o = Context.open do |cxt|
|
334
|
+
@cxt = cxt
|
335
|
+
cxt.eval("o = new Object(); o")
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def evaljs(js)
|
340
|
+
@cxt.open do
|
341
|
+
@cxt.eval(js)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
it "can have its properties manipulated via ruby style [] hash access" do
|
346
|
+
@o["foo"] = 'bar'
|
347
|
+
evaljs('o.foo').should == "bar"
|
348
|
+
evaljs('o.blue = "blam"')
|
349
|
+
@o["blue"].should == "blam"
|
350
|
+
end
|
351
|
+
|
352
|
+
it "doesn't matter if you use a symbol or a string to set a value" do
|
353
|
+
@o[:foo] = "bar"
|
354
|
+
@o['foo'].should == "bar"
|
355
|
+
@o['baz'] = "bang"
|
356
|
+
@o[:baz].should == "bang"
|
357
|
+
end
|
358
|
+
|
359
|
+
it "returns nil when the value is null, null, or not defined" do
|
360
|
+
@o[:foo].should be_nil
|
361
|
+
end
|
362
|
+
|
363
|
+
it "traverses the prototype chain when hash accessing properties from the ruby object" do
|
364
|
+
Context.open do |cxt|
|
365
|
+
cxt.eval(<<EOJS)['bar'].should == "baz"
|
366
|
+
function Foo() {}
|
367
|
+
Foo.prototype.bar = 'baz'
|
368
|
+
new Foo()
|
369
|
+
EOJS
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
it "is enumenable" do
|
374
|
+
@cxt.open do
|
375
|
+
evaljs("o.foo = 'bar'; o.bang = 'baz'; o[5] = 'flip'")
|
376
|
+
@o.inject({}) {|i,p| k,v = p; i.tap {i[k] = v}}.should == {"foo" => 'bar', "bang" => 'baz', 5 => 'flip'}
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
describe "Exception Handling" do
|
382
|
+
it "raises javascript exceptions as ruby exceptions" do
|
383
|
+
lambda {
|
384
|
+
Context.new.eval('foo')
|
385
|
+
}.should raise_error(JavascriptError)
|
386
|
+
end
|
387
|
+
|
388
|
+
it "can handle syntax errors" do
|
389
|
+
lambda {
|
390
|
+
Context.eval('does not compiles')
|
391
|
+
}.should raise_error
|
392
|
+
end
|
393
|
+
|
394
|
+
it "should track message state" do
|
395
|
+
begin
|
396
|
+
Context.open do |cxt|
|
397
|
+
cxt.eval("var foo = 'bar';\nsyntax error!", "foo.js")
|
398
|
+
end
|
399
|
+
rescue JavascriptError => e
|
400
|
+
e.line_number.should == 2
|
401
|
+
e.source_name.should == "foo.js"
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
data/therubyracer.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{therubyracer}
|
8
|
-
s.version = "0.5.
|
8
|
+
s.version = "0.5.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Charles Lowell", "Bill Robertson"]
|
12
|
-
s.date = %q{2010-02-
|
12
|
+
s.date = %q{2010-02-17}
|
13
13
|
s.description = %q{Call javascript code and manipulate javascript objects from ruby. Call ruby code and manipulate ruby objects from javascript.}
|
14
14
|
s.email = %q{cowboyd@thefrontside.net}
|
15
15
|
s.extensions = ["ext/v8/extconf.rb"]
|
@@ -639,6 +639,7 @@ Gem::Specification.new do |s|
|
|
639
639
|
"lib/v8.rb",
|
640
640
|
"lib/v8/context.rb",
|
641
641
|
"lib/v8/object.rb",
|
642
|
+
"lib/v8/tap.rb",
|
642
643
|
"lib/v8/to.rb",
|
643
644
|
"script/console",
|
644
645
|
"script/destroy",
|
@@ -661,6 +662,7 @@ Gem::Specification.new do |s|
|
|
661
662
|
s.test_files = [
|
662
663
|
"spec/ext/cxt_spec.rb",
|
663
664
|
"spec/ext/obj_spec.rb",
|
665
|
+
"spec/redjs/jsapi_spec.rb",
|
664
666
|
"spec/redjs_helper.rb",
|
665
667
|
"spec/spec_helper.rb",
|
666
668
|
"spec/v8/to_spec.rb"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: therubyracer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: x86-darwin-9
|
6
6
|
authors:
|
7
7
|
- Charles Lowell
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2010-02-
|
13
|
+
date: 2010-02-17 00:00:00 -06:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -606,6 +606,7 @@ files:
|
|
606
606
|
- ext/v8/upstream/2.0.6/src/ia32/jump-target-ia32.cc
|
607
607
|
- ext/v8/upstream/scons/engine/SCons/Executor.py
|
608
608
|
- ext/v8/upstream/2.0.6/src/arm/builtins-arm.cc
|
609
|
+
- lib/v8/tap.rb
|
609
610
|
- ext/v8/v8_template.cpp
|
610
611
|
- spec/v8/to_spec.rb
|
611
612
|
- ext/v8/upstream/scons/engine/SCons/Node/__init__.py
|
@@ -657,6 +658,7 @@ files:
|
|
657
658
|
- ext/v8/upstream/2.0.6/src/ia32/debug-ia32.cc
|
658
659
|
- ext/v8/upstream/2.0.6/src/disassembler.cc
|
659
660
|
- ext/v8/upstream/2.0.6/src/objects-debug.cc
|
661
|
+
- spec/redjs/jsapi_spec.rb
|
660
662
|
- lib/v8/v8.bundle
|
661
663
|
has_rdoc: true
|
662
664
|
homepage: http://github.com/cowboyd/therubyracer
|
@@ -689,6 +691,7 @@ summary: Embed the V8 Javascript interpreter into Ruby
|
|
689
691
|
test_files:
|
690
692
|
- spec/ext/cxt_spec.rb
|
691
693
|
- spec/ext/obj_spec.rb
|
694
|
+
- spec/redjs/jsapi_spec.rb
|
692
695
|
- spec/redjs_helper.rb
|
693
696
|
- spec/spec_helper.rb
|
694
697
|
- spec/v8/to_spec.rb
|