Module: Ruby2JS::Filter::AngularRB

Includes:
SEXP
Defined in:
lib/ruby2js/filter/angularrb.rb

Instance Method Summary collapse

Methods included from SEXP

#s

Instance Method Details

#extract_uses(block) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/ruby2js/filter/angularrb.rb', line 243

def extract_uses(block)
  # find the block
  while block.length == 1 and block.first and block.first.type == :begin
    block.push *block.shift.children
  end

  # find use class method calls
  uses = block.find_all do |node|
    node and node.type == :send and node.children[0..1] == [nil, :use]
  end

  # convert use calls into dependencies
  depends = []
  uses.each do |use|
    use.children[2..-1].each do |node|
      depends << node if [:str, :sym].include? node.type
    end
    block.delete use
  end

  depends
end

#initialize(*args) ⇒ Object



9
10
11
12
13
14
15
# File 'lib/ruby2js/filter/angularrb.rb', line 9

def initialize(*args)
  @ngApp = nil
  @ngAppUses = []
  @ngClassUses = []
  @ngClassOmit = []
  super
end

#ng_controller(node) ⇒ Object

input: controller :name do ... end



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/ruby2js/filter/angularrb.rb', line 133

def ng_controller(node)
  target = node.children.first
  target = target.updated(nil, [s(:lvar, @ngApp), 
    *target.children[1..-1]])

  block = process_all(node.children[2..-1])

  # convert use calls into args
  @ngClassUses -= @ngClassOmit
  args = node.children[1].children
  args += @ngClassUses.map {|sym| s(:arg, sym)} + extract_uses(block)
  args = args.map {|node| node.children.first.to_sym}.uniq.
    map {|sym| s(:arg, sym)}
  @ngClassUses, @ngClassOmit = [], []

  node.updated :block, [target, s(:args, *args), s(:begin, *block)]
end

#ng_factory(node) ⇒ Object

input:

factory :name do |uses|
...
end

output:

AppName.factory :name, [uses, lambda {|uses| ...}]


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/ruby2js/filter/angularrb.rb', line 187

def ng_factory(node)
  call = node.children.first
  call = call.updated(nil, [s(:lvar, @ngApp), *call.children[1..-1]])

  # insert return
  block = process_all(node.children[2..-1])
  tail = [block.pop || s(:nil)]
  while tail.length == 1 and tail.first.type == :begin
    tail = tail.first.children.dup
  end
  tail.push s(:return, tail.pop) unless tail.last.type == :return
  block.push (tail.length == 1 ? tail.first : s(:begin, *tail))

  # extract dependencies
  @ngClassUses.delete call.children[2].children[0]
  args = process_all(node.children[1].children)
  args += @ngClassUses.map {|sym| s(:arg, sym)} + extract_uses(block)
  args = args.map {|node| node.children.first.to_sym}.uniq.
    map {|sym| s(:arg, sym)}

  # construct a function
  function = s(:block, s(:send, nil, :lambda), s(:args, *args), *block)
  array = args.map {|arg| s(:str, arg.children.first.to_s)}

  s(:send, *call.children, s(:array, *array, function))
end

#ng_filter(node) ⇒ Object

input:

filter :name do |input|
...
end

output:

AppName.filter :name do
return lambda {|input| return ... }
end


160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/ruby2js/filter/angularrb.rb', line 160

def ng_filter(node)
  call = node.children.first

  # insert return
  args = process_all(node.children[1].children)
  block = process_all(node.children[2..-1])
  tail = [block.pop || s(:nil)]
  while tail.length == 1 and tail.first.type == :begin
    tail = tail.first.children.dup
  end
  tail.push s(:return, tail.pop) unless tail.last.type == :return
  block.push (tail.length == 1 ? tail.first : s(:begin, *tail))

  # construct a function returning a function
  inner = s(:block, s(:send, nil, :lambda), s(:args, *args), *block)
  outer = s(:send, s(:lvar, @ngApp), :filter, *call.children[2..-1])

  node.updated nil, [outer, s(:args), s(:return, inner)]
end

#on_block(node) ⇒ Object

input:

filter :name { ... }
controller :name { ... }
factory :name { ... }
directive :name { ... }


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/ruby2js/filter/angularrb.rb', line 109

def on_block(node)
  return super unless @ngApp
  call = node.children.first
  return super if call.children.first

  case call.children[1]
  when :controller
    ng_controller(node)
  when :factory
    ng_factory(node)
  when :filter
    ng_filter(node)
  when :directive
    ng_controller(node) # reuse template
  else
    super
  end
end

#on_casgn(node) ⇒ Object

input:

Constant = ...

output:

AppName.factory :name, [uses, lambda {|uses| ...}]


219
220
221
222
223
224
225
# File 'lib/ruby2js/filter/angularrb.rb', line 219

def on_casgn(node)
  return super if node.children[0]
  @ngClassOmit << node.children[1]
  return super unless @ngApp and @ngChildren.include? node
  ng_factory s(:block, s(:send, nil, :factory, s(:sym, node.children[1])),
    s(:args), process(node.children[2]))
end

#on_class(node) ⇒ Object

input:

class name {...}

output: app.factory(uses) do ... end



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/ruby2js/filter/angularrb.rb', line 81

def on_class(node)
  return super unless @ngApp and @ngChildren.include? node
  name = node.children.first
  if name.children.first == nil
    block = [node.children.last]
    uses = extract_uses(block)
    node = process s(:class, name, node.children[1], s(:begin, *block))

    @ngClassUses -= @ngClassOmit + [name.children.last]
    args = @ngClassUses.map {|sym| s(:arg, sym)} + uses
    args = args.map {|node| node.children.first.to_sym}.uniq.
      map {|sym| s(:arg, sym)}
    @ngClassUses, @ngClassOmit = [], []

   s(:block, s(:send, s(:lvar, @ngApp), :factory,
      s(:sym, name.children.last)), s(:args, *args), 
      s(:begin, node, s(:return, s(:const, nil, name.children.last))))
  else
    super
  end
end

#on_const(node) ⇒ Object



235
236
237
238
239
240
241
# File 'lib/ruby2js/filter/angularrb.rb', line 235

def on_const(node)
  if @ngClassUses
    @ngClassUses << node.children.last if not node.children.first
  end

  super
end

#on_gvar(node) ⇒ Object



227
228
229
230
231
232
233
# File 'lib/ruby2js/filter/angularrb.rb', line 227

def on_gvar(node)
  if @ngClassUses
    @ngClassUses << node.children.first
  end

  super
end

#on_module(node) ⇒ Object

input:

module Angular::AppName
use :Dependency
...
end

output:

AppName = angular.module("AppName", ["Dependency"])
...


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/ruby2js/filter/angularrb.rb', line 27

def on_module(node)
  module_name = node.children[0]
  parent_name = module_name.children[0]

  return super unless parent_name and parent_name.type == :const
  return super unless parent_name.children == [nil, :Angular]

  @ngApp = module_name.children[1]
  @ngChildren = node.children[1..-1]
  while @ngChildren.length == 1 and @ngChildren.first and @ngChildren.first.type == :begin
    @ngChildren = @ngChildren.first.children.dup
  end
  @ngAppUses = []

  block = process_all(node.children[1..-1])

  # convert use calls into dependencies
  depends = @ngAppUses.map {|sym| s(:sym, sym)} + extract_uses(block)
  depends = depends.map {|node| node.children.first.to_s}.uniq.
    map {|sym| s(:str, sym)}

  name, @ngApp, @ngChildren = @ngApp, nil, nil

  # construct app
  app = s(:send, s(:lvar, :angular), :module, s(:str, name.to_s), 
    s(:array, *depends.uniq))

  # return a single chained statement when there is only one call
  block.compact!
  if block.length == 0
    return app
  elsif block.length == 1
    call = block.first.children.first
    if block.first.type == :send and call == s(:lvar, name)
      return block.first.updated nil, [app, *block.first.children[1..-1]]
    elsif block.first.type == :block and call.children.first == s(:lvar, name)
      call = call.updated nil, [app, *call.children[1..-1]]
      return block.first.updated nil, [call, *block.first.children[1..-1]]
    end
  end

  # replace module with a constant assign followed by the module
  # contents all wrapped in an anonymous function
  s(:send, s(:block, s(:send, nil, :lambda), s(:args),
    s(:begin, s(:casgn, nil, name, app), *block)), :[])
end