jsrb 0.2.1 → 0.3.0

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.
@@ -20,7 +20,6 @@ module Jsrb
20
20
  def end
21
21
  finalize(nil)
22
22
  end
23
- alias end! end
24
23
 
25
24
  private
26
25
 
@@ -39,7 +38,7 @@ module Jsrb
39
38
  else
40
39
  @context.push(
41
40
  type: 'ExpressionStatement',
42
- expression: if_value.unwrap!
41
+ expression: if_value.object
43
42
  )
44
43
  end
45
44
  end
@@ -47,20 +46,26 @@ module Jsrb
47
46
  def create_proc(block)
48
47
  lambda do
49
48
  final_alternate_stmt = block && @context.new_block do
50
- @context.new_expression(
51
- type: 'CallExpression',
52
- callee: @context.ruby_to_js_ast(block),
53
- arguments: []
54
- ).as_return!
49
+ @context.push(
50
+ type: 'ReturnStatement',
51
+ argument: {
52
+ type: 'CallExpression',
53
+ callee: @context.ruby_to_js_ast(block),
54
+ arguments: []
55
+ }
56
+ )
55
57
  end
56
58
  if_stmt = @stack.reverse.reduce(final_alternate_stmt) do |alternate_stmt, cond_block|
57
59
  condition_expr, cond_block = cond_block
58
60
  consequent_stmt = @context.new_block do
59
- @context.new_expression(
60
- type: 'CallExpression',
61
- callee: @context.ruby_to_js_ast(cond_block),
62
- arguments: []
63
- ).as_return!
61
+ @context.push(
62
+ type: 'ReturnStatement',
63
+ argument: {
64
+ type: 'CallExpression',
65
+ callee: @context.ruby_to_js_ast(cond_block),
66
+ arguments: []
67
+ }
68
+ )
64
69
  end
65
70
  stmt_ast = {
66
71
  type: 'IfStatement',
@@ -2,209 +2,86 @@
2
2
 
3
3
  module Jsrb
4
4
  # ExprChain is a builder class that constructs JavaScript expressions
5
- # in natural phrase by chain method.
5
+ # in natural phrase by chaining methods.
6
+ #
7
+ # Note that ExprChain does NOT add any statement to be rendered.
8
+ # If you want to add the expression as an **ExpressionStatement**,
9
+ # use `Jsrb::Base#do!`:
10
+ #
11
+ # ```ruby
12
+ # js.do!(obj.foo = 100) # pushes a statement `obj.foo = 100;`.
13
+ # # where `obj.foo = 100` is still a chainable ExprChain instance,
14
+ # # so that we have to explicitly push it as statement with `js.do!`.
15
+ # ```
6
16
  class ExprChain
7
17
  JS_LOGICAL_OPS = %w[&& ||].freeze
8
18
 
19
+ #
20
+ # Gets a current wrapped AST node.
21
+ # @private
22
+ # @return [Hash, nil] current abstract syntax tree
23
+ attr_reader :object
24
+
9
25
  # Returns a new instance.
26
+ # @private
10
27
  # @param [Jsrb::JSStatementContext] context statement context instance
11
- # @param optional [Hash] object hash represents AST node
28
+ # @param [Hash, nil] object hash represents AST node
12
29
  def initialize(context, object = nil)
13
30
  @context = context
14
31
  @object = object
15
32
  end
16
33
 
17
- # Responds arbitrary method name
18
34
  #
19
- # ### `#...`, the last letter is neither `=` nor `?`, and no argument and no block
20
- # constructs a **MemberExpression**
35
+ # Responds to arbitrary method names
21
36
  #
22
- # ```rb
23
- # obj = jsrb.expr['obj']
24
- # obj.foo # represents `obj.foo`
25
- # ```
37
+ # @example
38
+ # obj = js.expr[:obj]
26
39
  #
27
- # ### `#...`, the last letter is neither `=` nor `?`, at least one argument or block
28
- # constructs a **CallExpression** of MemberExpression
40
+ # # If the last character of the method name is neither `=` nor `?`,
41
+ # # and no argument and no block given,
42
+ # # it constructs a **MemberExpression**.
29
43
  #
30
- # ```rb
31
- # obj = jsrb.expr['obj']
32
- # obj.foo(100) # represents `obj.foo(100)`
33
- # obj.foo { |x| x + 1 } # represents `obj.foo(function (x) { return x; })`
34
- # ```
44
+ # obj.foo # => ExprChain `obj.foo`
35
45
  #
36
- # ### `#...=`
37
- # pushes **ExpressionStatement** of a member assignment expression
46
+ # # If the last character of the method name is neither `=` nor `?`,
47
+ # # at least one argument or block given,
48
+ # # it constructs a **CallExpression** of MemberExpression.
38
49
  #
39
- # ```rb
40
- # obj = jsrb.expr['obj']
41
- # obj.foo = 100 # pushes `obj.foo = 100;` in the current context
42
- # ```
50
+ # obj.foo(100) # => ExprChain `obj.foo(100)`
51
+ # obj.foo { |x| x + 1 } # ExprChain `obj.foo(function (x) { return x; })`
43
52
  #
44
- # ### `#...?`
45
- # constructs **ConditionalExpression**
53
+ # # If the last character of the method name is `=`,
54
+ # # it constructs **AssignmentExpression** of a member assignment expression.
46
55
  #
47
- # ```rb
48
- # obj = jsrb.expr['obj']
49
- # obj.foo?(100, 200) # represents `obj.foo ? 100 : 200`
50
- # ```
56
+ # (obj.foo = 100) # ExprChain `obj.foo = 100`
51
57
  #
58
+ # # If the last character of the method name is `?`,
59
+ # # it constructs **ConditionalExpression**.
60
+ #
61
+ # obj.foo?(100, 200) # => ExprChain `obj.foo ? 100 : 200`
52
62
  def method_missing(name, *args, &block)
53
63
  if (matches = name.to_s.match(/\A(.+)\?\z/))
54
- member!(matches[1]).cond?(*args)
64
+ self[matches[1]].cond?(*args)
55
65
  elsif (matches = name.to_s.match(/\A(.+)=\z/))
56
- member!(matches[1]).set!(*args)
66
+ self[matches[1]].set(*args)
57
67
  elsif (function_name = self.class.custom_chains[name.to_sym])
58
- bind_chain!(function_name, *args, &block)
68
+ _bind_chain!(function_name, *args, &block)
59
69
  elsif args.empty? && !block
60
- member!(name.to_s)
70
+ self[name.to_s]
61
71
  else
62
- member!(name.to_s).call(*args, &block)
72
+ self[name.to_s].call(*args, &block)
63
73
  end
64
74
  end
65
75
 
66
76
  #
67
77
  # Constructs a **MemberExpression**
68
78
  #
69
- # ```rb
70
- # x = jsrb.var! :x
71
- # obj = jsrb.expr['someObj']
72
- # x.set! obj['field']
73
- # ```
74
- #
75
- # will be
76
- #
77
- # ```js
78
- # x = someObj['field'];
79
- # ```
80
- #
79
+ # @example
80
+ # obj = js.expr[:someObj]
81
+ # obj[:field] # => ExprChain `someObj['field']`
81
82
  # @param [String, Symbol] value name of member
82
83
  # @return [Jsrb::ExprChain] a chain represents **MemberExpression**
83
84
  def [](value)
84
- member!(value)
85
- end
86
-
87
- #
88
- # Gets current wrapped AST node.
89
- #
90
- # @return [Hash, nil] current abstract syntax tree
91
- def unwrap!
92
- @object
93
- end
94
-
95
- #
96
- # pushes the current expression as a **ExpressionStatement** into the context.
97
- #
98
- # ```rb
99
- # jsrb.expr['console'].log('hello').as_statement!
100
- # ```
101
- #
102
- # will be
103
- #
104
- # ```js
105
- # console.log('hello');
106
- # ```
107
- #
108
- # @return [nil]
109
- def as_statement!
110
- raise ArgumentError, "Can't chain as_statement! on empty context" unless @object
111
-
112
- @context.push(
113
- type: 'ExpressionStatement',
114
- expression: @object
115
- )
116
- nil # chain ends
117
- end
118
-
119
- #
120
- # pushes a **VariableDeclaration** whose initializer is the current expression into the context.
121
- #
122
- # ```rb
123
- # name = jsrb.var!(:name) { 'foo' }
124
- # ary = jsrb.var! :ary
125
- # obj = jsrb.var! :obj
126
- # result = jsrb.var!
127
- # ```
128
- #
129
- # will be
130
- #
131
- # ```js
132
- # var name = 'foo';
133
- # var ary;
134
- # var obj;
135
- # var _v1; //<- auto generate variable name
136
- # ```
137
- #
138
- # @param [String, Symbol] id variable name
139
- # @return [nil]
140
- def as_variable_declaration!(id)
141
- if @object
142
- @context.push(
143
- type: 'VariableDeclaration',
144
- declarations: [{
145
- type: 'VariableDeclarator',
146
- id: {
147
- type: 'Identifier',
148
- name: id.to_s
149
- },
150
- init: @object
151
- }],
152
- kind: 'var'
153
- )
154
- else
155
- @context.push(
156
- type: 'VariableDeclaration',
157
- declarations: [{
158
- type: 'VariableDeclarator',
159
- id: {
160
- type: 'Identifier',
161
- name: id.to_s
162
- }
163
- }],
164
- kind: 'var'
165
- )
166
- end
167
- end
168
-
169
- #
170
- # pushes a **ReturnStatement** whose argument is the current expression into the context.
171
- #
172
- # ```rb
173
- # jsrb.expr['getItems'].().as_return!
174
- # ```
175
- #
176
- # will be
177
- #
178
- # ```js
179
- # return getItems();
180
- # ```
181
- #
182
- # @return [nil]
183
- def as_return!
184
- @context.push(
185
- type: 'ReturnStatement',
186
- argument: @object
187
- )
188
- end
189
-
190
- #
191
- # Constructs a **MemberExpression**
192
- #
193
- # ```rb
194
- # x = jsrb.var! :x
195
- # obj = jsrb.expr['someObj']
196
- # x.set! obj.member! 'field'
197
- # ```
198
- #
199
- # will be
200
- #
201
- # ```js
202
- # x = someObj['field'];
203
- # ```
204
- #
205
- # @param [String, Symbol] value name of member
206
- # @return [Jsrb::ExprChain] a chain represents **MemberExpression**
207
- def member!(value)
208
85
  if @object
209
86
  self.class.new @context, type: 'MemberExpression',
210
87
  computed: true,
@@ -217,26 +94,16 @@ module Jsrb
217
94
  end
218
95
 
219
96
  #
220
- # Constructs a **AssignmentExpression** whose left is current expression.
221
- #
222
- # ```rb
223
- # x = jsrb.var! :x
224
- # y = jsrb.var! :y
225
- # y.assign!(x.assign! 100).as_statement!
226
- # ```
227
- #
228
- # will be
229
- #
230
- # ```js
231
- # var x;
232
- # var y;
233
- # y = x = 100;
234
- # ```
97
+ # Constructs a **AssignmentExpression** whose RHS is the current expression.
235
98
  #
99
+ # @example
100
+ # x = js.expr[:x]
101
+ # y = js.expr[:y]
102
+ # y.set(x.set 100) # => ExprChain `x = y = 100`
236
103
  # @param [Jsrb::ExprChain, convertible ruby values] value RHS value of assignment
237
104
  # @return [Jsrb::ExprChain] a chain represents **AssignmentExpression**
238
- def assign!(value)
239
- raise ArgumentError, "Can't chain assign! on empty context" unless @object
105
+ def set(value)
106
+ raise ArgumentError, "Can't chain set on empty context" unless @object
240
107
 
241
108
  self.class.new @context, type: 'AssignmentExpression',
242
109
  operator: '=',
@@ -244,46 +111,15 @@ module Jsrb
244
111
  right: @context.ruby_to_js_ast(value)
245
112
  end
246
113
 
247
- #
248
- # Pushes an **ExpressionStatement** of the assignment expression.
249
- #
250
- # ```rb
251
- # x = jsrb.var! :x
252
- # x.set! 100
253
- # ```
254
- #
255
- # will be
256
- #
257
- # ```js
258
- # var x;
259
- # x = 100;
260
- # ```
261
- #
262
- # @param [Jsrb::ExprChain, convertible ruby values] value RHS value of assignment
263
- # @return [nil]
264
- def set!(value)
265
- assign!(value).as_statement!
266
- end
267
-
268
114
  #
269
115
  # Constructs a **CallExpression**.
270
116
  #
271
- # ```rb
272
- # x = jsrb.var! :x
273
- # console = jsrb.expr['console']
274
- # console.log.('foo').as_statement!
275
- # console.log.call('bar').as_statement!
276
- # ```
277
- #
278
- # will be
279
- #
280
- # ```js
281
- # console.log('foo');
282
- # console.log('bar');
283
- # ```
284
- #
117
+ # @example
118
+ # console = js.expr[:console]
119
+ # console.log.('foo') # => ExprChain `console.log('foo')`
120
+ # console.log.call('bar') # => ExprChain `console.log('foo')`
285
121
  # @param [Jsrb::ExprChain, convertible ruby values] args arguments
286
- # @yield optional block represents final argument
122
+ # @yield optional block as a final function argument
287
123
  # @return [Jsrb::ExprChain] a chain represents **CallExpression**
288
124
  def call(*args, &block)
289
125
  raise ArgumentError, "Can't chain call on empty context" unless @object
@@ -300,21 +136,13 @@ module Jsrb
300
136
  #
301
137
  # Constructs a **NewExpression**.
302
138
  #
303
- # ```rb
304
- # x = jsrb.var! :x
305
- # x.set! jsrb.expr['Date'].new!
306
- # ```
307
- #
308
- # will be
309
- #
310
- # ```js
311
- # x = new Date;
312
- # ```
313
- #
139
+ # @example
140
+ # js.expr[:Date].new # => ExprChain `new Date`
141
+ # js.expr[:Date].new(2020, 1, 1) # => ExprChain `new Date(2020, 1, 1)`
314
142
  # @param [Jsrb::ExprChain, convertible ruby values] args arguments
315
143
  # @return [Jsrb::ExprChain] a chain represents **NewExpression**
316
- def new!(*args)
317
- raise ArgumentError, "Can't chain new! on empty context" unless @object
144
+ def new(*args)
145
+ raise ArgumentError, "Can't chain new on empty context" unless @object
318
146
 
319
147
  arguments = args.map do |arg|
320
148
  @context.ruby_to_js_ast(arg)
@@ -329,29 +157,21 @@ module Jsrb
329
157
  #
330
158
  # All ruby-overridable operators can also be used:
331
159
  # `** + - * / % >> << & ^ | <= < > >= == === != ! && ||`
332
- #
333
- # ```rb
334
- # x = jsrb.var! :x
335
- # x.set!(jsrb.expr['a'] < 100)
336
- # ```
337
- #
338
- # will be
339
- #
340
- # ```js
341
- # x = a < 100;
342
- # ```
343
- #
160
+ # @example
161
+ # (js.expr[:n] < 100) # => ExprChain `n < 100`
162
+ # !(js.expr[:b]) # => ExprChain `!b`
163
+ # js.expr[:a].op('||', 'default') # => ExprChain `a || 'default'`
344
164
  # @param [String, Symbol] operator operator
345
- # @param optional [Jsrb::ExprChain, convertible ruby values] args arguments contains RHS value
165
+ # @param [Jsrb::ExprChain, convertible ruby values] args RHS value for binary operators
346
166
  # @return [Jsrb::ExprChain] a chain represents **UnaryExpression**, **BinaryExpression** or **LogicalExpression**
347
- def op!(operator, *args)
348
- raise ArgumentError, "Can't chain op! on empty context" unless @object
167
+ def op(operator, *args)
168
+ raise ArgumentError, "Can't chain op on empty context" unless @object
349
169
 
350
170
  opstr = operator.to_s
351
171
  if args.size == 1
352
- _binary_op!(opstr, *args)
172
+ _binary_op(opstr, *args)
353
173
  elsif args.empty?
354
- _unary_op!(opstr)
174
+ _unary_op(opstr)
355
175
  else
356
176
  raise ArgumentError, "#{opstr} is not a valid operator"
357
177
  end
@@ -359,11 +179,11 @@ module Jsrb
359
179
 
360
180
  %i[** + - * / % >> << & ^ | <= < > >= == === != !].each do |operator|
361
181
  define_method(operator) do |*args|
362
- op!(operator, *args)
182
+ op(operator, *args)
363
183
  end
364
184
  end
365
185
 
366
- private def _binary_op!(opstr, arg)
186
+ private def _binary_op(opstr, arg)
367
187
  if JS_LOGICAL_OPS.include? opstr
368
188
  self.class.new @context, type: 'LogicalExpression',
369
189
  operator: opstr,
@@ -377,7 +197,7 @@ module Jsrb
377
197
  end
378
198
  end
379
199
 
380
- private def _unary_op!(opstr)
200
+ private def _unary_op(opstr)
381
201
  self.class.new @context, type: 'UnaryExpression',
382
202
  operator: opstr,
383
203
  argument: @object,
@@ -387,17 +207,8 @@ module Jsrb
387
207
  #
388
208
  # Constructs a **ConditionalExpression** whose test expression is the current expression.
389
209
  #
390
- # ```rb
391
- # x = jsrb.var! :x
392
- # x.set! (jsrb.expr['height'] < 300).cond?('lo', 'hi')
393
- # ```
394
- #
395
- # will be
396
- #
397
- # ```js
398
- # x = (height < 300) ? 'lo' : 'hi';
399
- # ```
400
- #
210
+ # @example
211
+ # (js.expr[:height] < 300).cond?('lo', 'hi') # => ExprChain `(height < 300) ? 'lo' : 'hi'`
401
212
  # @param [Jsrb::ExprChain, convertible ruby values] consequent expression used in consequent
402
213
  # @param [Jsrb::ExprChain, convertible ruby values] alternate expression used in alternate
403
214
  # @return [Jsrb::ExprChain] a chain represents **ConditionalExpression**
@@ -413,21 +224,12 @@ module Jsrb
413
224
  #
414
225
  # Constructs a **FunctionExpression** whose returned body is the current context.
415
226
  #
416
- # ```rb
417
- # f = jsrb.var! :f
418
- # f.set! (jsrb.expr['x'] * 2).for_all(:x)
419
- # ```
420
- #
421
- # will be
422
- #
423
- # ```js
424
- # f = function (x) { return x * 2; };
425
- # ```
426
- #
227
+ # @example
228
+ # (js.expr[:x] * 2).forall(:x) # => ExprChain `function (x) { return x * 2; }`
427
229
  # @param [String, Symbol] args argument identifiers
428
230
  # @return [Jsrb::ExprChain] a chain represents **FunctionExpression**
429
- def for_all!(*args)
430
- raise ArgumentError, "Can't chain for_all! on empty context" unless @object
231
+ def forall(*args)
232
+ raise ArgumentError, "Can't chain forall on empty context" unless @object
431
233
 
432
234
  self.class.new @context, type: 'FunctionExpression',
433
235
  id: nil,
@@ -448,8 +250,10 @@ module Jsrb
448
250
  }
449
251
  end
450
252
 
451
- def bind_chain!(function_name, *args, &block)
452
- raise ArgumentError, "Can't bind_chain! on empty context" unless @object
253
+ # Internal method for custom chains.
254
+ # @private
255
+ def _bind_chain!(function_name, *args, &block)
256
+ raise ArgumentError, "Can't _bind_chain! on empty context" unless @object
453
257
 
454
258
  arg_asts = [@object]
455
259
  args.each do |arg|
@@ -457,7 +261,7 @@ module Jsrb
457
261
  end
458
262
  arg_asts << @context.ruby_to_js_ast(block) if block_given?
459
263
  self.class.new @context, type: 'CallExpression',
460
- callee: self.class.new(@context).member!(function_name).unwrap!,
264
+ callee: self.class.new(@context)[function_name].object,
461
265
  arguments: arg_asts
462
266
  end
463
267
 
@@ -465,18 +269,10 @@ module Jsrb
465
269
  #
466
270
  # Adds a new chain method.
467
271
  #
468
- # ```rb
469
- # Jsrb::ExprChain.add_custom_chain('log_here', '__tap_log__')
470
- #
471
- # jsrb.expr['foo']['bar'].log_here.as_statement!
472
- # ```
473
- #
474
- # will be
475
- #
476
- # ```js
477
- # __tap_log__(foo['bar']);
478
- # ```
272
+ # @example
273
+ # Jsrb::ExprChain.add_custom_chain('log_here', '__tap_log__')
479
274
  #
275
+ # js.expr[:foo][:bar].log_here # => ExprChain `__tap_log__(foo['bar'])`
480
276
  # @param [Symbol] chain_method_name name of a chain method
481
277
  # @param [Symbol] function_name name of wrapper function
482
278
  def add_custom_chain(chain_method_name, function_name)
@@ -484,9 +280,8 @@ module Jsrb
484
280
  @_custom_chains[chain_method_name] = function_name
485
281
  end
486
282
 
487
- #
488
283
  # Show custom chains.
489
- #
284
+ # @private
490
285
  def custom_chains
491
286
  @_custom_chains || {}
492
287
  end