ruby2js 3.5.3 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -665
  3. data/lib/ruby2js.rb +60 -15
  4. data/lib/ruby2js/converter.rb +39 -3
  5. data/lib/ruby2js/converter/args.rb +6 -1
  6. data/lib/ruby2js/converter/assign.rb +159 -0
  7. data/lib/ruby2js/converter/begin.rb +7 -2
  8. data/lib/ruby2js/converter/case.rb +7 -2
  9. data/lib/ruby2js/converter/class.rb +80 -21
  10. data/lib/ruby2js/converter/class2.rb +107 -33
  11. data/lib/ruby2js/converter/def.rb +7 -3
  12. data/lib/ruby2js/converter/dstr.rb +8 -3
  13. data/lib/ruby2js/converter/for.rb +1 -1
  14. data/lib/ruby2js/converter/hash.rb +28 -6
  15. data/lib/ruby2js/converter/hide.rb +13 -0
  16. data/lib/ruby2js/converter/if.rb +10 -2
  17. data/lib/ruby2js/converter/import.rb +19 -4
  18. data/lib/ruby2js/converter/kwbegin.rb +9 -2
  19. data/lib/ruby2js/converter/literal.rb +14 -2
  20. data/lib/ruby2js/converter/logical.rb +1 -1
  21. data/lib/ruby2js/converter/module.rb +41 -4
  22. data/lib/ruby2js/converter/next.rb +10 -2
  23. data/lib/ruby2js/converter/opasgn.rb +8 -0
  24. data/lib/ruby2js/converter/redo.rb +14 -0
  25. data/lib/ruby2js/converter/return.rb +2 -1
  26. data/lib/ruby2js/converter/send.rb +73 -8
  27. data/lib/ruby2js/converter/vasgn.rb +5 -0
  28. data/lib/ruby2js/converter/while.rb +1 -1
  29. data/lib/ruby2js/converter/whilepost.rb +1 -1
  30. data/lib/ruby2js/converter/xstr.rb +2 -3
  31. data/lib/ruby2js/demo.rb +53 -0
  32. data/lib/ruby2js/es2022.rb +5 -0
  33. data/lib/ruby2js/es2022/strict.rb +3 -0
  34. data/lib/ruby2js/filter.rb +9 -1
  35. data/lib/ruby2js/filter/active_functions.rb +44 -0
  36. data/lib/ruby2js/filter/camelCase.rb +6 -3
  37. data/lib/ruby2js/filter/cjs.rb +2 -0
  38. data/lib/ruby2js/filter/esm.rb +118 -26
  39. data/lib/ruby2js/filter/functions.rb +137 -109
  40. data/lib/ruby2js/filter/{wunderbar.rb → jsx.rb} +29 -7
  41. data/lib/ruby2js/filter/node.rb +58 -14
  42. data/lib/ruby2js/filter/nokogiri.rb +12 -12
  43. data/lib/ruby2js/filter/react.rb +182 -57
  44. data/lib/ruby2js/filter/require.rb +102 -11
  45. data/lib/ruby2js/filter/return.rb +13 -1
  46. data/lib/ruby2js/filter/stimulus.rb +187 -0
  47. data/lib/ruby2js/jsx.rb +309 -0
  48. data/lib/ruby2js/namespace.rb +75 -0
  49. data/lib/ruby2js/serializer.rb +19 -12
  50. data/lib/ruby2js/sprockets.rb +40 -0
  51. data/lib/ruby2js/version.rb +3 -3
  52. data/ruby2js.gemspec +2 -2
  53. metadata +23 -13
  54. data/lib/ruby2js/filter/esm_migration.rb +0 -72
  55. data/lib/ruby2js/rails.rb +0 -63
@@ -9,6 +9,11 @@ module Ruby2JS
9
9
  state = @state
10
10
  props = false
11
11
 
12
+ if state == :expression and statements.empty?
13
+ puts 'null'
14
+ return
15
+ end
16
+
12
17
  statements.map! do |statement|
13
18
  case statement and statement.type
14
19
  when :defs, :defp
@@ -32,9 +37,9 @@ module Ruby2JS
32
37
  end
33
38
 
34
39
  def combine_properties(body)
35
- for i in 0...body.length-1
40
+ (0...body.length-1).each do |i|
36
41
  next unless body[i] and body[i].type == :prop
37
- for j in i+1...body.length
42
+ (i+1...body.length).each do |j|
38
43
  break unless body[j] and body[j].type == :prop
39
44
 
40
45
  if body[i].children[0] == body[j].children[0]
@@ -10,10 +10,15 @@ module Ruby2JS
10
10
 
11
11
  handle :case do |expr, *whens, other|
12
12
  begin
13
+ if @state == :expression
14
+ parse s(:kwbegin, @ast), @state
15
+ return
16
+ end
17
+
13
18
  inner, @inner = @inner, @ast
14
19
 
15
20
  has_range = whens.any? do |node|
16
- node.children.any? {|child| [:irange, :erange].include? child.type}
21
+ node.children.any? {|child| [:irange, :erange].include? child&.type}
17
22
  end
18
23
 
19
24
  if has_range
@@ -47,7 +52,7 @@ module Ruby2JS
47
52
 
48
53
  parse code, :statement
49
54
  last = code
50
- while last.type == :begin
55
+ while last&.type == :begin
51
56
  last = last.children.last
52
57
  end
53
58
 
@@ -11,13 +11,27 @@ module Ruby2JS
11
11
  # NOTE: class_extend is not generated by the parser, but instead produced
12
12
  # when ++class is encountered; it signals that this construct is
13
13
  # meant to extend an already existing JavaScrpt class.
14
+ #
15
+ # class_hash is an anonymous class as a value in a hash; the
16
+ # name has already been output so should be ignored other than
17
+ # in determining the namespace.
18
+ #
19
+ # class_module is a module that to be re-processed by this handler
20
+ # given the similarity between the two structures.
14
21
 
15
- handle :class, :class_extend, :class_module do |name, inheritance, *body|
16
- if @ast.type != :class
22
+ handle :class, :class_hash, :class_extend, :class_module do |name, inheritance, *body|
23
+ extend = @namespace.enter(name) unless @ast.type == :class_module
24
+
25
+ if !%i(class class_hash).include?(@ast.type) or extend
17
26
  init = nil
18
27
  else
19
- if es2015
20
- parse @ast.updated(:class2)
28
+ if es2015 and not extend
29
+ if @ast.type == :class_hash
30
+ parse @ast.updated(:class2, [nil, *@ast.children[1..-1]])
31
+ else
32
+ parse @ast.updated(:class2)
33
+ end
34
+ @namespace.leave unless @ast.type == :class_module
21
35
  return
22
36
  end
23
37
 
@@ -35,7 +49,7 @@ module Ruby2JS
35
49
  end
36
50
 
37
51
  body.compact!
38
- visible = {}
52
+ visible = @namespace.getOwnProps
39
53
  body.map! do |m|
40
54
  if \
41
55
  @ast.type == :class_module and m.type == :defs and
@@ -45,7 +59,7 @@ module Ruby2JS
45
59
  end
46
60
 
47
61
  node = if m.type == :def
48
- if m.children.first == :initialize
62
+ if m.children.first == :initialize and !visible[:initialize]
49
63
  # constructor: remove from body and overwrite init function
50
64
  init = m
51
65
  nil
@@ -54,17 +68,20 @@ module Ruby2JS
54
68
  sym = :"#{m.children.first.to_s[0..-2]}"
55
69
  s(:prop, s(:attr, name, :prototype), sym =>
56
70
  {enumerable: s(:true), configurable: s(:true),
57
- set: s(:block, s(:send, nil, :proc), *m.children[1..-1])})
71
+ set: s(:defm, nil, *m.children[1..-1])})
58
72
  else
59
- visible[m.children[0]] = s(:self)
60
73
 
61
74
  if not m.is_method?
75
+ visible[m.children[0]] = s(:self)
76
+
62
77
  # property getter
63
78
  s(:prop, s(:attr, name, :prototype), m.children.first =>
64
79
  {enumerable: s(:true), configurable: s(:true),
65
- get: s(:block, s(:send, nil, :proc), m.children[1],
80
+ get: s(:defm, nil, m.children[1],
66
81
  m.updated(:autoreturn, m.children[2..-1]))})
67
82
  else
83
+ visible[m.children[0]] = s(:autobind, s(:self))
84
+
68
85
  # method: add to prototype
69
86
  s(:method, s(:attr, name, :prototype),
70
87
  :"#{m.children[0].to_s.chomp('!')}=",
@@ -90,13 +107,14 @@ module Ruby2JS
90
107
  else
91
108
  # class method definition: add to prototype
92
109
  s(:prototype, s(:send, name, "#{m.children[1]}=",
93
- s(:block, s(:send, nil, :proc), *m.children[2..-1])))
110
+ s(:defm, nil, *m.children[2..-1])))
94
111
  end
95
112
 
96
113
  elsif m.type == :send and m.children.first == nil
97
114
  if m.children[1] == :attr_accessor
98
115
  m.children[2..-1].map do |child_sym|
99
116
  var = child_sym.children.first
117
+ visible[var] = s(:self)
100
118
  s(:prop, s(:attr, name, :prototype), var =>
101
119
  {enumerable: s(:true), configurable: s(:true),
102
120
  get: s(:block, s(:send, nil, :proc), s(:args),
@@ -107,6 +125,7 @@ module Ruby2JS
107
125
  elsif m.children[1] == :attr_reader
108
126
  m.children[2..-1].map do |child_sym|
109
127
  var = child_sym.children.first
128
+ visible[var] = s(:self)
110
129
  s(:prop, s(:attr, name, :prototype), var =>
111
130
  {get: s(:block, s(:send, nil, :proc), s(:args),
112
131
  s(:return, s(:ivar, :"@#{var}"))),
@@ -116,6 +135,7 @@ module Ruby2JS
116
135
  elsif m.children[1] == :attr_writer
117
136
  m.children[2..-1].map do |child_sym|
118
137
  var = child_sym.children.first
138
+ visible[var] = s(:self)
119
139
  s(:prop, s(:attr, name, :prototype), var =>
120
140
  {set: s(:block, s(:send, nil, :proc), s(:args, s(:arg, var)),
121
141
  s(:ivasgn, :"@#{var}", s(:lvar, var))),
@@ -125,9 +145,10 @@ module Ruby2JS
125
145
  elsif m.children[1] == :include
126
146
  s(:send, s(:block, s(:send, nil, :lambda), s(:args),
127
147
  s(:begin, *m.children[2..-1].map {|modname|
128
- s(:for, s(:lvasgn, :$_), modname,
129
- s(:send, s(:attr, name, :prototype), :[]=,
130
- s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
148
+ @namespace.defineProps @namespace.find(modname)
149
+ s(:for, s(:lvasgn, :$_), modname,
150
+ s(:send, s(:attr, name, :prototype), :[]=,
151
+ s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
131
152
  })), :[])
132
153
  elsif [:private, :protected, :public].include? m.children[1]
133
154
  raise Error.new("class #{m.children[1]} is not supported", @ast)
@@ -160,17 +181,23 @@ module Ruby2JS
160
181
  s(:send, s(:attr, name, :prototype),
161
182
  "#{m.children[0].children.first}=",
162
183
  s(:attr, s(:attr, name, :prototype), m.children[1].children.first))
163
- elsif m.type == :class
184
+ elsif m.type == :class or m.type == :module
164
185
  innerclass_name = m.children.first
165
186
  if innerclass_name.children.first
166
187
  innerclass_name = innerclass_name.updated(nil,
167
- [s(:attr, innerclass_name.children[0], name),
188
+ [s(:attr, name, innerclass_name.children[0].children.last),
168
189
  innerclass_name.children[1]])
169
190
  else
170
191
  innerclass_name = innerclass_name.updated(nil,
171
192
  [name, innerclass_name.children[1]])
172
193
  end
173
194
  m.updated(nil, [innerclass_name, *m.children[1..-1]])
195
+ elsif @ast.type == :class_module
196
+ m
197
+ elsif m.type == :defineProps
198
+ @namespace.defineProps m.children.first
199
+ visible.merge! m.children.first
200
+ nil
174
201
  else
175
202
  raise Error.new("class #{ m.type } not supported", @ast)
176
203
  end
@@ -194,7 +221,7 @@ module Ruby2JS
194
221
  # merge property definitions
195
222
  combine_properties(body)
196
223
 
197
- if inheritance
224
+ if inheritance and (@ast.type != :class_extend and !extend)
198
225
  body.unshift s(:send, name, :prototype=,
199
226
  s(:send, s(:const, nil, :Object), :create,
200
227
  s(:attr, inheritance, :prototype))),
@@ -206,10 +233,14 @@ module Ruby2JS
206
233
  methods = 0
207
234
  start = 0
208
235
  body.each do |node|
209
- if [:method, :prop].include? node.type and
236
+ if (node.type == :method or (node.type == :prop and es2015)) and
210
237
  node.children[0].type == :attr and
211
238
  node.children[0].children[1] == :prototype
212
239
  methods += 1
240
+ elsif node.type == :class and @ast.type == :class_module and es2015
241
+ methods += 1 if node.children.first.children.first == name
242
+ elsif node.type == :module and @ast.type == :class_module
243
+ methods += 1 if node.children.first.children.first == name
213
244
  elsif methods == 0
214
245
  start += 1
215
246
  else
@@ -219,14 +250,22 @@ module Ruby2JS
219
250
 
220
251
  # collapse sequence to a single assignment
221
252
  if \
222
- @ast.type != :class_extend and
223
- (methods > 1 or (methods == 1 and body[start].type == :prop))
253
+ @ast.type == :class_module or methods > 1 or
254
+ body[start]&.type == :prop
224
255
  then
225
256
  pairs = body[start...start+methods].map do |node|
226
257
  if node.type == :method
227
258
  replacement = node.updated(:pair, [
228
259
  s(:str, node.children[1].to_s.chomp('=')),
229
260
  node.children[2]])
261
+ elsif node.type == :class and node.children.first.children.first == name
262
+ sym = node.children.first.children.last
263
+ replacement = s(:pair, s(:sym, sym),
264
+ s(:class_hash, s(:const, nil, sym), nil, node.children.last))
265
+ elsif node.type == :module and node.children.first.children.first == name
266
+ sym = node.children.first.children.last
267
+ replacement = s(:pair, s(:sym, sym),
268
+ s(:module_hash, s(:const, nil, sym), node.children.last))
230
269
  else
231
270
  replacement = node.children[1].map do |prop, descriptor|
232
271
  node.updated(:pair, [s(:prop, prop), descriptor])
@@ -244,12 +283,30 @@ module Ruby2JS
244
283
  end
245
284
 
246
285
  if @ast.type == :class_module
286
+ start = 0 if methods == 0
287
+ if name
288
+ body[start...start+methods] =
289
+ s(:casgn, *name.children, s(:hash, *pairs.flatten))
290
+ else
291
+ body[start...start+methods] = s(:hash, *pairs.flatten)
292
+ end
293
+ elsif @ast.type == :class_extend or extend
247
294
  body[start...start+methods] =
248
- s(:casgn, *name.children, s(:hash, *pairs.flatten))
295
+ s(:assign, body[start].children.first, s(:hash, *pairs.flatten))
249
296
  else
250
297
  body[start...start+methods] =
251
298
  s(:send, name, :prototype=, s(:hash, *pairs.flatten))
252
299
  end
300
+
301
+ elsif (@ast.type == :class_extend or extend) and methods > 1
302
+
303
+ pairs = body[start...start+methods].map do |node|
304
+ node.updated(:pair, [
305
+ s(:sym, node.children[1].to_s[0..-2]), node.children[2]])
306
+ end
307
+
308
+ body[start...start+methods] =
309
+ s(:assign, body[start].children.first, s(:hash, *pairs))
253
310
  end
254
311
  end
255
312
 
@@ -258,7 +315,7 @@ module Ruby2JS
258
315
  constructor = init.updated(:constructor, [name, *init.children[1..-1]])
259
316
  @comments[constructor] = @comments[init] unless @comments[init].empty?
260
317
 
261
- if @ast.type == :class_extend
318
+ if @ast.type == :class_extend or extend
262
319
  if es2015
263
320
  constructor = s(:masgn, s(:mlhs,
264
321
  s(:attr, s(:casgn, *name.children, constructor), :prototype)),
@@ -285,6 +342,7 @@ module Ruby2JS
285
342
 
286
343
  # add locally visible interfaces to rbstack. See send.rb, const.rb
287
344
  @rbstack.push visible
345
+ @rbstack.last.merge!(@namespace.find(inheritance)) if inheritance
288
346
 
289
347
  parse s(:begin, *body.compact), :statement
290
348
  ensure
@@ -292,6 +350,7 @@ module Ruby2JS
292
350
  @class_name = class_name
293
351
  @class_parent = class_parent
294
352
  @rbstack.pop
353
+ @namespace.leave unless @ast.type == :class_module
295
354
  end
296
355
  end
297
356
 
@@ -9,11 +9,24 @@ module Ruby2JS
9
9
  # NOTE: this is the es2015 version of class
10
10
 
11
11
  handle :class2 do |name, inheritance, *body|
12
- if name.type == :const and name.children.first == nil
12
+ body.compact!
13
+ while body.length == 1 and body.first.type == :begin
14
+ body = body.first.children
15
+ end
16
+
17
+ proxied = body.find do |node|
18
+ node.type == :def and node.children.first == :method_missing
19
+ end
20
+
21
+ if not name
22
+ put 'class'
23
+ elsif name.type == :const and name.children.first == nil
13
24
  put 'class '
14
25
  parse name
26
+ put '$' if proxied
15
27
  else
16
28
  parse name
29
+ put '$' if proxied
17
30
  put ' = class'
18
31
  end
19
32
 
@@ -24,15 +37,11 @@ module Ruby2JS
24
37
 
25
38
  put " {"
26
39
 
27
- body.compact!
28
- while body.length == 1 and body.first.type == :begin
29
- body = body.first.children
30
- end
31
-
32
40
  begin
33
41
  class_name, @class_name = @class_name, name
34
42
  class_parent, @class_parent = @class_parent, inheritance
35
- @rbstack.push({})
43
+ @rbstack.push(@namespace.getOwnProps)
44
+ @rbstack.last.merge!(@namespace.find(inheritance)) if inheritance
36
45
  constructor = []
37
46
  index = 0
38
47
 
@@ -40,10 +49,17 @@ module Ruby2JS
40
49
  body.each do |m|
41
50
  if m.type == :def
42
51
  prop = m.children.first
43
- if prop == :initialize
52
+ if prop == :initialize and !@rbstack.last[:initialize]
44
53
  constructor = m.children[2..-1]
45
- elsif not prop.to_s.end_with? '='
46
- @rbstack.last[prop] = s(:self)
54
+ elsif prop.to_s.end_with? '='
55
+ @rbstack.last[prop.to_s[0..-2].to_sym] = s(:autobind, s(:self))
56
+ else
57
+ @rbstack.last[prop] = m.is_method? ? s(:autobind, s(:self)) : s(:self)
58
+ end
59
+ elsif m.type == :send and m.children[0..1] == [nil, :async]
60
+ if m.children[2].type == :def
61
+ prop = m.children[2].children.first
62
+ @rbstack.last[prop] = s(:autobind, s(:self))
47
63
  end
48
64
  end
49
65
  end
@@ -64,21 +80,21 @@ module Ruby2JS
64
80
  walk[child] if child.is_a? Parser::AST::Node
65
81
  end
66
82
 
67
- if ast.type == :send and ast.children.first == nil
68
- if ast.children[1] == :attr_accessor
69
- ast.children[2..-1].each_with_index do |child_sym, index2|
70
- ivars << :"@#{child_sym.children.first}"
71
- end
72
- elsif ast.children[1] == :attr_reader
73
- ast.children[2..-1].each_with_index do |child_sym, index2|
74
- ivars << :"@#{child_sym.children.first}"
75
- end
76
- elsif ast.children[1] == :attr_writer
77
- ast.children[2..-1].each_with_index do |child_sym, index2|
78
- ivars << :"@#{child_sym.children.first}"
79
- end
80
- end
81
- end
83
+ if ast.type == :send and ast.children.first == nil
84
+ if ast.children[1] == :attr_accessor
85
+ ast.children[2..-1].each_with_index do |child_sym, index2|
86
+ ivars << :"@#{child_sym.children.first}"
87
+ end
88
+ elsif ast.children[1] == :attr_reader
89
+ ast.children[2..-1].each_with_index do |child_sym, index2|
90
+ ivars << :"@#{child_sym.children.first}"
91
+ end
92
+ elsif ast.children[1] == :attr_writer
93
+ ast.children[2..-1].each_with_index do |child_sym, index2|
94
+ ivars << :"@#{child_sym.children.first}"
95
+ end
96
+ end
97
+ end
82
98
 
83
99
  end
84
100
  walk[@ast]
@@ -140,10 +156,10 @@ module Ruby2JS
140
156
  end
141
157
  end
142
158
 
143
- if m.type == :def || m.type == :async
159
+ if m.type == :def || m.type == :defm || m.type == :async
144
160
  @prop = m.children.first
145
161
 
146
- if @prop == :initialize
162
+ if @prop == :initialize and !@rbstack.last[:initialize]
147
163
  @prop = :constructor
148
164
 
149
165
  if constructor == [] or constructor == [(:super)]
@@ -190,7 +206,7 @@ module Ruby2JS
190
206
  @prop = "static #{m.children[1]}"
191
207
  end
192
208
 
193
- @prop.sub! 'static', 'static async' if m.type == :asyncs
209
+ @prop = @prop.sub('static', 'static async') if m.type == :asyncs
194
210
 
195
211
  m = m.updated(:def, m.children[1..3])
196
212
  begin
@@ -208,6 +224,7 @@ module Ruby2JS
208
224
  m.children[2..-1].each_with_index do |child_sym, index2|
209
225
  put @sep unless index2 == 0
210
226
  var = child_sym.children.first
227
+ @rbstack.last[var] = s(:self)
211
228
  put "get #{var}() {#{@nl}return this.#{p}#{var}#@nl}#@sep"
212
229
  put "set #{var}(#{var}) {#{@nl}this.#{p}#{var} = #{var}#@nl}"
213
230
  end
@@ -215,12 +232,14 @@ module Ruby2JS
215
232
  m.children[2..-1].each_with_index do |child_sym, index2|
216
233
  put @sep unless index2 == 0
217
234
  var = child_sym.children.first
235
+ @rbstack.last[var] = s(:self)
218
236
  put "get #{var}() {#{@nl}return this.#{p}#{var}#@nl}"
219
237
  end
220
238
  elsif m.children[1] == :attr_writer
221
239
  m.children[2..-1].each_with_index do |child_sym, index2|
222
240
  put @sep unless index2 == 0
223
241
  var = child_sym.children.first
242
+ @rbstack.last[var] = s(:self)
224
243
  put "set #{var}(#{var}) {#{@nl}this.#{p}#{var} = #{var}#@nl}"
225
244
  end
226
245
  elsif [:private, :protected, :public].include? m.children[1]
@@ -228,13 +247,27 @@ module Ruby2JS
228
247
  else
229
248
  if m.children[1] == :include
230
249
  m = m.updated(:begin, m.children[2..-1].map {|mname|
231
- s(:send, s(:const, nil, :Object), :assign,
232
- s(:attr, name, :prototype), mname)})
250
+ @namespace.defineProps @namespace.find(mname)
251
+ s(:assign, s(:attr, name, :prototype), mname)
252
+ })
233
253
  end
234
254
 
235
255
  skipped = true
236
256
  end
237
257
 
258
+ elsif es2022 and \
259
+ m.type == :send and m.children.first.type == :self and \
260
+ m.children[1].to_s.end_with? '='
261
+
262
+ put 'static '
263
+ parse m.updated(:lvasgn, [m.children[1].to_s.sub('=', ''),
264
+ m.children[2]])
265
+
266
+ elsif m.type == :defineProps
267
+ skipped = true
268
+ @namespace.defineProps m.children.first
269
+ @rbstack.last.merge! m.children.first
270
+
238
271
  else
239
272
  if m.type == :cvasgn and !underscored_private
240
273
  put 'static #$'; put m.children[0].to_s[2..-1]; put ' = '
@@ -246,7 +279,7 @@ module Ruby2JS
246
279
  if m.type == :casgn and m.children[0] == nil
247
280
  @rbstack.last[m.children[1]] = name
248
281
 
249
- if es2020
282
+ if es2022
250
283
  put 'static '; put m.children[1].to_s; put ' = '
251
284
  parse m.children[2]
252
285
  skipped = false
@@ -257,7 +290,7 @@ module Ruby2JS
257
290
  end
258
291
 
259
292
  if skipped
260
- post << [m, comments] if skipped
293
+ post << [m, comments] unless m.type == :defineProps
261
294
  else
262
295
  comments.reverse.each {|comment| insert location, comment}
263
296
  end
@@ -294,15 +327,56 @@ module Ruby2JS
294
327
  else
295
328
  parse m.updated(:send, [@class_name, *m.children[1..-1]])
296
329
  end
330
+ elsif m.type == :block and m.children.first.children.first == nil
331
+ # class method calls passing a block
332
+ parse s(:block, s(:send, name, *m.children.first.children[1..-1]),
333
+ *m.children[1..-1])
297
334
  else
298
335
  parse m, :statement
299
336
  end
300
337
  end
301
338
 
339
+ if proxied
340
+ put @sep
341
+
342
+ rename = name.updated(nil, [name.children.first, name.children.last.to_s + '$'])
343
+
344
+ if proxied.children[1].children.length == 1
345
+ # special case: if method_missing only has on argument, call it
346
+ # directly (i.e., don't pass arguments). This enables
347
+ # method_missing to return instance attributes (getters) as well
348
+ # as bound functions (methods).
349
+ forward = s(:send, s(:lvar, :obj), :method_missing, s(:lvar, :prop))
350
+ else
351
+ # normal case: return a function which, when called, will call
352
+ # method_missing with method name and arguments.
353
+ forward = s(:block, s(:send, nil, :proc), s(:args, s(:restarg, :args)),
354
+ s(:send, s(:lvar, :obj), :method_missing, s(:lvar, :prop),
355
+ s(:splat, s(:lvar, :args))))
356
+ end
357
+
358
+ proxy = s(:return, s(:send, s(:const, nil, :Proxy), :new,
359
+ s(:send, rename, :new, s(:splat, s(:lvar, :args))),
360
+ s(:hash, s(:pair, s(:sym, :get), s(:block, s(:send, nil, :proc),
361
+ s(:args, s(:arg, :obj), s(:arg, :prop)),
362
+ s(:if, s(:in?, s(:lvar, :prop), s(:lvar, :obj)),
363
+ s(:return, s(:send, s(:lvar, :obj), :[], s(:lvar, :prop))),
364
+ s(:return, forward))))))
365
+ )
366
+
367
+ if name.children.first == nil
368
+ proxy = s(:def, name.children.last, s(:args, s(:restarg, :args)), proxy)
369
+ else
370
+ proxy = s(:defs, *name.children, s(:args, s(:restarg, :args)), proxy)
371
+ end
372
+
373
+ parse proxy
374
+ end
375
+
302
376
  ensure
303
377
  @class_name = class_name
304
378
  @class_parent = class_parent
305
- @rbstack.pop
379
+ @namespace.defineProps @rbstack.pop
306
380
  end
307
381
  end
308
382
  end