ruby2js 1.4.0 → 1.5.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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YTU0NmY4ZDc4NDJiMGVjZGNiMmJjOWJhZmZkYzk3ODUwNjQ4YjVjMw==
4
+ YmM0YzQzZjZkNGQ5YTMzZTA0N2Q4YjU3ZTJjNGFkMDZjYTY3YTdmZQ==
5
5
  data.tar.gz: !binary |-
6
- NzRlMTE2ZWY5NTRjYjQ1ODBhMDUzMzZjMzFkNjY3M2IxNDVjY2VkNQ==
6
+ ZjVlOTkzMWE4NjcwMDI0MjdlYTdmZDAyMmYzZGVjNWM0ZWM0ZDI4Yg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZDk5MGVjOTdmNWRiNWY1ODhmMTMxMjdhOWMwNWEzNTUxYThiMGVlYWRjOTY4
10
- YjU4MDM2OWI1ZmFhMjZhOTM3NzIwYjgyZWI4ZjA5ODE1MmFhMWU5YmZlYmYw
11
- OTcyMmNhYzE0NDMyZDNlMTAxODdjYzEyNjBjOTY0ZmI0OGNmMzA=
9
+ YTBiMjdjZTA1ZjYyNWRkMjI5OTVmYzRhOTIxYmNmMjQwMTEzNjEwZTQ5OGFk
10
+ NjUzMzQzOGFlZTU4ODM2ZjJlN2E5NTcwMzEwYzNmMjRkNzg4ZjdkYmM2N2U0
11
+ OWNlY2Q2MzUyMGZjY2UxNjBlYjU3ZTc2ODAzZGY5ZjdjNjBiMGY=
12
12
  data.tar.gz: !binary |-
13
- NDhmNTY1OGNhNWVmYmRmMGVkZTMwMTg4ZTMzMGY0YjE0YjFiY2UwNmYzYjNi
14
- YTc4ZmViYWFjZWFjOGIyY2FhOThjNWM1M2E1M2MzZDZmYjI3NDQ5YjE5MjU5
15
- MjA0ZTk3NzkzYjA2ZTIzNGY2MWU4NzNmZjE3NjFmM2FmYzM3N2U=
13
+ NThlN2I2N2RiMzJlMDY0OWIzMGYwN2Y4MjQzZTkxNDdhZWJlMGFhMjkxZjY5
14
+ ZDMzMDdkNzI0OTNjNGVjNGNmZjlmNDZkMTU4ODg5MTRhN2JjNGJhOTdjNjVh
15
+ M2IwYjkzYTg4ZWUzNjEwZDBlYzA3YzBhODE4YmE2M2E3Y2E1YWU=
data/README.md CHANGED
@@ -96,8 +96,14 @@ facilitates iteration over arrays. `forEach` is the JavaScript equivalent.
96
96
  Mapping this is fine until you start using a framework like jQuery which
97
97
  provides a function named [each](http://api.jquery.com/jQuery.each/).
98
98
 
99
- These approaches aren’t mutually exclusive. With enough static transformations
100
- and runtime libraries, one could reproduce any functionality desired. Just be
99
+ Fortunately, Ruby provides `?` and `!` as legal suffixes for method names,
100
+ Ruby2js filters do an exact match, so if you select a filter that maps `each`
101
+ to `forEach`, `each!` will pass through the filter. The final code that emits
102
+ JavaScript function calls and parameter accesses will strip off these
103
+ suffixes.
104
+
105
+ Static transformations and runtime libraries aren't aren’t mutually exclusive.
106
+ With enough of each, one could reproduce any functionality desired. Just be
101
107
  forewarned, that implementing a function like `method_missing` would require a
102
108
  _lot_ of work.
103
109
 
@@ -131,36 +137,81 @@ the script.
131
137
 
132
138
  * [functions](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/functions.rb)
133
139
 
134
- * `.to_s` becomes `to_String`
135
- * `.to_a` becomes `to_Array`
136
- * `.to_i` becomes `parseInt`
137
- * `.to_f` becomes `parseFloat`
138
- * `.ord` becomes `charCodeAt(0)`
140
+ * `.all?` becomes `.every`
141
+ * `.any?` becomes `.some`
139
142
  * `.chr` becomes `fromCharCode`
140
- * `.sub` becomes `replace`
141
- * `.gsub` becomes `replace //g`
142
- * `x.sub!` and `x.gsub!` become equivalent `x = x.replace` statements
143
+ * `.clear` becomes `.length = 0`
144
+ * `.each` becomes `forEach`
145
+ * `.each_with_index` becomes `forEach`
146
+ * `.empty?` becomes `.length == 0`
143
147
  * `.first` becomes `[0]`
148
+ * `.first(n)` becomes `.slice(0, n)`
149
+ * `.gsub` becomes `replace //g`
150
+ * `.include?` becomes `.indexOf() != -1`
151
+ * `.keys` becomes `Object.keys()`
144
152
  * `.last` becomes `[*.length-1]`
153
+ * `.last(n)` becomes `.slice(*.length-1, *.length)`
154
+ * `.max` becomes `Math.max.apply(Math)`
155
+ * `.min` becomes `Math.min.apply(Math)`
156
+ * `.ord` becomes `charCodeAt(0)`
157
+ * `puts` becomes `console.log`
158
+ * `.replace` becomes `.length = 0; ...push.apply(*)`
159
+ * `.sub` becomes `replace`
160
+ * `.to_a` becomes `to_Array`
161
+ * `.to_f` becomes `parseFloat`
162
+ * `.to_i` becomes `parseInt`
163
+ * `.to_s` becomes `to_String`
145
164
  * `[-n]` becomes `[*.length-n]` for literal values of `n`
146
- * `[n..m]` becomes `.slice(n,m+1)`
147
165
  * `[n...m]` becomes `.slice(n,m)`
166
+ * `[n..m]` becomes `.slice(n,m+1)`
148
167
  * `[/r/, n]` becomes `.match(/r/)[n]`
149
- * `.empty?` becomes `.length == 0`
150
- * `.clear!` becomes `.length = 0`
151
- * `.replace!` becomes `.length = 0; ...push.apply(*)`
152
- * `.include?` becomes `.indexOf() != -1`
153
- * `.any?` becomes `.some`
154
- * `.all?` becomes `.every`
155
- * `puts` becomes `console.log`
156
- * `.each` becomes `forEach` unless jquery is included
157
- * `.each_with_index` becomes `forEach`
168
+ * `.sub!` and `.gsub!` become equivalent `x = x.replace` statements
169
+ * `.map!`, `.reverse!`, and `.select` become equivalent
170
+ `.splice(0, .length, *.method())` statements
158
171
  * `setInterval` and `setTimeout` allow block to be treated as the
159
172
  first parameter on the call
160
173
  * for the following methods, if the block consists entirely of a simple
161
174
  expression (or ends with one), a `return` is added prior to the
162
175
  expression: `sub`, `gsub`, `any?`, `all?`, `map`.
163
176
 
177
+ * [underscore](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/underscore.rb)
178
+
179
+ * `.clone()` becomes `_.clone()`
180
+ * `.compact()` becomes `_.compact()`
181
+ * `.count_by {}` becomes `_.countBy {}`
182
+ * `.find {}` becomes `_.find {}`
183
+ * `.find_by()` becomes `_.findWhere()`
184
+ * `.flatten()` becomes `_.flatten()`
185
+ * `.group_by {}` becomes `_.groupBy {}`
186
+ * `.has_key?()` becomes `_.has()`
187
+ * `.index_by {}` becomes `_.indexBy {}`
188
+ * `.invert()` becomes `_.invert()`
189
+ * `.invoke(&:n)` becomes `_.invoke(, :n)`
190
+ * `.map(&:n)` becomes `_.pluck(, :n)`
191
+ * `.merge!()` becomes `_.extend()`
192
+ * `.merge()` becomes `_.extend({}, )`
193
+ * `.reduce {}` becomes `_.reduce {}`
194
+ * `.reduce()` becomes `_.reduce()`
195
+ * `.reject {}` becomes `_.reject {}`
196
+ * `.sample()` becomes `_.sample()`
197
+ * `.select {}` becomes `_.select {}`
198
+ * `.shuffle()` becomes `_.shuffle()`
199
+ * `.size()` becomes `_.size()`
200
+ * `.sort()` becomes `_.sort_by(, _.identity)`
201
+ * `.sort_by {}` becomes `_.sortBy {}`
202
+ * `.times {}` becomes `_.times {}`
203
+ * `.values()` becomes `_.values()`
204
+ * `.where()` becomes `_.where()`
205
+ * `.zip()` becomes `_.zip()`
206
+ * `(n...m)` becomes `_.range(n, m)`
207
+ * `(n..m)` becomes `_.range(n, m+1)`
208
+ * `.compact!`, `.flatten!`, `shuffle!`, `reject!`, `sort_by!`, and
209
+ `.uniq` become equivalent `.splice(0, .length, *.method())` statements
210
+ * for the following methods, if the block consists entirely of a simple
211
+ expression (or ends with one), a `return` is added prior to the
212
+ expression: `reduce`, `sort_by`, `group_by`, `index_by`, `count_by`,
213
+ `find`, `select`, `reject`.
214
+
164
215
  * [jquery](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/jquery.rb)
165
216
 
166
217
  * maps Ruby unary operator `~` to jQuery `$` function
@@ -212,6 +263,10 @@ the script.
212
263
  Picking a Ruby to JS mapping tool
213
264
  ---
214
265
 
266
+ > dsl — A domain specific language, where code is written in one language and
267
+ > errors are given in another.
268
+ > -- [Devil’s Dictionary of Programming](http://programmingisterrible.com/post/65781074112/devils-dictionary-of-programming)
269
+
215
270
  If you simply want to get a job done, and would like a mature and tested
216
271
  framework, and only use one of the many integrations that
217
272
  [Opal](http://opalrb.org/) provides, then Opal is the way to go right now.
data/lib/ruby2js.rb CHANGED
@@ -13,8 +13,26 @@ module Ruby2JS
13
13
  end
14
14
 
15
15
  class Processor < Parser::AST::Processor
16
+ BINARY_OPERATORS = Converter::OPERATORS[2..-1].flatten
17
+
16
18
  def on_attr(node)
17
- node.updated nil, [process(node.children.first), node.children.last]
19
+ on_send(node)
20
+ end
21
+
22
+ def on_send(node)
23
+ if node.children.length > 2 and node.children.last.type == :block_pass
24
+ method = node.children.last.children.first.children.last
25
+ if BINARY_OPERATORS.include? method
26
+ return on_block s(:block, s(:send, *node.children[0..-2]),
27
+ s(:args, s(:arg, :a), s(:arg, :b)), s(:return,
28
+ process(s(:send, s(:lvar, :a), method, s(:lvar, :b)))))
29
+ else
30
+ return on_block s(:block, s(:send, *node.children[0..-2]),
31
+ s(:args, s(:arg, :item)), s(:return,
32
+ process(s(:attr, s(:lvar, :item), method))))
33
+ end
34
+ end
35
+ super
18
36
  end
19
37
  end
20
38
  end
@@ -67,14 +67,6 @@ module Ruby2JS
67
67
  Parser::AST::Node.new(type, args)
68
68
  end
69
69
 
70
- def is_method?(node)
71
- return false unless node.type == :send
72
- return true unless node.loc
73
- selector = node.loc.selector
74
- return true unless selector.source_buffer
75
- selector.source_buffer.source[selector.end_pos] == '('
76
- end
77
-
78
70
  @@handlers = []
79
71
  def self.handle(*types, &block)
80
72
  types.each do |type|
@@ -103,6 +95,21 @@ module Ruby2JS
103
95
  end
104
96
  end
105
97
 
98
+ module Parser
99
+ module AST
100
+ class Node
101
+ def is_method?
102
+ return false if type == :attr
103
+ return true if children.length > 2
104
+ return true unless loc
105
+ selector = loc.selector
106
+ return true unless selector.source_buffer
107
+ selector.source_buffer.source[selector.end_pos] == '('
108
+ end
109
+ end
110
+ end
111
+ end
112
+
106
113
  # see https://github.com/whitequark/parser/blob/master/doc/AST_FORMAT.md
107
114
 
108
115
  require 'ruby2js/converter/arg'
@@ -77,6 +77,13 @@ module Ruby2JS
77
77
  # class method call
78
78
  s(:send, name, *m.children[1..-1])
79
79
  end
80
+ elsif m.type == :block and m.children.first.children.first == nil
81
+ # class method calls passing a block
82
+ s(:block, s(:send, name, *m.children.first.children[1..-1]),
83
+ *m.children[1..-1])
84
+ elsif [:send, :block].include? m.type
85
+ # pass through method calls with non-nil targets
86
+ m
80
87
  elsif m.type == :lvasgn
81
88
  # class variable
82
89
  s(:send, name, "#{m.children[0]}=", *m.children[1..-1])
@@ -12,7 +12,7 @@ module Ruby2JS
12
12
  end
13
13
  end
14
14
 
15
- EXPRESSIONS = [ :array, :float, :hash, :int, :lvar, :nil, :send,
15
+ EXPRESSIONS = [ :array, :float, :hash, :int, :lvar, :nil, :send, :attr,
16
16
  :str, :sym, :dstr, :dsym ]
17
17
 
18
18
  handle :autoreturn do |*statements|
@@ -18,9 +18,8 @@ module Ruby2JS
18
18
 
19
19
  width = ((ast.type == :sendw && !@nl.empty?) ? 0 : @width)
20
20
 
21
- if method =~ /\w[!?]$/
22
- raise NotImplementedError, "invalid method name #{ method }"
23
- end
21
+ # strip '!' and '?' decorations
22
+ method = method.to_s[0..-2] if method =~ /\w[!?]$/
24
23
 
25
24
  # three ways to define anonymous functions
26
25
  if method == :new and receiver and receiver.children == [nil, :Proc]
@@ -73,6 +72,9 @@ module Ruby2JS
73
72
  elsif method == :!~
74
73
  "!#{ parse args.first }.test(#{ parse receiver })"
75
74
 
75
+ elsif method == :<< and args.length == 1 and @state == :statement
76
+ "#{ parse receiver }.push(#{ parse args.first })"
77
+
76
78
  elsif OPERATORS.flatten.include? method
77
79
  "#{ group_receiver ? group(receiver) : parse(receiver) } #{ method } #{ group_target ? group(target) : parse(target) }"
78
80
 
@@ -104,7 +106,7 @@ module Ruby2JS
104
106
 
105
107
  args = args.map {|a| parse a}.join(', ')
106
108
 
107
- if args.length > 0 or is_method?(ast)
109
+ if ast.is_method?
108
110
  "new #{ parse receiver }(#{ args })"
109
111
  else
110
112
  "new #{ parse receiver }"
@@ -121,19 +123,15 @@ module Ruby2JS
121
123
  "typeof #{ parse args.first }"
122
124
 
123
125
  else
124
- if args.length == 0 and not is_method?(ast)
126
+ if not ast.is_method?
125
127
  if receiver
126
128
  "#{ parse receiver }.#{ method }"
127
129
  else
128
130
  parse s(:lvasgn, method), @state
129
131
  end
130
- elsif args.length == 1 and args.first.type == :splat
131
- parse s(:send, s(:attr, receiver, method), :apply, receiver,
132
- args.first.children.first)
133
- elsif args.length > 0 and args.last.type == :splat
134
- parse s(:send, s(:attr, receiver, method), :apply, receiver,
135
- s(:send, s(:array, *args[0..-2]), :concat,
136
- args[-1].children.first))
132
+ elsif args.length > 0 and args.any? {|arg| arg.type == :splat}
133
+ parse s(:send, s(:attr, receiver, method), :apply,
134
+ (receiver || s(:nil)), s(:array, *args))
137
135
  else
138
136
  call = "#{ parse receiver }#{ '.' if receiver && method}#{ method }"
139
137
  args = args.map {|a| parse a}
@@ -350,6 +350,15 @@ module Ruby2JS
350
350
  end
351
351
  end
352
352
 
353
+ # convert cvar referencess in controllers to self
354
+ def on_cvar(node)
355
+ if @ngContext == :controller
356
+ process s(:attr, s(:self), node.children.first.to_s[2..-1])
357
+ else
358
+ super
359
+ end
360
+ end
361
+
353
362
  # input:
354
363
  # watch 'expression' do |oldvalue, newvalue|
355
364
  # ...
@@ -376,9 +385,9 @@ module Ruby2JS
376
385
  method = call.children[1]
377
386
  expression = call.children[2]
378
387
  end
379
- call = s(:send, target, method, process(expression),
388
+ call = s(:send, process(target), method, process(expression),
380
389
  *process_all(call.children[3..-1]))
381
- node = node.updated nil, [call, *process_all(node.children[1..-1])]
390
+ node.updated nil, [call, *process_all(node.children[1..-1])]
382
391
  end
383
392
 
384
393
  # input:
@@ -417,6 +426,20 @@ module Ruby2JS
417
426
  end
418
427
  end
419
428
 
429
+ # convert cvar assignments in controllers to self
430
+ def on_cvasgn(node)
431
+ if @ngContext == :controller
432
+ if node.children.length == 1
433
+ process s(:attr, s(:self), "#{node.children.first.to_s[2..-1]}")
434
+ else
435
+ process s(:send, s(:self), "#{node.children.first.to_s[2..-1]}=",
436
+ node.children.last)
437
+ end
438
+ else
439
+ super
440
+ end
441
+ end
442
+
420
443
  NG_METHOD_MAP = {
421
444
  :apply! => [:$rootScope, :$apply],
422
445
  :apply => [:$scope, :$apply],
@@ -6,28 +6,39 @@ module Ruby2JS
6
6
  include SEXP
7
7
 
8
8
  def on_send(node)
9
- target = process(node.children.first)
10
- args = process_all(node.children[2..-1])
9
+ target = node.children.first
11
10
 
12
- if node.children[1] == :to_s
13
- s(:send, target, :toString, *args)
11
+ args = node.children[2..-1]
12
+
13
+ if [:max, :min].include? node.children[1] and args.length == 0
14
+ return super unless node.is_method?
15
+ process s(:send, s(:attr, s(:const, nil, :Math), node.children[1]),
16
+ :apply, s(:const, nil, :Math), target)
17
+
18
+ elsif node.children[1] == :keys and node.children.length == 2
19
+ if node.is_method?
20
+ process s(:send, s(:const, nil, :Object), :keys, target)
21
+ else
22
+ super
23
+ end
24
+
25
+ elsif node.children[1] == :to_s
26
+ process s(:send, target, :toString, *args)
14
27
 
15
28
  elsif node.children[1] == :to_a
16
- s(:send, target, :toArray, *args)
29
+ process s(:send, target, :toArray, *args)
17
30
 
18
31
  elsif node.children[1] == :to_i
19
- node.updated nil, [nil, :parseInt, target, *args]
32
+ process node.updated :send, [nil, :parseInt, target, *args]
20
33
 
21
34
  elsif node.children[1] == :to_f
22
- process node.updated nil, [nil, :parseFloat, target, *args]
35
+ process node.updated :send, [nil, :parseFloat, target, *args]
23
36
 
24
- elsif node.children[1] == :sub and node.children.length == 4
25
- source, method, before, after = node.children
26
- process node.updated nil, [source, :replace, before, after]
37
+ elsif node.children[1] == :sub and args.length == 2
38
+ process node.updated nil, [target, :replace, *args]
27
39
 
28
40
  elsif [:sub!, :gsub!].include? node.children[1]
29
41
  method = :"#{node.children[1].to_s[0..-2]}"
30
- target = node.children[0]
31
42
  if target.type == :lvar
32
43
  process s(:lvasgn, target.children[0], s(:send,
33
44
  s(:lvar, target.children[0]), method, *node.children[2..-1]))
@@ -56,46 +67,64 @@ module Ruby2JS
56
67
 
57
68
  elsif node.children[1] == :ord and node.children.length == 2
58
69
  if target.type == :str
59
- s(:int, target.children.last.ord)
70
+ process s(:int, target.children.last.ord)
60
71
  else
61
- node.updated nil, [target, :charCodeAt, s(:int, 0)]
72
+ process node.updated nil, [target, :charCodeAt, s(:int, 0)]
62
73
  end
63
74
 
64
75
  elsif node.children[1] == :chr and node.children.length == 2
65
76
  if target.type == :int
66
- s(:str, target.children.last.chr)
77
+ process s(:str, target.children.last.chr)
67
78
  else
68
- node.updated nil, [s(:const, nil, :String), :fromCharCode, target]
79
+ process node.updated nil, [s(:const, nil, :String), :fromCharCode,
80
+ target]
69
81
  end
70
82
 
71
83
  elsif node.children[1] == :empty? and node.children.length == 2
72
- s(:send, s(:attr, target, :length), :==, s(:int, 0))
84
+ process s(:send, s(:attr, target, :length), :==, s(:int, 0))
73
85
 
74
- elsif node.children[1] == :clear! and node.children.length == 2
75
- s(:send, target, :length=, s(:int, 0))
86
+ elsif node.children[1] == :clear and node.children.length == 2
87
+ if node.is_method?
88
+ process s(:send, target, :length=, s(:int, 0))
89
+ else
90
+ super
91
+ end
76
92
 
77
- elsif node.children[1] == :replace! and node.children.length == 3
78
- s(:begin, s(:send, target, :length=, s(:int, 0)),
93
+ elsif node.children[1] == :replace and node.children.length == 3
94
+ process s(:begin, s(:send, target, :length=, s(:int, 0)),
79
95
  s(:send, target, :push, s(:splat, node.children[2])))
80
96
 
81
97
  elsif node.children[1] == :include? and node.children.length == 3
82
- s(:send, s(:send, target, :indexOf, args.first), :!=, s(:int, -1))
98
+ process s(:send, s(:send, target, :indexOf, args.first), :!=,
99
+ s(:int, -1))
83
100
 
84
101
  elsif node.children[1] == :each
85
- if @each # disable `each` mapping, see jquery filter for an example
86
- super
87
- else
88
- node.updated nil, [target, :forEach, *args]
89
- end
102
+ process node.updated nil, [target, :forEach, *args]
90
103
 
91
104
  elsif node.children[0..1] == [nil, :puts]
92
- s(:send, s(:attr, nil, :console), :log, *args)
105
+ process s(:send, s(:attr, nil, :console), :log, *args)
106
+
107
+ elsif node.children[1] == :first
108
+ if node.children.length == 2
109
+ process node.updated nil, [target, :[], s(:int, 0)]
110
+ elsif node.children.length == 3
111
+ process on_send node.updated nil, [target, :[], s(:erange,
112
+ s(:int, 0), node.children[2])]
113
+ else
114
+ super
115
+ end
93
116
 
94
- elsif node.children[1..-1] == [:first]
95
- node.updated nil, [target, :[], s(:int, 0)]
117
+ elsif node.children[1] == :last
118
+ if node.children.length == 2
119
+ process on_send node.updated nil, [target, :[], s(:int, -1)]
120
+ elsif node.children.length == 3
121
+ process node.updated nil, [target, :slice,
122
+ s(:send, s(:attr, target, :length), :-, node.children[2]),
123
+ s(:attr, target, :length)]
124
+ else
125
+ super
126
+ end
96
127
 
97
- elsif node.children[1..-1] == [:last]
98
- on_send node.updated nil, [target, :[], s(:int, -1)]
99
128
 
100
129
  elsif node.children[1] == :[]
101
130
  index = args.first
@@ -103,7 +132,7 @@ module Ruby2JS
103
132
  # resolve negative literal indexes
104
133
  i = proc do |index|
105
134
  if index.type == :int and index.children.first < 0
106
- s(:send, s(:attr, target, :length), :-,
135
+ process s(:send, s(:attr, target, :length), :-,
107
136
  s(:int, -index.children.first))
108
137
  else
109
138
  index
@@ -111,18 +140,18 @@ module Ruby2JS
111
140
  end
112
141
 
113
142
  if index.type == :regexp
114
- s(:send, s(:send, target, :match, index), :[],
143
+ process s(:send, s(:send, target, :match, index), :[],
115
144
  args[1] || s(:int, 0))
116
145
 
117
146
  elsif node.children.length != 3
118
147
  super
119
148
 
120
149
  elsif index.type == :int and index.children.first < 0
121
- node.updated nil, [target, :[], i.(index)]
150
+ process node.updated nil, [target, :[], i.(index)]
122
151
 
123
152
  elsif index.type == :erange
124
153
  start, finish = index.children
125
- node.updated nil, [target, :slice, i.(start), i.(finish)]
154
+ process node.updated nil, [target, :slice, i.(start), i.(finish)]
126
155
 
127
156
  elsif index.type == :irange
128
157
  start, finish = index.children
@@ -136,14 +165,22 @@ module Ruby2JS
136
165
  else
137
166
  finish = s(:send, finish, :+, s(:int, 1))
138
167
  end
139
- node.updated nil, [target, :slice, start, finish]
168
+ process node.updated nil, [target, :slice, start, finish]
140
169
 
141
170
  else
142
171
  super
143
172
  end
144
173
 
174
+ elsif node.children[1] == :reverse! and node.is_method?
175
+ # input: a.reverse!
176
+ # output: a.splice(0, a.length, *a.reverse)
177
+ target = node.children.first
178
+ process s(:send, target, :splice, s(:int, 0),
179
+ s(:attr, target, :length), s(:splat, s(:send, target,
180
+ :"#{node.children[1].to_s[0..-2]}", *node.children[2..-1])))
181
+
145
182
  elsif node.children[1] == :each_with_index
146
- node.updated nil, [target, :forEach, *args]
183
+ process node.updated nil, [target, :forEach, *args]
147
184
 
148
185
  else
149
186
  super
@@ -155,7 +192,8 @@ module Ruby2JS
155
192
  if [:setInterval, :setTimeout].include? call.children[1]
156
193
  return super unless call.children.first == nil
157
194
  block = process s(:block, s(:send, nil, :proc), *node.children[1..-1])
158
- call.updated nil, [*call.children[0..1], block, *call.children[2..-1]]
195
+ on_send call.updated nil, [*call.children[0..1], block,
196
+ *call.children[2..-1]]
159
197
 
160
198
  elsif [:sub, :gsub, :sub!, :gsub!].include? call.children[1]
161
199
  return super if call.children.first == nil
@@ -163,20 +201,35 @@ module Ruby2JS
163
201
  s(:autoreturn, *node.children[2..-1]))
164
202
  process call.updated(nil, [*call.children, block])
165
203
 
204
+ elsif call.children[1] == :select and call.children.length == 2
205
+ call = call.updated nil, [call.children.first, :filter]
206
+ node.updated nil, [process(call), process(node.children[1]),
207
+ s(:autoreturn, *process_all(node.children[2..-1]))]
208
+
166
209
  elsif call.children[1] == :any? and call.children.length == 2
167
210
  call = call.updated nil, [call.children.first, :some]
168
- process node.updated nil, [call, node.children[1],
169
- s(:autoreturn, *node.children[2..-1])]
211
+ node.updated nil, [process(call), process(node.children[1]),
212
+ s(:autoreturn, *process_all(node.children[2..-1]))]
170
213
 
171
214
  elsif call.children[1] == :all? and call.children.length == 2
172
215
  call = call.updated nil, [call.children.first, :every]
173
- process node.updated nil, [call, node.children[1],
174
- s(:autoreturn, *node.children[2..-1])]
216
+ node.updated nil, [process(call), process(node.children[1]),
217
+ s(:autoreturn, *process_all(node.children[2..-1]))]
175
218
 
176
219
  elsif call.children[1] == :map and call.children.length == 2
177
220
  node.updated nil, [process(call), process(node.children[1]),
178
221
  s(:autoreturn, *process_all(node.children[2..-1]))]
179
222
 
223
+ elsif [:map!, :select!].include? call.children[1]
224
+ # input: a.map! {expression}
225
+ # output: a.splice(0, a.length, *a.map {expression})
226
+ method = (call.children[1] == :map! ? :map : :select)
227
+ target = call.children.first
228
+ process s(:send, target, :splice, s(:splat, s(:send, s(:array,
229
+ s(:int, 0), s(:attr, target, :length)), :concat,
230
+ s(:block, s(:send, target, method, *call.children[2..-1]),
231
+ *node.children[1..-1]))))
232
+
180
233
  else
181
234
  super
182
235
  end
@@ -65,10 +65,6 @@ module Ruby2JS
65
65
  module JQuery
66
66
  include SEXP
67
67
 
68
- def initialize
69
- @each = true # disable each mapping, see functions filter
70
- end
71
-
72
68
  # map $$ to $
73
69
  def on_gvar(node)
74
70
  if node.children[0] == :$$
@@ -128,6 +124,7 @@ module Ruby2JS
128
124
  # possible getter/setter
129
125
  method = node.children[1]
130
126
  method = method.to_s.chomp('=') if method =~ /=$/
127
+ method = :each! if method == :each
131
128
  rewrite = [rewrite_tilda[node.children[0]],
132
129
  method, *node.children[2..-1]]
133
130
  if props.include? node.children[1]
@@ -0,0 +1,186 @@
1
+ require 'ruby2js'
2
+
3
+ module Ruby2JS
4
+ module Filter
5
+ module Underscore
6
+ include SEXP
7
+
8
+ def on_send(node)
9
+ return super if node.children.first and node.children.first.children.last == :_
10
+
11
+ if [:clone, :shuffle, :size, :compact, :flatten, :invert, :values,
12
+ :uniq].include? node.children[1]
13
+ if node.is_method? and node.children.length == 2
14
+ process s(:send, s(:lvar, :_), node.children[1], node.children[0])
15
+ else
16
+ super
17
+ end
18
+ elsif node.children[1] == :sample and node.children.length <= 3
19
+ process s(:send, s(:lvar, :_), :sample, node.children[0],
20
+ *node.children[2..-1])
21
+ elsif node.children[1] == :has_key? and node.children.length == 3
22
+ process s(:send, s(:lvar, :_), :has, node.children[0],
23
+ node.children[2])
24
+ elsif node.children[1] == :sort and node.children.length == 2
25
+ if node.is_method?
26
+ process s(:send, s(:lvar, :_), :sortBy, node.children[0],
27
+ s(:attr, s(:lvar, :_), :identity))
28
+ else
29
+ super
30
+ end
31
+ elsif node.children[1] == :map
32
+ if node.children.length == 3 and node.children[2].type == :block_pass
33
+ process s(:send, s(:lvar, :_), :pluck, node.children[0],
34
+ node.children[2].children.first)
35
+ else
36
+ super
37
+ end
38
+ elsif node.children[1] == :merge and node.children.length >= 3
39
+ process s(:send, s(:lvar, :_), :extend, s(:hash), node.children[0],
40
+ *node.children[2..-1])
41
+ elsif node.children[1] == :merge! and node.children.length >= 3
42
+ process s(:send, s(:lvar, :_), :extend, node.children[0],
43
+ *node.children[2..-1])
44
+ elsif node.children[1] == :zip and node.children.length >= 3
45
+ process s(:send, s(:lvar, :_), :zip, node.children[0],
46
+ *node.children[2..-1])
47
+ elsif node.children[1] == :invoke
48
+ if node.children.length >= 3 and node.children.last.type==:block_pass
49
+ process s(:send, s(:lvar, :_), :invoke, node.children[0],
50
+ node.children.last.children.first,
51
+ *node.children[2..-2])
52
+ else
53
+ super
54
+ end
55
+ elsif [:where, :find_by].include? node.children[1]
56
+ method = node.children[1] == :where ? :where : :findWhere
57
+ process s(:send, s(:lvar, :_), method, node.children[0],
58
+ *node.children[2..-1])
59
+ elsif node.children[1] == :reduce
60
+ if node.children.length == 3 and node.children[2].type == :sym
61
+ # input: a.reduce(:+)
62
+ # output: _.reduce(_.rest(a),
63
+ # proc {|memo, item| return memo+item},
64
+ # a[0])
65
+ process s(:send, s(:lvar, :_), :reduce,
66
+ s(:send, s(:lvar, :_), :rest, node.children.first),
67
+ s(:block, s(:send, nil, :proc),
68
+ s(:args, s(:arg, :memo), s(:arg, :item)),
69
+ s(:autoreturn, s(:send, s(:lvar, :memo),
70
+ node.children[2].children.first, s(:lvar, :item)))),
71
+ s(:send, node.children.first, :[], s(:int, 0)))
72
+ elsif node.children.last.type == :block_pass
73
+ on_send node.updated(nil, [*node.children[0..1],
74
+ node.children[2].children.first])
75
+ elsif node.children.length == 4 and node.children[3].type == :sym
76
+ # input: a.reduce(n, :+)
77
+ # output: _.reduce(a, proc {|memo, item| return memo+item}, n)
78
+ process s(:send, s(:lvar, :_), :reduce, node.children.first,
79
+ s(:block, s(:send, nil, :proc),
80
+ s(:args, s(:arg, :memo), s(:arg, :item)),
81
+ s(:autoreturn, s(:send, s(:lvar, :memo),
82
+ node.children[3].children.first, s(:lvar, :item)))),
83
+ node.children[2])
84
+ else
85
+ super
86
+ end
87
+
88
+ elsif [:compact!, :flatten!, :shuffle!, :uniq!].
89
+ include? node.children[1] and node.is_method?
90
+ # input: a.compact!
91
+ # output: a.splice(0, a.length, *a.compact)
92
+ target = node.children.first
93
+ process s(:send, target, :splice, s(:int, 0),
94
+ s(:attr, target, :length), s(:splat, s(:send, target,
95
+ :"#{node.children[1].to_s[0..-2]}", *node.children[2..-1])))
96
+ else
97
+ super
98
+ end
99
+ end
100
+
101
+ def on_block(node)
102
+ call = node.children.first
103
+ if [:sort_by, :group_by, :index_by, :count_by].include? call.children[1]
104
+ # input: a.sort_by {}
105
+ # output: _.sortBy {return expression}
106
+ method = call.children[1].to_s.sub(/\_by$/,'By').to_sym
107
+ process s(:block, s(:send, s(:lvar, :_), method,
108
+ call.children.first), node.children[1],
109
+ s(:autoreturn, node.children[2]))
110
+ elsif [:find, :reject].include? call.children[1]
111
+ if call.children.length == 2
112
+ # input: a.find {|item| item > 0}
113
+ # output: _.find(a) {|item| return item > 0}
114
+ process s(:block, s(:send, s(:lvar, :_), call.children[1],
115
+ call.children.first), node.children[1],
116
+ s(:autoreturn, node.children[2]))
117
+ else
118
+ super
119
+ end
120
+
121
+ elsif call.children[1] == :times and call.children.length == 2
122
+ # input: 5.times {|i| console.log i}
123
+ # output: _.find(5) {|i| console.log(i)}
124
+ process s(:block, s(:send, s(:lvar, :_), call.children[1],
125
+ call.children.first), node.children[1], node.children[2])
126
+
127
+ elsif call.children[1] == :reduce
128
+ if call.children.length == 2
129
+ # input: a.reduce {|memo, item| memo+item}
130
+ # output: _.reduce(_.rest(a),
131
+ # proc {|memo, item| return memo+item},
132
+ # a[0])
133
+ process s(:send, s(:lvar, :_), :reduce,
134
+ s(:send, s(:lvar, :_), :rest, call.children.first),
135
+ s(:block, s(:send, nil, :proc),
136
+ node.children[1], s(:autoreturn, node.children[2])),
137
+ s(:send, call.children.first, :[], s(:int, 0)))
138
+ elsif call.children.length == 3
139
+ # input: a.reduce(n) {|memo, item| memo+item}
140
+ # output: _.reduce(a, proc {|memo, item| return memo+item}, n)
141
+ process s(:send, s(:lvar, :_), :reduce, call.children.first,
142
+ s(:block, s(:send, nil, :proc),
143
+ node.children[1], s(:autoreturn, node.children[2])),
144
+ call.children[2])
145
+ end
146
+
147
+ elsif [:map!, :reject!, :select!, :sort_by!].include? call.children[1]
148
+ # input: a.map! {expression}
149
+ # output: a.splice(0, a.length, *a.map {expression})
150
+ method = :"#{call.children[1].to_s[0..-2]}"
151
+ target = call.children.first
152
+ process s(:send, target, :splice, s(:splat, s(:send, s(:array,
153
+ s(:int, 0), s(:attr, target, :length)), :concat,
154
+ s(:block, s(:send, target, method, *call.children[2..-1]),
155
+ *node.children[1..-1]))))
156
+
157
+ else
158
+ super
159
+ end
160
+ end
161
+
162
+ def on_erange(node)
163
+ process s(:send, s(:lvar, :_), :range, *node.children)
164
+ end
165
+
166
+ def on_irange(node)
167
+ if node.children.last.type == :int
168
+ process s(:send, s(:lvar, :_), :range, node.children.first,
169
+ s(:int, node.children.last.children.last+1))
170
+ else
171
+ process s(:send, s(:lvar, :_), :range, node.children.first,
172
+ s(:send, node.children.last, :+, s(:int, 1)))
173
+ end
174
+ end
175
+
176
+ def on_for(node)
177
+ # pass through irange, erange unprocessed
178
+ return super unless [:irange, :erange].include? node.children[1].type
179
+ s(:for, process(node.children[0]), s(node.children[1].type,
180
+ *process_all(node.children[1].children)), process(node.children[2]))
181
+ end
182
+ end
183
+
184
+ DEFAULTS.push Underscore
185
+ end
186
+ end
@@ -1,7 +1,7 @@
1
1
  module Ruby2JS
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 4
4
+ MINOR = 5
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
data/ruby2js.gemspec CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "ruby2js"
5
- s.version = "1.4.0"
5
+ s.version = "1.5.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 = "2014-01-18"
9
+ s.date = "2014-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/rails.rb", "lib/ruby2js/version.rb", "lib/ruby2js/converter", "lib/ruby2js/converter/kwbegin.rb", "lib/ruby2js/converter/const.rb", "lib/ruby2js/converter/return.rb", "lib/ruby2js/converter/prototype.rb", "lib/ruby2js/converter/opasgn.rb", "lib/ruby2js/converter/xstr.rb", "lib/ruby2js/converter/args.rb", "lib/ruby2js/converter/defs.rb", "lib/ruby2js/converter/literal.rb", "lib/ruby2js/converter/array.rb", "lib/ruby2js/converter/if.rb", "lib/ruby2js/converter/nil.rb", "lib/ruby2js/converter/logical.rb", "lib/ruby2js/converter/next.rb", "lib/ruby2js/converter/while.rb", "lib/ruby2js/converter/whilepost.rb", "lib/ruby2js/converter/arg.rb", "lib/ruby2js/converter/case.rb", "lib/ruby2js/converter/break.rb", "lib/ruby2js/converter/hash.rb", "lib/ruby2js/converter/for.rb", "lib/ruby2js/converter/boolean.rb", "lib/ruby2js/converter/module.rb", "lib/ruby2js/converter/var.rb", "lib/ruby2js/converter/undef.rb", "lib/ruby2js/converter/blockpass.rb", "lib/ruby2js/converter/until.rb", "lib/ruby2js/converter/regexp.rb", "lib/ruby2js/converter/untilpost.rb", "lib/ruby2js/converter/masgn.rb", "lib/ruby2js/converter/cvasgn.rb", "lib/ruby2js/converter/block.rb", "lib/ruby2js/converter/ivar.rb", "lib/ruby2js/converter/send.rb", "lib/ruby2js/converter/vasgn.rb", "lib/ruby2js/converter/defined.rb", "lib/ruby2js/converter/def.rb", "lib/ruby2js/converter/sym.rb", "lib/ruby2js/converter/cvar.rb", "lib/ruby2js/converter/ivasgn.rb", "lib/ruby2js/converter/casgn.rb", "lib/ruby2js/converter/self.rb", "lib/ruby2js/converter/begin.rb", "lib/ruby2js/converter/dstr.rb", "lib/ruby2js/converter/class.rb", "lib/ruby2js/cgi.rb", "lib/ruby2js/converter.rb", "lib/ruby2js/filter", "lib/ruby2js/filter/return.rb", "lib/ruby2js/filter/strict.rb", "lib/ruby2js/filter/angularrb.rb", "lib/ruby2js/filter/angular-resource.rb", "lib/ruby2js/filter/functions.rb", "lib/ruby2js/filter/jquery.rb", "lib/ruby2js/filter/angular-route.rb", "lib/ruby2js/sinatra.rb", "lib/ruby2js.rb"]
12
+ s.files = ["ruby2js.gemspec", "README.md", "lib/ruby2js", "lib/ruby2js/rails.rb", "lib/ruby2js/version.rb", "lib/ruby2js/converter", "lib/ruby2js/converter/kwbegin.rb", "lib/ruby2js/converter/const.rb", "lib/ruby2js/converter/return.rb", "lib/ruby2js/converter/prototype.rb", "lib/ruby2js/converter/opasgn.rb", "lib/ruby2js/converter/xstr.rb", "lib/ruby2js/converter/args.rb", "lib/ruby2js/converter/defs.rb", "lib/ruby2js/converter/literal.rb", "lib/ruby2js/converter/array.rb", "lib/ruby2js/converter/if.rb", "lib/ruby2js/converter/nil.rb", "lib/ruby2js/converter/logical.rb", "lib/ruby2js/converter/next.rb", "lib/ruby2js/converter/while.rb", "lib/ruby2js/converter/whilepost.rb", "lib/ruby2js/converter/arg.rb", "lib/ruby2js/converter/case.rb", "lib/ruby2js/converter/break.rb", "lib/ruby2js/converter/hash.rb", "lib/ruby2js/converter/for.rb", "lib/ruby2js/converter/boolean.rb", "lib/ruby2js/converter/module.rb", "lib/ruby2js/converter/var.rb", "lib/ruby2js/converter/undef.rb", "lib/ruby2js/converter/blockpass.rb", "lib/ruby2js/converter/until.rb", "lib/ruby2js/converter/regexp.rb", "lib/ruby2js/converter/untilpost.rb", "lib/ruby2js/converter/masgn.rb", "lib/ruby2js/converter/cvasgn.rb", "lib/ruby2js/converter/block.rb", "lib/ruby2js/converter/ivar.rb", "lib/ruby2js/converter/send.rb", "lib/ruby2js/converter/vasgn.rb", "lib/ruby2js/converter/defined.rb", "lib/ruby2js/converter/def.rb", "lib/ruby2js/converter/sym.rb", "lib/ruby2js/converter/cvar.rb", "lib/ruby2js/converter/ivasgn.rb", "lib/ruby2js/converter/casgn.rb", "lib/ruby2js/converter/self.rb", "lib/ruby2js/converter/begin.rb", "lib/ruby2js/converter/dstr.rb", "lib/ruby2js/converter/class.rb", "lib/ruby2js/cgi.rb", "lib/ruby2js/converter.rb", "lib/ruby2js/filter", "lib/ruby2js/filter/return.rb", "lib/ruby2js/filter/strict.rb", "lib/ruby2js/filter/angularrb.rb", "lib/ruby2js/filter/underscore.rb", "lib/ruby2js/filter/angular-resource.rb", "lib/ruby2js/filter/functions.rb", "lib/ruby2js/filter/jquery.rb", "lib/ruby2js/filter/angular-route.rb", "lib/ruby2js/sinatra.rb", "lib/ruby2js.rb"]
13
13
  s.homepage = "http://github.com/rubys/ruby2js"
14
14
  s.licenses = ["MIT"]
15
15
  s.require_paths = ["lib"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby2js
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Ruby
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-18 00:00:00.000000000 Z
11
+ date: 2014-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -85,6 +85,7 @@ files:
85
85
  - lib/ruby2js/filter/return.rb
86
86
  - lib/ruby2js/filter/strict.rb
87
87
  - lib/ruby2js/filter/angularrb.rb
88
+ - lib/ruby2js/filter/underscore.rb
88
89
  - lib/ruby2js/filter/angular-resource.rb
89
90
  - lib/ruby2js/filter/functions.rb
90
91
  - lib/ruby2js/filter/jquery.rb