jsrb 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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