jsrb 0.2.0 → 0.2.1

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
2
  SHA256:
3
- metadata.gz: 5b2cff3db7ac17a89b029bc7782e8735b7f9959c4595804c39537af8d6232077
4
- data.tar.gz: b7700dc452ea93b9ba17cbf381bb79acfd00747f6e653b309cf24174811598d3
3
+ metadata.gz: 364d2bd1fb5aa8359d2396ab0cba0063fc7679a41e7dfd476cce1cf431ff9c33
4
+ data.tar.gz: d4b4bc5e5ae5de748f87e431ddcd325386116cc3771a830d3eb688b0ff4a0e5c
5
5
  SHA512:
6
- metadata.gz: 934089fa92db021a2d90e738286de40dbc094286a5f0f4ed281626bd6d666b66d25d643ac4f117289b616c3c4b8d173fac5dcb86d29a645df7cf415c8c4e4939
7
- data.tar.gz: 9a1de7b3fd7a00e7facb9c633352e06a142ccb01680d819bba30d3f56c7be226682a00693098068ad85fee4a76ef83a3246377e2c85e5399553b6794adf6defb
6
+ metadata.gz: 8f1f633f7475c81868867379eee537c7bdd8ca84502a0849e86cc79dead234ef0a96147018b492b8da4e6fa5f60089809e56d9cb929ab055a705238540d74928
7
+ data.tar.gz: 8c61e38d356b3a64cf37cc4a6200756d234baaddc6c4df1b110c1510bc898bc0f537d866d79a15ded681ce543f1db196ae9963124b6668ac5e240540133c3790
data/.rubocop.yml CHANGED
@@ -32,6 +32,9 @@ Style/LambdaCall:
32
32
  Exclude:
33
33
  - 'spec/**/*'
34
34
 
35
+ Style/MethodMissingSuper:
36
+ Enabled: false
37
+
35
38
  Metrics/BlockLength:
36
39
  Exclude:
37
40
  - 'spec/**/*'
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --markup-provider=redcarpet
2
+ --markup=markdown
data/Gemfile CHANGED
@@ -7,6 +7,8 @@ gemspec
7
7
 
8
8
  group :development, :test do
9
9
  gem 'pry-byebug'
10
+ gem 'redcarpet'
10
11
  gem 'rubocop', '~> 0.61.0'
11
12
  gem 'simplecov'
13
+ gem 'yard'
12
14
  end
data/README.md CHANGED
@@ -6,7 +6,7 @@ Jsrb is a template engine to generate JavaScript code in simple Ruby DSL.
6
6
 
7
7
  # Getting Started
8
8
 
9
- Jsrb handler works in jsrb files, which contain `.js.jsrb` extension. All ruby syntax is available and `jsrb` is provided in it. You can construct JavaScript code via `jsrb`.
9
+ Jsrb handler works in `.jsrb` view files. All ruby syntax is available and `jsrb` is provided in it. You can construct JavaScript code via `jsrb`.
10
10
 
11
11
  ```rb
12
12
  name = jsrb.var!(:name) { 'foo' }
data/jsrb.gemspec CHANGED
@@ -25,6 +25,8 @@ Gem::Specification.new do |spec|
25
25
  'public gem pushes.'
26
26
  end
27
27
 
28
+ spec.metadata['yard.run'] = 'yri'
29
+
28
30
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
29
31
  f.match(%r{^(test|spec|features)/})
30
32
  end
data/lib/jsrb/base.rb CHANGED
@@ -1,11 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jsrb
4
+ # Base is a centralized class for Jsrb template.
5
+ # `jsrb`, accessed from views (i.e. `*.js.jsrb` files), is an instance of Jsrb::Base.
6
+ #
7
+ # Jsrb::Base provides some utilities to push statements,
8
+ # construct expressions and generate the final JavaScript code,
9
+ # with preserving a statement context to build the abstract syntax tree.
4
10
  class Base
5
11
  def initialize
6
12
  @context = JSStatementContext.new
7
13
  end
8
14
 
15
+ #
16
+ # Generates executable JavaScript code from current context.
17
+ #
18
+ # @return [String] generated executable JavaScript code
9
19
  def generate_code
10
20
  generator = self.class.code_generator_class.new
11
21
  generator.call type: 'Program',
@@ -13,6 +23,14 @@ module Jsrb
13
23
  body: @context.stacks.first
14
24
  end
15
25
 
26
+ #
27
+ # Pushes a VariableDeclaration to the current context
28
+ # and returns an access to created identifier.
29
+ #
30
+ # @param optional [Symbol] name a name of identifier, autogenerated if not specified.
31
+ # @yield optional block for initializer
32
+ # @yieldreturn an initializer expression (optional)
33
+ # @return [Jsrb::ExprChain] the expression that represents constructed new identifier
16
34
  def var!(id = nil)
17
35
  id ||= @context.gen_var_name!
18
36
  if block_given?
@@ -25,28 +43,57 @@ module Jsrb
25
43
  expr.member!(id)
26
44
  end
27
45
 
46
+ #
47
+ # Constructs a new conditional chain that **pushes an IfStatement** to the current context
48
+ # after the chain ended.
49
+ #
50
+ # @param [Jsrb::ExprChain, convertible ruby values] cond_expr an expression for the test
51
+ # @yield new context block for the consequent case
52
+ # @return [Jsrb::CondChain] condition chainable instance
28
53
  def if!(cond_expr, &block)
29
54
  CondChain.new(@context, false).elsif(cond_expr, &block)
30
55
  end
31
56
 
57
+ #
58
+ # Constructs a new conditional chain that **returns a conditional expression** after the chain ended.
59
+ #
60
+ # @param [Jsrb::ExprChain, convertible ruby values] cond_expr an expression for the test
61
+ # @yield new context block for the consequent case
62
+ # @return [Jsrb::CondChain] condition chainable instance
32
63
  def if(cond_expr, &block)
33
64
  CondChain.new(@context, true).elsif(cond_expr, &block)
34
65
  end
35
66
 
67
+ #
68
+ # Constructs a new expression chain with a given JavaScript AST node.
69
+ #
70
+ # @param optional [convertible ruby values] object represents JavaScript expression AST node
71
+ # @return [Jsrb::ExprChain] chainable instance
36
72
  def expr(object = nil)
37
73
  @context.new_expression(object)
38
74
  end
39
75
 
40
76
  class << self
41
- def code_generator_class
42
- @code_generator_class ||= Object.const_get(code_generator)
43
- end
44
-
77
+ #
78
+ # Shows JavaScript generator class name, 'Jsrb::NotFastGenerator' by default.
79
+ #
80
+ # ### **Help wanted!**
81
+ #
82
+ # *Jsrb::NotFastGenerator uses ExecJS and escodegen to generate JavaScript.
83
+ # It could be more efficient and get better error messages if you
84
+ # rewrite in Ruby*.
85
+ #
45
86
  def code_generator
46
87
  @code_generator || 'Jsrb::NotFastGenerator'
47
88
  end
48
89
 
49
90
  attr_writer :code_generator
91
+
92
+ private
93
+
94
+ def code_generator_class
95
+ @code_generator_class ||= Object.const_get(code_generator)
96
+ end
50
97
  end
51
98
  end
52
99
  end
@@ -1,15 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jsrb
4
+ # ExprChain is a builder class that constructs JavaScript expressions
5
+ # in natural phrase by chain method.
4
6
  class ExprChain
5
7
  JS_LOGICAL_OPS = %w[&& ||].freeze
6
8
 
9
+ # Returns a new instance.
10
+ # @param [Jsrb::JSStatementContext] context statement context instance
11
+ # @param optional [Hash] object hash represents AST node
7
12
  def initialize(context, object = nil)
8
13
  @context = context
9
14
  @object = object
10
15
  end
11
16
 
12
- # rubocop:disable Style/MethodMissingSuper
17
+ # Responds arbitrary method name
18
+ #
19
+ # ### `#...`, the last letter is neither `=` nor `?`, and no argument and no block
20
+ # constructs a **MemberExpression**
21
+ #
22
+ # ```rb
23
+ # obj = jsrb.expr['obj']
24
+ # obj.foo # represents `obj.foo`
25
+ # ```
26
+ #
27
+ # ### `#...`, the last letter is neither `=` nor `?`, at least one argument or block
28
+ # constructs a **CallExpression** of MemberExpression
29
+ #
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
+ # ```
35
+ #
36
+ # ### `#...=`
37
+ # pushes **ExpressionStatement** of a member assignment expression
38
+ #
39
+ # ```rb
40
+ # obj = jsrb.expr['obj']
41
+ # obj.foo = 100 # pushes `obj.foo = 100;` in the current context
42
+ # ```
43
+ #
44
+ # ### `#...?`
45
+ # constructs **ConditionalExpression**
46
+ #
47
+ # ```rb
48
+ # obj = jsrb.expr['obj']
49
+ # obj.foo?(100, 200) # represents `obj.foo ? 100 : 200`
50
+ # ```
51
+ #
13
52
  def method_missing(name, *args, &block)
14
53
  if (matches = name.to_s.match(/\A(.+)\?\z/))
15
54
  member!(matches[1]).cond?(*args)
@@ -23,22 +62,50 @@ module Jsrb
23
62
  member!(name.to_s).call(*args, &block)
24
63
  end
25
64
  end
26
- # rubocop:enable Style/MethodMissingSuper
27
65
 
66
+ #
67
+ # Constructs a **MemberExpression**
68
+ #
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
+ #
81
+ # @param [String, Symbol] value name of member
82
+ # @return [Jsrb::ExprChain] a chain represents **MemberExpression**
28
83
  def [](value)
29
84
  member!(value)
30
85
  end
31
86
 
32
- %i[** + - * / % >> << & ^ | <= < > >= == === != !].each do |operator|
33
- define_method(operator) do |*args|
34
- op!(operator, *args)
35
- end
36
- end
37
-
87
+ #
88
+ # Gets current wrapped AST node.
89
+ #
90
+ # @return [Hash, nil] current abstract syntax tree
38
91
  def unwrap!
39
92
  @object
40
93
  end
41
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]
42
109
  def as_statement!
43
110
  raise ArgumentError, "Can't chain as_statement! on empty context" unless @object
44
111
 
@@ -49,6 +116,27 @@ module Jsrb
49
116
  nil # chain ends
50
117
  end
51
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]
52
140
  def as_variable_declaration!(id)
53
141
  if @object
54
142
  @context.push(
@@ -78,6 +166,20 @@ module Jsrb
78
166
  end
79
167
  end
80
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]
81
183
  def as_return!
82
184
  @context.push(
83
185
  type: 'ReturnStatement',
@@ -85,6 +187,23 @@ module Jsrb
85
187
  )
86
188
  end
87
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**
88
207
  def member!(value)
89
208
  if @object
90
209
  self.class.new @context, type: 'MemberExpression',
@@ -97,6 +216,25 @@ module Jsrb
97
216
  end
98
217
  end
99
218
 
219
+ #
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
+ # ```
235
+ #
236
+ # @param [Jsrb::ExprChain, convertible ruby values] value RHS value of assignment
237
+ # @return [Jsrb::ExprChain] a chain represents **AssignmentExpression**
100
238
  def assign!(value)
101
239
  raise ArgumentError, "Can't chain assign! on empty context" unless @object
102
240
 
@@ -106,10 +244,47 @@ module Jsrb
106
244
  right: @context.ruby_to_js_ast(value)
107
245
  end
108
246
 
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]
109
264
  def set!(value)
110
265
  assign!(value).as_statement!
111
266
  end
112
267
 
268
+ #
269
+ # Constructs a **CallExpression**.
270
+ #
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
+ #
285
+ # @param [Jsrb::ExprChain, convertible ruby values] args arguments
286
+ # @yield optional block represents final argument
287
+ # @return [Jsrb::ExprChain] a chain represents **CallExpression**
113
288
  def call(*args, &block)
114
289
  raise ArgumentError, "Can't chain call on empty context" unless @object
115
290
 
@@ -122,6 +297,22 @@ module Jsrb
122
297
  arguments: js_args
123
298
  end
124
299
 
300
+ #
301
+ # Constructs a **NewExpression**.
302
+ #
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
+ #
314
+ # @param [Jsrb::ExprChain, convertible ruby values] args arguments
315
+ # @return [Jsrb::ExprChain] a chain represents **NewExpression**
125
316
  def new!(*args)
126
317
  raise ArgumentError, "Can't chain new! on empty context" unless @object
127
318
 
@@ -133,6 +324,26 @@ module Jsrb
133
324
  arguments: arguments
134
325
  end
135
326
 
327
+ #
328
+ # Constructs a **UnaryExpression**, **BinaryExpression** or **LogicalExpression**.
329
+ #
330
+ # All ruby-overridable operators can also be used:
331
+ # `** + - * / % >> << & ^ | <= < > >= == === != ! && ||`
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
+ #
344
+ # @param [String, Symbol] operator operator
345
+ # @param optional [Jsrb::ExprChain, convertible ruby values] args arguments contains RHS value
346
+ # @return [Jsrb::ExprChain] a chain represents **UnaryExpression**, **BinaryExpression** or **LogicalExpression**
136
347
  def op!(operator, *args)
137
348
  raise ArgumentError, "Can't chain op! on empty context" unless @object
138
349
 
@@ -146,6 +357,12 @@ module Jsrb
146
357
  end
147
358
  end
148
359
 
360
+ %i[** + - * / % >> << & ^ | <= < > >= == === != !].each do |operator|
361
+ define_method(operator) do |*args|
362
+ op!(operator, *args)
363
+ end
364
+ end
365
+
149
366
  private def _binary_op!(opstr, arg)
150
367
  if JS_LOGICAL_OPS.include? opstr
151
368
  self.class.new @context, type: 'LogicalExpression',
@@ -167,6 +384,23 @@ module Jsrb
167
384
  prefix: true
168
385
  end
169
386
 
387
+ #
388
+ # Constructs a **ConditionalExpression** whose test expression is the current expression.
389
+ #
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
+ #
401
+ # @param [Jsrb::ExprChain, convertible ruby values] consequent expression used in consequent
402
+ # @param [Jsrb::ExprChain, convertible ruby values] alternate expression used in alternate
403
+ # @return [Jsrb::ExprChain] a chain represents **ConditionalExpression**
170
404
  def cond?(consequent, alternate)
171
405
  raise ArgumentError, "Can't chain cond? on empty context" unless @object
172
406
 
@@ -176,12 +410,33 @@ module Jsrb
176
410
  alternate: @context.ruby_to_js_ast(alternate)
177
411
  end
178
412
 
413
+ #
414
+ # Constructs a **FunctionExpression** whose returned body is the current context.
415
+ #
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
+ #
427
+ # @param [String, Symbol] args argument identifiers
428
+ # @return [Jsrb::ExprChain] a chain represents **FunctionExpression**
179
429
  def for_all!(*args)
180
430
  raise ArgumentError, "Can't chain for_all! on empty context" unless @object
181
431
 
182
432
  self.class.new @context, type: 'FunctionExpression',
183
433
  id: nil,
184
- params: args.map { |arg| @context.ruby_to_js_ast(arg) },
434
+ params: args.map { |arg|
435
+ {
436
+ type: 'Identifier',
437
+ name: arg.to_s
438
+ }
439
+ },
185
440
  body: {
186
441
  type: 'BlockStatement',
187
442
  body: [
@@ -207,11 +462,31 @@ module Jsrb
207
462
  end
208
463
 
209
464
  class << self
465
+ #
466
+ # Adds a new chain method.
467
+ #
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
+ # ```
479
+ #
480
+ # @param [Symbol] chain_method_name name of a chain method
481
+ # @param [Symbol] function_name name of wrapper function
210
482
  def add_custom_chain(chain_method_name, function_name)
211
483
  @_custom_chains ||= {}
212
484
  @_custom_chains[chain_method_name] = function_name
213
485
  end
214
486
 
487
+ #
488
+ # Show custom chains.
489
+ #
215
490
  def custom_chains
216
491
  @_custom_chains || {}
217
492
  end
data/lib/jsrb/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jsrb
4
- VERSION = '0.2.0'
4
+ VERSION = '0.2.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shun MIZUKAMI
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-10 00:00:00.000000000 Z
11
+ date: 2019-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: execjs
@@ -120,6 +120,7 @@ files:
120
120
  - ".rubocop.yml"
121
121
  - ".ruby-version"
122
122
  - ".travis.yml"
123
+ - ".yardopts"
123
124
  - Gemfile
124
125
  - LICENSE.txt
125
126
  - README.md
@@ -139,6 +140,7 @@ licenses:
139
140
  - MIT
140
141
  metadata:
141
142
  allowed_push_host: https://rubygems.org
143
+ yard.run: yri
142
144
  post_install_message:
143
145
  rdoc_options: []
144
146
  require_paths: