Jsrb
Jsrb is a template engine to generate JavaScript code in simple Ruby DSL.
Getting Started
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
.
name = jsrb.var!(:name) { 'foo' }
ary = jsrb.var! :ary
obj = jsrb.var! :obj
result = jsrb.var!
# var name = 'foo';
# var ary;
# var obj;
# var _v1; <- auto generate variable name
ary.set!([1, 2, 3])
# ary = [1, 2, 3];
obj.set!(
name: name,
profile: {
age: 20,
sex: 'm'
}
)
# obj = {
# name: name,
# profile: {
# age: 20,
# sex: 'm'
# }
# };
result.set! (obj.name + "(" + obj.profile.age + ")")
# _v1 = obj.name + "(" + obj.profile.age + ")";
ary.set! ary.map { |x| x * 2 }
# ary = ary.map(function(x) {
# return x * 2;
# });
jsrb.if!(ary[1] === 4) {
result.set! 'four'
}.elsif(ary[1] === 2) {
result.set! 'two'
}.else {
result.set! 'other'
}
# // the actual output doesn't looks like this, but will be better code in regard to variable scope.
# if (ary[1] === 4) {
# _v1 = 'four'
# } else if (ary[1] === 2) {
# _v1 = 'two'
# } else {
# _v1 = 'other'
# }
result.set! jsrb.expr.Date.new!
# _v1 = new Date;
jsrb.expr.console.log('hello').as_statement!
# console.log('hello');
Usage
Statements
Variable declaration
jsrb.var!
pushes a VariableDeclaration into current context.
# with variable name and initializer
jsrb.var!('varname') { 100 }
# var varname = 100;
# without initializer
jsrb.var!('varname')
# var varname;
# variable name is auto-generated if not specified
jsrb.var!
# var _v1;
# var! returns Jsrb::ExprChain instance, so that you can
# assign value with `.set!` method.
a = jsrb.var!
a.set! 100
# var _v1;
# v1 = 100;
If statement, and conditional expression
jsrb.if!
pushes an IfStatement into current context, jsrb.if
to build a conditional expression.
# start with `#if!`
# and chain `#elsif` to add next case.
# Note that this is a statement, not expression.
jsrb.if!(v === 1) {
# ..
}.elsif(v === 2) {
# ..
}.else {
# ..
}
# if you don't need else clause, close with `#end`
jsrb.if!(v === 1) {
# ..
}.end
# if you want to regard this as an expression, use `#if` without exclamation.
v.set! jsrb.if(v === 1) {
# ..
}.else {
# ..
}
Assignment statement
ExprChain#set!
pushes an assignment statement (ExpressionStatement of AssignmentExpression).
a = jsrb.var! :a
a.set! 100
# var a;
# a = 100;
Expression statement
ExprChain#as_statement!
pushes an ExpressionStatement of the left hand side of chain.
get_elements = jsrb.expr['getElements']
get_elements.('.foo').forEach { |n| n.delete.() }.as_statement!
# getElements('.foo').forEach(function(n) { return n.delete(); });
Expression chain
Expression chain (ExprChain
class) is an utility class to construct JavaScript expressions.
Initialize with wrapping a ruby value
jsrb.expr
create a new ExprChain
instance, taking an initial value optionally.
Some methods in jsrb automatically convert ruby expression to ExprChain.
x = jsrb.var! :x
x.set! jsrb.expr(100)
# x = 100;
# set! automatically wrap with ExprChain.
x.set! 100
# x = 100;
# If you need to compare by operator with another ExprChain,
# you have to wrap first.
x.set!(jsrb.expr(100) < jsrb.expr.y)
# x.set!(100 < jsrb.expr.y) will fail.
Chains
Member expression
ExprChain#[], #member!, #..
constructs MemberExpression.
#[]
and #member!
is safe. #..
can be used only if the name has no conflict.
x = jsrb.var! :x
obj = jsrb.expr['someObj']
# jsrb.expr with no argument constructs empty chain,
# in which every member chain will be an identifier.
x.set! obj.field
# x = someObj['field'];
x.set! obj['field']
# x = someObj['field'];
x.set! obj.member! 'field'
# x = someObj['field'];
x.set! obj.send # NOTE that this is interpreted as a ruby Object's method, and causes an error.
Function Call
ExprChain#call, so #.(), #.. with argument or block
constructs CallExpression.
x = jsrb.var! :x
console = jsrb.expr['console']
# using call method
console.log.('foo').as_statement!
# console.log('foo')
console.log.call('foo').as_statement!
# console.log('foo')
# using dynamic method
# if #..() has at least one argument or block, it will be a call expression.
console.log('foo').as_statement!
# console.log('foo')
x.map { |item| item.field }.as_statement!
# x.map(function(item) { return item.field; });
Operators
Any ruby-overridable and JS-existing operators are overridden for chaining.
Supported operators are: ** + - * / % >> << & ^ | <= < > >= == === != ! && ||
.
x = jsrb.var! :x
a = jsrb.expr['a']
x.set!(a === 1)
# x = a === 1;
x.set!(a * a)
# x = a * a;
x.set!(3 * a) # raises an error because Fixnum does not accept ExprChain as RHS.
New
ExprChain#new!
constructs NewExpression.
x = jsrb.var! :x
x.set! jsrb.expr['Date'].new!
# x = new Date;
Function expression
ExprChain#for_all!
constructs FunctionExpression. You can also construct it from Proc directly or passing a block.
ary = jsrb.var! :ary, [1, 2, 3]
ary.map((jsrb.expr['x'] * 2).for_all!('x')).as_statement!
# ary.map(function(x) { return x * 2; });
ary.map { |x| x * 2 }.as_statement!
# ary.map(function(x) { return x * 2; });
ary.map(->(x) { x * 2 }).as_statement!
# ary.map(function(x) { return x * 2; });
Conversion
Every Ruby's generic value will be converted to Javascript value by following rule:
Ruby value | JavaScript |
---|---|
finite 100 |
100 |
NaN Float::NAN |
NaN |
+Infinity Float::INFINITY |
Number.POSITIVE_INFINITY |
-Infinity Float::INFINITY |
Number.NEGATIVE_INFINITY |
true |
true |
false |
false |
:foo |
"foo" |
"foo" |
"foo" |
nil |
null |
[1, 2, 3] |
[1, 2, 3] |
{ foo: 'bar' } |
{ "foo": "bar" } |
->(x, y, z) { x + y + z } |
function (x, y, z) { return x + y + z; } |
Customize Chain
You can add custom chain methods in ExprChain
via Jsrb::ExprChain.#add_custom_chain
.
Jsrb::ExprChain.add_custom_chain('log_here', '__tap_log__')
jsrb.expr['foo']['bar'].log_here.as_statement!
# __tap_log__(foo['bar']);
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/effective-spa/jsrb.
License
The gem is available as open source under the terms of the MIT License.