opal 0.10.0.beta3 → 0.10.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/CHANGELOG.md +9 -1
- data/HACKING.md +2 -2
- data/docs/compiled_ruby.md +6 -6
- data/lib/opal/cli_runners/nodejs.rb +3 -1
- data/lib/opal/nodes/args/mlhsarg.rb +1 -3
- data/lib/opal/nodes/def.rb +18 -54
- data/lib/opal/nodes/helpers.rb +3 -3
- data/lib/opal/nodes/iter.rb +32 -3
- data/lib/opal/nodes/logic.rb +5 -5
- data/lib/opal/nodes/node_with_args.rb +31 -0
- data/lib/opal/parser/lexer.rb +9 -7
- data/lib/opal/sprockets/processor.rb +2 -2
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/array.rb +16 -0
- data/opal/corelib/basic_object.rb +1 -1
- data/opal/corelib/class.rb +15 -0
- data/opal/corelib/constants.rb +1 -1
- data/opal/corelib/enumerable.rb +32 -62
- data/opal/corelib/file.rb +2 -0
- data/opal/corelib/helpers.rb +41 -1
- data/opal/corelib/module.rb +26 -10
- data/opal/corelib/runtime.js +47 -12
- data/spec/filters/bugs/date.rb +0 -9
- data/spec/filters/bugs/hash.rb +0 -2
- data/spec/filters/bugs/kernel.rb +0 -5
- data/spec/filters/bugs/language.rb +4 -19
- data/spec/filters/bugs/module.rb +0 -30
- data/spec/filters/bugs/pathname.rb +5 -0
- data/spec/filters/bugs/proc.rb +0 -6
- data/spec/filters/unsupported/thread.rb +1 -0
- data/spec/lib/compiler_spec.rb +29 -29
- data/spec/opal/core/runtime/truthy_spec.rb +26 -0
- data/spec/ruby_specs +0 -3
- data/stdlib/date.rb +21 -0
- data/stdlib/native.rb +1 -1
- data/stdlib/nodejs/file.rb +36 -11
- data/stdlib/nodejs/io.rb +55 -0
- data/stdlib/pathname.rb +43 -0
- data/stdlib/promise.rb +1 -1
- data/tasks/testing.rake +55 -31
- data/test/nodejs/test_file.rb +30 -0
- metadata +6 -2
@@ -34,4 +34,30 @@ describe "Opal truthyness" do
|
|
34
34
|
|
35
35
|
is_falsey.should be_true
|
36
36
|
end
|
37
|
+
|
38
|
+
it "should consider false, nil, null, and undefined as not truthy" do
|
39
|
+
called = nil
|
40
|
+
[`false`, `nil`, `null`, `undefined`].each do |v|
|
41
|
+
if v
|
42
|
+
called = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
called.should be_nil
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should true as truthy" do
|
50
|
+
if `true`
|
51
|
+
called = true
|
52
|
+
end
|
53
|
+
|
54
|
+
called.should be_true
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should handle logic operators correctly for false, nil, null, and undefined" do
|
58
|
+
(`false` || `nil` || `null` || `undefined` || 1).should == 1
|
59
|
+
[`false`, `nil`, `null`, `undefined`].each do |v|
|
60
|
+
`#{1 && v} === #{v}`.should == true
|
61
|
+
end
|
62
|
+
end
|
37
63
|
end
|
data/spec/ruby_specs
CHANGED
@@ -100,9 +100,6 @@ ruby/core/unboundmethod
|
|
100
100
|
ruby/language
|
101
101
|
!ruby/language/constants_spec
|
102
102
|
!ruby/language/execution_spec
|
103
|
-
!ruby/language/next_spec
|
104
|
-
!ruby/language/return_spec
|
105
|
-
!ruby/language/yield_spec
|
106
103
|
|
107
104
|
ruby/library/base64
|
108
105
|
ruby/library/bigdecimal
|
data/stdlib/date.rb
CHANGED
@@ -338,6 +338,10 @@ class Date
|
|
338
338
|
def today
|
339
339
|
wrap `new Date()`
|
340
340
|
end
|
341
|
+
|
342
|
+
def gregorian_leap?(year)
|
343
|
+
`(new Date(#{year}, 1, 29).getMonth()-1) === 0`
|
344
|
+
end
|
341
345
|
end
|
342
346
|
|
343
347
|
def initialize(year = -4712, month = 1, day = 1, start = ITALY)
|
@@ -411,6 +415,10 @@ class Date
|
|
411
415
|
|
412
416
|
def <=>(other)
|
413
417
|
%x{
|
418
|
+
if (other.$$is_number) {
|
419
|
+
return #{self.jd <=> other}
|
420
|
+
}
|
421
|
+
|
414
422
|
var a = #@date, b = #{other}.date;
|
415
423
|
a.setHours(0, 0, 0, 0);
|
416
424
|
b.setHours(0, 0, 0, 0);
|
@@ -601,6 +609,19 @@ class Date
|
|
601
609
|
`#@date.getFullYear()`
|
602
610
|
end
|
603
611
|
|
612
|
+
def cwday
|
613
|
+
`#@date.getDay() || 7;`
|
614
|
+
end
|
615
|
+
|
616
|
+
def cweek
|
617
|
+
%x{
|
618
|
+
var d = new Date(#@date);
|
619
|
+
d.setHours(0,0,0);
|
620
|
+
d.setDate(d.getDate()+4-(d.getDay()||7));
|
621
|
+
return Math.ceil((((d-new Date(d.getFullYear(),0,1))/8.64e7)+1)/7);
|
622
|
+
}
|
623
|
+
end
|
624
|
+
|
604
625
|
%x{
|
605
626
|
function days_in_month(year, month) {
|
606
627
|
var leap = ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
|
data/stdlib/native.rb
CHANGED
@@ -59,7 +59,7 @@ module Native
|
|
59
59
|
if (prop instanceof Function) {
|
60
60
|
var converted = new Array(args.length);
|
61
61
|
|
62
|
-
for (var i = 0,
|
62
|
+
for (var i = 0, l = args.length; i < l; i++) {
|
63
63
|
var item = args[i],
|
64
64
|
conv = #{try_convert(`item`)};
|
65
65
|
|
data/stdlib/nodejs/file.rb
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
%x{
|
2
|
+
var warnings = {};
|
3
|
+
function handle_unsupported_feature(message) {
|
4
|
+
switch (Opal.config.unsupported_features_severity) {
|
5
|
+
case 'error':
|
6
|
+
#{Kernel.raise NotImplementedError, `message`}
|
7
|
+
break;
|
8
|
+
case 'warning':
|
9
|
+
warn(message)
|
10
|
+
break;
|
11
|
+
default: // ignore
|
12
|
+
// noop
|
13
|
+
}
|
14
|
+
}
|
15
|
+
function warn(string) {
|
16
|
+
if (warnings[string]) {
|
17
|
+
return;
|
18
|
+
}
|
19
|
+
warnings[string] = true;
|
20
|
+
#{warn(`string`)};
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
1
24
|
class File < IO
|
2
25
|
include ::IO::Writable
|
3
26
|
include ::IO::Readable
|
@@ -35,14 +58,6 @@ class File < IO
|
|
35
58
|
end
|
36
59
|
end
|
37
60
|
|
38
|
-
def self.basename(path, ext = undefined)
|
39
|
-
`__path__.basename(#{path}, #{ext})`
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.dirname(path)
|
43
|
-
`__path__.dirname(#{path})`
|
44
|
-
end
|
45
|
-
|
46
61
|
def self.join(*paths)
|
47
62
|
`__path__.join.apply(__path__, #{paths})`
|
48
63
|
end
|
@@ -80,16 +95,20 @@ class File < IO
|
|
80
95
|
File::Stat.new(path)
|
81
96
|
end
|
82
97
|
|
98
|
+
def self.mtime path
|
99
|
+
`__fs__.statSync(#{path}).mtime`
|
100
|
+
end
|
101
|
+
|
83
102
|
# Instance Methods
|
84
103
|
|
85
104
|
def initialize(path, flags)
|
86
105
|
binary_flag_regexp = /b/
|
87
106
|
encoding_flag_regexp = /:(.*)/
|
88
107
|
# binary flag is unsupported
|
89
|
-
|
108
|
+
`handle_unsupported_feature("Binary flag (b) is unsupported by Node.js openSync method, removing flag.")` if flags.match(binary_flag_regexp)
|
90
109
|
flags = flags.gsub(binary_flag_regexp, '')
|
91
110
|
# encoding flag is unsupported
|
92
|
-
|
111
|
+
`handle_unsupported_feature("Encoding flag (:encoding) is unsupported by Node.js openSync method, removing flag.")` if flags.match(encoding_flag_regexp)
|
93
112
|
flags = flags.gsub(encoding_flag_regexp, '')
|
94
113
|
@path = path
|
95
114
|
@flags = flags
|
@@ -109,6 +128,10 @@ class File < IO
|
|
109
128
|
def close
|
110
129
|
`__fs__.closeSync(#{@fd})`
|
111
130
|
end
|
131
|
+
|
132
|
+
def mtime
|
133
|
+
`__fs__.statSync(#{@path}).mtime`
|
134
|
+
end
|
112
135
|
end
|
113
136
|
|
114
137
|
class File::Stat
|
@@ -120,9 +143,11 @@ class File::Stat
|
|
120
143
|
@path = path
|
121
144
|
end
|
122
145
|
|
123
|
-
|
124
146
|
def file?
|
125
147
|
`__fs__.statSync(#{@path}).isFile()`
|
126
148
|
end
|
127
149
|
|
150
|
+
def mtime
|
151
|
+
`__fs__.statSync(#{@path}).mtime`
|
152
|
+
end
|
128
153
|
end
|
data/stdlib/nodejs/io.rb
CHANGED
@@ -1,3 +1,58 @@
|
|
1
|
+
class IO
|
2
|
+
|
3
|
+
@__fs__ = node_require :fs
|
4
|
+
`var __fs__ = #{@__fs__}`
|
5
|
+
|
6
|
+
attr_reader :eof
|
7
|
+
attr_reader :lineno
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@eof = false
|
11
|
+
@lineno = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def read
|
15
|
+
if @eof
|
16
|
+
''
|
17
|
+
else
|
18
|
+
res = `__fs__.readFileSync(#{@path}).toString()`
|
19
|
+
@eof = true
|
20
|
+
@lineno = res.size
|
21
|
+
res
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def each_line(separator = $/, &block)
|
26
|
+
if @eof
|
27
|
+
return block_given? ? self : [].to_enum
|
28
|
+
end
|
29
|
+
|
30
|
+
if block_given?
|
31
|
+
lines = File.read(@path)
|
32
|
+
%x{
|
33
|
+
self.eof = false;
|
34
|
+
self.lineno = 0;
|
35
|
+
var chomped = #{lines.chomp},
|
36
|
+
trailing = lines.length != chomped.length,
|
37
|
+
splitted = chomped.split(separator);
|
38
|
+
for (var i = 0, length = splitted.length; i < length; i++) {
|
39
|
+
self.lineno += 1;
|
40
|
+
if (i < length - 1 || trailing) {
|
41
|
+
#{yield `splitted[i] + separator`};
|
42
|
+
}
|
43
|
+
else {
|
44
|
+
#{yield `splitted[i]`};
|
45
|
+
}
|
46
|
+
}
|
47
|
+
self.eof = true;
|
48
|
+
}
|
49
|
+
self
|
50
|
+
else
|
51
|
+
read.each_line
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
1
56
|
STDOUT.write_proc = -> (string) {`process.stdout.write(string)`}
|
2
57
|
STDERR.write_proc = -> (string) {`process.stderr.write(string)`}
|
3
58
|
|
data/stdlib/pathname.rb
CHANGED
@@ -166,6 +166,49 @@ class Pathname
|
|
166
166
|
|
167
167
|
alias :to_str :to_path
|
168
168
|
alias :to_s :to_path
|
169
|
+
|
170
|
+
SAME_PATHS = if File::FNM_SYSCASE.nonzero?
|
171
|
+
# Avoid #zero? here because #casecmp can return nil.
|
172
|
+
proc {|a, b| a.casecmp(b) == 0}
|
173
|
+
else
|
174
|
+
proc {|a, b| a == b}
|
175
|
+
end
|
176
|
+
|
177
|
+
def relative_path_from(base_directory)
|
178
|
+
dest_directory = self.cleanpath.to_s
|
179
|
+
base_directory = base_directory.cleanpath.to_s
|
180
|
+
dest_prefix = dest_directory
|
181
|
+
dest_names = []
|
182
|
+
while r = chop_basename(dest_prefix)
|
183
|
+
dest_prefix, basename = r
|
184
|
+
dest_names.unshift basename if basename != '.'
|
185
|
+
end
|
186
|
+
base_prefix = base_directory
|
187
|
+
base_names = []
|
188
|
+
while r = chop_basename(base_prefix)
|
189
|
+
base_prefix, basename = r
|
190
|
+
base_names.unshift basename if basename != '.'
|
191
|
+
end
|
192
|
+
unless SAME_PATHS[dest_prefix, base_prefix]
|
193
|
+
raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
|
194
|
+
end
|
195
|
+
while !dest_names.empty? &&
|
196
|
+
!base_names.empty? &&
|
197
|
+
SAME_PATHS[dest_names.first, base_names.first]
|
198
|
+
dest_names.shift
|
199
|
+
base_names.shift
|
200
|
+
end
|
201
|
+
if base_names.include? '..'
|
202
|
+
raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
|
203
|
+
end
|
204
|
+
base_names.fill('..')
|
205
|
+
relpath_names = base_names + dest_names
|
206
|
+
if relpath_names.empty?
|
207
|
+
Pathname.new('.')
|
208
|
+
else
|
209
|
+
Pathname.new(File.join(*relpath_names))
|
210
|
+
end
|
211
|
+
end
|
169
212
|
end
|
170
213
|
|
171
214
|
module Kernel
|
data/stdlib/promise.rb
CHANGED
data/tasks/testing.rake
CHANGED
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:rspec) do |t|
|
|
8
8
|
t.pattern = 'spec/lib/**/*_spec.rb'
|
9
9
|
end
|
10
10
|
|
11
|
-
module
|
11
|
+
module MSpecSuite
|
12
12
|
extend self
|
13
13
|
|
14
14
|
def stubs
|
@@ -125,9 +125,9 @@ DESC
|
|
125
125
|
url = "http://localhost:#{port}/"
|
126
126
|
|
127
127
|
mkdir_p File.dirname(filename)
|
128
|
-
|
128
|
+
MSpecSuite.write_file filename, MSpecSuite.specs(ENV.to_hash.merge 'SUITE' => suite)
|
129
129
|
|
130
|
-
|
130
|
+
MSpecSuite.stubs.each {|s| ::Opal::Config.stubbed_files << s }
|
131
131
|
|
132
132
|
Opal::Config.arity_check_enabled = true
|
133
133
|
Opal::Config.freezing_stubs_enabled = true
|
@@ -157,10 +157,10 @@ DESC
|
|
157
157
|
|
158
158
|
filename = "tmp/mspec_#{platform}.rb"
|
159
159
|
mkdir_p File.dirname(filename)
|
160
|
-
bm_filepath =
|
161
|
-
|
160
|
+
bm_filepath = MSpecSuite.bm_filepath if ENV['BM']
|
161
|
+
MSpecSuite.write_file filename, MSpecSuite.specs(ENV.to_hash.merge 'SUITE' => suite), bm_filepath
|
162
162
|
|
163
|
-
stubs =
|
163
|
+
stubs = MSpecSuite.stubs.map{|s| "-s#{s}"}.join(' ')
|
164
164
|
|
165
165
|
sh "ruby -rbundler/setup -r#{__dir__}/testing/mspec_special_calls "\
|
166
166
|
"bin/opal -gmspec #{include_paths} #{stubs} -R#{platform} -Dwarning -A #{filename}"
|
@@ -199,47 +199,71 @@ task :jshint do
|
|
199
199
|
end
|
200
200
|
end
|
201
201
|
|
202
|
+
|
203
|
+
module MinitestSuite
|
204
|
+
extend self
|
205
|
+
|
206
|
+
def build_js_command(files, options = {})
|
207
|
+
includes = options.fetch(:includes, [])
|
208
|
+
js_filename = options.fetch(:js_filename, [])
|
209
|
+
|
210
|
+
includes << 'vendored-minitest'
|
211
|
+
include_paths = includes.map {|i| " -I#{i}"}.join
|
212
|
+
|
213
|
+
requires = files.map{|f| "require '#{f}'"}
|
214
|
+
rb_filename = js_filename.sub(/\.js$/, '.rb')
|
215
|
+
mkdir_p File.dirname(rb_filename)
|
216
|
+
File.write rb_filename, requires.join("\n")
|
217
|
+
|
218
|
+
stubs = "-soptparse -sio/console -stimeout -smutex_m -srubygems -stempfile -smonitor"
|
219
|
+
|
220
|
+
"ruby -rbundler/setup bin/opal #{include_paths} #{stubs} -Dwarning -A #{rb_filename} -c > #{js_filename}"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
202
224
|
task :cruby_tests do
|
203
225
|
if ENV.key? 'FILES'
|
204
|
-
files = Dir[ENV['FILES']
|
205
|
-
|
226
|
+
files = Dir[ENV['FILES']]
|
227
|
+
includes = %w[test . tmp lib]
|
206
228
|
else
|
207
|
-
|
208
|
-
test_dir = Pathname("#{__dir__}/../test/cruby/test")
|
229
|
+
includes = %w[test test/cruby/test]
|
209
230
|
files = %w[
|
210
231
|
benchmark/test_benchmark.rb
|
211
232
|
ruby/test_call.rb
|
212
233
|
opal/test_keyword.rb
|
213
234
|
base64/test_base64.rb
|
214
235
|
opal/unsupported_and_bugs.rb
|
215
|
-
]
|
216
|
-
if path.end_with?('.rb')
|
217
|
-
path
|
218
|
-
else
|
219
|
-
glob = test_dir.join(path+"/test_*.rb").to_s
|
220
|
-
size = test_dir.to_s.size
|
221
|
-
Dir[glob].map { |file| file[size+1..-1] }
|
222
|
-
end
|
223
|
-
end
|
236
|
+
]
|
224
237
|
end
|
225
|
-
include_paths << ' -Ivendored-minitest'
|
226
238
|
|
227
|
-
requires = files.map{|f| "require '#{f}'"}
|
228
|
-
filename = 'tmp/cruby_tests.rb'
|
229
239
|
js_filename = 'tmp/cruby_tests.js'
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
240
|
+
build_js_command = MinitestSuite.build_js_command(
|
241
|
+
%w[
|
242
|
+
opal/platform
|
243
|
+
opal-parser
|
244
|
+
] + files,
|
245
|
+
includes: includes,
|
246
|
+
js_filename: js_filename,
|
247
|
+
)
|
248
|
+
sh build_js_command
|
249
|
+
sh "NODE_PATH=stdlib/nodejs/node_modules node #{js_filename}"
|
250
|
+
end
|
236
251
|
|
237
|
-
|
238
|
-
|
252
|
+
task :test_nodejs do
|
253
|
+
js_filename = 'tmp/test_nodejs.js'
|
254
|
+
build_js_command = MinitestSuite.build_js_command(
|
255
|
+
%w[
|
256
|
+
opal-parser.rb
|
257
|
+
test_file.rb
|
258
|
+
],
|
259
|
+
includes: %w[test/nodejs],
|
260
|
+
js_filename: js_filename,
|
261
|
+
)
|
262
|
+
sh build_js_command
|
239
263
|
sh "NODE_PATH=stdlib/nodejs/node_modules node #{js_filename}"
|
240
264
|
end
|
241
265
|
|
242
266
|
task :mspec => [:mspec_phantomjs, :mspec_nodejs, :mspec_sprockets_phantomjs]
|
243
|
-
task :minitest => [:cruby_tests]
|
267
|
+
task :minitest => [:cruby_tests, :test_nodejs]
|
244
268
|
task :test_all => [:rspec, :mspec, :minitest]
|
245
269
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Copied from cruby and modified to skip unsupported syntaxes
|
2
|
+
require 'test/unit'
|
3
|
+
require 'nodejs'
|
4
|
+
require 'nodejs/file'
|
5
|
+
|
6
|
+
class TestNodejsFile < Test::Unit::TestCase
|
7
|
+
def test_write_read
|
8
|
+
path = '/tmp/testing_nodejs_file_implementation'
|
9
|
+
contents = 'foobar'
|
10
|
+
assert !File.exist?(path)
|
11
|
+
File.write path, contents
|
12
|
+
|
13
|
+
assert_equal(contents, File.read(path))
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_read_file
|
17
|
+
File.write('tmp/foo', 'bar')
|
18
|
+
assert_equal("bar", File.read('tmp/foo'))
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_read_each_line
|
22
|
+
File.write('tmp/bar', "one\ntwo")
|
23
|
+
file = File.new('tmp/bar', 'r')
|
24
|
+
lines = []
|
25
|
+
file.each_line do |line|
|
26
|
+
lines << line
|
27
|
+
end
|
28
|
+
assert_equal(lines.length, 2)
|
29
|
+
end
|
30
|
+
end
|