ruby2js 1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ruby2js
1
+ Ruby2js
2
2
  =======
3
3
 
4
4
  Minimal yet extensible Ruby to JavaScript conversion.
@@ -64,7 +64,7 @@ and to provide a runtime library that adds properties to global JavaScript
64
64
  objects to handle this. It’s the approach that [Opal](http://opalrb.org/)
65
65
  takes. It is a fine approach, with a number of benefits. It also has some
66
66
  notable drawbacks. For example,
67
- [Readability](http://opalrb.org/try/#code:a%20%3D%20%22abc%22%3B%20puts%20a[-1])
67
+ [readability](http://opalrb.org/try/#code:a%20%3D%20%22abc%22%3B%20puts%20a[-1])
68
68
  and
69
69
  [compatibility with other frameworks](https://github.com/opal/opal/issues/400).
70
70
 
@@ -93,6 +93,93 @@ and runtime libraries, one could reproduce any functionality desired. Just be
93
93
  forewarned, that implementing a function like `method_missing` would require a
94
94
  _lot_ of work.
95
95
 
96
+ Integrations
97
+ ---
98
+
99
+ While this is a low level library suitable for DIY integration, one of the
100
+ obvious uses of a tool that produces JavaScript is by web servers. Ruby2JS
101
+ includes three such integrations:
102
+
103
+ * [CGI](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/cgi.rb)
104
+ * [Sinatra](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/sinatra.rb)
105
+ * [Rails](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/rails.rb)
106
+
107
+ As you might expect, CGI is a bit sluggish. By constrast, Sinatra is speedy.
108
+ Rails is not only equally speedy on the first call, after that it will
109
+ avoid the coversion entirely and serve cached results instead.
110
+
111
+ Filters
112
+ ---
113
+
114
+ In general, making use of a filter is as simple as requiring it. If multiple
115
+ filters are selected, they will all be applied in parallel in one pass through
116
+ the script.
117
+
118
+ * [strict](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/strict.rb)
119
+ adds `'use strict';` to the output
120
+
121
+ * [return](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/return.rb)
122
+ adds `return` to the last expression in functions
123
+
124
+ * [functions](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/functions.rb)
125
+
126
+ * `to_s` becomes `to_String`
127
+ * `to_i` becomes `parseInt`
128
+ * `to_f` becomes `parseFloat`
129
+ * `sub` becomes `replace`
130
+ * `gsub` becomes `replace //g`
131
+ * `first` becomes `[0]`
132
+ * `last` becomes `[*.length-1]`
133
+ * `[-n]` becomes `[*.length-n]` for literal values of `n`
134
+ * `[n..m]` becomes `slice(n,m+1)`
135
+ * `[n...m]` becomes `slice(n,m)`
136
+ * `puts` becomes `console.log`
137
+ * `each` becomes `forEach` unless jquery is included
138
+ * `each_with_index` becomes `forEach`
139
+
140
+ * [jquery](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/jquery.rb)
141
+
142
+ * maps Ruby unary operator `~` to jQuery `$` function
143
+ * maps Ruby attribute syntax to jquery attribute syntax
144
+ * maps `$$` to jQuery `$` function
145
+
146
+ * [angularrb](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/angularrb.rb)
147
+
148
+ * maps Ruby `module` to `angular.module`
149
+ * maps `filter`, `controller`, `factory`, and `directive` to calls to
150
+ angular module functions.
151
+ * maps `use` statements to formal arguments or array values (as
152
+ appropriate) depending on the module function.
153
+ * tracks globals variable and constant references and adds additional
154
+ implicit `use` statements
155
+ * maps constant assignments in an angular module to a filter
156
+ * maps class definitions in an angular module to a filter
157
+
158
+ * [angular-route](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/angular-routerb.rb)
159
+
160
+ * maps `case` statements on `$routeProvider` to angular.js module
161
+ configuration.
162
+ * adds implicit module `use` of `ngRoute` when such a `case` statement
163
+ is encountered
164
+
165
+ * [angular-resource](https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/angular-resource.rb)
166
+ * maps `$resource.new` statements on `$resource` function calls.
167
+ * adds implicit module `use` of `ngResource` when `$resource.new` calls
168
+ are encountered
169
+
170
+ [Wunderbar](https://github.com/rubys/wunderbar) includes additional demos:
171
+
172
+ * [wiki](https://github.com/rubys/wunderbar/blob/master/demo/wiki.rb) makes
173
+ use of the jquery filter.
174
+
175
+ * [angularjs](https://github.com/rubys/wunderbar/blob/master/demo/angularjs.rb)
176
+ makes use of the angular filters to implement the
177
+ [angular.js tutorial](http://docs.angularjs.org/tutorial). This demo
178
+ includes:
179
+ * [view](https://github.com/rubys/wunderbar/blob/master/demo/views/index._html)
180
+ * [partials](https://github.com/rubys/wunderbar/tree/master/demo/partials)
181
+ * [js](https://github.com/rubys/wunderbar/tree/master/demo/js)
182
+
96
183
  Picking a Ruby to JS mapping tool
97
184
  ---
98
185
 
@@ -107,12 +194,15 @@ limitations as to what JavaScript can be produced.
107
194
  [Try](http://intertwingly.net/projects/ruby2js/all) for yourself.
108
195
  [Compare](http://opalrb.org/try/#code:).
109
196
 
197
+ And, of course, the right solution might be to use
198
+ [CoffeeScript](http://coffeescript.org/) instead.
199
+
110
200
  License
111
201
  ---
112
202
 
113
203
  (The MIT License)
114
204
 
115
- Copyright (c) 2009,2013 Macario Ortega, Sam Ruby
205
+ Copyright (c) 2009, 2013 Macario Ortega, Sam Ruby
116
206
 
117
207
  Permission is hereby granted, free of charge, to any person obtaining
118
208
  a copy of this software and associated documentation files (the
data/lib/ruby2js.rb CHANGED
@@ -47,7 +47,7 @@ module Ruby2JS
47
47
  pending = false
48
48
  blank = true
49
49
  lines.each do |line|
50
- if line.start_with? '}' or line.start_with? ']'
50
+ if ')}]'.include? line[0]
51
51
  pre.sub!(/^ /,'')
52
52
  line.sub!(/([,;])$/,"\\1\n")
53
53
  pending = true
@@ -56,7 +56,7 @@ module Ruby2JS
56
56
  end
57
57
 
58
58
  line.sub! /^/, pre
59
- if line.end_with? '{' or line.end_with? '['
59
+ if '({['.include? line[-1]
60
60
  pre += ' '
61
61
  line.sub!(/^/,"\n") unless blank or pending
62
62
  pending = true
@@ -0,0 +1,40 @@
1
+ # Example usage:
2
+ #
3
+ # hello.cgi:
4
+ #
5
+ # require 'ruby2js/cgi'
6
+ # __END__
7
+ # alert 'Hello World!'
8
+ #
9
+ # Using an optional filter:
10
+ #
11
+ # require 'ruby2js/filter/functions'
12
+
13
+ require 'ruby2js'
14
+
15
+ at_exit do
16
+ status = 200
17
+ headers = []
18
+
19
+ begin
20
+ require 'time'
21
+ modtime = File.stat($0).mtime.rfc2822
22
+ headers << "Last-Modified: #{modtime}\r\n"
23
+ status = 304 if ENV['HTTP_IF_MODIFIED_SINCE'] == modtime
24
+ rescue
25
+ end
26
+
27
+ if status == 200
28
+ require 'digest/md5'
29
+ js = Ruby2JS.convert(DATA.read)
30
+ etag = Digest::MD5.hexdigest(js).inspect
31
+ headers << "Etag: #{etag}\r\n"
32
+ status = 304 if ENV['HTTP_IF_NONE_MATCH'] == etag
33
+ end
34
+
35
+ if status == 200
36
+ print "#{headers.join}\r\n#{js}"
37
+ else
38
+ print "Status: 304 Not Modified\r\n\r\n"
39
+ end
40
+ end
@@ -103,6 +103,7 @@ require 'ruby2js/converter/casgn'
103
103
  require 'ruby2js/converter/class'
104
104
  require 'ruby2js/converter/const'
105
105
  require 'ruby2js/converter/def'
106
+ require 'ruby2js/converter/defs'
106
107
  require 'ruby2js/converter/defined'
107
108
  require 'ruby2js/converter/dstr'
108
109
  require 'ruby2js/converter/for'
@@ -114,6 +115,7 @@ require 'ruby2js/converter/kwbegin'
114
115
  require 'ruby2js/converter/literal'
115
116
  require 'ruby2js/converter/logical'
116
117
  require 'ruby2js/converter/masgn'
118
+ require 'ruby2js/converter/module'
117
119
  require 'ruby2js/converter/next'
118
120
  require 'ruby2js/converter/nil'
119
121
  require 'ruby2js/converter/opasgn'
@@ -0,0 +1,13 @@
1
+ module Ruby2JS
2
+ class Converter
3
+
4
+ # (def (self) :foo
5
+ # (args)
6
+ # (...)
7
+
8
+ handle :defs do |target, method, args, body|
9
+ parse s(:send, target, "#{method}=",
10
+ s(:block, s(:send, nil, :lambda), args, body))
11
+ end
12
+ end
13
+ end
@@ -14,7 +14,7 @@ module Ruby2JS
14
14
  "#{key}: #{parse right}"
15
15
  end
16
16
 
17
- if pairs.map {|item| item.length+2}.reduce(&:+).to_i < 72
17
+ if pairs.map {|item| item.length+2}.reduce(&:+).to_i < 70
18
18
  "{#{ pairs.join(', ') }}"
19
19
  else
20
20
  "{#@nl#{ pairs.join(",#@ws") }#@nl}"
@@ -0,0 +1,56 @@
1
+ module Ruby2JS
2
+ class Converter
3
+
4
+ # (module
5
+ # (const nil :A)
6
+ # (...)
7
+
8
+ handle :module do |name, *body|
9
+ symbols = []
10
+
11
+ while body.length == 1 and body.first.type == :begin
12
+ body = body.first.children
13
+ end
14
+
15
+ visibility = :public
16
+ omit = []
17
+
18
+ body.each do |node|
19
+ if node.type == :send and node.children.first == nil
20
+ if [:public, :private, :protected].include? node.children[1]
21
+ if node.children.length == 2
22
+ visibility = node.children[1]
23
+ omit << node
24
+ elsif node.children[1] == :public
25
+ omit << node
26
+ node.children[2..-1].each do |sym|
27
+ symbols << sym.children.first if sym.type == :sym
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ next unless visibility == :public
34
+
35
+ if node.type == :casgn and node.children.first == nil
36
+ symbols << node.children[1]
37
+ elsif node.type == :def
38
+ symbols << node.children.first
39
+ elsif node.type == :class and node.children.first.children.first == nil
40
+ symbols << node.children.first.children.last
41
+ end
42
+ end
43
+
44
+ body = body - omit + [s(:return, s(:hash,
45
+ *symbols.map {|sym| s(:pair, s(:sym, sym), s(:lvar, sym))}))]
46
+
47
+ body = s(:send, s(:block, s(:send, nil, :lambda), s(:args),
48
+ s(:begin, *body)), :[])
49
+ if name.children.first == nil
50
+ parse s(:lvasgn, name.children.last, body)
51
+ else
52
+ parse s(:send, name.children.first, "#{name.children.last}=", body)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -14,12 +14,22 @@ module Ruby2JS
14
14
  raise NotImplementedError, "invalid method name #{ method }"
15
15
  end
16
16
 
17
+ # three ways to define anonymous functions
17
18
  if method == :new and receiver and receiver.children == [nil, :Proc]
18
19
  return parse args.first
19
20
  elsif not receiver and [:lambda, :proc].include? method
20
21
  return parse args.first
21
22
  end
22
23
 
24
+ # call anonymous function
25
+ if [:call, :[]].include? method and receiver and receiver.type == :block
26
+ t2,m2,*args2 = receiver.children.first.children
27
+ if not t2 and [:lambda, :proc].include? m2 and args2.length == 0
28
+ receiver = (@state == :statement ? group(receiver) : parse(receiver))
29
+ return parse s(:send, nil, receiver, *args)
30
+ end
31
+ end
32
+
23
33
  op_index = operator_index method
24
34
  if op_index != -1
25
35
  target = args.first
@@ -89,8 +99,15 @@ module Ruby2JS
89
99
  s(:send, s(:array, *args[0..-2]), :concat,
90
100
  args[-1].children.first))
91
101
  else
92
- args = args.map {|a| parse a}.join(', ')
93
- "#{ parse receiver }#{ '.' if receiver }#{ method }(#{ args })"
102
+ call = "#{ parse receiver }#{ '.' if receiver }#{ method }"
103
+ args = args.map {|a| parse a}
104
+ if args.any? {|arg| arg.to_s.include? "\n"}
105
+ "#{ call }(#{ args.join(', ') })"
106
+ elsif args.map {|arg| arg.length+2}.reduce(&:+).to_i < 70
107
+ "#{ call }(#{ args.join(', ') })"
108
+ else
109
+ "#{ call }(#@nl#{ args.join(",#@ws") }#@nl)"
110
+ end
94
111
  end
95
112
  end
96
113
  end
@@ -0,0 +1,26 @@
1
+ require 'parser/current'
2
+ require 'ruby2js/filter/angularrb'
3
+
4
+ module Ruby2JS
5
+ module Filter
6
+ module AngularResource
7
+ include SEXP
8
+
9
+ # input:
10
+ # $resource.new(args)
11
+ #
12
+ # output:
13
+ # $resource(args)
14
+
15
+ def on_send(node)
16
+ return super unless @ngApp and node.children[1] == :new
17
+ return super unless node.children[0] == s(:gvar, :$resource)
18
+ node = super(node)
19
+ @ngAppUses << :ngResource
20
+ node.updated nil, [nil, :$resource, *node.children[2..-1]]
21
+ end
22
+ end
23
+
24
+ DEFAULTS.push AngularResource
25
+ end
26
+ end
@@ -0,0 +1,59 @@
1
+ require 'parser/current'
2
+ require 'ruby2js/filter/angularrb'
3
+
4
+ module Ruby2JS
5
+ module Filter
6
+ module AngularRoute
7
+ include SEXP
8
+
9
+ # input:
10
+ # case $routeProvider
11
+ # when '/path'
12
+ # templateUrl = 'partials/path.html'
13
+ # else
14
+ # redirectTo '/path'
15
+ # end
16
+ #
17
+ # output:
18
+ # AppName.config(["$routeProvider", function($routeProvider) {
19
+ # $routeProvider.when("/path", {templateUrl: 'partials/path.html'}).
20
+ # otherwise({redirectTo: "/path"}))
21
+
22
+ def on_case(node)
23
+ rp = :$routeProvider
24
+ return super unless @ngApp and node.children.first == s(:gvar, rp)
25
+ @ngAppUses << :ngRoute
26
+ code = s(:lvar, rp)
27
+
28
+ hash = proc do |pairs|
29
+ if pairs.length == 1 and pairs.first.type == :begin
30
+ pairs = pairs.first.children
31
+ end
32
+ s(:hash, *pairs.map {|pair|
33
+ if pair.type == :send
34
+ s(:pair, s(:sym, pair.children[1]), pair.children[2])
35
+ else
36
+ s(:pair, s(:sym, pair.children[0]), pair.children[1])
37
+ end
38
+ })
39
+ end
40
+
41
+ node.children[1..-2].each do |child|
42
+ code = s(:send, code, :when, child.children.first,
43
+ hash[child.children[1..-1]])
44
+ end
45
+
46
+ if node.children.last
47
+ code = s(:send, code, :otherwise,
48
+ hash[node.children[-1..-1]])
49
+ end
50
+
51
+ s(:send, s(:lvar, @ngApp), :config, s(:array, s(:str, rp.to_s),
52
+ s(:block,
53
+ s(:send, nil, :proc), s(:args, s(:arg, rp)), code)))
54
+ end
55
+ end
56
+
57
+ DEFAULTS.push AngularRoute
58
+ end
59
+ end
@@ -8,6 +8,9 @@ module Ruby2JS
8
8
 
9
9
  def initialize(*args)
10
10
  @ngApp = nil
11
+ @ngAppUses = []
12
+ @ngClassUses = []
13
+ @ngClassOmit = []
11
14
  super
12
15
  end
13
16
 
@@ -29,6 +32,8 @@ module Ruby2JS
29
32
  return super unless parent_name.children == [nil, :Angular]
30
33
 
31
34
  @ngApp = module_name.children[1]
35
+ @ngChildren = node.children
36
+ @ngAppUses = []
32
37
 
33
38
  # find the block
34
39
  block = process_all(node.children[1..-1])
@@ -36,62 +41,83 @@ module Ruby2JS
36
41
  block = block.first.children.dup
37
42
  end
38
43
 
39
- # find use class method calls
40
- uses = block.find_all do |node|
41
- node and node.type == :send and node.children[0..1] == [nil, :use]
44
+ factories = []
45
+ block.compact.each do |child|
46
+ if child.type == :class and child.children.first.children.first == nil
47
+ name = child.children.first
48
+ if name.children.first == nil
49
+ name = name.children.last
50
+ factories << on_block(s(:block, s(:send, nil, :factory,
51
+ s(:sym, name)), s(:args), s(:return, s(:const, nil, name))))
52
+ end
53
+ end
42
54
  end
55
+ block += factories
43
56
 
44
57
  # convert use calls into dependencies
45
- depends = []
46
- uses.each do |use|
47
- pending = []
48
- use.children[2..-1].each do |node|
49
- break unless [:str, :sym].include? node.type
50
- pending << node
58
+ depends = @ngAppUses.map {|sym| s(:sym, sym)} + extract_uses(block)
59
+ depends = depends.map {|node| node.children.first.to_s}.uniq.
60
+ map {|sym| s(:str, sym)}
61
+
62
+ name, @ngApp, @ngChildren = @ngApp, nil, nil
63
+
64
+ # construct app
65
+ app = s(:send, s(:lvar, :angular), :module, s(:str, name.to_s),
66
+ s(:array, *depends.uniq))
67
+
68
+ # return a single chained statement when there is only one call
69
+ block.compact!
70
+ if block.length == 0
71
+ return app
72
+ elsif block.length == 1
73
+ call = block.first.children.first
74
+ if block.first.type == :send and call == s(:lvar, name)
75
+ return block.first.updated nil, [app, *block.first.children[1..-1]]
76
+ elsif block.first.type == :block and call.children.first == s(:lvar, name)
77
+ call = call.updated nil, [app, *call.children[1..-1]]
78
+ return block.first.updated nil, [call, *block.first.children[1..-1]]
51
79
  end
52
- depends += pending
53
- block.delete use
54
80
  end
55
81
 
56
- # build constant assignment statement
57
- casgn = s(:casgn, nil, @ngApp, s(:send,
58
- s(:lvar, :angular),
59
- :module,
60
- s(:str, @ngApp.to_s),
61
- s(:array, *depends)))
62
-
63
- @ngApp = nil
64
-
65
- # replace module with a constant assign followed by the module contents
66
- node.updated :begin, [casgn, *block.compact]
82
+ # replace module with a constant assign followed by the module
83
+ # contents all wrapped in an anonymous function
84
+ s(:send, s(:block, s(:send, nil, :lambda), s(:args),
85
+ s(:begin, s(:casgn, nil, name, app), *block)), :[])
67
86
  end
68
87
 
69
88
  # input:
70
- # class Name < Angular::Controller
71
- # use :$service
72
- # ...
73
- # end
74
- #
75
- # output:
76
- # AppName.controller :Name do |$service|
77
- # ...
78
- # end
89
+ # filter :name { ... }
90
+ # controller :name { ... }
91
+ # factory :name { ... }
79
92
 
80
- def on_class(node)
93
+ def on_block(node)
81
94
  return super unless @ngApp
82
- return super unless node.children.length == 3
83
-
84
- # (const nil :Name)
85
- name = node.children.first
86
- return super unless name.type == :const and name.children.first == nil
95
+ call = node.children.first
96
+ return super if call.children.first
97
+
98
+ case call.children[1]
99
+ when :controller
100
+ ng_controller(node)
101
+ when :factory
102
+ ng_factory(node)
103
+ when :filter
104
+ ng_filter(node)
105
+ when :directive
106
+ ng_controller(node) # reuse template
107
+ else
108
+ super
109
+ end
110
+ end
87
111
 
88
- # (const (const nil :Angular) :Controller)
89
- parent = node.children[1]
90
- return super unless parent and parent.children.length == 2
91
- return super unless parent.children[0]
92
- return super unless parent.children[0].type == :const
93
- return super unless parent.children[0].children == [nil, :Angular]
94
- return super unless [:Controller].include? parent.children[1]
112
+ # input:
113
+ # controller :name do
114
+ # ...
115
+ # end
116
+ #
117
+ def ng_controller(node)
118
+ target = node.children.first
119
+ target = target.updated(nil, [s(:lvar, @ngApp),
120
+ *target.children[1..-1]])
95
121
 
96
122
  # find the block
97
123
  block = process_all(node.children[2..-1])
@@ -99,31 +125,15 @@ module Ruby2JS
99
125
  block = block.first.children.dup
100
126
  end
101
127
 
102
- # find use class method calls
103
- uses = block.find_all do |node|
104
- node.type == :send and node.children[0..1] == [nil, :use]
105
- end
106
-
107
128
  # convert use calls into args
108
- args = []
109
- uses.each do |use|
110
- pending = []
111
- use.children[2..-1].each do |node|
112
- break unless [:str, :sym].include? node.type
113
- pending << s(:arg, *node.children)
114
- end
115
- args += pending
116
- block.delete use
117
- end
118
-
119
- # build Appname.controller call statement
120
- call = s(:send,
121
- s(:const, nil, @ngApp),
122
- :controller,
123
- s(:sym, name.children.last))
124
-
125
- # replace class with a block
126
- node.updated :block, [call, s(:args, *args), s(:begin, *block)]
129
+ @ngClassUses -= @ngClassOmit
130
+ args = node.children[1].children
131
+ args += @ngClassUses.map {|sym| s(:arg, sym)} + extract_uses(block)
132
+ args = args.map {|node| node.children.first.to_sym}.uniq.
133
+ map {|sym| s(:arg, sym)}
134
+ @ngClassUses = @ngClassOmit = []
135
+
136
+ node.updated :block, [target, s(:args, *args), s(:begin, *block)]
127
137
  end
128
138
 
129
139
  # input:
@@ -135,27 +145,106 @@ module Ruby2JS
135
145
  # AppName.filter :name do
136
146
  # return lambda {|input| return ... }
137
147
  # end
138
- def on_block(node)
139
- return super unless @ngApp
140
-
148
+ def ng_filter(node)
141
149
  call = node.children.first
142
- return super unless call.children[0..1] == [nil, :filter]
143
150
 
144
151
  # insert return
145
- children = process_all(node.children[1..-1])
146
- block = [children.pop || s(:nil)]
147
- while block.length == 1 and block.first.type == :begin
148
- block = block.first.children.dup
152
+ args = process_all(node.children[1].children)
153
+ block = process_all(node.children[2..-1])
154
+ tail = [block.pop || s(:nil)]
155
+ while tail.length == 1 and tail.first.type == :begin
156
+ tail = tail.first.children.dup
149
157
  end
150
- block.push s(:return, block.pop) unless block.last.type == :return
151
- children.push (block.length == 1 ? block.first : s(:begin, *block))
158
+ tail.push s(:return, tail.pop) unless tail.last.type == :return
159
+ block.push (tail.length == 1 ? tail.first : s(:begin, *tail))
152
160
 
153
161
  # construct a function returning a function
154
- inner = s(:block, s(:send, nil, :lambda), *children)
162
+ inner = s(:block, s(:send, nil, :lambda), s(:args, *args), *block)
155
163
  outer = s(:send, s(:lvar, @ngApp), :filter, *call.children[2..-1])
156
164
 
157
165
  node.updated nil, [outer, s(:args), s(:return, inner)]
158
166
  end
167
+
168
+ # input:
169
+ # factory :name do |uses|
170
+ # ...
171
+ # end
172
+ #
173
+ # output:
174
+ # AppName.factory :name, [uses, lambda {|uses| ...}]
175
+ def ng_factory(node)
176
+ call = node.children.first
177
+ call = call.updated(nil, [s(:lvar, @ngApp), *call.children[1..-1]])
178
+
179
+ # insert return
180
+ block = process_all(node.children[2..-1])
181
+ tail = [block.pop || s(:nil)]
182
+ while tail.length == 1 and tail.first.type == :begin
183
+ tail = tail.first.children.dup
184
+ end
185
+ tail.push s(:return, tail.pop) unless tail.last.type == :return
186
+ block.push (tail.length == 1 ? tail.first : s(:begin, *tail))
187
+
188
+ # extract dependencies
189
+ @ngClassUses.delete call.children[2].children[0]
190
+ args = process_all(node.children[1].children)
191
+ args += @ngClassUses.map {|sym| s(:arg, sym)} + extract_uses(block)
192
+ args = args.map {|node| node.children.first.to_sym}.uniq.
193
+ map {|sym| s(:arg, sym)}
194
+
195
+ # construct a function
196
+ function = s(:block, s(:send, nil, :lambda), s(:args, *args), *block)
197
+ array = args.map {|arg| s(:str, arg.children.first.to_s)}
198
+
199
+ s(:send, *call.children, s(:array, *array, function))
200
+ end
201
+
202
+ # input:
203
+ # Constant = ...
204
+ #
205
+ # output:
206
+ # AppName.factory :name, [uses, lambda {|uses| ...}]
207
+ def on_casgn(node)
208
+ return super if node.children[0]
209
+ @ngClassOmit << node.children[1]
210
+ return super unless @ngApp and @ngChildren.include? node
211
+ ng_factory s(:block, s(:send, nil, :factory, s(:sym, node.children[1])),
212
+ s(:args), process(node.children[2]))
213
+ end
214
+
215
+ def on_gvar(node)
216
+ if @ngClassUses
217
+ @ngClassUses << node.children.first
218
+ end
219
+
220
+ super
221
+ end
222
+
223
+ def on_const(node)
224
+ if @ngClassUses
225
+ @ngClassUses << node.children.last if not node.children.first
226
+ end
227
+
228
+ super
229
+ end
230
+
231
+ def extract_uses(block)
232
+ # find use class method calls
233
+ uses = block.find_all do |node|
234
+ node and node.type == :send and node.children[0..1] == [nil, :use]
235
+ end
236
+
237
+ # convert use calls into dependencies
238
+ depends = []
239
+ uses.each do |use|
240
+ use.children[2..-1].each do |node|
241
+ depends << node if [:str, :sym].include? node.type
242
+ end
243
+ block.delete use
244
+ end
245
+
246
+ depends
247
+ end
159
248
  end
160
249
 
161
250
  DEFAULTS.push AngularRB
@@ -0,0 +1,20 @@
1
+ require 'ruby2js'
2
+
3
+ module Ruby2JS
4
+ module Filter
5
+ module Strict
6
+ include SEXP
7
+
8
+ def process(node)
9
+ if @strict
10
+ super
11
+ else
12
+ @strict = true
13
+ s(:begin, s(:str, 'use strict'), super(node))
14
+ end
15
+ end
16
+ end
17
+
18
+ DEFAULTS.push Strict
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ # Example usage:
2
+ #
3
+ # $ echo gem 'ruby2js', require: 'ruby2js/rails' > Gemfile
4
+ # $ bundle update
5
+ # $ rails generate controller Say hello
6
+ # $ echo 'alert "Hello world!"' > app/views/say/hello.js.rb
7
+ # $ rails server
8
+ # $ curl http://localhost:3000/say/hello.js
9
+ #
10
+ # Using an optional filter:
11
+ #
12
+ # $ echo 'require "ruby2js/filter/functions"' > config/initializers/ruby2js.rb
13
+
14
+ require 'ruby2js'
15
+
16
+ module Ruby2JS
17
+ module Rails
18
+ class Template
19
+ cattr_accessor :default_format
20
+ self.default_format = Mime::JS
21
+ def self.call(template)
22
+ "Ruby2JS.convert(#{template.source.inspect})"
23
+ end
24
+ end
25
+
26
+ ActionView::Template.register_template_handler :rb, Template
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ # Example usage:
2
+ #
3
+ # sinatra.rb:
4
+ #
5
+ # require 'ruby2js/sinatra'
6
+ # get '/test.js' do
7
+ # ruby2js :test
8
+ # end
9
+ #
10
+ # views/test.rb:
11
+ #
12
+ # alert 'Hello World!'
13
+ #
14
+ # Using an optional filter:
15
+ #
16
+ # require 'ruby2js/filter/functions'
17
+
18
+ require 'sinatra'
19
+ require 'ruby2js'
20
+
21
+ class Ruby2JSTemplate < Tilt::Template
22
+ self.default_mime_type = 'application/javascript'
23
+
24
+ def prepare
25
+ end
26
+
27
+ def evaluate(scope, locals, &block)
28
+ @output ||= Ruby2JS.convert(data)
29
+ end
30
+
31
+ def allows_script?
32
+ false
33
+ end
34
+ end
35
+
36
+ Tilt.register 'rb', Ruby2JSTemplate
37
+
38
+ helpers do
39
+ def ruby2js(*args)
40
+ content_type 'application/javascript'
41
+ render('rb', *args)
42
+ end
43
+ end
@@ -2,7 +2,7 @@ module Ruby2JS
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
4
  MINOR = 1
5
- TINY = 1
5
+ TINY = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/ruby2js.gemspec CHANGED
@@ -2,22 +2,22 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "ruby2js"
5
- s.version = "1.1.1"
5
+ s.version = "1.1.2"
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 = "2013-11-22"
9
+ s.date = "2013-12-01"
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/version.rb", "lib/ruby2js/converter", "lib/ruby2js/converter/orasgn.rb", "lib/ruby2js/converter/kwbegin.rb", "lib/ruby2js/converter/const.rb", "lib/ruby2js/converter/return.rb", "lib/ruby2js/converter/opasgn.rb", "lib/ruby2js/converter/xstr.rb", "lib/ruby2js/converter/args.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/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/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/ivasgn.rb", "lib/ruby2js/converter/casgn.rb", "lib/ruby2js/converter/self.rb", "lib/ruby2js/converter/andasgn.rb", "lib/ruby2js/converter/begin.rb", "lib/ruby2js/converter/dstr.rb", "lib/ruby2js/converter/class.rb", "lib/ruby2js/converter.rb", "lib/ruby2js/filter", "lib/ruby2js/filter/return.rb", "lib/ruby2js/filter/angularrb.rb", "lib/ruby2js/filter/functions.rb", "lib/ruby2js/filter/jquery.rb", "lib/ruby2js.rb"]
12
+ s.files = ["ruby2js.gemspec", "README.md", "lib/ruby2js", "lib/ruby2js/sinatra.rb", "lib/ruby2js/converter.rb", "lib/ruby2js/filter", "lib/ruby2js/filter/angular-route.rb", "lib/ruby2js/filter/jquery.rb", "lib/ruby2js/filter/return.rb", "lib/ruby2js/filter/angularrb.rb", "lib/ruby2js/filter/strict.rb", "lib/ruby2js/filter/functions.rb", "lib/ruby2js/filter/angular-resource.rb", "lib/ruby2js/converter", "lib/ruby2js/converter/ivasgn.rb", "lib/ruby2js/converter/self.rb", "lib/ruby2js/converter/undef.rb", "lib/ruby2js/converter/casgn.rb", "lib/ruby2js/converter/nil.rb", "lib/ruby2js/converter/logical.rb", "lib/ruby2js/converter/ivar.rb", "lib/ruby2js/converter/class.rb", "lib/ruby2js/converter/for.rb", "lib/ruby2js/converter/arg.rb", "lib/ruby2js/converter/literal.rb", "lib/ruby2js/converter/dstr.rb", "lib/ruby2js/converter/return.rb", "lib/ruby2js/converter/defined.rb", "lib/ruby2js/converter/boolean.rb", "lib/ruby2js/converter/next.rb", "lib/ruby2js/converter/array.rb", "lib/ruby2js/converter/def.rb", "lib/ruby2js/converter/args.rb", "lib/ruby2js/converter/regexp.rb", "lib/ruby2js/converter/var.rb", "lib/ruby2js/converter/defs.rb", "lib/ruby2js/converter/if.rb", "lib/ruby2js/converter/orasgn.rb", "lib/ruby2js/converter/masgn.rb", "lib/ruby2js/converter/hash.rb", "lib/ruby2js/converter/whilepost.rb", "lib/ruby2js/converter/break.rb", "lib/ruby2js/converter/opasgn.rb", "lib/ruby2js/converter/begin.rb", "lib/ruby2js/converter/xstr.rb", "lib/ruby2js/converter/vasgn.rb", "lib/ruby2js/converter/block.rb", "lib/ruby2js/converter/send.rb", "lib/ruby2js/converter/sym.rb", "lib/ruby2js/converter/case.rb", "lib/ruby2js/converter/kwbegin.rb", "lib/ruby2js/converter/const.rb", "lib/ruby2js/converter/module.rb", "lib/ruby2js/converter/until.rb", "lib/ruby2js/converter/untilpost.rb", "lib/ruby2js/converter/andasgn.rb", "lib/ruby2js/converter/blockpass.rb", "lib/ruby2js/converter/while.rb", "lib/ruby2js/cgi.rb", "lib/ruby2js/version.rb", "lib/ruby2js/rails.rb", "lib/ruby2js.rb"]
13
13
  s.homepage = "http://github.com/rubys/ruby2js"
14
14
  s.licenses = ["MIT"]
15
15
  s.require_paths = ["lib"]
16
- s.rubygems_version = "2.0.7"
16
+ s.rubygems_version = "1.8.11"
17
17
  s.summary = "Minimal yet extensible Ruby to JavaScript conversion."
18
18
 
19
19
  if s.respond_to? :specification_version then
20
- s.specification_version = 4
20
+ s.specification_version = 3
21
21
 
22
22
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
23
23
  s.add_runtime_dependency(%q<parser>, [">= 0"])
metadata CHANGED
@@ -1,29 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby2js
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Sam Ruby
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-11-22 00:00:00.000000000 Z
12
+ date: 2013-12-01 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: parser
15
- requirement: !ruby/object:Gem::Requirement
16
+ requirement: &8336660 !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: '0'
20
22
  type: :runtime
21
23
  prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ! '>='
25
- - !ruby/object:Gem::Version
26
- version: '0'
24
+ version_requirements: *8336660
27
25
  description: ! " The base package maps Ruby syntax to JavaScript semantics.\n Filters
28
26
  may be provided to add Ruby-specific or framework specific\n behavior.\n"
29
27
  email: rubys@intertwingly.net
@@ -33,77 +31,86 @@ extra_rdoc_files: []
33
31
  files:
34
32
  - ruby2js.gemspec
35
33
  - README.md
36
- - lib/ruby2js/version.rb
37
- - lib/ruby2js/converter/orasgn.rb
38
- - lib/ruby2js/converter/kwbegin.rb
39
- - lib/ruby2js/converter/const.rb
40
- - lib/ruby2js/converter/return.rb
41
- - lib/ruby2js/converter/opasgn.rb
42
- - lib/ruby2js/converter/xstr.rb
43
- - lib/ruby2js/converter/args.rb
44
- - lib/ruby2js/converter/literal.rb
45
- - lib/ruby2js/converter/array.rb
46
- - lib/ruby2js/converter/if.rb
34
+ - lib/ruby2js/sinatra.rb
35
+ - lib/ruby2js/converter.rb
36
+ - lib/ruby2js/filter/angular-route.rb
37
+ - lib/ruby2js/filter/jquery.rb
38
+ - lib/ruby2js/filter/return.rb
39
+ - lib/ruby2js/filter/angularrb.rb
40
+ - lib/ruby2js/filter/strict.rb
41
+ - lib/ruby2js/filter/functions.rb
42
+ - lib/ruby2js/filter/angular-resource.rb
43
+ - lib/ruby2js/converter/ivasgn.rb
44
+ - lib/ruby2js/converter/self.rb
45
+ - lib/ruby2js/converter/undef.rb
46
+ - lib/ruby2js/converter/casgn.rb
47
47
  - lib/ruby2js/converter/nil.rb
48
48
  - lib/ruby2js/converter/logical.rb
49
- - lib/ruby2js/converter/next.rb
50
- - lib/ruby2js/converter/while.rb
51
- - lib/ruby2js/converter/whilepost.rb
52
- - lib/ruby2js/converter/arg.rb
53
- - lib/ruby2js/converter/case.rb
54
- - lib/ruby2js/converter/break.rb
55
- - lib/ruby2js/converter/hash.rb
49
+ - lib/ruby2js/converter/ivar.rb
50
+ - lib/ruby2js/converter/class.rb
56
51
  - lib/ruby2js/converter/for.rb
52
+ - lib/ruby2js/converter/arg.rb
53
+ - lib/ruby2js/converter/literal.rb
54
+ - lib/ruby2js/converter/dstr.rb
55
+ - lib/ruby2js/converter/return.rb
56
+ - lib/ruby2js/converter/defined.rb
57
57
  - lib/ruby2js/converter/boolean.rb
58
- - lib/ruby2js/converter/var.rb
59
- - lib/ruby2js/converter/undef.rb
60
- - lib/ruby2js/converter/blockpass.rb
61
- - lib/ruby2js/converter/until.rb
58
+ - lib/ruby2js/converter/next.rb
59
+ - lib/ruby2js/converter/array.rb
60
+ - lib/ruby2js/converter/def.rb
61
+ - lib/ruby2js/converter/args.rb
62
62
  - lib/ruby2js/converter/regexp.rb
63
- - lib/ruby2js/converter/untilpost.rb
63
+ - lib/ruby2js/converter/var.rb
64
+ - lib/ruby2js/converter/defs.rb
65
+ - lib/ruby2js/converter/if.rb
66
+ - lib/ruby2js/converter/orasgn.rb
64
67
  - lib/ruby2js/converter/masgn.rb
68
+ - lib/ruby2js/converter/hash.rb
69
+ - lib/ruby2js/converter/whilepost.rb
70
+ - lib/ruby2js/converter/break.rb
71
+ - lib/ruby2js/converter/opasgn.rb
72
+ - lib/ruby2js/converter/begin.rb
73
+ - lib/ruby2js/converter/xstr.rb
74
+ - lib/ruby2js/converter/vasgn.rb
65
75
  - lib/ruby2js/converter/block.rb
66
- - lib/ruby2js/converter/ivar.rb
67
76
  - lib/ruby2js/converter/send.rb
68
- - lib/ruby2js/converter/vasgn.rb
69
- - lib/ruby2js/converter/defined.rb
70
- - lib/ruby2js/converter/def.rb
71
77
  - lib/ruby2js/converter/sym.rb
72
- - lib/ruby2js/converter/ivasgn.rb
73
- - lib/ruby2js/converter/casgn.rb
74
- - lib/ruby2js/converter/self.rb
78
+ - lib/ruby2js/converter/case.rb
79
+ - lib/ruby2js/converter/kwbegin.rb
80
+ - lib/ruby2js/converter/const.rb
81
+ - lib/ruby2js/converter/module.rb
82
+ - lib/ruby2js/converter/until.rb
83
+ - lib/ruby2js/converter/untilpost.rb
75
84
  - lib/ruby2js/converter/andasgn.rb
76
- - lib/ruby2js/converter/begin.rb
77
- - lib/ruby2js/converter/dstr.rb
78
- - lib/ruby2js/converter/class.rb
79
- - lib/ruby2js/converter.rb
80
- - lib/ruby2js/filter/return.rb
81
- - lib/ruby2js/filter/angularrb.rb
82
- - lib/ruby2js/filter/functions.rb
83
- - lib/ruby2js/filter/jquery.rb
85
+ - lib/ruby2js/converter/blockpass.rb
86
+ - lib/ruby2js/converter/while.rb
87
+ - lib/ruby2js/cgi.rb
88
+ - lib/ruby2js/version.rb
89
+ - lib/ruby2js/rails.rb
84
90
  - lib/ruby2js.rb
85
91
  homepage: http://github.com/rubys/ruby2js
86
92
  licenses:
87
93
  - MIT
88
- metadata: {}
89
94
  post_install_message:
90
95
  rdoc_options: []
91
96
  require_paths:
92
97
  - lib
93
98
  required_ruby_version: !ruby/object:Gem::Requirement
99
+ none: false
94
100
  requirements:
95
101
  - - ! '>='
96
102
  - !ruby/object:Gem::Version
97
103
  version: '0'
98
104
  required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
99
106
  requirements:
100
107
  - - ! '>='
101
108
  - !ruby/object:Gem::Version
102
109
  version: '0'
103
110
  requirements: []
104
111
  rubyforge_project:
105
- rubygems_version: 2.0.7
112
+ rubygems_version: 1.8.11
106
113
  signing_key:
107
- specification_version: 4
114
+ specification_version: 3
108
115
  summary: Minimal yet extensible Ruby to JavaScript conversion.
109
116
  test_files: []
checksums.yaml DELETED
@@ -1,15 +0,0 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NDlkZTE5MGYzYzBlZDc0YzBmZmU5YmE2NWZmNTU1MWQwYWE1ZjVmNw==
5
- data.tar.gz: !binary |-
6
- ZWM4OGU4OTgzMGU0ZjAxMDc1MGFhOGNmMDU2YWZjOWU0ZmNjYWFjNg==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- YzNiMmJmNTg5MDBiZmQzMTc1OGQ5ZDNiNTEyYzZjN2FjNjlhNjAwYTA2NGVl
10
- ZWYxZjZiNDc0NDFhZjI1YmZlMjU3ZGNkOWVlNjEwNTg0NGFlZDhhZmNlZTBj
11
- YzI0OGYyZGRjMmI3NDRlODk0ZTNkYzBjNGQ4MDYxNWUwYzVhOGQ=
12
- data.tar.gz: !binary |-
13
- NGNiMTlkMGEyYzVjNDg3ODc2OGQ3ZDE1ODcyZTk0YjRiN2Q3MGNlZjlhNWQ1
14
- MmNlOGFiYjNlM2RiMjJmZDZjYzUxMzc5NGMzZjg4MmU3NDEwZjM3ZDg0NGNi
15
- NGRmOWQ3OWQwNzI3ZjNhZTNkNDA4MzEzY2YwODY2NWE1NTRmNTM=