opal 0.3.37 → 0.3.38

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGELOG.md +11 -0
  2. data/Gemfile +0 -8
  3. data/README.md +7 -6
  4. data/bin/opal +18 -10
  5. data/config.ru +8 -21
  6. data/lib/assets/javascripts/opal-parser.js.erb +3 -60
  7. data/lib/assets/javascripts/opal/array.rb +87 -43
  8. data/lib/assets/javascripts/opal/basic_object.rb +4 -0
  9. data/lib/assets/javascripts/opal/boolean.rb +5 -3
  10. data/lib/assets/javascripts/opal/class.rb +19 -4
  11. data/lib/assets/javascripts/opal/comparable.rb +1 -1
  12. data/lib/assets/javascripts/opal/enumerable.rb +32 -0
  13. data/lib/assets/javascripts/opal/error.rb +1 -1
  14. data/lib/assets/javascripts/opal/hash.rb +166 -103
  15. data/lib/assets/javascripts/opal/json.rb +3 -2
  16. data/lib/assets/javascripts/opal/kernel.rb +9 -1
  17. data/lib/assets/javascripts/opal/native.rb +29 -0
  18. data/lib/assets/javascripts/opal/nil_class.rb +9 -1
  19. data/lib/assets/javascripts/opal/numeric.rb +6 -4
  20. data/lib/assets/javascripts/opal/parser.js +57 -0
  21. data/lib/assets/javascripts/opal/proc.rb +5 -5
  22. data/lib/assets/javascripts/opal/regexp.rb +1 -1
  23. data/lib/assets/javascripts/opal/runtime.js +13 -0
  24. data/lib/assets/javascripts/opal/string.rb +14 -3
  25. data/lib/assets/javascripts/opal/time.rb +1 -1
  26. data/lib/opal.rb +1 -1
  27. data/lib/opal/parser.rb +39 -13
  28. data/lib/opal/processor.rb +13 -2
  29. data/lib/opal/version.rb +1 -1
  30. data/opal.gemspec +3 -0
  31. data/spec/core/array/fill_spec.rb +26 -0
  32. data/spec/core/array/try_convert_spec.rb +15 -0
  33. data/spec/core/enumerable/each_slice_spec.rb +24 -0
  34. data/spec/core/enumerable/group_by_spec.rb +16 -0
  35. data/spec/core/module/const_get_spec.rb +34 -0
  36. data/spec/core/module/undef_method_spec.rb +67 -0
  37. data/spec/core/nil/to_h_spec.rb +10 -0
  38. data/spec/core/proc/element_reference_spec.rb +21 -0
  39. data/spec/{core → core_ext}/array/to_json_spec.rb +0 -0
  40. data/spec/core_ext/method_missing_spec.rb +43 -0
  41. data/spec/core_ext/native/fixtures/classes.rb +5 -0
  42. data/spec/core_ext/native/initialize_spec.rb +8 -0
  43. data/spec/core_ext/native/method_missing_spec.rb +7 -0
  44. data/spec/core_ext/native/to_native_spec.rb +7 -0
  45. data/spec/grammar/parser_spec.rb +1 -1
  46. data/spec/language/super_spec.rb +20 -0
  47. data/spec/language/variables_spec.rb +69 -0
  48. data/spec/spec_helper.rb +9 -5
  49. metadata +66 -8
  50. data/lib/assets/javascripts/opal/core.rb +0 -36
  51. data/spec/autorun.rb +0 -3
@@ -38,11 +38,12 @@ module JSON
38
38
  return arr;
39
39
  }
40
40
  else {
41
- var hash = #{ {} }, v, map = hash.map;
41
+ var hash = #{ {} }, v, map = hash.map, keys = hash.keys;
42
42
 
43
43
  for (var k in value) {
44
44
  if (__hasOwn.call(value, k)) {
45
45
  v = to_opal(value[k]);
46
+ keys.push(k);
46
47
  map[k] = v;
47
48
  }
48
49
  }
@@ -52,4 +53,4 @@ module JSON
52
53
  }
53
54
  };
54
55
  }
55
- end
56
+ end
@@ -8,6 +8,10 @@ module Kernel
8
8
  alias :instance_eval :instance_eval
9
9
  alias :instance_exec :instance_exec
10
10
 
11
+ def method_missing(symbol, *args, &block)
12
+ raise NoMethodError, "undefined method `#{symbol}' for #{inspect}"
13
+ end
14
+
11
15
  def =~(obj)
12
16
  false
13
17
  end
@@ -16,6 +20,10 @@ module Kernel
16
20
  `#{self} == other`
17
21
  end
18
22
 
23
+ def as_json
24
+ nil
25
+ end
26
+
19
27
  def method(name)
20
28
  %x{
21
29
  var recv = #{self},
@@ -64,7 +72,7 @@ module Kernel
64
72
  end
65
73
 
66
74
  def class
67
- `return #{self}._klass`
75
+ `#{self}._klass`
68
76
  end
69
77
 
70
78
  def define_singleton_method(name, &body)
@@ -0,0 +1,29 @@
1
+ module Native
2
+ def initialize(native)
3
+ %x{
4
+ if (#{native} == null) {
5
+ #{ raise "null or undefined passed to Native" };
6
+ }
7
+ }
8
+
9
+ @native = native
10
+ end
11
+
12
+ def method_missing(symbol, *args, &block)
13
+ native = @native
14
+
15
+ %x{
16
+ var func;
17
+
18
+ if (func = #{native}[#{symbol}]) {
19
+ return func.call(#{native});
20
+ }
21
+ }
22
+
23
+ nil
24
+ end
25
+
26
+ def to_native
27
+ @native
28
+ end
29
+ end
@@ -15,6 +15,10 @@ class NilClass
15
15
  `other === nil`
16
16
  end
17
17
 
18
+ def as_json
19
+ self
20
+ end
21
+
18
22
  def inspect
19
23
  'nil'
20
24
  end
@@ -31,6 +35,10 @@ class NilClass
31
35
  []
32
36
  end
33
37
 
38
+ def to_h
39
+ `__opal.hash()`
40
+ end
41
+
34
42
  def to_i
35
43
  0
36
44
  end
@@ -48,4 +56,4 @@ class NilClass
48
56
  def to_s
49
57
  ''
50
58
  end
51
- end
59
+ end
@@ -1,10 +1,8 @@
1
1
  class Numeric < `Number`
2
- %x{
3
- Numeric.prototype._isNumber = true;
4
- }
5
-
6
2
  include Comparable
7
3
 
4
+ `def._isNumber = true`
5
+
8
6
  def +(other)
9
7
  `#{self} + other`
10
8
  end
@@ -95,6 +93,10 @@ class Numeric < `Number`
95
93
  `Math.abs(#{self})`
96
94
  end
97
95
 
96
+ def as_json
97
+ self
98
+ end
99
+
98
100
  def ceil
99
101
  `Math.ceil(#{self})`
100
102
  end
@@ -0,0 +1,57 @@
1
+ (function() {
2
+ // quick exit if not insde browser
3
+ if (typeof(window) === 'undefined' || typeof(document) === 'undefined') {
4
+ return;
5
+ }
6
+
7
+ function findRubyScripts() {
8
+ var all = document.getElementsByTagName('script');
9
+ for (var i = 0, script; i < all.length; i++) {
10
+ script = all[i];
11
+ if (script.type === 'text/ruby') {
12
+ if (script.src) {
13
+ request(script.src, function(result) {
14
+ runRuby(result);
15
+ });
16
+ }
17
+ else {
18
+ runRuby(script.innerHTML);
19
+ }
20
+ }
21
+ else if (script.type === 'text/erb') {
22
+ runERB(script.innerHTML);
23
+ }
24
+ }
25
+ }
26
+
27
+ function runRuby(source) {
28
+ var js = Opal.Opal.Parser.$new().$parse(source);
29
+ eval('(' + js + ')()');
30
+ }
31
+
32
+ function request(url, callback) {
33
+ var xhr = new (window.ActiveXObject || XMLHttpRequest)('Microsoft.XMLHTTP');
34
+ xhr.open('GET', url, true);
35
+ if ('overrideMimeType' in xhr) {
36
+ xhr.overrideMimeType('text/plain');
37
+ }
38
+ xhr.onreadystatechange = function() {
39
+ if (xhr.readyState === 4) {
40
+ if (xhr.status === 0 || xhr.status === 200) {
41
+ callback(xhr.responseText);
42
+ }
43
+ else {
44
+ throw new Error('Could not load ruby at: ' + url);
45
+ }
46
+ }
47
+ };
48
+ xhr.send(null);
49
+ }
50
+
51
+ if (window.addEventListener) {
52
+ window.addEventListener('DOMContentLoaded', findRubyScripts, false);
53
+ }
54
+ else {
55
+ window.attachEvent('onload', findRubyScripts);
56
+ }
57
+ })();
@@ -1,8 +1,6 @@
1
1
  class Proc < `Function`
2
- %x{
3
- Proc.prototype._isProc = true;
4
- Proc.prototype.is_lambda = true;
5
- }
2
+ `def._isProc = true`
3
+ `def.is_lambda = true`
6
4
 
7
5
  def self.new(&block)
8
6
  `if (block === nil) no_block_given();`
@@ -14,6 +12,8 @@ class Proc < `Function`
14
12
  `#{self}.apply(null, #{args})`
15
13
  end
16
14
 
15
+ alias [] call
16
+
17
17
  def to_proc
18
18
  self
19
19
  end
@@ -25,7 +25,7 @@ class Proc < `Function`
25
25
  end
26
26
 
27
27
  def arity
28
- `this.length - 1`
28
+ `#{self}.length - 1`
29
29
  end
30
30
  end
31
31
 
@@ -69,4 +69,4 @@ class Regexp < `RegExp`
69
69
  end
70
70
 
71
71
  class MatchData
72
- end
72
+ end
@@ -142,6 +142,10 @@
142
142
  constructor._donate = __donate;
143
143
  constructor._sdonate = __sdonate;
144
144
 
145
+ constructor['$==='] = module_eqq;
146
+ constructor.$to_s = module_to_s;
147
+ constructor.toString = module_to_s;
148
+
145
149
  Opal[id] = constructor;
146
150
 
147
151
  return constructor;
@@ -169,6 +173,7 @@
169
173
 
170
174
  constructor['$==='] = module_eqq;
171
175
  constructor.$to_s = module_to_s;
176
+ constructor.toString = module_to_s;
172
177
 
173
178
  var smethods;
174
179
 
@@ -199,6 +204,7 @@
199
204
 
200
205
  constructor['$==='] = module_eqq;
201
206
  constructor.$to_s = module_to_s;
207
+ constructor.toString = module_to_s;
202
208
 
203
209
  var smethods = constructor._smethods = Class._methods.slice();
204
210
  for (var i = 0, length = smethods.length; i < length; i++) {
@@ -221,6 +227,13 @@
221
227
  };
222
228
 
223
229
  Opal.puts = function(a) { console.log(a); };
230
+
231
+ // Method missing dispatcher
232
+ Opal.mm = function(mid) {
233
+ return function() {
234
+ return this.$method_missing.apply(this, [mid].concat(__slice.call(arguments)));
235
+ }
236
+ };
224
237
 
225
238
  // Initialization
226
239
  // --------------
@@ -1,8 +1,8 @@
1
1
  class String < `String`
2
- `String.prototype._isString = true`
3
-
4
2
  include Comparable
5
3
 
4
+ `def._isString = true`
5
+
6
6
  def self.try_convert(what)
7
7
  what.to_str
8
8
  rescue
@@ -131,6 +131,10 @@ class String < `String`
131
131
  }
132
132
  end
133
133
 
134
+ def as_json
135
+ self
136
+ end
137
+
134
138
  def capitalize
135
139
  `#{self}.charAt(0).toUpperCase() + #{self}.substr(1).toLowerCase()`
136
140
  end
@@ -179,7 +183,14 @@ class String < `String`
179
183
  def count(str)
180
184
  `(#{self}.length - #{self}.replace(new RegExp(str,"g"), '').length) / str.length`
181
185
  end
182
-
186
+
187
+ def dasherize
188
+ `#{self}.replace(/[-\\s]+/g, '-')
189
+ .replace(/([A-Z\\d]+)([A-Z][a-z])/g, '$1-$2')
190
+ .replace(/([a-z\\d])([A-Z])/g, '$1-$2')
191
+ .toLowerCase()`
192
+ end
193
+
183
194
  def demodulize
184
195
  %x{
185
196
  var idx = #{self}.lastIndexOf('::');
@@ -107,4 +107,4 @@ class Time < `Date`
107
107
  end
108
108
 
109
109
  alias_native :year, :getFullYear
110
- end
110
+ end
data/lib/opal.rb CHANGED
@@ -16,7 +16,7 @@ module Opal
16
16
  # @param [String] file the filename to use when parsing
17
17
  # @return [String] the resulting javascript code
18
18
  def self.parse(str, file='(file)')
19
- Parser.new.parse str, file
19
+ Parser.new.parse str, :file => file
20
20
  end
21
21
 
22
22
  def self.core_dir
data/lib/opal/parser.rb CHANGED
@@ -83,10 +83,9 @@ module Opal
83
83
  # @param [String] source the ruby code to parse
84
84
  # @param [String] file the filename representing this code
85
85
  # @return [String] string of javascript code
86
- def parse(source, file = '(file)')
86
+ def parse(source, options = {})
87
87
  @grammar = Grammar.new
88
88
  @requires = []
89
- @file = file
90
89
  @line = 1
91
90
  @indent = ''
92
91
  @unique = 0
@@ -96,7 +95,12 @@ module Opal
96
95
  :slice => true
97
96
  }
98
97
 
99
- top @grammar.parse(source, file)
98
+ # options
99
+ @file = options[:file] || '(file)'
100
+ @method_missing = (options[:method_missing] != false)
101
+ @optimized_operators = (options[:optimized_operators] != false)
102
+
103
+ top @grammar.parse(source, @file)
100
104
  end
101
105
 
102
106
  # This is called when a parsing/processing error occurs. This
@@ -179,17 +183,17 @@ module Opal
179
183
  code = @indent + process(s(:scope, sexp), :stmt)
180
184
  }
181
185
 
182
- @scope.add_temp "__opal = Opal"
183
186
  @scope.add_temp "self = __opal.top"
184
187
  @scope.add_temp "__scope = __opal"
185
188
  @scope.add_temp "nil = __opal.nil"
189
+ @scope.add_temp "$mm = __opal.mm"
186
190
  @scope.add_temp "def = #{current_self}._klass.prototype" if @scope.defines_defn
187
191
  @helpers.keys.each { |h| @scope.add_temp "__#{h} = __opal.#{h}" }
188
192
 
189
193
  code = INDENT + @scope.to_vars + "\n" + code
190
194
  end
191
195
 
192
- "(function() {\n#{ code }\n})();\n"
196
+ "(function(__opal) {\n#{ code }\n})(Opal);\n"
193
197
  end
194
198
 
195
199
  # Every time the parser enters a new scope, this is called with
@@ -497,7 +501,7 @@ module Opal
497
501
  meth, recv, arg = sexp
498
502
  mid = mid_to_jsid meth.to_s
499
503
 
500
- if @parser_uses_optimized_operators
504
+ if @optimized_operators
501
505
  with_temp do |a|
502
506
  with_temp do |b|
503
507
  l = process recv, :expr
@@ -507,9 +511,9 @@ module Opal
507
511
  [a, l, b, r, a, a, meth.to_s, b, a, mid, b]
508
512
  end
509
513
  end
514
+ else
515
+ "#{process recv, :recv}#{mid}(#{process arg, :expr})"
510
516
  end
511
-
512
- "#{process recv, :recv}#{mid}(#{process arg, :expr})"
513
517
  end
514
518
 
515
519
  def js_block_given(sexp, level)
@@ -807,16 +811,38 @@ module Opal
807
811
  tmprecv = @scope.new_temp
808
812
  elsif splat and recv != [:self] and recv[0] != :lvar
809
813
  tmprecv = @scope.new_temp
814
+ else # method_missing
815
+ tmprecv = @scope.new_temp
810
816
  end
811
817
 
812
818
  args = ""
813
819
 
814
820
  recv_code = process recv, :recv
815
821
 
816
- args = process arglist, :expr
822
+ if @method_missing
823
+ call_recv = s(:js_tmp, tmprecv || recv_code)
824
+ arglist.insert 1, call_recv unless splat
825
+ args = process arglist, :expr
826
+
827
+ dispatch = if tmprecv
828
+ "((#{tmprecv} = #{recv_code})#{mid} || $mm('#{ meth.to_s }'))"
829
+ else
830
+ "(#{recv_code}#{mid} || $mm('#{ meth.to_s }'))"
831
+ end
832
+
833
+ result = if splat
834
+ "#{dispatch}.apply(#{process call_recv, :expr}, #{args})"
835
+ else
836
+ "#{dispatch}.call(#{args})"
837
+ end
838
+ else
839
+ args = process arglist, :expr
840
+ dispatch = tmprecv ? "(#{tmprecv} = #{recv_code})#{mid}" : "#{recv_code}#{mid}"
841
+ result = splat ? "#{dispatch}.apply(#{tmprecv || recv_code}, #{args})" : "#{dispatch}(#{args})"
842
+ end
817
843
 
818
- dispatch = tmprecv ? "(#{tmprecv} = #{recv_code})#{mid}" : "#{recv_code}#{mid}"
819
- splat ? "#{dispatch}.apply(#{tmprecv || recv_code}, #{args})" : "#{dispatch}(#{args})"
844
+ @scope.queue_temp tmprecv if tmprecv
845
+ result
820
846
  end
821
847
 
822
848
  # s(:arglist, [arg [, arg ..]])
@@ -1256,7 +1282,7 @@ module Opal
1256
1282
  map = hash_keys.map { |k| "#{k}: #{hash_obj[k]}"}
1257
1283
 
1258
1284
  @helpers[:hash2] = true
1259
- "__hash2({#{map.join(', ')}})"
1285
+ "__hash2([#{hash_keys.join ', '}], {#{map.join(', ')}})"
1260
1286
  else
1261
1287
  @helpers[:hash] = true
1262
1288
  "__hash(#{sexp.map { |p| process p, :expr }.join ', '})"
@@ -1808,7 +1834,7 @@ module Opal
1808
1834
  #
1809
1835
  # s(:zsuper)
1810
1836
  def process_zsuper(exp, level)
1811
- js_super "[self].concat(__slice.call(arguments))"
1837
+ js_super "__slice.call(arguments)"
1812
1838
  end
1813
1839
 
1814
1840
  def js_super args