opal 0.10.0.beta3 → 0.10.0.beta4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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