ruby2js 3.0.2 → 3.0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: '009edffb834a3c8c298d4ad601160d30c4781891a4fe7b229e6dbc6f4284569e'
4
- data.tar.gz: 186d6e6ec33a0fee107623c3ed7994e9dea0741cf454bb79005acbb6be7f6429
2
+ SHA1:
3
+ metadata.gz: a00761caa853bb0ecbd0d4531a47373e5bfde02c
4
+ data.tar.gz: 96f676c25a71ea845b8f914303bd5a550e3a564d
5
5
  SHA512:
6
- metadata.gz: de2efb3d8d13a282af0401ce3a6216423564933483d3be4ed160eb1ef2bbf3092fdae6c9325d09298d6c86012de936c764178a0767a08b66b49b6d6d53bd9daa
7
- data.tar.gz: 3558c528e771c98fd50ece66410dd12d3a33c57af85e89e47a1c04f743f4a531cb971dbdf197291f8e6b1530d1895ea9c40aaf8814d64d094eb2f397092504a2
6
+ metadata.gz: b41112e1bce8ff9a65c3dd2fa12f25e7b7fdd460c80f022e24486ede24848cca05895c30b602189c001472dc3e7be00c032e8ab6b2324dccb27d71089beafdb6
7
+ data.tar.gz: 8656d7be7bc9a581216096b122fd8b887943bb02b9302706bfa0d047e12f0fadc20e07adbf3e17ce837492fae120bd0f9ca274bfad7dce9392e3627f687f102b
data/README.md CHANGED
@@ -45,6 +45,12 @@ mapped to
45
45
  [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),
46
46
  so avoid these if you wish to target users running IE8 or lower.
47
47
 
48
+ While both Ruby and JavaScript have open classes, Ruby unifies the syntax for
49
+ defining and extending an existing class, whereas JavaScript does not. This
50
+ means that Ruby2JS needs to be told when a class is being extended, which is
51
+ done by prepending the `class` keyword with two plus signs, thus:
52
+ `++class C; ...; end`.
53
+
48
54
  Filters may be provided to add Ruby-specific or framework specific
49
55
  behavior. Filters are essentially macro facilities that operate on
50
56
  an AST representation of the code.
data/lib/ruby2js.rb CHANGED
@@ -84,14 +84,16 @@ module Ruby2JS
84
84
  @ast = ast
85
85
  end
86
86
 
87
- # handle all of the 'invented' ast types
87
+ # handle all of the 'invented/synthetic' ast types
88
88
  def on_async(node); on_def(node); end
89
89
  def on_asyncs(node); on_defs(node); end
90
90
  def on_attr(node); on_send(node); end
91
91
  def on_autoreturn(node); on_return(node); end
92
92
  def on_await(node); on_send(node); end
93
93
  def on_call(node); on_send(node); end
94
+ def on_class_extend(node); on_send(node); end
94
95
  def on_constructor(node); on_def(node); end
96
+ def on_defm(node); on_defs(node); end
95
97
  def on_defp(node); on_defs(node); end
96
98
  def on_for_of(node); on_for(node); end
97
99
  def on_in?(node); on_send(node); end
@@ -19,7 +19,7 @@ module Ruby2JS
19
19
  :=== => :'!=='
20
20
  }
21
21
 
22
- GROUP_OPERATORS = [:begin, :dstr, :dsym, :and, :or]
22
+ GROUP_OPERATORS = [:begin, :dstr, :dsym, :and, :or, :casgn]
23
23
 
24
24
  VASGN = [:cvasgn, :ivasgn, :gvasgn, :lvasgn]
25
25
 
@@ -182,7 +182,7 @@ module Ruby2JS
182
182
  walk = proc do |ast|
183
183
  if ast.loc and ast.loc.expression
184
184
  filename = ast.loc.expression.source_buffer.name
185
- if filename
185
+ if filename and not filename.empty?
186
186
  filename = filename.dup.untaint
187
187
  @timestamps[filename] ||= File.mtime(filename)
188
188
  end
@@ -13,13 +13,39 @@ module Ruby2JS
13
13
  scope, @scope = @scope, false
14
14
  mark = output_location
15
15
 
16
- put 'switch ('; parse expr; puts ') {'
16
+ has_range = whens.any? do |node|
17
+ node.children.any? {|child| [:irange, :erange].include? child.type}
18
+ end
19
+
20
+ if has_range
21
+ # https://stackoverflow.com/questions/5619832/switch-on-ranges-of-integers-in-javascript
22
+ puts 'switch (true) {'
23
+ else
24
+ put 'switch ('; parse expr; puts ') {'
25
+ end
17
26
 
18
27
  whens.each_with_index do |node, index|
19
28
  puts '' unless index == 0
20
29
 
21
30
  *values, code = node.children
22
- values.each {|value| put 'case '; parse value; put ":#@ws"}
31
+
32
+ values.each do |value|
33
+ put 'case ';
34
+ if has_range
35
+ if value.type == :irange
36
+ parse expr; put ' >= '; parse value.children.first; put " && "
37
+ parse expr; put ' <= '; parse value.children.last; put ":#@ws"
38
+ elsif value.type == :erange
39
+ parse expr; put ' >= '; parse value.children.first; put " && "
40
+ parse expr; put ' < '; parse value.children.last; put ":#@ws"
41
+ else
42
+ parse expr; put ' == '; parse value; put ":#@ws"
43
+ end
44
+ else
45
+ parse value; put ":#@ws"
46
+ end
47
+ end
48
+
23
49
  parse code, :statement
24
50
  put "#{@sep}break#@sep" if other or index < whens.length-1
25
51
  end
@@ -8,10 +8,12 @@ module Ruby2JS
8
8
  multi_assign_declarations if @state == :statement
9
9
 
10
10
  begin
11
- if es2015
12
- put "const "
13
- else
14
- put "var "
11
+ if @state == :statement
12
+ if es2015
13
+ put "const "
14
+ else
15
+ put "var "
16
+ end
15
17
  end
16
18
 
17
19
  cbase ||= @rbstack.map {|rb| rb[var]}.compact.last
@@ -8,16 +8,24 @@ module Ruby2JS
8
8
 
9
9
  # NOTE: :prop and :method macros are defined at the bottom of this file
10
10
 
11
- handle :class do |name, inheritance, *body|
12
- if es2015
13
- parse @ast.updated(:class2)
14
- return
15
- end
11
+ # NOTE: class_extend is not generated by the parser, but instead produced
12
+ # when ++class is encountered; it signals that this construct is
13
+ # meant to extend an already existing JavaScrpt class.
16
14
 
17
- if inheritance
18
- init = s(:def, :initialize, s(:args), s(:super))
15
+ handle :class, :class_extend do |name, inheritance, *body|
16
+ if @ast.type == :class_extend
17
+ init = nil
19
18
  else
20
- init = s(:def, :initialize, s(:args), nil)
19
+ if es2015 and @ast.type != :class_extend
20
+ parse @ast.updated(:class2)
21
+ return
22
+ end
23
+
24
+ if inheritance
25
+ init = s(:def, :initialize, s(:args), s(:super))
26
+ else
27
+ init = s(:def, :initialize, s(:args), nil)
28
+ end
21
29
  end
22
30
 
23
31
  body.compact!
@@ -53,7 +61,7 @@ module Ruby2JS
53
61
  # method: add to prototype
54
62
  s(:method, s(:attr, name, :prototype),
55
63
  :"#{m.children[0].to_s.chomp('!')}=",
56
- s(:def, nil, *m.children[1..-1]))
64
+ s(:defm, nil, *m.children[1..-1]))
57
65
  end
58
66
  end
59
67
 
@@ -182,7 +190,10 @@ module Ruby2JS
182
190
  end
183
191
 
184
192
  # collapse sequence to a single assignment
185
- if methods > 1 or (methods == 1 and body[start].type == :prop)
193
+ if
194
+ @ast.type == :class and
195
+ (methods > 1 or (methods == 1 and body[start].type == :prop))
196
+ then
186
197
  pairs = body[start...start+methods].map do |node|
187
198
  if node.type == :method
188
199
  replacement = node.updated(:pair, [
@@ -209,9 +220,26 @@ module Ruby2JS
209
220
  end
210
221
 
211
222
  # prepend constructor
212
- constructor = init.updated(:constructor, [name, *init.children[1..-1]])
213
- @comments[constructor] = @comments[init] unless @comments[init].empty?
214
- body.unshift constructor
223
+ if init
224
+ constructor = init.updated(:constructor, [name, *init.children[1..-1]])
225
+ @comments[constructor] = @comments[init] unless @comments[init].empty?
226
+
227
+ if @ast.type == :class_extend
228
+ if es2015
229
+ constructor = s(:masgn, s(:mlhs,
230
+ s(:attr, s(:casgn, *name.children, constructor), :prototype)),
231
+ s(:array, s(:attr, name, :prototype)))
232
+ else
233
+ constructor = s(:send, s(:block, s(:send, nil, :proc),
234
+ s(:args, s(:shadowarg, :$_)), s(:begin,
235
+ s(:gvasgn, :$_, s(:attr, name, :prototype)),
236
+ s(:send, s(:casgn, *name.children, constructor),
237
+ :prototype=, s(:gvar, :$_)))), :[])
238
+ end
239
+ end
240
+
241
+ body.unshift constructor
242
+ end
215
243
 
216
244
  begin
217
245
  # save class name
@@ -7,7 +7,17 @@ module Ruby2JS
7
7
  # resolve anonymous receivers against rbstack
8
8
  receiver ||= @rbstack.map {|rb| rb[name]}.compact.last
9
9
 
10
- parse receiver; put '.' if receiver; put name
10
+ if receiver
11
+ if Parser::AST::Node === receiver and receiver.type == :cbase
12
+ put 'Function("return this")().'
13
+ else
14
+ parse receiver
15
+ put '.'
16
+ end
17
+
18
+ end
19
+
20
+ put name
11
21
  end
12
22
  end
13
23
  end
@@ -6,7 +6,7 @@ module Ruby2JS
6
6
  # (arg :x)
7
7
  # (...)
8
8
 
9
- handle :def, :async do |name, args, body=nil|
9
+ handle :def, :defm, :async do |name, args, body=nil|
10
10
  body ||= s(:begin)
11
11
  if name =~ /[!?]$/
12
12
  raise NotImplementedError, "invalid method name #{ name }"
@@ -91,7 +91,7 @@ module Ruby2JS
91
91
  put 'async ' if @ast.type == :async
92
92
 
93
93
  # es2015 fat arrow support
94
- if not name and es2015 and @state != :method
94
+ if not name and es2015 and @state != :method and @ast.type != :defm
95
95
  put '('; parse args; put ') => '
96
96
 
97
97
  expr = body
@@ -131,7 +131,16 @@ module Ruby2JS
131
131
  put ')'
132
132
 
133
133
  elsif [:-@, :+@, :~, '~'].include? method
134
- put method.to_s[0]; parse receiver
134
+ if
135
+ receiver.type == :send and
136
+ receiver.children[1] == :+@ and
137
+ Parser::AST::Node === receiver.children[0] and
138
+ receiver.children[0].type == :class
139
+ then
140
+ parse receiver.children[0].updated(:class_extend)
141
+ else
142
+ put method.to_s[0]; parse receiver
143
+ end
135
144
 
136
145
  elsif method == :=~
137
146
  parse args.first; put '.test('; parse receiver; put ')'
@@ -143,7 +152,8 @@ module Ruby2JS
143
152
  parse receiver; put '.push('; parse args.first; put ')'
144
153
 
145
154
  elsif method == :<=>
146
- raise NotImplementedError, "use of <=>"
155
+ parse receiver; put ' < '; parse args.first; put ' ? -1 : '
156
+ parse receiver; put ' > '; parse args.first; put ' ? 1 : 0'
147
157
 
148
158
  elsif OPERATORS.flatten.include?(method) and not LOGICAL.include?(method)
149
159
  (group_receiver ? group(receiver) : parse(receiver))
@@ -153,7 +163,7 @@ module Ruby2JS
153
163
  elsif method =~ /=$/
154
164
  multi_assign_declarations if @state == :statement
155
165
 
156
- parse receiver
166
+ (group_receiver ? group(receiver) : parse(receiver))
157
167
  put "#{ '.' if receiver }#{ method.to_s.sub(/=$/, ' =') } "
158
168
  parse args.first, (@state == :method ? :method : :expression)
159
169
 
@@ -195,7 +205,7 @@ module Ruby2JS
195
205
  end
196
206
  elsif args.length == 1 and args.first.type == :send
197
207
  # accommodation for JavaScript like new syntax w/argument list
198
- parse s(:send, s(:const, nil, args.first.children[1]), :new,
208
+ parse s(:send, s(:const, *args.first.children[0..1]), :new,
199
209
  *args.first.children[2..-1]), @state
200
210
  elsif args.length == 1 and args.first.type == :const
201
211
  # accommodation for JavaScript like new syntax w/o argument list
@@ -112,18 +112,26 @@ module Ruby2JS
112
112
  process S(:send, target, :==, s(:nil))
113
113
 
114
114
  elsif [:start_with?, :end_with?].include? method and args.length == 1
115
- if args.first.type == :str
116
- length = S(:int, args.first.children.first.length)
117
- else
118
- length = S(:attr, *args, :length)
119
- end
120
-
121
- if method == :start_with?
122
- process S(:send, S(:send, target, :substring, s(:int, 0),
123
- length), :==, *args)
115
+ if es2015
116
+ if method == :start_with?
117
+ process S(:send, target, :startsWith, *args)
118
+ else
119
+ process S(:send, target, :endsWith, *args)
120
+ end
124
121
  else
125
- process S(:send, S(:send, target, :slice,
126
- S(:send, length, :-@)), :==, *args)
122
+ if args.first.type == :str
123
+ length = S(:int, args.first.children.first.length)
124
+ else
125
+ length = S(:attr, *args, :length)
126
+ end
127
+
128
+ if method == :start_with?
129
+ process S(:send, S(:send, target, :substring, s(:int, 0),
130
+ length), :==, *args)
131
+ else
132
+ process S(:send, S(:send, target, :slice,
133
+ S(:send, length, :-@)), :==, *args)
134
+ end
127
135
  end
128
136
 
129
137
  elsif method == :clear and args.length == 0 and node.is_method?
@@ -2,7 +2,7 @@ module Ruby2JS
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 3
4
4
  MINOR = 0
5
- TINY = 2
5
+ TINY = 3
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
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: 3.0.2
4
+ version: 3.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Ruby
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-24 00:00:00.000000000 Z
11
+ date: 2018-03-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -130,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
130
  version: '0'
131
131
  requirements: []
132
132
  rubyforge_project:
133
- rubygems_version: 2.7.4
133
+ rubygems_version: 2.6.11
134
134
  signing_key:
135
135
  specification_version: 4
136
136
  summary: Minimal yet extensible Ruby to JavaScript conversion.