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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -0
  3. data/CHANGELOG.md +9 -1
  4. data/HACKING.md +2 -2
  5. data/docs/compiled_ruby.md +6 -6
  6. data/lib/opal/cli_runners/nodejs.rb +3 -1
  7. data/lib/opal/nodes/args/mlhsarg.rb +1 -3
  8. data/lib/opal/nodes/def.rb +18 -54
  9. data/lib/opal/nodes/helpers.rb +3 -3
  10. data/lib/opal/nodes/iter.rb +32 -3
  11. data/lib/opal/nodes/logic.rb +5 -5
  12. data/lib/opal/nodes/node_with_args.rb +31 -0
  13. data/lib/opal/parser/lexer.rb +9 -7
  14. data/lib/opal/sprockets/processor.rb +2 -2
  15. data/lib/opal/version.rb +1 -1
  16. data/opal/corelib/array.rb +16 -0
  17. data/opal/corelib/basic_object.rb +1 -1
  18. data/opal/corelib/class.rb +15 -0
  19. data/opal/corelib/constants.rb +1 -1
  20. data/opal/corelib/enumerable.rb +32 -62
  21. data/opal/corelib/file.rb +2 -0
  22. data/opal/corelib/helpers.rb +41 -1
  23. data/opal/corelib/module.rb +26 -10
  24. data/opal/corelib/runtime.js +47 -12
  25. data/spec/filters/bugs/date.rb +0 -9
  26. data/spec/filters/bugs/hash.rb +0 -2
  27. data/spec/filters/bugs/kernel.rb +0 -5
  28. data/spec/filters/bugs/language.rb +4 -19
  29. data/spec/filters/bugs/module.rb +0 -30
  30. data/spec/filters/bugs/pathname.rb +5 -0
  31. data/spec/filters/bugs/proc.rb +0 -6
  32. data/spec/filters/unsupported/thread.rb +1 -0
  33. data/spec/lib/compiler_spec.rb +29 -29
  34. data/spec/opal/core/runtime/truthy_spec.rb +26 -0
  35. data/spec/ruby_specs +0 -3
  36. data/stdlib/date.rb +21 -0
  37. data/stdlib/native.rb +1 -1
  38. data/stdlib/nodejs/file.rb +36 -11
  39. data/stdlib/nodejs/io.rb +55 -0
  40. data/stdlib/pathname.rb +43 -0
  41. data/stdlib/promise.rb +1 -1
  42. data/tasks/testing.rake +55 -31
  43. data/test/nodejs/test_file.rb +30 -0
  44. 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, length = args.length; i < length; i++) {
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
 
@@ -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
- warn "Binary flag (b) is unsupported by Node.js openSync method, removing flag." if flags.match(binary_flag_regexp)
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
- warn "Encoding flag (:encoding) is unsupported by Node.js openSync method, removing flag." if flags.match(encoding_flag_regexp)
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
@@ -353,7 +353,7 @@ class Promise
353
353
  def initialize(depth, block)
354
354
  @depth = depth
355
355
 
356
- super success: -> {
356
+ super success: proc {
357
357
  trace = Trace.it(self).reverse
358
358
  trace.pop
359
359
 
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 Testing
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
- Testing.write_file filename, Testing.specs(ENV.to_hash.merge 'SUITE' => suite)
128
+ MSpecSuite.write_file filename, MSpecSuite.specs(ENV.to_hash.merge 'SUITE' => suite)
129
129
 
130
- Testing.stubs.each {|s| ::Opal::Config.stubbed_files << s }
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 = Testing.bm_filepath if ENV['BM']
161
- Testing.write_file filename, Testing.specs(ENV.to_hash.merge 'SUITE' => suite), bm_filepath
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 = Testing.stubs.map{|s| "-s#{s}"}.join(' ')
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'] || 'test/test_*.rb']
205
- include_paths = '-Itest -I. -Itmp -Ilib'
226
+ files = Dir[ENV['FILES']]
227
+ includes = %w[test . tmp lib]
206
228
  else
207
- include_paths = '-Itest/cruby/test -Itest'
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
- ].flat_map do |path|
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
- mkdir_p File.dirname(filename)
231
- File.write filename, requires.join("\n")
232
-
233
- stubs = "-soptparse -sio/console -stimeout -smutex_m -srubygems -stempfile -smonitor"
234
-
235
- puts "== Running: #{files.join ", "}"
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
- sh "ruby -rbundler/setup "\
238
- "bin/opal #{include_paths} #{stubs} -ropal/platform -ropal-parser -Dwarning -A #{filename} -c > #{js_filename}"
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