README.md in jsrb-0.1.0 vs README.md in jsrb-0.2.0

- old
+ new

@@ -1,15 +1,295 @@ -[![CircleCI](https://circleci.com/gh/effective-spa/jsrb.svg?style=svg)](https://circleci.com/gh/effective-spa/jsrb) [![codecov](https://codecov.io/gh/effective-spa/jsrb/branch/master/graph/badge.svg)](https://codecov.io/gh/effective-spa/jsrb) [![Maintainability](https://api.codeclimate.com/v1/badges/8bf4666ed7b983f01519/maintainability)](https://codeclimate.com/github/effective-spa/jsrb/maintainability) +[![CircleCI](https://circleci.com/gh/effective-spa/jsrb.svg?style=svg)](https://circleci.com/gh/effective-spa/jsrb) [![codecov](https://codecov.io/gh/effective-spa/jsrb/branch/master/graph/badge.svg)](https://codecov.io/gh/effective-spa/jsrb) [![Maintainability](https://api.codeclimate.com/v1/badges/8bf4666ed7b983f01519/maintainability)](https://codeclimate.com/github/effective-spa/jsrb/maintainability) [![Gem Version](https://badge.fury.io/rb/jsrb.svg)](https://badge.fury.io/rb/jsrb) -**This library is still development version. It's currently not recommended to be used in production environment.** - # Jsrb Jsrb is a template engine to generate JavaScript code in simple Ruby DSL. -## Usage +# Getting Started -TODO +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`. + +```rb +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. + +```rb +# 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**. + +```rb +# 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). + +```rb +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. + +```rb +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. + +```rb +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. + +```rb +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**. + +```rb +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: `** + - * / % >> << & ^ | <= < > >= == === != ! && ||`. + +```rb +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**. + +```rb +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. + +```rb +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`. + +```rb +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.