ruby2js 1.11.1 → 1.12.0

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.
@@ -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: