ruby2js 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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