opal 0.3.19 → 0.3.20

Sign up to get free protection for your applications and to get access to all the features.
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)