lib/jsrb/base.rb in jsrb-0.2.1 vs lib/jsrb/base.rb in jsrb-0.3.0
- old
+ new
@@ -1,15 +1,16 @@
# frozen_string_literal: true
module Jsrb
- # Base is a centralized class for Jsrb template.
- # `jsrb`, accessed from views (i.e. `*.js.jsrb` files), is an instance of Jsrb::Base.
+ # `Jsrb::Base` is a centralized class for Jsrb template.
+ # `js`, accessed from views (i.e. `*.jsrb` files), is an instance of Jsrb::Base.
#
- # Jsrb::Base provides some utilities to push statements,
- # construct expressions and generate the final JavaScript code,
- # with preserving a statement context to build the abstract syntax tree.
+ # `Jsrb::Base` provides the interface for pushing statements,
+ # constructing expressions and generating JavaScript outputs
+ # with handling an internal statement context properly.
class Base
+ # @private
def initialize
@context = JSStatementContext.new
end
#
@@ -22,76 +23,206 @@
sourceType: 'script',
body: @context.stacks.first
end
#
- # Pushes a VariableDeclaration to the current context
- # and returns an access to created identifier.
+ # **Pushes** an ExpressionStatement to the current context
#
- # @param optional [Symbol] name a name of identifier, autogenerated if not specified.
+ # @example
+ # js.do!(js.expr[:x].set(100))
+ # # =>
+ # # x = 100;
+ # @param [Jsrb::ExprChain] expr target expression.
+ # @return [nil]
+ def do!(expr)
+ ast = expr.object
+ raise ArgumentError, 'Expression is empty' unless ast
+
+ @context.push(
+ type: 'ExpressionStatement',
+ expression: ast
+ )
+ end
+
+ #
+ # **Pushes** an assignment statement `lhs = rhs;`.
+ # This is a short hand of `js.do! lhs_expr.set(rhs_expr)`
+ #
+ # @example
+ # a = js.var! :a
+ # js.set!(a, 'dog')
+ # # =>
+ # # var a;
+ # # a = 'dog';
+ #
+ # # directly pass a symbol of identifier name
+ # js.set!(:x, 100)
+ # # =>
+ # # x = 100;
+ # @param [Jsrb::ExprChain, String, Symbol] expr target expression.
+ # @return [nil]
+ def set!(lhs, rhs)
+ lhs_expr = lhs.is_a?(ExprChain) ? lhs : expr(lhs)
+ do! lhs_expr.set(rhs)
+ end
+
+ #
+ # **Pushes** a VariableDeclaration to the current context
+ # and returns an access to the created identifier.
+ # @example
+ # name = js.var!(:name) { 'foo' }
+ # # var name = 'foo';
+ #
+ # ary = js.var! :ary
+ # # var ary;
+ #
+ # obj = js.var! :obj
+ # # var obj;
+ #
+ # result = js.var!
+ # # var _v1; //<- auto generate variable name
+ # @param [Symbol] name a name of identifier, autogenerated if not given
# @yield optional block for initializer
- # @yieldreturn an initializer expression (optional)
- # @return [Jsrb::ExprChain] the expression that represents constructed new identifier
+ # @yieldreturn an initializer expression
+ # @return [Jsrb::ExprChain] the expression which represents a newly created identifier
def var!(id = nil)
id ||= @context.gen_var_name!
- if block_given?
- raw_expr = yield
- val = raw_expr.is_a?(ExprChain) ? raw_expr : expr(@context.ruby_to_js_ast(raw_expr))
- val.as_variable_declaration!(id)
+ val_ast =
+ if block_given?
+ raw_expr = yield
+ raw_expr.is_a?(ExprChain) ? raw_expr.unwrap : @context.ruby_to_js_ast(raw_expr)
+ end
+ if val_ast
+ @context.push(
+ type: 'VariableDeclaration',
+ declarations: [{
+ type: 'VariableDeclarator',
+ id: {
+ type: 'Identifier',
+ name: id.to_s
+ },
+ init: val_ast
+ }],
+ kind: 'var'
+ )
else
- expr.as_variable_declaration!(id)
+ @context.push(
+ type: 'VariableDeclaration',
+ declarations: [{
+ type: 'VariableDeclarator',
+ id: {
+ type: 'Identifier',
+ name: id.to_s
+ }
+ }],
+ kind: 'var'
+ )
end
- expr.member!(id)
+ expr[id]
end
#
- # Constructs a new conditional chain that **pushes an IfStatement** to the current context
- # after the chain ended.
+ # **Starts** a new conditional chain that pushes an IfStatement
+ # to the current context at end.
#
+ # @example
+ # js.if!(expr1) {
+ # # ..
+ # }.elsif(expr2) {
+ # # ..
+ # }.else {
+ # # ..
+ # }
+ # # =>
+ # # // The actual output will have certain immediate functions
+ # # // that preserve variable scope for each case.
+ # # if (..expr1..) {
+ # # // ..
+ # # } else if (..expr2..) {
+ # # // ..
+ # # } else {
+ # # // ..
+ # # }
+ #
+ # # If you don't have else clause, close with `#end`.
+ # js.if!(expr1) {
+ # # ..
+ # }.elsif(expr2) {
+ # # ..
+ # }.end
+ # # =>
+ # # if (..expr1..) {
+ # # // ..
+ # # } else if (..expr2..) {
+ # # // ..
+ # # }
# @param [Jsrb::ExprChain, convertible ruby values] cond_expr an expression for the test
# @yield new context block for the consequent case
# @return [Jsrb::CondChain] condition chainable instance
def if!(cond_expr, &block)
CondChain.new(@context, false).elsif(cond_expr, &block)
end
#
- # Constructs a new conditional chain that **returns a conditional expression** after the chain ended.
+ # **Constructs** a new conditional chain that
+ # represents a conditional expression at end.
#
+ # @example
+ # result = var!
+ # js.do! result, js.if(expr1) {
+ # result_expr1
+ # }.elsif(expr2) {
+ # result_expr2
+ # }.else {
+ # result_expr3
+ # }
+ # # =>
+ # # // The actual output will have certain immediate functions
+ # # // that preserve variable scope for each case.
+ # # var _v1;
+ # # _v1 = (function() {
+ # # if (..expr1..) {
+ # # return ..result_expr1..;
+ # # } else if (..expr2..) {
+ # # return ..result_expr2..;
+ # # } else {
+ # # return ..result_expr3..;
+ # # }
+ # # })()
# @param [Jsrb::ExprChain, convertible ruby values] cond_expr an expression for the test
# @yield new context block for the consequent case
# @return [Jsrb::CondChain] condition chainable instance
def if(cond_expr, &block)
CondChain.new(@context, true).elsif(cond_expr, &block)
end
#
- # Constructs a new expression chain with a given JavaScript AST node.
+ # **Constructs** a new expression chain with a given JavaScript AST node.
#
- # @param optional [convertible ruby values] object represents JavaScript expression AST node
+ # @param [convertible ruby values] object represents JavaScript expression AST node
# @return [Jsrb::ExprChain] chainable instance
def expr(object = nil)
@context.new_expression(object)
end
class << self
#
- # Shows JavaScript generator class name, 'Jsrb::NotFastGenerator' by default.
+ # Shows JavaScript generator class name, `'Jsrb::NotFastGenerator'` by default.
#
# ### **Help wanted!**
#
# *Jsrb::NotFastGenerator uses ExecJS and escodegen to generate JavaScript.
- # It could be more efficient and get better error messages if you
- # rewrite in Ruby*.
+ # It could be more efficient and get better error messages if we
+ # implement it in Ruby*.
#
+ # @return [String] class name of a code generator
def code_generator
@code_generator || 'Jsrb::NotFastGenerator'
end
attr_writer :code_generator
- private
-
+ # @private
def code_generator_class
@code_generator_class ||= Object.const_get(code_generator)
end
end
end