opal 0.3.19 → 0.3.20

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 (66) hide show
  1. data/.gitignore +3 -1
  2. data/Gemfile +3 -2
  3. data/README.md +304 -48
  4. data/Rakefile +1 -2
  5. data/core/alpha.rb +2 -1
  6. data/core/array.rb +92 -96
  7. data/core/basic_object.rb +1 -10
  8. data/core/boolean.rb +6 -18
  9. data/core/class.rb +9 -10
  10. data/core/comparable.rb +1 -1
  11. data/core/enumerable.rb +11 -11
  12. data/core/enumerator.rb +2 -10
  13. data/core/error.rb +16 -31
  14. data/core/hash.rb +32 -36
  15. data/core/json.rb +50 -0
  16. data/core/kernel.rb +48 -57
  17. data/core/load_order +3 -5
  18. data/core/module.rb +37 -35
  19. data/core/nil_class.rb +4 -0
  20. data/core/numeric.rb +10 -30
  21. data/core/proc.rb +1 -1
  22. data/core/range.rb +3 -4
  23. data/core/regexp.rb +21 -6
  24. data/core/runtime.js +278 -370
  25. data/core/string.rb +21 -37
  26. data/core/struct.rb +11 -3
  27. data/core/time.rb +44 -37
  28. data/lib/opal.rb +3 -3
  29. data/lib/opal/builder.rb +48 -27
  30. data/lib/opal/builder_task.rb +3 -20
  31. data/lib/opal/grammar.rb +18 -13
  32. data/lib/opal/grammar.y +7 -4
  33. data/lib/opal/parser.rb +290 -199
  34. data/lib/opal/scope.rb +187 -176
  35. data/lib/opal/version.rb +1 -1
  36. data/test/core/kernel/define_singleton_method_spec.rb +21 -0
  37. data/test/core/time/at_spec.rb +7 -0
  38. data/test/core/time/day_spec.rb +5 -0
  39. data/test/core/time/friday_spec.rb +9 -0
  40. data/test/core/time/hour_spec.rb +5 -0
  41. data/test/core/time/min_spec.rb +5 -0
  42. data/test/core/time/monday_spec.rb +9 -0
  43. data/test/core/time/month_spec.rb +5 -0
  44. data/test/core/time/now_spec.rb +5 -0
  45. data/test/core/time/saturday_spec.rb +9 -0
  46. data/test/index.html +2 -1
  47. data/test/language/singleton_class_spec.rb +0 -16
  48. data/test/opal/array/to_json_spec.rb +7 -0
  49. data/test/opal/boolean/singleton_class_spec.rb +9 -0
  50. data/test/opal/boolean/to_json_spec.rb +9 -0
  51. data/test/opal/hash/to_json_spec.rb +9 -0
  52. data/test/opal/json/parse_spec.rb +31 -0
  53. data/test/opal/kernel/to_json_spec.rb +5 -0
  54. data/test/opal/nil/to_json_spec.rb +5 -0
  55. data/test/opal/numeric/to_json_spec.rb +6 -0
  56. data/test/opal/runtime/call_spec.rb +16 -0
  57. data/test/opal/runtime/defined_spec.rb +11 -0
  58. data/test/opal/runtime/super_spec.rb +16 -0
  59. data/test/opal/string/to_json_spec.rb +6 -0
  60. data/test/spec_helper.rb +1 -3
  61. metadata +48 -15
  62. data/core/dir.rb +0 -89
  63. data/core/file.rb +0 -85
  64. data/core/match_data.rb +0 -35
  65. data/core/rational.rb +0 -16
  66. data/test/core/file/expand_path_spec.rb +0 -20
@@ -1,7 +1,6 @@
1
1
  class String < `String`
2
2
  %x{
3
- def._isString = true;
4
- var string_class = this;
3
+ String_prototype._isString = true;
5
4
  }
6
5
 
7
6
  include Comparable
@@ -80,7 +79,7 @@ class String < `String`
80
79
  def =~(other)
81
80
  %x{
82
81
  if (typeof other === 'string') {
83
- throw RubyTypeError.$new('string given');
82
+ #{ raise 'string given' };
84
83
  }
85
84
 
86
85
  return #{other =~ self};
@@ -88,8 +87,7 @@ class String < `String`
88
87
  end
89
88
 
90
89
  # TODO: implement range based accessors
91
- # TODO: implement regex based accessors
92
- def [](index, length = undefined)
90
+ def [](index, length)
93
91
  %x{
94
92
  if (length == null) {
95
93
  if (index < 0) {
@@ -162,9 +160,7 @@ class String < `String`
162
160
  `this.charAt(0)`
163
161
  end
164
162
 
165
- def downcase
166
- `this.toLowerCase()`
167
- end
163
+ alias_native :downcase, :toLowerCase
168
164
 
169
165
  alias each_char chars
170
166
 
@@ -204,12 +200,10 @@ class String < `String`
204
200
  `this.toString() === val.toString()`
205
201
  end
206
202
 
207
- def getbyte(index)
208
- `this.charCodeAt(index)`
209
- end
203
+ alias_native :getbyte, :charCodeAt
210
204
 
211
- def gsub(pattern, replace = undefined, &block)
212
- return enum_for :gsub, pattern, replace if !block && `pattern === undefined`
205
+ def gsub(pattern, replace, &block)
206
+ return enum_for :gsub, pattern, replace if !block && `pattern == null`
213
207
 
214
208
  if pattern.is_a?(String)
215
209
  pattern = /#{Regexp.escape(pattern)}/
@@ -224,9 +218,7 @@ class String < `String`
224
218
  }
225
219
  end
226
220
 
227
- def hash
228
- `this.toString()`
229
- end
221
+ alias_native :hash, :toString
230
222
 
231
223
  def hex
232
224
  to_i 16
@@ -236,7 +228,7 @@ class String < `String`
236
228
  `this.indexOf(other) !== -1`
237
229
  end
238
230
 
239
- def index(what, offset = undefined)
231
+ def index(what, offset)
240
232
  unless String === what || Regexp === what
241
233
  raise TypeError, "type mismatch: #{what.class} given"
242
234
  end
@@ -244,7 +236,7 @@ class String < `String`
244
236
  %x{
245
237
  var result = -1;
246
238
 
247
- if (offset !== undefined) {
239
+ if (offset != null) {
248
240
  if (offset < 0) {
249
241
  offset = this.length - offset;
250
242
  }
@@ -315,7 +307,7 @@ class String < `String`
315
307
  `this.replace(/^\\s*/, '')`
316
308
  end
317
309
 
318
- def match(pattern, pos = undefined, &block)
310
+ def match(pattern, pos, &block)
319
311
  (pattern.is_a?(Regexp) ? pattern : /#{Regexp.escape(pattern)}/).match(self, pos, &block)
320
312
  end
321
313
 
@@ -377,15 +369,13 @@ class String < `String`
377
369
  `this.replace(/^\\s*/, '').replace(/\\s*$/, '')`
378
370
  end
379
371
 
380
- def sub(pattern, replace = undefined, &block)
372
+ def sub(pattern, replace, &block)
381
373
  %x{
382
374
  if (typeof(replace) === 'string') {
383
375
  return this.replace(pattern, replace);
384
376
  }
385
377
  if (block !== nil) {
386
378
  return this.replace(pattern, function(str) {
387
- //$opal.match_data = arguments
388
-
389
379
  return block.call(__context, str);
390
380
  });
391
381
  }
@@ -394,13 +384,13 @@ class String < `String`
394
384
  return this.replace(pattern, function(str) {
395
385
  var value = #{replace[str]};
396
386
 
397
- return (value === null) ? undefined : #{value.to_s};
387
+ return (value == null) ? nil : #{value.to_s};
398
388
  });
399
389
  }
400
390
  else {
401
391
  replace = #{String.try_convert(replace)};
402
392
 
403
- if (replace === null) {
393
+ if (replace == null) {
404
394
  #{raise TypeError, "can't convert #{replace.class} into String"};
405
395
  }
406
396
 
@@ -433,7 +423,7 @@ class String < `String`
433
423
  return $1 ? $0.toUpperCase() : $0.toLowerCase();
434
424
  });
435
425
 
436
- if (this._klass === string_class) {
426
+ if (this._klass === String) {
437
427
  return str;
438
428
  }
439
429
 
@@ -455,11 +445,7 @@ class String < `String`
455
445
  %x{
456
446
  var result = parseFloat(this);
457
447
 
458
- if (isNaN(result)) {
459
- return 0;
460
- }
461
-
462
- return result;
448
+ return isNaN(result) ? 0 : result;
463
449
  }
464
450
  end
465
451
 
@@ -475,6 +461,8 @@ class String < `String`
475
461
  }
476
462
  end
477
463
 
464
+ alias to_json inspect
465
+
478
466
  def to_proc
479
467
  %x{
480
468
  var self = this, jsid = mid_to_jsid(self);
@@ -483,17 +471,13 @@ class String < `String`
483
471
  }
484
472
  end
485
473
 
486
- def to_s
487
- `this.toString()`
488
- end
474
+ alias_native :to_s, :toString
489
475
 
490
476
  alias to_str to_s
491
477
 
492
478
  alias to_sym intern
493
479
 
494
- def upcase
495
- `this.toUpperCase()`
496
- end
480
+ alias_native :upcase, :toUpperCase
497
481
  end
498
482
 
499
- Symbol = String
483
+ Symbol = String
@@ -2,7 +2,7 @@ class Struct
2
2
  def self.new(name, *args)
3
3
  return super unless self == Struct
4
4
 
5
- if name.is_a?(String)
5
+ if name[0] == name[0].upcase
6
6
  Struct.const_set(name, new(*args))
7
7
  else
8
8
  args.unshift name
@@ -14,6 +14,10 @@ class Struct
14
14
  end
15
15
 
16
16
  def self.define_struct_attribute(name)
17
+ if self == Struct
18
+ raise ArgumentError, 'you cannot define attributes to the Struct class'
19
+ end
20
+
17
21
  members << name
18
22
 
19
23
  define_method name do
@@ -26,6 +30,10 @@ class Struct
26
30
  end
27
31
 
28
32
  def self.members
33
+ if self == Struct
34
+ raise ArgumentError, 'the Struct class has no members'
35
+ end
36
+
29
37
  @members ||= []
30
38
  end
31
39
 
@@ -42,7 +50,7 @@ class Struct
42
50
  end
43
51
 
44
52
  def [](name)
45
- if name.is_a?(Integer)
53
+ if Integer === name
46
54
  raise IndexError, "offset #{name} too large for struct(size:#{members.size})" if name >= members.size
47
55
 
48
56
  name = members[name]
@@ -54,7 +62,7 @@ class Struct
54
62
  end
55
63
 
56
64
  def []=(name, value)
57
- if name.is_a?(Integer)
65
+ if Integer === name
58
66
  raise IndexError, "offset #{name} too large for struct(size:#{members.size})" if name >= members.size
59
67
 
60
68
  name = members[name]
@@ -1,36 +1,43 @@
1
- class Time
1
+ class Time < `Date`
2
2
  include Comparable
3
3
 
4
4
  def self.at(seconds, frac = 0)
5
- result = allocate
6
- `result.time = new Date(seconds * 1000 + frac)`
7
- result
5
+ allocate `seconds * 1000 + frac`
8
6
  end
9
7
 
10
- def self.now
11
- result = allocate
12
- `result.time = new Date()`
13
- result
8
+ def self.new(year, month, day, hour, minute, second, millisecond)
9
+ %x{
10
+ switch (arguments.length) {
11
+ case 1:
12
+ return new Date(year);
13
+ case 2:
14
+ return new Date(year, month - 1);
15
+ case 3:
16
+ return new Date(year, month - 1, day);
17
+ case 4:
18
+ return new Date(year, month - 1, day, hour);
19
+ case 5:
20
+ return new Date(year, month - 1, day, hour, minute);
21
+ case 6:
22
+ return new Date(year, month - 1, day, hour, minute, second);
23
+ case 7:
24
+ return new Date(year, month - 1, day, hour, minute, second, millisecond);
25
+ default:
26
+ return new Date();
27
+ }
28
+ }
14
29
  end
15
30
 
16
- def initialize
17
- `this.time = new Date()`
31
+ def self.now
32
+ allocate
18
33
  end
19
34
 
20
35
  def +(other)
21
- %x{
22
- var res = #{Time.allocate};
23
- res.time = new Date(#{to_f + other.to_f});
24
- return res;
25
- }
36
+ Time.allocate(self.to_f + other.to_f)
26
37
  end
27
38
 
28
39
  def -(other)
29
- %x{
30
- var res = #{Time.allocate};
31
- res.time = new Date(#{to_f - other.to_f});
32
- return res;
33
- }
40
+ Time.allocate(self.to_f - other.to_f)
34
41
  end
35
42
 
36
43
  def <=>(other)
@@ -38,7 +45,7 @@ class Time
38
45
  end
39
46
 
40
47
  def day
41
- `this.time.getDate()`
48
+ `this.getDate()`
42
49
  end
43
50
 
44
51
  def eql?(other)
@@ -46,66 +53,66 @@ class Time
46
53
  end
47
54
 
48
55
  def friday?
49
- `this.time.getDay() === 5`
56
+ `this.getDay() === 5`
50
57
  end
51
58
 
52
59
  def hour
53
- `this.time.getHours()`
60
+ `this.getHours()`
54
61
  end
55
62
 
56
63
  alias mday day
57
64
 
58
65
  def min
59
- `this.time.getMinutes()`
66
+ `this.getMinutes()`
60
67
  end
61
68
 
62
69
  def mon
63
- `this.time.getMonth() + 1`
70
+ `this.getMonth() + 1`
64
71
  end
65
72
 
66
73
  def monday?
67
- `this.time.getDay() === 1`
74
+ `this.getDay() === 1`
68
75
  end
69
76
 
70
77
  alias month mon
71
78
 
72
79
  def saturday?
73
- `this.time.getDay() === 6`
80
+ `this.getDay() === 6`
74
81
  end
75
82
 
76
83
  def sec
77
- `this.time.getSeconds()`
84
+ `this.getSeconds()`
78
85
  end
79
86
 
80
87
  def sunday?
81
- `this.time.getDay() === 0`
88
+ `this.getDay() === 0`
82
89
  end
83
90
 
84
91
  def thursday?
85
- `this.time.getDay() === 4`
92
+ `this.getDay() === 4`
86
93
  end
87
94
 
88
95
  def to_f
89
- `this.time.getTime() / 1000`
96
+ `this.getTime() / 1000`
90
97
  end
91
98
 
92
99
  def to_i
93
- `parseInt(this.time.getTime() / 1000)`
100
+ `parseInt(this.getTime() / 1000)`
94
101
  end
95
102
 
96
103
  def tuesday?
97
- `this.time.getDay() === 2`
104
+ `this.getDay() === 2`
98
105
  end
99
106
 
100
107
  def wday
101
- `this.time.getDay()`
108
+ `this.getDay()`
102
109
  end
103
110
 
104
111
  def wednesday?
105
- `this.time.getDay() === 3`
112
+ `this.getDay() === 3`
106
113
  end
107
114
 
108
115
  def year
109
- `this.time.getFullYear()`
116
+ `this.getFullYear()`
110
117
  end
111
- end
118
+ end
@@ -5,9 +5,9 @@ require 'opal/version'
5
5
 
6
6
  module Opal
7
7
  # Parse given string of ruby into javascript
8
- def self.parse(str)
9
- js = Parser.new.parse str
10
- "(#{js}).call(Opal.top);"
8
+ def self.parse(str, file='(file)')
9
+ js = Parser.new.parse str, file
10
+ "(#{js})();"
11
11
  end
12
12
 
13
13
  # Returns opal runtime js code (string)
@@ -12,23 +12,17 @@ module Opal
12
12
 
13
13
  methods = Parser::METHOD_NAMES.map { |f, t| "'#{f}': '$#{t}$'" }
14
14
  runtime = File.read(File.join core_dir, 'runtime.js')
15
- corelib = Opal.parse corelib.join("\n")
15
+ corelib = Opal.parse corelib.join("\n"), '(corelib)'
16
16
 
17
17
  [
18
- "/*!",
19
- " * Opal v#{Opal::VERSION}",
20
- " * http://opalrb.org",
21
- " *",
22
- " * Copyright 2012, Adam Beynon",
23
- " * Released under the MIT License",
24
- " */",
18
+ "// Opal v#{Opal::VERSION}",
19
+ "// http://opalrb.org",
20
+ "// Copyright 2012, Adam Beynon",
21
+ "// Released under the MIT License",
25
22
  "(function(undefined) {",
26
23
  runtime,
27
- "var method_names = {#{ methods.join ', ' }},",
28
- "reverse_method_names = {};",
29
- "for (var id in method_names) {",
30
- "reverse_method_names[method_names[id]] = id;",
31
- "}",
24
+ "var method_names = {#{ methods.join ', ' }};",
25
+ "Opal.version = #{ Opal::VERSION.inspect };",
32
26
  corelib,
33
27
  "}).call(this);"
34
28
  ].join("\n")
@@ -41,7 +35,6 @@ module Opal
41
35
  def initialize(options = {})
42
36
  @sources = Array(options[:files])
43
37
  @options = options
44
- @debug = !!options[:debug]
45
38
  end
46
39
 
47
40
  def build
@@ -50,11 +43,13 @@ module Opal
50
43
  end
51
44
 
52
45
  @dir = File.expand_path(@options[:dir] || Dir.getwd)
53
- @main = @options[:main]
54
46
 
55
47
  files = files_for @sources
56
48
  FileUtils.mkdir_p File.dirname(out)
57
49
 
50
+ @files = {}
51
+ @requires = {}
52
+
58
53
  build_to files, out
59
54
  end
60
55
 
@@ -70,36 +65,62 @@ module Opal
70
65
  end
71
66
  end
72
67
 
73
- files.map! { |f| File.expand_path f }
74
-
75
68
  files
76
69
  end
77
70
 
78
71
  def build_to(files, out)
79
- @parser = Parser.new :debug => @debug
72
+ @parser = Parser.new
80
73
 
81
74
  files.each { |file| build_file(file) }
82
75
 
83
76
  File.open(out, 'w+') do |o|
84
- files.each do |file|
85
- js = build_file file
86
- o.puts js
77
+ build_order(@requires).each do |f|
78
+ o.puts @files[f]
87
79
  end
80
+ end
81
+ end
82
+
83
+ # @param [Hash<Array<String>>] files hash of dependencies
84
+ def build_order(files)
85
+ all = files.keys
86
+ result = []
87
+ handled = {}
88
+
89
+ all.each { |r| _find_build_order r, files, handled, result }
90
+ result
91
+ end
88
92
 
89
- o.puts "Opal.require(#{@main.inspect});" if @main
93
+ def _find_build_order(file, files, handled, result)
94
+ if handled[file] or !files[file]
95
+ return
90
96
  end
97
+
98
+ handled[file] = true
99
+
100
+ files[file].each do |r|
101
+ _find_build_order r, files, handled, result
102
+ end
103
+
104
+ result << file
91
105
  end
92
106
 
93
107
  def build_file(file)
94
- lib_name = lib_name_for file
108
+ lib_name = lib_name_for file
109
+ parser_name = parser_name_for file
95
110
 
96
- code = if File.extname(file) == '.rb'
97
- @parser.parse File.read(file), file
111
+ if File.extname(file) == '.rb'
112
+ code = @parser.parse File.read(file), parser_name
113
+ @requires[lib_name] = @parser.requires
114
+ code = "(#{code})();"
98
115
  else
99
- File.read file
116
+ code = File.read file
100
117
  end
101
118
 
102
- "Opal.define(#{lib_name.inspect}, #{code});"
119
+ @files[lib_name] = "// file #{ parser_name }\n#{ code }"
120
+ end
121
+
122
+ def parser_name_for(file)
123
+ file.sub /^#{@dir}\//, ''
103
124
  end
104
125
 
105
126
  def lib_name_for(file)