class Jsrb::ExprChain

ExprChain is a builder class that constructs JavaScript expressions in natural phrase by chaining methods.

Note that ExprChain does NOT add any statement to be rendered. If you want to add the expression as an ExpressionStatement, use `Jsrb::Base#do!`:

“`ruby js.do!(obj.foo = 100) # pushes a statement `obj.foo = 100;`. # where `obj.foo = 100` is still a chainable ExprChain instance, # so that we have to explicitly push it as statement with `js.do!`. “`

Constants

JS_LOGICAL_OPS

Attributes

object[R]

Gets a current wrapped AST node. @private @return [Hash, nil] current abstract syntax tree

Public Class Methods

add_custom_chain(chain_method_name, function_name) click to toggle source

Adds a new chain method.

@example

Jsrb::ExprChain.add_custom_chain('log_here', '__tap_log__')

js.expr[:foo][:bar].log_here # => ExprChain `__tap_log__(foo['bar'])`

@param [Symbol] chain_method_name name of a chain method @param [Symbol] function_name name of wrapper function

# File lib/jsrb/expr_chain.rb, line 278
def add_custom_chain(chain_method_name, function_name)
  @_custom_chains ||= {}
  @_custom_chains[chain_method_name] = function_name
end
custom_chains() click to toggle source

Show custom chains. @private

# File lib/jsrb/expr_chain.rb, line 285
def custom_chains
  @_custom_chains || {}
end
new(context, object = nil) click to toggle source

Returns a new instance. @private @param [Jsrb::JSStatementContext] context statement context instance @param [Hash, nil] object hash represents AST node

# File lib/jsrb/expr_chain.rb, line 29
def initialize(context, object = nil)
  @context = context
  @object = object
end

Public Instance Methods

[](value) click to toggle source

Constructs a MemberExpression

@example

obj = js.expr[:someObj]
obj[:field] # => ExprChain `someObj['field']`

@param [String, Symbol] value name of member @return [Jsrb::ExprChain] a chain represents MemberExpression

# File lib/jsrb/expr_chain.rb, line 84
def [](value)
  if @object
    self.class.new @context, type: 'MemberExpression',
                             computed: true,
                             object: @object,
                             property: @context.ruby_to_js_ast(value)
  else
    self.class.new @context, type: 'Identifier',
                             name: value.to_s
  end
end
_bind_chain!(function_name, *args, &block) click to toggle source

Internal method for custom chains. @private

# File lib/jsrb/expr_chain.rb, line 255
def _bind_chain!(function_name, *args, &block)
  raise ArgumentError, "Can't _bind_chain! on empty context" unless @object

  arg_asts = [@object]
  args.each do |arg|
    arg_asts << @context.ruby_to_js_ast(arg)
  end
  arg_asts << @context.ruby_to_js_ast(block) if block_given?
  self.class.new @context, type: 'CallExpression',
                           callee: self.class.new(@context)[function_name].object,
                           arguments: arg_asts
end
call(*args, &block) click to toggle source

Constructs a CallExpression.

@example

console = js.expr[:console]
console.log.('foo') # => ExprChain `console.log('foo')`
console.log.call('bar') # => ExprChain `console.log('foo')`

@param [Jsrb::ExprChain, convertible ruby values] args arguments @yield optional block as a final function argument @return [Jsrb::ExprChain] a chain represents CallExpression

# File lib/jsrb/expr_chain.rb, line 124
def call(*args, &block)
  raise ArgumentError, "Can't chain call on empty context" unless @object

  js_args = args.map do |arg|
    @context.ruby_to_js_ast(arg)
  end
  js_args << @context.ruby_to_js_ast(block) if block
  self.class.new @context, type: 'CallExpression',
                           callee: @object,
                           arguments: js_args
end
cond?(consequent, alternate) click to toggle source

Constructs a ConditionalExpression whose test expression is the current expression.

@example

(js.expr[:height] < 300).cond?('lo', 'hi') # => ExprChain `(height < 300) ? 'lo' : 'hi'`

@param [Jsrb::ExprChain, convertible ruby values] consequent expression used in consequent @param [Jsrb::ExprChain, convertible ruby values] alternate expression used in alternate @return [Jsrb::ExprChain] a chain represents ConditionalExpression

# File lib/jsrb/expr_chain.rb, line 215
def cond?(consequent, alternate)
  raise ArgumentError, "Can't chain cond? on empty context" unless @object

  self.class.new @context, type: 'ConditionalExpression',
                           test: @object,
                           consequent: @context.ruby_to_js_ast(consequent),
                           alternate: @context.ruby_to_js_ast(alternate)
end
forall(*args) click to toggle source

Constructs a FunctionExpression whose returned body is the current context.

@example

(js.expr[:x] * 2).forall(:x) # => ExprChain `function (x) { return x * 2; }`

@param [String, Symbol] args argument identifiers @return [Jsrb::ExprChain] a chain represents FunctionExpression

# File lib/jsrb/expr_chain.rb, line 231
def forall(*args)
  raise ArgumentError, "Can't chain forall on empty context" unless @object

  self.class.new @context, type: 'FunctionExpression',
                           id: nil,
                           params: args.map { |arg|
                             {
                               type: 'Identifier',
                               name: arg.to_s
                             }
                           },
                           body: {
                             type: 'BlockStatement',
                             body: [
                               {
                                 type: 'ReturnStatement',
                                 argument: @object
                               }
                             ]
                           }
end
method_missing(name, *args, &block) click to toggle source

Responds to arbitrary method names

@example

obj = js.expr[:obj]

# If the last character of the method name is neither `=` nor `?`,
# and no argument and no block given,
# it constructs a **MemberExpression**.

obj.foo # => ExprChain `obj.foo`

# If the last character of the method name is neither `=` nor `?`,
# at least one argument or block given,
# it constructs a **CallExpression** of MemberExpression.

obj.foo(100) # => ExprChain `obj.foo(100)`
obj.foo { |x| x + 1 } # ExprChain `obj.foo(function (x) { return x; })`

# If the last character of the method name is `=`,
# it constructs **AssignmentExpression** of a member assignment expression.

(obj.foo = 100) # ExprChain `obj.foo = 100`

# If the last character of the method name is `?`,
# it constructs **ConditionalExpression**.

obj.foo?(100, 200) # => ExprChain `obj.foo ? 100 : 200`
# File lib/jsrb/expr_chain.rb, line 62
def method_missing(name, *args, &block)
  if (matches = name.to_s.match(/\A(.+)\?\z/))
    self[matches[1]].cond?(*args)
  elsif (matches = name.to_s.match(/\A(.+)=\z/))
    self[matches[1]].set(*args)
  elsif (function_name = self.class.custom_chains[name.to_sym])
    _bind_chain!(function_name, *args, &block)
  elsif args.empty? && !block
    self[name.to_s]
  else
    self[name.to_s].call(*args, &block)
  end
end
new(*args) click to toggle source

Constructs a NewExpression.

@example

js.expr[:Date].new # => ExprChain `new Date`
js.expr[:Date].new(2020, 1, 1) # => ExprChain `new Date(2020, 1, 1)`

@param [Jsrb::ExprChain, convertible ruby values] args arguments @return [Jsrb::ExprChain] a chain represents NewExpression

# File lib/jsrb/expr_chain.rb, line 144
def new(*args)
  raise ArgumentError, "Can't chain new on empty context" unless @object

  arguments = args.map do |arg|
    @context.ruby_to_js_ast(arg)
  end
  self.class.new @context, type: 'NewExpression',
                           callee: @object,
                           arguments: arguments
end
op(operator, *args) click to toggle source

Constructs a UnaryExpression, BinaryExpression or LogicalExpression.

All ruby-overridable operators can also be used: `** + - * / % >> << & ^ | <= < > >= == === != ! && ||` @example

(js.expr[:n] < 100) # => ExprChain `n < 100`
!(js.expr[:b]) # => ExprChain `!b`
js.expr[:a].op('||', 'default') # => ExprChain `a || 'default'`

@param [String, Symbol] operator operator @param [Jsrb::ExprChain, convertible ruby values] args RHS value for binary operators @return [Jsrb::ExprChain] a chain represents UnaryExpression, BinaryExpression or LogicalExpression

# File lib/jsrb/expr_chain.rb, line 167
def op(operator, *args)
  raise ArgumentError, "Can't chain op on empty context" unless @object

  opstr = operator.to_s
  if args.size == 1
    _binary_op(opstr, *args)
  elsif args.empty?
    _unary_op(opstr)
  else
    raise ArgumentError, "#{opstr} is not a valid operator"
  end
end
set(value) click to toggle source

Constructs a AssignmentExpression whose RHS is the current expression.

@example

x = js.expr[:x]
y = js.expr[:y]
y.set(x.set 100) # => ExprChain `x = y = 100`

@param [Jsrb::ExprChain, convertible ruby values] value RHS value of assignment @return [Jsrb::ExprChain] a chain represents AssignmentExpression

# File lib/jsrb/expr_chain.rb, line 105
def set(value)
  raise ArgumentError, "Can't chain set on empty context" unless @object

  self.class.new @context, type: 'AssignmentExpression',
                           operator: '=',
                           left: @object,
                           right: @context.ruby_to_js_ast(value)
end

Private Instance Methods

_binary_op(opstr, arg) click to toggle source
# File lib/jsrb/expr_chain.rb, line 186
        def _binary_op(opstr, arg)
  if JS_LOGICAL_OPS.include? opstr
    self.class.new @context, type: 'LogicalExpression',
                             operator: opstr,
                             left: @object,
                             right: @context.ruby_to_js_ast(arg)
  else
    self.class.new @context, type: 'BinaryExpression',
                             operator: opstr,
                             left: @object,
                             right: @context.ruby_to_js_ast(arg)
  end
end
_unary_op(opstr) click to toggle source
# File lib/jsrb/expr_chain.rb, line 200
        def _unary_op(opstr)
  self.class.new @context, type: 'UnaryExpression',
                           operator: opstr,
                           argument: @object,
                           prefix: true
end