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.