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
Gets a current wrapped AST node. @private @return [Hash, nil] current abstract syntax tree
Public Class Methods
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
Show custom chains. @private
# File lib/jsrb/expr_chain.rb, line 285 def custom_chains @_custom_chains || {} end
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
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
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
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
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
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
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
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
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
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
# 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
# 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