ruby2js 1.11.1 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,396 @@
1
+ # A filter to support usage of React. Overview of translations provided:
2
+ # * classes that inherit from React are converted to React.createClass
3
+ # calls.
4
+ # * Wunderbar style element definitions are converted to
5
+ # React.createElement calls.
6
+ #
7
+ # Related files:
8
+ # spec/react_spec.rb contains a specification
9
+ # demo/react-tutorial.rb contains a working sample
10
+ #
11
+ # Experimental conversions provided:
12
+ # * $x becomes this.refs.x
13
+ # * @x becomes this.state.x
14
+ # * @@x becomes this.props.x
15
+ # * ~x becomes this.refs.x.getDOMNode()
16
+ #
17
+ require 'ruby2js'
18
+
19
+ module Ruby2JS
20
+ module Filter
21
+ module React
22
+ include SEXP
23
+
24
+ # Example conversion
25
+ # before:
26
+ # (class (const nil :Foo) (const nil :React) nil)
27
+ # after:
28
+ # (casgn nil :foo, (send :React :createClass (hash (sym :displayName)
29
+ # (:str, "Foo"))))
30
+ def on_class(node)
31
+ cname, inheritance, *body = node.children
32
+ return super unless cname.children.first == nil
33
+ return super unless inheritance == s(:const, nil, :React)
34
+
35
+ # traverse down to actual list of class statements
36
+ if body.length == 1
37
+ if not body.first
38
+ body = []
39
+ elsif body.first.type == :begin
40
+ body = body.first.children
41
+ end
42
+ end
43
+
44
+ # abort conversion unless all body statements are method definitions
45
+ return super unless body.all? {|child| child.type == :def}
46
+
47
+ begin
48
+ react, @react = @react, true
49
+
50
+ # automatically capture the displayName for the class
51
+ pairs = [s(:pair, s(:sym, :displayName),
52
+ s(:str, cname.children.last.to_s))]
53
+
54
+ # add a proc/function for each method
55
+ body.each do |child|
56
+ mname, args, *block = child.children
57
+ pairs << s(:pair, s(:sym, mname), s(:block, s(:send, nil, :proc),
58
+ args, process(s((child.is_method? ? :begin : :autoreturn),
59
+ *block))))
60
+ end
61
+ ensure
62
+ @react = react
63
+ end
64
+
65
+ # emit a createClass statement
66
+ s(:casgn, nil, cname.children.last,
67
+ s(:send, inheritance, :createClass, s(:hash, *pairs)))
68
+ end
69
+
70
+ def on_send(node)
71
+ # enable React filtering within React class method calls
72
+ if node.children.first == s(:const, nil, :React)
73
+ begin
74
+ react, @react = @react, true
75
+ return super
76
+ ensure
77
+ @react = react
78
+ end
79
+ end
80
+
81
+ return super unless @react
82
+
83
+ if node.children[0] == nil and node.children[1] == :_
84
+ # text nodes
85
+ node.children[2]
86
+
87
+ elsif node.children[0] == nil and node.children[1] =~ /^_\w/
88
+ # map method calls starting with an underscore to React calls
89
+ # to create an element.
90
+ #
91
+ # input:
92
+ # _a 'name', href: 'link'
93
+ # output:
94
+ # React.createElement("a", {href: "link"}, "name")
95
+ #
96
+ tag = node.children[1].to_s[1..-1]
97
+ pairs = []
98
+ text = block = nil
99
+ node.children[2..-1].each do |child|
100
+ if child.type == :hash
101
+ # convert _ to - in attribute names
102
+ pairs += child.children.map do |pair|
103
+ key, value = pair.children
104
+ if key.type == :sym
105
+ s(:pair, s(:str, key.children[0].to_s.gsub('_', '-')), value)
106
+ else
107
+ pair
108
+ end
109
+ end
110
+
111
+ elsif child.type == :block
112
+ # :block arguments are inserted by on_block logic below
113
+ block = child
114
+
115
+ else
116
+ # everything else added as text
117
+ text = child
118
+ end
119
+ end
120
+
121
+ # extract all class names
122
+ classes = pairs.find_all do |pair|
123
+ key = pair.children.first.children.first
124
+ [:class, 'class', :className, 'className'].include? key
125
+ end
126
+
127
+ # combine all classes into a single value (or expression)
128
+ if classes.length > 0
129
+ expr = nil
130
+ values = classes.map do |pair|
131
+ if [:sym, :str].include? pair.children.last.type
132
+ pair.children.last.children.first.to_s
133
+ else
134
+ expr = pair.children.last
135
+ ''
136
+ end
137
+ end
138
+ pairs -= classes
139
+ value = s(:str, values.join(' '))
140
+ value = s(:send, value, :+, expr) if expr
141
+ pairs.unshift s(:pair, s(:sym, :className), value)
142
+ end
143
+
144
+ # search for the presence of a 'for' attribute
145
+ htmlFor = pairs.find_index do |pair|
146
+ ['for', :for].include? pair.children.first.children.first
147
+ end
148
+
149
+ # replace 'for' attribute (if any) with 'htmlFor'
150
+ if htmlFor
151
+ pairs[htmlFor] = s(:pair, s(:sym, :htmlFor),
152
+ pairs[htmlFor].children.last)
153
+ end
154
+
155
+ # construct hash (or nil) from pairs
156
+ hash = (pairs.length > 0 ? process(s(:hash, *pairs)) : s(:nil))
157
+
158
+ # based on case of tag name, build a HTML tag or React component
159
+ if tag =~ /^[A-Z]/
160
+ params = [s(:const, nil, tag), hash]
161
+ else
162
+ params = [s(:str, tag), hash]
163
+ end
164
+
165
+ # handle nested elements
166
+ if block
167
+
168
+ # traverse down to actual list of nested statements
169
+ args = block.children[2..-1]
170
+ if args.length == 1
171
+ if not args.first
172
+ args = []
173
+ elsif args.first.type == :begin
174
+ args = args.first.children
175
+ end
176
+ end
177
+
178
+ # check for normal case: only elements and text
179
+ simple = args.all? do |arg|
180
+ arg = arg.children.first if arg.type == :block
181
+ while arg.type == :send and arg.children.first != nil
182
+ arg = arg.children.first
183
+ end
184
+ arg.type == :send and arg.children[1] =~ /^_/
185
+ end
186
+
187
+ begin
188
+ if simple
189
+ # in the normal case, each process each argument
190
+ reactApply, @reactApply = @reactApply, false
191
+ params += args.map {|arg| process(arg)}
192
+ else
193
+ reactApply, @reactApply = @reactApply, true
194
+
195
+ # collect children and apply. Intermediate representation
196
+ # will look something like the following:
197
+ #
198
+ # React.createElement(*proc {
199
+ # $_ = ['tag', hash]
200
+ # $_.push(React.createElement(...))
201
+ # }())
202
+ #
203
+ # Base Ruby2JS processing will convert the 'splat' to 'apply'
204
+ params = [s(:splat, s(:send, s(:block, s(:send, nil, :proc),
205
+ s(:args), s(:begin, s(:lvasgn, :$_, s(:array, *params)),
206
+ *args.map {|arg| process arg},
207
+ s(:return, s(:lvar, :$_)))), :[]))]
208
+ end
209
+ ensure
210
+ @reactApply = reactApply
211
+ end
212
+
213
+ elsif text
214
+ # add text
215
+ params << process(text)
216
+ end
217
+
218
+ # construct element using params
219
+ element = s(:send, s(:const, nil, :React), :createElement, *params)
220
+
221
+ if @reactApply
222
+ # if apply is set, emit code that pushes result
223
+ s(:send, s(:gvar, :$_), :push, element)
224
+ else
225
+ # simple/normal case: simply return the element
226
+ element
227
+ end
228
+
229
+ # map method calls involving i/g/c vars to straight calls
230
+ #
231
+ # input:
232
+ # @x.(a,b,c)
233
+ # output:
234
+ # @x(a,b,c)
235
+ elsif node.children[1] == :call
236
+ if [:ivar, :gvar, :cvar].include? node.children.first.type
237
+ return process(s(:send, node.children.first, nil,
238
+ *node.children[2..-1]))
239
+ else
240
+ return super
241
+ end
242
+
243
+ elsif node.children[1] == :~
244
+ # map ~expression.method to $(expression).method
245
+
246
+ if node.children[0] and node.children[0].type == :op_asgn
247
+ asgn = node.children[0]
248
+ if asgn.children[0] and asgn.children[0].type == :send
249
+ inner = asgn.children[0]
250
+ return on_send s(:send, s(:send, inner.children[0],
251
+ (inner.children[1].to_s+'=').to_sym,
252
+ s(:send, s(:send, s(:send, inner.children[0], :~),
253
+ *inner.children[1..-1]), *asgn.children[1..-1])), :~)
254
+ else
255
+ return on_send asgn.updated nil, [s(:send, asgn.children[0], :~),
256
+ *asgn.children[1..-1]]
257
+ end
258
+ end
259
+
260
+ rewrite_tilda = proc do |node|
261
+ # Example conversion:
262
+ # before:
263
+ # (send (send nil :a) :text) :~)
264
+ # after:
265
+ # (send (send (gvar :$a), :getDOMNode)), :text)
266
+ if node.type == :send and node.children[0]
267
+ if node.children[1] == :~ and node.children[0].children[1] == :~
268
+ # consecutive tildes
269
+ if node.children[0].children[0].children[1] == :~
270
+ result = node.children[0].children[0].children[0]
271
+ else
272
+ result = s(:attr, node.children[0].children[0], :~)
273
+ end
274
+ s(:attr, s(:attr, process(result), :~), :~)
275
+ else
276
+ # possible getter/setter
277
+ method = node.children[1]
278
+ method = method.to_s.chomp('=') if method =~ /=$/
279
+ rewrite = [rewrite_tilda[node.children[0]],
280
+ method, *node.children[2..-1]]
281
+ rewrite[1] = node.children[1]
282
+ node.updated nil, rewrite
283
+ end
284
+ elsif node.children.first == nil and Symbol === node.children[1]
285
+ # innermost expression is a scalar
286
+ s(:send, s(:gvar, "$#{node.children[1]}"), :getDOMNode)
287
+ elsif node.type == :lvar
288
+ s(:send, s(:gvar, "$#{node.children[0]}"), :getDOMNode)
289
+ else
290
+ # leave alone
291
+ node
292
+ end
293
+ end
294
+
295
+ return process rewrite_tilda[node].children[0]
296
+
297
+ elsif node.children[0] and node.children[0].type == :send
298
+ # determine if markaby style class and id names are being used
299
+ child = node
300
+ test = child.children.first
301
+ while test and test.type == :send and not test.is_method?
302
+ child, test = test, test.children.first
303
+ end
304
+
305
+ if child.children[0] == nil and child.children[1] =~ /^_\w/
306
+ # capture the arguments provided on the current node
307
+ children = node.children[2..-1]
308
+
309
+ # convert method calls to id and class values
310
+ while node != child
311
+ if node.children[1] !~ /!$/
312
+ # convert method name to hash {className: name} pair
313
+ pair = s(:pair, s(:sym, :className),
314
+ s(:str, node.children[1].to_s.gsub('_','-')))
315
+ else
316
+ # convert method name to hash {id: name} pair
317
+ pair = s(:pair, s(:sym, :id),
318
+ s(:str, node.children[1].to_s[0..-2].gsub('_','-')))
319
+ end
320
+
321
+ # if a hash argument is already passed, merge in id value
322
+ hash = children.find_index {|node| node.type == :hash}
323
+ if hash
324
+ children[hash] = s(:hash, pair, *children[hash].children)
325
+ else
326
+ children.unshift s(:hash, pair)
327
+ end
328
+
329
+ # advance to next node
330
+ node = node.children.first
331
+ end
332
+
333
+ # collapse series of method calls into a single call
334
+ return process(s(:send, *node.children[0..1], *children))
335
+ else
336
+ super
337
+ end
338
+
339
+ else
340
+ super
341
+ end
342
+ end
343
+
344
+ # convert blocks to proc arguments
345
+ def on_block(node)
346
+ return super unless @react
347
+ return super unless node.children[1].children.empty?
348
+
349
+ # traverse through potential "css proxy" style method calls
350
+ child = node.children.first
351
+ test = child.children.first
352
+ while test and test.type == :send and not test.is_method?
353
+ child, test = test, test.children.first
354
+ end
355
+
356
+ # append block as a standalone proc to wunderbar style method call
357
+ if child.children[0] == nil and child.children[1] =~ /^_\w/
358
+ block = s(:block, s(:send, nil, :proc),
359
+ s(:args, s(:arg, :_index), s(:arg, :_parent)),
360
+ *node.children[2..-1])
361
+ return on_send s(:send, *node.children.first.children, block)
362
+ end
363
+
364
+ super
365
+ end
366
+
367
+ # convert global variables to refs
368
+ def on_gvar(node)
369
+ return super unless @react
370
+ s(:attr, s(:attr, s(:self), :refs), node.children.first.to_s[1..-1])
371
+ end
372
+
373
+ # convert instance variables to state
374
+ def on_ivar(node)
375
+ return super unless @react
376
+ s(:attr, s(:attr, s(:self), :state), node.children.first.to_s[1..-1])
377
+ end
378
+
379
+ # convert instance variable assignments to setState calls
380
+ def on_ivasgn(node)
381
+ return super unless @react
382
+ s(:send, s(:self), :setState, s(:hash, s(:pair,
383
+ s(:str, node.children.first.to_s[1..-1]),
384
+ process(node.children.last))))
385
+ end
386
+
387
+ # convert class variables to props
388
+ def on_cvar(node)
389
+ return super unless @react
390
+ s(:attr, s(:attr, s(:self), :props), node.children.first.to_s[2..-1])
391
+ end
392
+ end
393
+
394
+ DEFAULTS.push React
395
+ end
396
+ end
@@ -1,8 +1,8 @@
1
1
  module Ruby2JS
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 11
5
- TINY = 1
4
+ MINOR = 12
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/ruby2js.gemspec CHANGED
@@ -2,17 +2,18 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "ruby2js"
5
- s.version = "1.11.1"
5
+ s.version = "1.12.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Sam Ruby"]
9
- s.date = "2015-01-02"
9
+ s.date = "2015-01-23"
10
10
  s.description = " The base package maps Ruby syntax to JavaScript semantics.\n Filters may be provided to add Ruby-specific or framework specific\n behavior.\n"
11
11
  s.email = "rubys@intertwingly.net"
12
- s.files = ["ruby2js.gemspec", "README.md", "lib/ruby2js", "lib/ruby2js/converter.rb", "lib/ruby2js/version.rb", "lib/ruby2js/filter", "lib/ruby2js/filter/jquery.rb", "lib/ruby2js/filter/return.rb", "lib/ruby2js/filter/underscore.rb", "lib/ruby2js/filter/angularrb.rb", "lib/ruby2js/filter/minitest-jasmine.rb", "lib/ruby2js/filter/camelCase.rb", "lib/ruby2js/filter/strict.rb", "lib/ruby2js/filter/functions.rb", "lib/ruby2js/filter/angular-resource.rb", "lib/ruby2js/filter/angular-route.rb", "lib/ruby2js/sinatra.rb", "lib/ruby2js/rails.rb", "lib/ruby2js/cgi.rb", "lib/ruby2js/converter", "lib/ruby2js/converter/self.rb", "lib/ruby2js/converter/arg.rb", "lib/ruby2js/converter/dstr.rb", "lib/ruby2js/converter/xstr.rb", "lib/ruby2js/converter/sym.rb", "lib/ruby2js/converter/return.rb", "lib/ruby2js/converter/break.rb", "lib/ruby2js/converter/for.rb", "lib/ruby2js/converter/defined.rb", "lib/ruby2js/converter/cvasgn.rb", "lib/ruby2js/converter/whilepost.rb", "lib/ruby2js/converter/logical.rb", "lib/ruby2js/converter/args.rb", "lib/ruby2js/converter/def.rb", "lib/ruby2js/converter/prototype.rb", "lib/ruby2js/converter/class.rb", "lib/ruby2js/converter/nthref.rb", "lib/ruby2js/converter/opasgn.rb", "lib/ruby2js/converter/module.rb", "lib/ruby2js/converter/kwbegin.rb", "lib/ruby2js/converter/send.rb", "lib/ruby2js/converter/boolean.rb", "lib/ruby2js/converter/masgn.rb", "lib/ruby2js/converter/hash.rb", "lib/ruby2js/converter/cvar.rb", "lib/ruby2js/converter/ivasgn.rb", "lib/ruby2js/converter/case.rb", "lib/ruby2js/converter/const.rb", "lib/ruby2js/converter/vasgn.rb", "lib/ruby2js/converter/untilpost.rb", "lib/ruby2js/converter/begin.rb", "lib/ruby2js/converter/ivar.rb", "lib/ruby2js/converter/while.rb", "lib/ruby2js/converter/next.rb", "lib/ruby2js/converter/nil.rb", "lib/ruby2js/converter/blockpass.rb", "lib/ruby2js/converter/super.rb", "lib/ruby2js/converter/defs.rb", "lib/ruby2js/converter/array.rb", "lib/ruby2js/converter/block.rb", "lib/ruby2js/converter/if.rb", "lib/ruby2js/converter/until.rb", "lib/ruby2js/converter/regexp.rb", "lib/ruby2js/converter/casgn.rb", "lib/ruby2js/converter/undef.rb", "lib/ruby2js/converter/literal.rb", "lib/ruby2js/converter/var.rb", "lib/ruby2js.rb"]
12
+ s.files = ["ruby2js.gemspec", "README.md", "lib/ruby2js", "lib/ruby2js/converter.rb", "lib/ruby2js/version.rb", "lib/ruby2js/filter", "lib/ruby2js/filter/jquery.rb", "lib/ruby2js/filter/return.rb", "lib/ruby2js/filter/underscore.rb", "lib/ruby2js/filter/angularrb.rb", "lib/ruby2js/filter/minitest-jasmine.rb", "lib/ruby2js/filter/camelCase.rb", "lib/ruby2js/filter/strict.rb", "lib/ruby2js/filter/functions.rb", "lib/ruby2js/filter/angular-resource.rb", "lib/ruby2js/filter/react.rb", "lib/ruby2js/filter/angular-route.rb", "lib/ruby2js/sinatra.rb", "lib/ruby2js/rails.rb", "lib/ruby2js/cgi.rb", "lib/ruby2js/converter", "lib/ruby2js/converter/self.rb", "lib/ruby2js/converter/arg.rb", "lib/ruby2js/converter/dstr.rb", "lib/ruby2js/converter/xstr.rb", "lib/ruby2js/converter/sym.rb", "lib/ruby2js/converter/return.rb", "lib/ruby2js/converter/break.rb", "lib/ruby2js/converter/for.rb", "lib/ruby2js/converter/defined.rb", "lib/ruby2js/converter/cvasgn.rb", "lib/ruby2js/converter/whilepost.rb", "lib/ruby2js/converter/logical.rb", "lib/ruby2js/converter/args.rb", "lib/ruby2js/converter/def.rb", "lib/ruby2js/converter/prototype.rb", "lib/ruby2js/converter/class.rb", "lib/ruby2js/converter/nthref.rb", "lib/ruby2js/converter/opasgn.rb", "lib/ruby2js/converter/module.rb", "lib/ruby2js/converter/kwbegin.rb", "lib/ruby2js/converter/send.rb", "lib/ruby2js/converter/boolean.rb", "lib/ruby2js/converter/masgn.rb", "lib/ruby2js/converter/hash.rb", "lib/ruby2js/converter/cvar.rb", "lib/ruby2js/converter/ivasgn.rb", "lib/ruby2js/converter/case.rb", "lib/ruby2js/converter/const.rb", "lib/ruby2js/converter/vasgn.rb", "lib/ruby2js/converter/untilpost.rb", "lib/ruby2js/converter/begin.rb", "lib/ruby2js/converter/ivar.rb", "lib/ruby2js/converter/while.rb", "lib/ruby2js/converter/next.rb", "lib/ruby2js/converter/nil.rb", "lib/ruby2js/converter/blockpass.rb", "lib/ruby2js/converter/super.rb", "lib/ruby2js/converter/defs.rb", "lib/ruby2js/converter/array.rb", "lib/ruby2js/converter/block.rb", "lib/ruby2js/converter/if.rb", "lib/ruby2js/converter/until.rb", "lib/ruby2js/converter/regexp.rb", "lib/ruby2js/converter/casgn.rb", "lib/ruby2js/converter/undef.rb", "lib/ruby2js/converter/literal.rb", "lib/ruby2js/converter/var.rb", "lib/ruby2js.rb"]
13
13
  s.homepage = "http://github.com/rubys/ruby2js"
14
14
  s.licenses = ["MIT"]
15
15
  s.require_paths = ["lib"]
16
+ s.required_ruby_version = Gem::Requirement.new(">= 1.9.3")
16
17
  s.rubygems_version = "1.8.23"
17
18
  s.summary = "Minimal yet extensible Ruby to JavaScript conversion."
18
19
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby2js
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.1
4
+ version: 1.12.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-01-02 00:00:00.000000000 Z
12
+ date: 2015-01-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: parser
@@ -47,6 +47,7 @@ files:
47
47
  - lib/ruby2js/filter/strict.rb
48
48
  - lib/ruby2js/filter/functions.rb
49
49
  - lib/ruby2js/filter/angular-resource.rb
50
+ - lib/ruby2js/filter/react.rb
50
51
  - lib/ruby2js/filter/angular-route.rb
51
52
  - lib/ruby2js/sinatra.rb
52
53
  - lib/ruby2js/rails.rb
@@ -111,7 +112,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
112
  requirements:
112
113
  - - ! '>='
113
114
  - !ruby/object:Gem::Version
114
- version: '0'
115
+ version: 1.9.3
115
116
  required_rubygems_version: !ruby/object:Gem::Requirement
116
117
  none: false
117
118
  requirements: