diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2a19a59
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/db/*
diff --git a/README.md b/README.md
index 4b83a43..7162b1b 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,142 @@
-# FlatDB
-A simple NoSQL database written in Lua
+FlatDB
+===========
+
+FlatDB is a lua library that implements a serverless, zero-configuration, NoSQL database engine.
+It provides a key-value storage system using plain Lua tables.
+
+When To Use FlatDB
+===========
+
+When you want to use SQLite to store data, just take a glance at FlatDB.
+When Lua acts in your program as the major language or the embedded scripting language, just try using FlatDB.
+
+Usage
+==========
+Copy *flatdb.lua* file to your project or where your lua libraries stored.
+Then write this in any Lua file where you want to use it:
+```lua
+local flatdb = require 'flatdb'
+```
+
+1. Bind a directory as a database
+```lua
+local db = flatdb('./db')
+```
+
+2. Open or create a book
+```lua
+if not db.book then
+ db.book = {}
+end
+```
+
+3. Store key-value items
+```lua
+db.book.key = 'value'
+-- equivalent to db.book['key'] = 'value'
+```
+
+4. Retrieve items
+```lua
+print(db.book.key) -- prints 'value'
+```
+
+5. Save to file
+```lua
+db:save()
+-- 'book' will be saved to './db/book'
+```
+
+More usage can be found in the *cli.lua*(a Redis-like command line interface example using FlatDB).
+
+Quick Look
+==========
+
+```lua
+local flatdb = require("flatdb")
+
+-- open a directory as a database, 'db' is just a plain empty Lua table that can contain books
+local db = flatdb("./db")
+
+-- open or create a book named "default", it also a plain empty Lua table where key-value data stored in
+if not db.default then
+ db.default = {}
+end
+
+-- extend db methods for getting values from 'default' book
+flatdb.hack.get = function(db, key)
+ return db.default[key]
+end
+
+-- extend db methods for setting values to 'default' book
+flatdb.hack.set = function(db, key, value)
+ db.default[key] = value
+end
+
+-- extend db methods for watching new key
+flatdb.hack.guard = function(db, f)
+ setmetatable(db.default, {__newindex = f})
+end
+
+-- get key-value data from 'default' book
+print(db:get("hello"))
+
+-- set key-value data to 'default' book
+db:set("hello", "world")
+
+-- get key-value data from 'default' book
+print(db:get("hello"))
+
+-- set guard function
+db:guard(function(book, key, value)
+ print("CREATE KEY permission denied!")
+end)
+
+-- try creating new key-value data to 'default' book
+db:set("key1", 1)
+db:set("key2", 2)
+
+-- update an existing key-value item
+db:set("hello", "bye")
+
+print(db:get("key1")) -- prints nil
+print(db:get("key2")) -- prints nil
+print(db:get("hello")) -- prints 'bye'
+
+-- store 'default' book to './db/default' file
+db:save()
+
+```
+
+API
+==========
+
+- **Functions**
+
+ - **flatdb(dir) --> db**
+
+ Bind a directory as a database, returns nil if 'dir' doesn't exists. Otherwise, it returns a 'db' obeject.
+
+ - **db:save([book])**
+
+ Save all books or the given book(if specified) contained in db to file. The 'book' argument is a string, the book's name.
+
+- **Tables**
+
+ - **flatdb.hack**
+
+ The 'hack' table contains db's methods. There is only one method 'save(db, book)' in it by default.
+ It is usually used to extend db methods.
+
+Dependencies
+=======
+
+- [pp](https://github.com/luapower/pp)
+- [lfs](http://keplerproject.github.io/luafilesystem/)
+
+All above libraries can be found in [LuaPower](https://luapower.com/).
+
+License
+=======
+
+FlatDB is distributed under the MIT license.
diff --git a/cli.lua b/cli.lua
new file mode 100644
index 0000000..eda6768
--- /dev/null
+++ b/cli.lua
@@ -0,0 +1,80 @@
+local flatdb = require("flatdb")
+local pp = require("pp")
+
+local function split(s, sep, maxsplit, plain)
+ assert(sep and sep ~= "")
+ maxsplit = maxsplit or 1/0
+ local items = {}
+ if #s > 0 then
+ local init = 1
+ for i = 1, maxsplit do
+ local m, n = s:find(sep, init, plain)
+ if m and m <= n then
+ table.insert(items, s:sub(init, m - 1))
+ init = n + 1
+ else
+ break
+ end
+ end
+ table.insert(items, s:sub(init))
+ end
+ return items
+end
+
+local db = assert(flatdb("./db"))
+local book = "default"
+
+local function handle(input)
+ local c = split(input, " ", 2)
+ local cmd, key, value = string.upper(c[1]), c[2], c[3]
+ if cmd == "GET" then
+ print(pp.format(db[book][key]))
+ elseif cmd == "SET" then
+ local ok, tmp = pcall(load("return "..tostring(value), "=(load)", "t", db))
+ db[book][key] = ok and tmp or value
+ print("OK")
+ elseif cmd == "SAVE" then
+ print(db:save(key) and "OK" or "ERROR")
+ elseif cmd == "TOUCH" then
+ if not db[key] then
+ db[key] = {}
+ end
+ print("OK")
+ elseif cmd == "OPEN" then
+ if db[key] then
+ book = key
+ print("OK")
+ else
+ print("ERROR")
+ end
+ elseif cmd == "INCR" then
+ if not db[book][key] then
+ db[book][key] = 0
+ end
+ db[book][key] = db[book][key] + 1
+ print("OK")
+ elseif cmd == "DECR" then
+ if not db[book][key] then
+ db[book][key] = 0
+ end
+ db[book][key] = db[book][key] - 1
+ print("OK")
+ end
+end
+
+local function main()
+ if not db.default then
+ db.default = {}
+ end
+ io.stdout:setvbuf("no")
+ while true do
+ io.write("> ")
+ local input = io.read("*l")
+ if string.upper(input) == "EXIT" then
+ break
+ end
+ handle(input)
+ end
+end
+
+main()
diff --git a/flatdb.lua b/flatdb.lua
new file mode 100644
index 0000000..12416aa
--- /dev/null
+++ b/flatdb.lua
@@ -0,0 +1,70 @@
+local lfs = require("lfs")
+local pp = require("pp")
+
+local function isFile(path)
+ return lfs.attributes(path, "mode") == "file"
+end
+
+local function isDir(path)
+ return lfs.attributes(path, "mode") == "directory"
+end
+
+local function load_book(path)
+ return dofile(path)
+end
+
+local function store_book(path, book)
+ if type(book) == "table" then
+ local f = io.open(path, "wb")
+ if f then
+ f:write("return ")
+ f:write(pp.format(book))
+ f:close()
+ return true
+ end
+ end
+ return false
+end
+
+local pool = {}
+
+local db_funcs = {
+ save = function(db, bk)
+ if bk then
+ if type(bk) == "string" and type(db[bk]) == "table" then
+ return store_book(pool[db].."/"..bk, db[bk])
+ else
+ return false
+ end
+ end
+ for bk, book in pairs(db) do
+ store_book(pool[db].."/"..bk, book)
+ end
+ return true
+ end
+}
+
+local mt = {
+ __index = function(db, k)
+ if db_funcs[k] then return db_funcs[k] end
+ if isFile(pool[db].."/"..k) then
+ db[k] = load_book(pool[db].."/"..k)
+ end
+ return rawget(db, k)
+ end
+}
+
+pool.hack = db_funcs
+
+return setmetatable(pool, {
+ __mode = "kv",
+ __call = function(pool, path)
+ if pool[path] then return pool[path] end
+ if not isDir(path) then return end
+ local db = {}
+ setmetatable(db, mt)
+ pool[path] = db
+ pool[db] = path
+ return db
+ end
+})