ruby2js 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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=