Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ It also provides the `crud-storage` and `crud-router` roles for
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Quickstart](#quickstart)
- [API](#api)
- [Insert](#insert)
- [Get](#get)
Expand All @@ -37,6 +38,29 @@ It also provides the `crud-storage` and `crud-router` roles for

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Quickstart

First, [install Tarantool](https://www.tarantool.io/en/download).

Now you have the following options how to learn crud API and use it in a
project:

* Play with crud on a testing dataset on a single instance:

```shell
$ git clone https://github.com/tarantool/crud.git
$ cd crud
$ tarantoolctl rocks make
$ ./doc/playground.lua
tarantool> crud.select('customers', {{'<=', 'age', 35}}, {first = 10})
tarantool> crud.select('developers', nil, {first = 6})
```
* Add crud into dependencies of a Cartridge application and add crud roles into
dependencies of your roles (see [Cartridge roles](#cartridge-roles) section).
* Add crud into dependencies of your application (rockspec, RPM spec -- depends
of your choice) and call crud initialization code from storage and router
code (see [API](#api) section).

## API

The CRUD operations should be called from router.
Expand Down
150 changes: 150 additions & 0 deletions doc/playground.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#!/usr/bin/env tarantool

-- How to run:
--
-- $ ./doc/playground.lua
--
-- Or
--
-- $ KEEP_DATA=1 ./doc/playground.lua
--
-- What to do next:
--
-- Choose an example from README.md, doc/select.md or doc/pairs.md
-- and run. For example:
--
-- tarantool> crud.select('customers', {{'<=', 'age', 35}}, {first = 10})
-- tarantool> crud.select('developers', nil, {first = 6})

local fio = require('fio')
local console = require('console')
local vshard = require('vshard')
local crud = require('crud')

-- Trick to don't leave *.snap, *.xlog files. See
-- test/tuple_keydef.test.lua in the tuple-keydef module.
if os.getenv('KEEP_DATA') ~= nil then
box.cfg()
else
local tempdir = fio.tempdir()
box.cfg({
memtx_dir = tempdir,
wal_mode = 'none',
})
fio.rmtree(tempdir)
end

-- Setup vshard.
_G.vshard = vshard
box.once('guest', function()
box.schema.user.grant('guest', 'super')
end)
local uri = 'guest@localhost:3301'
local cfg = {
bucket_count = 3000,
sharding = {
[box.info().cluster.uuid] = {
replicas = {
[box.info().uuid] = {
uri = uri,
name = 'storage',
master = true,
},
},
},
},
}
vshard.storage.cfg(cfg, box.info().uuid)
vshard.router.cfg(cfg)
vshard.router.bootstrap()

-- Create the 'customers' space.
box.once('customers', function()
box.schema.create_space('customers', {
format = {
{name = 'id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned'},
{name = 'name', type = 'string'},
{name = 'age', type = 'number'},
}
})
box.space.customers:create_index('primary_index', {
parts = {
{field = 1, type = 'unsigned'},
},
})
box.space.customers:create_index('bucket_id', {
parts = {
{field = 2, type = 'unsigned'},
},
unique = false,
})
box.space.customers:create_index('age', {
parts = {
{field = 4, type = 'number'},
},
unique = false,
})

-- Fill the space.
box.space.customers:insert({1, 477, 'Elizabeth', 12})
box.space.customers:insert({2, 401, 'Mary', 46})
box.space.customers:insert({3, 2804, 'David', 33})
box.space.customers:insert({4, 1161, 'William', 81})
box.space.customers:insert({5, 1172, 'Jack', 35})
box.space.customers:insert({6, 1064, 'William', 25})
box.space.customers:insert({7, 693, 'Elizabeth', 18})
end)

-- Create the developers space.
box.once('developers', function()
box.schema.create_space('developers', {
format = {
{name = 'id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned'},
{name = 'name', type = 'string'},
{name = 'surname', type = 'string'},
{name = 'age', type = 'number'},
}
})
box.space.developers:create_index('primary_index', {
parts = {
{field = 1, type = 'unsigned'},
},
})
box.space.developers:create_index('bucket_id', {
parts = {
{field = 2, type = 'unsigned'},
},
unique = false,
})
box.space.developers:create_index('age_index', {
parts = {
{field = 5, type = 'number'},
},
unique = false,
})
box.space.developers:create_index('full_name', {
parts = {
{field = 3, type = 'string'},
{field = 4, type = 'string'},
},
unique = false,
})

-- Fill the space.
box.space.developers:insert({1, 477, 'Alexey', 'Adams', 20})
box.space.developers:insert({2, 401, 'Sergey', 'Allred', 21})
box.space.developers:insert({3, 2804, 'Pavel', 'Adams', 27})
box.space.developers:insert({4, 1161, 'Mikhail', 'Liston', 51})
box.space.developers:insert({5, 1172, 'Dmitry', 'Jacobi', 16})
box.space.developers:insert({6, 1064, 'Alexey', 'Sidorov', 31})
end)

-- Initialize crud.
crud.init_storage()
crud.init_router()

-- Start a console.
console.start()
os.exit()
133 changes: 133 additions & 0 deletions test/doc/playground_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
local yaml = require('yaml')
local t = require('luatest')
local g = t.group()

local popen_ok, popen = pcall(require, 'popen')

g.before_all(function()
t.skip_if(not popen_ok, 'no built-in popen module')
t.skip_if(jit.os == 'OSX', 'popen is broken on Mac OS: ' ..
'https://github.com/tarantool/tarantool/issues/6674')
end)

-- Run ./doc/playground.lua, execute a request and compare the
-- output with reference return values.
--
-- The first arguments is the request string. All the following
-- arguments are expected return values (as Lua values).
--
-- The function ignores trailing `null` values in the YAML
-- output.
local function check_request(request, ...)
local ph, err = popen.new({'./doc/playground.lua'}, {
stdin = popen.opts.PIPE,
stdout = popen.opts.PIPE,
stderr = popen.opts.DEVNULL,
})
if ph == nil then
error('popen.new: ' .. tostring(err))
end

local ok, err = ph:write(request, {timeout = 1})
if not ok then
ph:close()
error('ph:write: ' .. tostring(err))
end
ph:shutdown({stdin = true})

-- Read everything until EOF.
local chunks = {}
while true do
local chunk, err = ph:read()
if chunk == nil then
ph:close()
error('ph:read: ' .. tostring(err))
end
if chunk == '' then break end -- EOF
table.insert(chunks, chunk)
end

local status = ph:wait()
assert(status.state == popen.state.EXITED)

-- Glue all chunks, parse response.
local stdout = table.concat(chunks)
local response_yaml = string.match(stdout, '%-%-%-.-%.%.%.')
local response = yaml.decode(response_yaml)

-- NB: This call does NOT differentiate `nil` and `box.NULL`.
t.assert_equals(response, {...})
end

local cases = {
test_select_customers = {
request = "crud.select('customers', {{'<=', 'age', 35}}, {first = 10})",
retval_1 = {
metadata = {
{name = 'id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned'},
{name = 'name', type = 'string'},
{name = 'age', type = 'number'},
},
rows = {
{5, 1172, 'Jack', 35},
{3, 2804, 'David', 33},
{6, 1064, 'William', 25},
{7, 693, 'Elizabeth', 18},
{1, 477, 'Elizabeth', 12},
},
}
},
test_select_developers = {
request = "crud.select('developers', nil, {first = 6})",
retval_1 = {
metadata = {
{name = 'id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned'},
{name = 'name', type = 'string'},
{name = 'surname', type = 'string'},
{name = 'age', type = 'number'},
},
rows = {
{1, 477, 'Alexey', 'Adams', 20},
{2, 401, 'Sergey', 'Allred', 21},
{3, 2804, 'Pavel', 'Adams', 27},
{4, 1161, 'Mikhail', 'Liston', 51},
{5, 1172, 'Dmitry', 'Jacobi', 16},
{6, 1064, 'Alexey', 'Sidorov', 31},
},
},
},
test_insert = {
request = ("crud.insert('developers', %s)"):format(
"{100, nil, 'Alfred', 'Hitchcock', 123}"),
retval_1 = {
metadata = {
{name = 'id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned'},
{name = 'name', type = 'string'},
{name = 'surname', type = 'string'},
{name = 'age', type = 'number'},
},
rows = {
{100, 2976, 'Alfred', 'Hitchcock', 123},
},
}
},
test_error = {
request = [[
do
local res, err = crud.select('non_existent', nil, {first = 10})
return res, err and err.err or nil
end
]],
retval_1 = box.NULL,
retval_2 = 'Space "non_existent" doesn\'t exist',
},
}

for case_name, case in pairs(cases) do
g[case_name] = function()
check_request(case.request, case.retval_1, case.retval_2)
end
end