ruby2js 1.3.0 → 1.4.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
- ZGIyMDBkZWFkNDE5ZDFlOTliNDE0MDMwMjgwMmNlYWZkZDcxOWNiMg==
4
+ YTU0NmY4ZDc4NDJiMGVjZGNiMmJjOWJhZmZkYzk3ODUwNjQ4YjVjMw==
5
5
  data.tar.gz: !binary |-
6
- NzIxOTk3NjM1YzRkMDA1NTI3ZThiMTI0MzE3NzIyYWFhNmE3MmIwMA==
6
+ NzRlMTE2ZWY5NTRjYjQ1ODBhMDUzMzZjMzFkNjY3M2IxNDVjY2VkNQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- MjRmNDE4NWQxN2MxNTAxOThkZTUzYzFiMGU0ZDZiM2EwNTI3ZDZlYmQyY2Fh
10
- MDg4MGM3YjVlMWI4NGQwMWY0ODhmMjQ5YTE1ZjdmN2JmOWRiYWI5ZDdjNzM0
11
- ZDM0NWExNWFjZjg1NzQ1MTNhNWNkMGMwYjg1MjIxMDdkYjI3NDU=
9
+ ZDk5MGVjOTdmNWRiNWY1ODhmMTMxMjdhOWMwNWEzNTUxYThiMGVlYWRjOTY4
10
+ YjU4MDM2OWI1ZmFhMjZhOTM3NzIwYjgyZWI4ZjA5ODE1MmFhMWU5YmZlYmYw
11
+ OTcyMmNhYzE0NDMyZDNlMTAxODdjYzEyNjBjOTY0ZmI0OGNmMzA=
12
12
  data.tar.gz: !binary |-
13
- OGYxYTNmNjNjNWY5OTkxYmQ0YTljNDhkM2JmZmU0NmY5ZTMzNTZhZjc3OWVi
14
- NjcxZDQ3NmQ2N2Y1M2QzNTNmYjE5ODZjNTNmZDc2YTM0MGVhOTAzZWE4YTc0
15
- NDFiOGU4NjU2YjA1MGYzOGE3Mjk2ODM5Y2IzNTc2MjdjNDFkMGY=
13
+ NDhmNTY1OGNhNWVmYmRmMGVkZTMwMTg4ZTMzMGY0YjE0YjFiY2UwNmYzYjNi
14
+ YTc4ZmViYWFjZWFjOGIyY2FhOThjNWM1M2E1M2MzZDZmYjI3NDQ5YjE5MjU5
15
+ MjA0ZTk3NzkzYjA2ZTIzNGY2MWU4NzNmZjE3NjFmM2FmYzM3N2U=
data/README.md CHANGED
@@ -13,6 +13,14 @@ calls IF there are either one or more arguments passed OR parenthesis are
13
13
  used, otherwise Ruby method calls become JavaScript property accesses.
14
14
  By default, methods, lambdas, and procs return `undefined`.
15
15
 
16
+ Ruby attribute accessors, as well as getter and setter method definitions, are
17
+ mapped to
18
+ [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2FdefineProperty),
19
+ so avoid these if you wish to target users running IE8 or lower. Instance
20
+ methods defined with no arguments are presumed to be defining a property
21
+ accessor; if you want a method with no arguments append a `!` to the method
22
+ name.
23
+
16
24
  Filters may be provided to add Ruby-specific or framework specific
17
25
  behavior. Filters are essentially macro facilities that operate on
18
26
  an AST representation of the code.
@@ -127,10 +135,11 @@ the script.
127
135
  * `.to_a` becomes `to_Array`
128
136
  * `.to_i` becomes `parseInt`
129
137
  * `.to_f` becomes `parseFloat`
130
- * `.sub` becomes `replace`
131
138
  * `.ord` becomes `charCodeAt(0)`
132
139
  * `.chr` becomes `fromCharCode`
140
+ * `.sub` becomes `replace`
133
141
  * `.gsub` becomes `replace //g`
142
+ * `x.sub!` and `x.gsub!` become equivalent `x = x.replace` statements
134
143
  * `.first` becomes `[0]`
135
144
  * `.last` becomes `[*.length-1]`
136
145
  * `[-n]` becomes `[*.length-n]` for literal values of `n`
@@ -148,6 +157,9 @@ the script.
148
157
  * `.each_with_index` becomes `forEach`
149
158
  * `setInterval` and `setTimeout` allow block to be treated as the
150
159
  first parameter on the call
160
+ * for the following methods, if the block consists entirely of a simple
161
+ expression (or ends with one), a `return` is added prior to the
162
+ expression: `sub`, `gsub`, `any?`, `all?`, `map`.
151
163
 
152
164
  * [jquery](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/jquery.rb)
153
165
 
@@ -6,6 +6,16 @@ module Ruby2JS
6
6
  OPERATORS = [:[], :[]=], [:not, :!], [:*, :/, :%], [:+, :-], [:>>, :<<],
7
7
  [:<=, :<, :>, :>=], [:==, :!=, :===, :"!=="], [:and, :or]
8
8
 
9
+ INVERT_OP = {
10
+ :< => :>=,
11
+ :<= => :>,
12
+ :== => :!=,
13
+ :!= => :==,
14
+ :> => :<=,
15
+ :>= => :<,
16
+ :=== => :'!=='
17
+ }
18
+
9
19
  attr_accessor :binding, :ivars
10
20
 
11
21
  def initialize( ast, vars = {} )
@@ -6,6 +6,8 @@ module Ruby2JS
6
6
  # (const nil :B)
7
7
  # (...)
8
8
 
9
+ # NOTE: macro :prop is defined at the bottom of this file
10
+
9
11
  handle :class do |name, inheritance, *body|
10
12
  init = s(:def, :initialize, s(:args))
11
13
  body.compact!
@@ -20,9 +22,22 @@ module Ruby2JS
20
22
  # constructor: remove from body and overwrite init function
21
23
  init = m
22
24
  nil
25
+ elsif m.children.first =~ /=/
26
+ # property setter
27
+ sym = :"#{m.children.first.to_s[0..-2]}"
28
+ s(:prop, s(:attr, name, :prototype), sym,
29
+ enumerable: s(:true), configurable: s(:true),
30
+ set: s(:block, s(:send, nil, :proc), *m.children[1..-1]))
31
+ elsif m.children[1].children.length == 0 and m.children.first !~ /!/
32
+ # property getter
33
+ s(:prop, s(:attr, name, :prototype), m.children.first,
34
+ enumerable: s(:true), configurable: s(:true),
35
+ get: s(:block, s(:send, nil, :proc), m.children[1],
36
+ s(:autoreturn, *m.children[2..-1])))
23
37
  else
24
38
  # method: add to prototype
25
- s(:send, s(:attr, name, :prototype), "#{m.children[0]}=",
39
+ s(:send, s(:attr, name, :prototype),
40
+ :"#{m.children[0].to_s.chomp('!')}=",
26
41
  s(:block, s(:send, nil, :proc), *m.children[1..-1]))
27
42
  end
28
43
  elsif m.type == :defs and m.children.first == s(:self)
@@ -30,8 +45,38 @@ module Ruby2JS
30
45
  s(:prototype, s(:send, name, "#{m.children[1]}=",
31
46
  s(:block, s(:send, nil, :proc), *m.children[2..-1])))
32
47
  elsif m.type == :send and m.children.first == nil
33
- # class method call
34
- s(:send, name, *m.children[1..-1])
48
+ if m.children[1] == :attr_accessor
49
+ m.children[2..-1].map do |sym|
50
+ var = sym.children.first
51
+ s(:prop, s(:attr, name, :prototype), var,
52
+ enumerable: s(:true), configurable: s(:true),
53
+ get: s(:block, s(:send, nil, :proc), s(:args),
54
+ s(:return, s(:ivar, :"@#{var}"))),
55
+ set: s(:block, s(:send, nil, :proc), s(:args, s(:arg, var)),
56
+ s(:ivasgn, :"@#{var}", s(:lvar, var))))
57
+ end
58
+ elsif m.children[1] == :attr_reader
59
+ m.children[2..-1].map do |sym|
60
+ var = sym.children.first
61
+ s(:prop, s(:attr, name, :prototype), var,
62
+ get: s(:block, s(:send, nil, :proc), s(:args),
63
+ s(:return, s(:ivar, :"@#{var}"))),
64
+ enumerable: s(:true),
65
+ configurable: s(:true))
66
+ end
67
+ elsif m.children[1] == :attr_writer
68
+ m.children[2..-1].map do |sym|
69
+ var = sym.children.first
70
+ s(:prop, s(:attr, name, :prototype), var,
71
+ set: s(:block, s(:send, nil, :proc), s(:args, s(:arg, var)),
72
+ s(:ivasgn, :"@#{var}", s(:lvar, var))),
73
+ enumerable: s(:true),
74
+ configurable: s(:true))
75
+ end
76
+ else
77
+ # class method call
78
+ s(:send, name, *m.children[1..-1])
79
+ end
35
80
  elsif m.type == :lvasgn
36
81
  # class variable
37
82
  s(:send, name, "#{m.children[0]}=", *m.children[1..-1])
@@ -49,24 +94,49 @@ module Ruby2JS
49
94
  end
50
95
  end
51
96
 
97
+ body.flatten!
98
+
99
+ # merge nearby property definitions
100
+ for i in 0...body.length-1
101
+ next unless body[i] and body[i].type == :prop
102
+ for j in i+1...body.length
103
+ break unless body[j] and body[j].type == :prop
104
+ if body[i].children[0..1] == body[j].children[0..1]
105
+ merge = body[i].children[2].merge(body[j].children[2])
106
+ body[j] = s(:prop, *body[j].children[0..1], merge)
107
+ body[i] = nil
108
+ end
109
+ end
110
+ end
111
+
52
112
  if inheritance
53
113
  body.unshift s(:send, name, :prototype=, s(:send, inheritance, :new))
54
114
  else
115
+ body.compact!
116
+
55
117
  # look for a sequence of methods
56
118
  methods = 0
57
- body.compact.each do |node|
58
- break unless node and node.type == :send and node.children[0]
59
- break unless node.children[0].type == :attr
60
- break unless node.children[0].children[0..1] == [name, :prototype]
61
- break unless node.children[1] =~ /=$/
119
+ body.each do |node|
120
+ break unless node
121
+ if node.type == :send
122
+ break unless node.children[0] and node.children[0].type == :attr
123
+ break unless node.children[0].children[0..1] == [name, :prototype]
124
+ break unless node.children[1] =~ /=$/
125
+ elsif node.type != :prop
126
+ break
127
+ end
62
128
  methods += 1
63
129
  end
64
130
 
65
131
  # collapse sequence of methods to a single assignment
66
- if methods > 1
67
- body.compact!
132
+ if methods > 1 or (methods == 1 and body[0].type == :prop)
68
133
  pairs = body[0...methods].map do |node|
69
- s(:pair, s(:str, node.children[1].chomp('=')), node.children[2])
134
+ if node.type == :send
135
+ s(:pair, s(:str, node.children[1].to_s.chomp('=')),
136
+ node.children[2])
137
+ else
138
+ s(:pair, s(:prop, node.children[1]), node.children[2])
139
+ end
70
140
  end
71
141
  body.shift(methods)
72
142
  body.unshift s(:send, name, :prototype=, s(:hash, *pairs))
@@ -87,5 +157,12 @@ module Ruby2JS
87
157
  @class_name = class_name
88
158
  end
89
159
  end
160
+
161
+ # macro that expands into Object.defineProperty(obj, prop, descriptor)
162
+ handle :prop do |obj, prop, descriptor|
163
+ parse s(:send, s(:const, nil, :Object), :defineProperty,
164
+ obj, s(:sym, prop), s(:hash,
165
+ *descriptor.map { |key, value| s(:pair, s(:sym, key), value) }))
166
+ end
90
167
  end
91
168
  end
@@ -8,7 +8,7 @@ module Ruby2JS
8
8
  # NOTE: undefined is not produced directly by Parser
9
9
 
10
10
  handle :defined?, :undefined? do |var|
11
- op = (@ast.type == :defined? ? "!==" : "===")
11
+ op = (@ast.type == :defined? ? :"!==" : :===)
12
12
  "typeof #{ parse var } #{ op } 'undefined'"
13
13
  end
14
14
  end
@@ -9,11 +9,26 @@ module Ruby2JS
9
9
  handle :hash do |*pairs|
10
10
  pairs.map! do |node|
11
11
  left, right = node.children
12
- key = parse left
13
- key = $1 if key =~ /\A"([a-zA-Z_$][a-zA-Z_$0-9]*)"\Z/
14
- "#{key}: #{parse right}"
12
+ if left.type == :prop
13
+ result = []
14
+ if right[:get]
15
+ result << "get #{left.children[0]}#{
16
+ parse(right[:get]).sub(/^function/,'')}"
17
+ end
18
+ if right[:set]
19
+ result << "set #{left.children[0]}#{
20
+ parse(right[:set]).sub(/^function/,'')}"
21
+ end
22
+ result
23
+ else
24
+ key = parse left
25
+ key = $1 if key =~ /\A"([a-zA-Z_$][a-zA-Z_$0-9]*)"\Z/
26
+ "#{key}: #{parse right}"
27
+ end
15
28
  end
16
29
 
30
+ pairs.flatten!
31
+
17
32
  if pairs.map {|item| item.length+2}.reduce(&:+).to_i < @width-10
18
33
  "{#{ pairs.join(', ') }}"
19
34
  else
@@ -6,25 +6,10 @@ module Ruby2JS
6
6
  # (...)
7
7
  # (...))
8
8
 
9
- INVERT_OP = {
10
- :< => :>=,
11
- :<= => :>,
12
- :== => :!=,
13
- :!= => :==,
14
- :> => :<=,
15
- :>= => :<
16
- }
17
-
18
9
  handle :if do |condition, then_block, else_block|
19
- # return parse condition if not else_block and not then_block
10
+ # return parse not condition if else_block and no then_block
20
11
  if else_block and not then_block
21
- if condition.type == :send and INVERT_OP.include? condition.children[1]
22
- return parse(s(:if, s(:send, condition.children[0],
23
- INVERT_OP[condition.children[1]], condition.children[2]),
24
- else_block,nil), @state)
25
- else
26
- return parse(s(:if, s(:send, condition, :!), else_block, nil), @state)
27
- end
12
+ return parse(s(:if, s(:not, condition), else_block, nil), @state)
28
13
  end
29
14
 
30
15
  then_block ||= s(:nil)
@@ -45,6 +30,7 @@ module Ruby2JS
45
30
 
46
31
  output
47
32
  else
33
+ else_block ||= s(:nil)
48
34
  "(#{ parse condition } ? #{ parse then_block } : #{ parse else_block })"
49
35
  end
50
36
  end
@@ -9,10 +9,10 @@ module Ruby2JS
9
9
  # (...)
10
10
  # (...))
11
11
 
12
- # (not
12
+ # Note: not handled below
13
13
  # (...))
14
14
 
15
- handle :and, :or, :not do |left, right=nil|
15
+ handle :and, :or do |left, right|
16
16
  type = @ast.type
17
17
  op_index = operator_index type
18
18
 
@@ -22,21 +22,37 @@ module Ruby2JS
22
22
  left = parse left
23
23
  left = "(#{ left })" if lgroup
24
24
 
25
- if right
26
- rgroup = LOGICAL.include?( right.type ) &&
27
- op_index < operator_index( right.type )
28
- rgroup = true if right.type == :begin
29
- right = parse right
30
- right = "(#{ right })" if rgroup
31
- end
25
+ rgroup = LOGICAL.include?( right.type ) &&
26
+ op_index < operator_index( right.type )
27
+ rgroup = true if right.type == :begin
28
+ right = parse right
29
+ right = "(#{ right })" if rgroup
32
30
 
33
- case type
34
- when :and
35
- "#{ left } && #{ right }"
36
- when :or
37
- "#{ left } || #{ right }"
31
+ "#{ left } #{ type==:and ? '&&' : '||' } #{ right }"
32
+ end
33
+
34
+ # (not
35
+ # (...))
36
+
37
+ handle :not do |expr|
38
+
39
+ if expr.type == :send and INVERT_OP.include? expr.children[1]
40
+ parse(s(:send, expr.children[0], INVERT_OP[expr.children[1]],
41
+ expr.children[2]))
42
+ elsif expr.type == :defined?
43
+ parse s(:undefined?, *expr.children)
44
+ elsif expr.type == :or
45
+ parse s(:and, s(:not, expr.children[0]), s(:not, expr.children[1]))
46
+ elsif expr.type == :and
47
+ parse s(:or, s(:not, expr.children[0]), s(:not, expr.children[1]))
38
48
  else
39
- "!#{ left }"
49
+ group = LOGICAL.include?( expr.type ) &&
50
+ operator_index( :not ) < operator_index( expr.type )
51
+ group = true if expr and expr.type == :begin
52
+ expr = parse expr
53
+ expr = "(#{ expr })" if group
54
+
55
+ "!#{ expr }"
40
56
  end
41
57
  end
42
58
  end
@@ -11,5 +11,26 @@ module Ruby2JS
11
11
  "return"
12
12
  end
13
13
  end
14
+
15
+ EXPRESSIONS = [ :array, :float, :hash, :int, :lvar, :nil, :send,
16
+ :str, :sym, :dstr, :dsym ]
17
+
18
+ handle :autoreturn do |*statements|
19
+ return if statements == [nil]
20
+ block = statements.dup
21
+ while block.length == 1 and block.first.type == :begin
22
+ block = block.first.children.dup
23
+ end
24
+
25
+ if EXPRESSIONS.include? block.last.type
26
+ block.push s(:return, block.pop)
27
+ end
28
+
29
+ if block.length == 1
30
+ parse block.first, @state
31
+ else
32
+ parse s(:begin, *block), @state
33
+ end
34
+ end
14
35
  end
15
36
  end
@@ -56,12 +56,7 @@ module Ruby2JS
56
56
  end
57
57
 
58
58
  if method == :!
59
- if receiver.type == :defined?
60
- parse s(:undefined?, *receiver.children)
61
- else
62
- group_receiver ||= (receiver.type != :send && receiver.children.length > 1)
63
- "!#{ group_receiver ? group(receiver) : parse(receiver) }"
64
- end
59
+ parse s(:not, receiver)
65
60
 
66
61
  elsif method == :[]
67
62
  "#{ parse receiver }[#{ args.map {|arg| parse arg}.join(', ') }]"
@@ -140,7 +135,7 @@ module Ruby2JS
140
135
  s(:send, s(:array, *args[0..-2]), :concat,
141
136
  args[-1].children.first))
142
137
  else
143
- call = "#{ parse receiver }#{ '.' if receiver }#{ method }"
138
+ call = "#{ parse receiver }#{ '.' if receiver && method}#{ method }"
144
139
  args = args.map {|a| parse a}
145
140
  if args.any? {|arg| arg.to_s.include? "\n"}
146
141
  "#{ call }(#{ args.join(', ') })"
@@ -53,6 +53,11 @@ module Ruby2JS
53
53
  # ...
54
54
 
55
55
  def on_module(node)
56
+ ngContext, @ngContext = @ngContext, :module
57
+ @ngModule = node.children
58
+ if @ngModule.last and @ngModule.last.type == :begin
59
+ @ngModule = @ngModule.last.children
60
+ end
56
61
  module_name = node.children[0]
57
62
  parent_name = module_name.children[0]
58
63
 
@@ -97,6 +102,8 @@ module Ruby2JS
97
102
  # contents all wrapped in an anonymous function
98
103
  s(:send, s(:block, s(:send, nil, :lambda), s(:args),
99
104
  s(:begin, s(:casgn, nil, name, app), *block)), :[])
105
+ ensure
106
+ @ngContext = ngContext
100
107
  end
101
108
 
102
109
  # input:
@@ -107,7 +114,9 @@ module Ruby2JS
107
114
  # ...
108
115
  # end
109
116
  def on_class(node)
117
+ ngContext, @ngContext = @ngContext, :class
110
118
  return super unless @ngApp and @ngChildren.include? node
119
+
111
120
  name = node.children.first
112
121
  if name.children.first == nil
113
122
  @ngClassUses, @ngClassOmit = [], []
@@ -128,6 +137,9 @@ module Ruby2JS
128
137
  else
129
138
  super
130
139
  end
140
+ ensure
141
+ @ngClassUses, @ngClassOmit = [], []
142
+ @ngContext = ngContext
131
143
  end
132
144
 
133
145
  # input:
@@ -156,6 +168,8 @@ module Ruby2JS
156
168
  ng_factory(node)
157
169
  when :filter
158
170
  ng_filter(node)
171
+ when :config
172
+ ng_config(node)
159
173
  when :directive
160
174
  hash = AngularRB.hash(node.children[2..-1])
161
175
  if hash
@@ -163,7 +177,11 @@ module Ruby2JS
163
177
  end
164
178
  ng_controller(node, :directive)
165
179
  when :watch
166
- ng_watch(node)
180
+ ng_watch(node, :$watch)
181
+ when :on
182
+ ng_watch(node, :$on)
183
+ when :observe
184
+ ng_observe(node)
167
185
  else
168
186
  super
169
187
  end
@@ -172,13 +190,44 @@ module Ruby2JS
172
190
  end
173
191
  end
174
192
 
193
+ # input:
194
+ # config :service do
195
+ # name = value
196
+ # end
197
+ #
198
+ # output:
199
+ # AppName.config("service") do |service|
200
+ # service.name = value
201
+ # end
202
+ def ng_config(node)
203
+ call = node.children.first
204
+ services = call.children[2..-1].map {|sym| sym.children[0]}
205
+ list = node.children[2..-1]
206
+
207
+ if services.length == 1
208
+ hash = AngularRB.hash(node.children[2..-1])
209
+ if hash
210
+ service = call.children[2].children[0]
211
+ list = hash.children.map do |pair|
212
+ s(:send, s(:gvar, service), "#{pair.children[0].children[0]}=",
213
+ pair.children[1])
214
+ end
215
+ end
216
+ end
217
+
218
+ s(:send, @ngApp, :config,
219
+ s(:array, *services.map {|sym| s(:str, sym.to_s)},
220
+ s(:block, s(:send, nil, :proc),
221
+ s(:args, *services.map {|sym| s(:arg, sym)}), s(:begin, *list))))
222
+ end
223
+
175
224
  # input:
176
225
  # controller :name do
177
226
  # ...
178
227
  # end
179
228
  #
180
229
  # output:
181
- # AppName.controller("name", do |uses|
230
+ # AppName.controller("name") do |uses|
182
231
  # ...
183
232
  # end
184
233
  def ng_controller(node, scope)
@@ -216,6 +265,7 @@ module Ruby2JS
216
265
  :or, :regexp, :self, :send, :str, :sym, :true, :undefined?, :xstr ]
217
266
 
218
267
  def ng_filter(node)
268
+ ngContext, @ngContext = @ngContext, :filter
219
269
  @ngClassUses, @ngClassOmit = [], []
220
270
  call = node.children.first
221
271
 
@@ -235,6 +285,9 @@ module Ruby2JS
235
285
  outer = s(:send, @ngApp, :filter, *call.children[2..-1])
236
286
 
237
287
  node.updated nil, [outer, s(:args, *uses), s(:return, inner)]
288
+ ensure
289
+ @ngClassUses, @ngClassOmit = [], []
290
+ @ngContext = ngContext
238
291
  end
239
292
 
240
293
  # input:
@@ -245,6 +298,7 @@ module Ruby2JS
245
298
  # output:
246
299
  # AppName.factory :name, [uses, lambda {|uses| ...}]
247
300
  def ng_factory(node)
301
+ ngContext, @ngContext = @ngContext, :factory
248
302
  call = node.children.first
249
303
  call = call.updated(nil, [@ngApp, *call.children[1..-1]])
250
304
 
@@ -269,6 +323,9 @@ module Ruby2JS
269
323
  array = args.map {|arg| s(:str, arg.children.first.to_s)}
270
324
 
271
325
  s(:send, *call.children, s(:array, *array, function))
326
+ ensure
327
+ @ngClassUses, @ngClassOmit = [], []
328
+ @ngContext = ngContext
272
329
  end
273
330
 
274
331
  # input:
@@ -302,13 +359,47 @@ module Ruby2JS
302
359
  # $scope.$watch 'expression' do |oldvalue, newvalue|
303
360
  # ...
304
361
  # end
305
- def ng_watch(node)
362
+ #
363
+ # also handles 'on'
364
+ #
365
+ def ng_watch(node, method)
306
366
  call = node.children.first
307
367
  if @ngContext == :controller and call.children.first == nil
308
- call = s(:send, s(:gvar, :$scope), :$watch, *call.children[2..-1])
309
- node = node.updated nil, [call, *node.children[1..-1]]
368
+ target = s(:gvar, :$scope)
369
+ expression = call.children[2]
370
+ if not [:str, :dstr, :sym, :dsym].include? expression.type
371
+ expression = s(:block, s(:send, nil, :proc), s(:args),
372
+ s(:return, expression))
373
+ end
374
+ else
375
+ target = nil
376
+ method = call.children[1]
377
+ expression = call.children[2]
378
+ end
379
+ call = s(:send, target, method, process(expression),
380
+ *process_all(call.children[3..-1]))
381
+ node = node.updated nil, [call, *process_all(node.children[1..-1])]
382
+ end
383
+
384
+ # input:
385
+ # observe attr.name do |value|
386
+ # ...
387
+ # end
388
+ #
389
+ # output:
390
+ # attr.$observe('name') do |value|
391
+ # ...
392
+ # end
393
+ def ng_observe(node)
394
+ if @ngContext == :directive
395
+ call = node.children[0]
396
+ expression = call.children[2]
397
+ call = s(:send, expression.children[0], :$observe,
398
+ s(:sym, expression.children[1]))
399
+ process node.updated nil, [call, *node.children[1..-1]]
400
+ else
401
+ node.updated nil, process_all(node.children)
310
402
  end
311
- return process node
312
403
  end
313
404
 
314
405
  # convert ivar assignments in controllers to $scope
@@ -326,6 +417,80 @@ module Ruby2JS
326
417
  end
327
418
  end
328
419
 
420
+ NG_METHOD_MAP = {
421
+ :apply! => [:$rootScope, :$apply],
422
+ :apply => [:$scope, :$apply],
423
+ :broadcast! => [:$rootScope, :$broadcast],
424
+ :broadcast => [:$scope, :$broadcast],
425
+ :digest! => [:$rootScope, :$digest],
426
+ :digest => [:$scope, :$digest],
427
+ :emit => [:$scope, :$emit],
428
+ :evalAsync! => [:$rootScope, :$evalAsync],
429
+ :evalAsync => [:$scope, :$evalAsync],
430
+ :eval! => [:$rootScope, :$eval],
431
+ :eval => [:$scope, :$eval],
432
+ :filter => [nil, :$filter],
433
+ :parent => [:$scope, :$parent],
434
+ }
435
+
436
+ def on_send(node)
437
+ if @ngContext == :controller
438
+ # map well known method names to the appropriate service
439
+ scope, method = NG_METHOD_MAP[node.children[1]]
440
+
441
+ return super unless node.children.first == nil and method
442
+
443
+ scope = s(:gvar, scope) if scope
444
+ process s(:gvar, method) unless scope
445
+
446
+ process node.updated nil, [scope, method, *node.children[2..-1]]
447
+
448
+ elsif @ngContext == :module and node.children[0]
449
+ return super unless @ngModule.include? node
450
+ child = node
451
+ while child and child.type == :send
452
+ child = child.children[0]
453
+ end
454
+
455
+ # singleton configuration syntax
456
+ return super unless child and (child.type == :gvar or
457
+ (child.type == :const and child.children[0] == nil))
458
+
459
+ service = child.children.last
460
+ s(:send, @ngApp, :config, s(:array, s(:str, service.to_s), s(:block,
461
+ s(:send, nil, :proc), s(:args, s(:arg, service)), node)))
462
+
463
+ elsif @ngContext == :directive
464
+ if node.children[0..1] == [nil, :interpolate]
465
+
466
+ @ngClassUses << :$interpolate
467
+ if node.children.length == 3
468
+ process node.updated nil, [nil, :$interpolate, node.children[2]]
469
+ else
470
+ process s(:send, s(:send, nil, :$interpolate, node.children[2]),
471
+ nil, node.children[3])
472
+ end
473
+
474
+ elsif node.children[0..1] == [nil, :compile]
475
+
476
+ @ngClassUses << :$compile
477
+ if node.children.length == 3
478
+ process node.updated nil, [nil, :$compile, node.children[2]]
479
+ else
480
+ process s(:send, s(:send, nil, :$compile, node.children[2]),
481
+ nil, node.children[3])
482
+ end
483
+
484
+
485
+ else
486
+ super
487
+ end
488
+
489
+ else
490
+ super
491
+ end
492
+ end
493
+
329
494
  # convert instance method definitions in controllers to $scope
330
495
  def on_def(node)
331
496
  if @ngContext == :controller
@@ -25,6 +25,24 @@ module Ruby2JS
25
25
  source, method, before, after = node.children
26
26
  process node.updated nil, [source, :replace, before, after]
27
27
 
28
+ elsif [:sub!, :gsub!].include? node.children[1]
29
+ method = :"#{node.children[1].to_s[0..-2]}"
30
+ target = node.children[0]
31
+ if target.type == :lvar
32
+ process s(:lvasgn, target.children[0], s(:send,
33
+ s(:lvar, target.children[0]), method, *node.children[2..-1]))
34
+ elsif target.type == :send
35
+ if target.children[0] == nil
36
+ process s(:lvasgn, target.children[1], s(:send,
37
+ s(:lvar, target.children[1]), method, *node.children[2..-1]))
38
+ else
39
+ process s(:send, target.children[0], :"#{target.children[1]}=",
40
+ s(:send, target, method, *node.children[2..-1]))
41
+ end
42
+ else
43
+ super
44
+ end
45
+
28
46
  elsif node.children[1] == :gsub and node.children.length == 4
29
47
  source, method, before, after = node.children
30
48
  if before.type == :regexp
@@ -139,18 +157,25 @@ module Ruby2JS
139
157
  block = process s(:block, s(:send, nil, :proc), *node.children[1..-1])
140
158
  call.updated nil, [*call.children[0..1], block, *call.children[2..-1]]
141
159
 
142
- elsif [:sub, :gsub].include? call.children[1]
160
+ elsif [:sub, :gsub, :sub!, :gsub!].include? call.children[1]
143
161
  return super if call.children.first == nil
144
- block = s(:block, s(:send, nil, :proc), *node.children[1..-1])
162
+ block = s(:block, s(:send, nil, :proc), node.children[1],
163
+ s(:autoreturn, *node.children[2..-1]))
145
164
  process call.updated(nil, [*call.children, block])
146
165
 
147
166
  elsif call.children[1] == :any? and call.children.length == 2
148
167
  call = call.updated nil, [call.children.first, :some]
149
- node.updated nil, [call, *node.children[1..-1]]
168
+ process node.updated nil, [call, node.children[1],
169
+ s(:autoreturn, *node.children[2..-1])]
150
170
 
151
171
  elsif call.children[1] == :all? and call.children.length == 2
152
172
  call = call.updated nil, [call.children.first, :every]
153
- node.updated nil, [call, *node.children[1..-1]]
173
+ process node.updated nil, [call, node.children[1],
174
+ s(:autoreturn, *node.children[2..-1])]
175
+
176
+ elsif call.children[1] == :map and call.children.length == 2
177
+ node.updated nil, [process(call), process(node.children[1]),
178
+ s(:autoreturn, *process_all(node.children[2..-1]))]
154
179
 
155
180
  else
156
181
  super
@@ -10,46 +10,19 @@ module Ruby2JS
10
10
  def on_block(node)
11
11
  children = process_all(node.children)
12
12
 
13
- # find the block
14
- block = [children.pop || s(:nil)]
15
- while block.length == 1 and block.first.type == :begin
16
- block = block.first.children.dup
17
- end
18
-
19
- if EXPRESSIONS.include? block.last.type
20
- block.push s(:return, block.pop)
21
- end
22
-
23
- if block.length == 1
24
- children.push block.first
25
- else
26
- children.push s(:begin, *block)
27
- end
28
-
29
- node.updated nil, children
13
+ children[-1] = s(:nil) if children.last == nil
14
+
15
+ node.updated nil, [*node.children[0..1],
16
+ s(:autoreturn, *children[2..-1])]
30
17
  end
31
18
 
32
19
  def on_def(node)
33
20
  children = process_all(node.children[1..-1])
34
- children.unshift node.children.first
35
-
36
- # find the block
37
- block = [children.pop || s(:nil)]
38
- while block.length == 1 and block.first.type == :begin
39
- block = block.first.children.dup
40
- end
41
-
42
- if EXPRESSIONS.include? block.last.type
43
- block.push s(:return, block.pop)
44
- end
45
21
 
46
- if block.length == 1
47
- children.push block.first
48
- else
49
- children.push s(:begin, *block)
50
- end
22
+ children[-1] = s(:nil) if children.last == nil
51
23
 
52
- node.updated nil, children
24
+ node.updated nil, [node.children[0], children.first,
25
+ s(:autoreturn, *children[1..-1])]
53
26
  end
54
27
  end
55
28
 
@@ -1,7 +1,7 @@
1
1
  module Ruby2JS
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 3
4
+ MINOR = 4
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
data/ruby2js.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "ruby2js"
5
- s.version = "1.3.0"
5
+ s.version = "1.4.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-07"
9
+ s.date = "2014-01-18"
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
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"]
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.3.0
4
+ version: 1.4.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-07 00:00:00.000000000 Z
11
+ date: 2014-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser