├── .gitignore
├── AUTHORS
├── LICENSE
├── README.md
├── TODO
├── doc
├── .gitignore
└── en
│ └── welcome
├── examples
├── .gitignore
├── ajaxexample
│ ├── app
│ │ └── handler_entry.lua
│ ├── initial
│ │ └── .gitignore
│ ├── media
│ │ ├── css
│ │ │ └── .gitignore
│ │ ├── images
│ │ │ └── .gitignore
│ │ ├── js
│ │ │ ├── .gitignore
│ │ │ └── jquery-1.5.min.js
│ │ ├── plugins
│ │ │ └── .gitignore
│ │ └── uploads
│ │ │ └── .gitignore
│ ├── models
│ │ └── .gitignore
│ ├── plugins
│ │ └── .gitignore
│ ├── settings.lua
│ └── views
│ │ ├── form.html
│ │ ├── index.html
│ │ └── result.html
├── apptest
│ ├── app
│ │ └── handler_entry.lua
│ ├── initial
│ │ └── .gitignore
│ ├── media
│ │ ├── css
│ │ │ └── .gitignore
│ │ ├── images
│ │ │ └── .gitignore
│ │ ├── js
│ │ │ └── .gitignore
│ │ ├── plugins
│ │ │ └── .gitignore
│ │ └── uploads
│ │ │ └── .gitignore
│ ├── models
│ │ └── .gitignore
│ ├── plugins
│ │ └── .gitignore
│ ├── settings.lua
│ └── views
│ │ └── index.html
├── formprocess
│ ├── app
│ │ └── handler_entry.lua
│ ├── initial
│ │ └── .gitignore
│ ├── media
│ │ ├── css
│ │ │ └── .gitignore
│ │ ├── images
│ │ │ └── .gitignore
│ │ ├── js
│ │ │ └── .gitignore
│ │ ├── plugins
│ │ │ └── .gitignore
│ │ └── uploads
│ │ │ └── .gitignore
│ ├── models
│ │ └── .gitignore
│ ├── plugins
│ │ └── .gitignore
│ ├── settings.lua
│ └── views
│ │ ├── form.html
│ │ ├── index.html
│ │ └── result.html
├── pagejump
│ ├── app
│ │ └── handler_entry.lua
│ ├── initial
│ │ └── .gitignore
│ ├── media
│ │ ├── css
│ │ │ └── .gitignore
│ │ ├── images
│ │ │ └── .gitignore
│ │ ├── js
│ │ │ └── .gitignore
│ │ ├── plugins
│ │ │ └── .gitignore
│ │ └── uploads
│ │ │ └── .gitignore
│ ├── models
│ │ └── .gitignore
│ ├── plugins
│ │ └── .gitignore
│ ├── settings.lua
│ └── views
│ │ ├── index.html
│ │ ├── index2.html
│ │ └── index3.html
└── workwithdb
│ ├── app
│ └── handler_entry.lua
│ ├── initial
│ └── .gitignore
│ ├── media
│ ├── css
│ │ └── .gitignore
│ ├── images
│ │ └── .gitignore
│ ├── js
│ │ └── .gitignore
│ ├── plugins
│ │ └── .gitignore
│ └── uploads
│ │ └── .gitignore
│ ├── models
│ ├── .gitignore
│ └── myuser.lua
│ ├── plugins
│ └── .gitignore
│ ├── settings.lua
│ └── views
│ ├── form.html
│ ├── index.html
│ └── result.html
├── src
├── bin
│ ├── bamboo
│ ├── bamboo_handler
│ └── shell.lua
├── cmd_tmpls
│ ├── createapp
│ │ ├── app
│ │ │ └── handler_entry.lua
│ │ ├── initial
│ │ │ └── .gitignore
│ │ ├── media
│ │ │ ├── css
│ │ │ │ └── .gitignore
│ │ │ ├── images
│ │ │ │ └── .gitignore
│ │ │ ├── js
│ │ │ │ └── .gitignore
│ │ │ ├── plugins
│ │ │ │ └── .gitignore
│ │ │ └── uploads
│ │ │ │ └── .gitignore
│ │ ├── models
│ │ │ └── .gitignore
│ │ ├── plugins
│ │ │ └── .gitignore
│ │ ├── settings.lua
│ │ └── views
│ │ │ └── index.html
│ ├── createcontroller
│ │ └── newcontroller.lua
│ ├── createmodel
│ │ └── newmodel.lua
│ ├── createplugin
│ │ ├── init.lua
│ │ └── testplugin.html
│ └── pluginmedia
│ │ ├── css
│ │ └── .gitignore
│ │ ├── images
│ │ └── .gitignore
│ │ ├── js
│ │ └── .gitignore
│ │ └── uploads
│ │ └── .gitignore
├── cmds.lua
├── db
│ ├── driver.lua
│ ├── mongo.lua
│ ├── mongo
│ │ └── mongo_driver.lua
│ ├── mongo_on_luamongo.lua
│ ├── mongo_on_mongol.lua
│ ├── mysql.lua
│ ├── redis.lua
│ └── redis
│ │ ├── Scheme.md
│ │ ├── fifo.lua
│ │ ├── hash.lua
│ │ ├── list.lua
│ │ ├── set.lua
│ │ ├── string.lua
│ │ ├── zfifo.lua
│ │ └── zset.lua
├── errors.lua
├── form.lua
├── globals.lua
├── i18n.lua
├── init.lua
├── lib
│ ├── gdutil.lua
│ └── zmqev.lua
├── mixins
│ ├── custom.lua
│ ├── fakedel.lua
│ ├── fulltext.lua
│ ├── model-indexhash.lua
│ └── mvm.lua
├── model.lua
├── models
│ ├── group.lua
│ ├── image.lua
│ ├── message.lua
│ ├── node.lua
│ ├── permission.lua
│ ├── upload.lua
│ ├── upload_bak.lua
│ └── user.lua
├── mvm
│ ├── init.lua
│ ├── prototype.lua
│ └── validate.lua
├── plugin.lua
├── queryset.lua
├── session.lua
├── task.lua
├── testing.lua
├── thread.lua
├── timer.lua
├── utils.lua
├── view.lua
├── web.lua
├── webserver_driver.lua
└── widget.lua
├── tests
├── .gitignore
├── ajaxpaginator_test
│ ├── app
│ │ └── handler_entry.lua
│ ├── cachecall
│ │ └── callbacks.lua
│ ├── initial
│ │ └── .gitignore
│ ├── media
│ │ ├── css
│ │ │ └── .gitignore
│ │ ├── images
│ │ │ └── .gitignore
│ │ ├── js
│ │ │ ├── .gitignore
│ │ │ └── jquery.min.js
│ │ ├── plugins
│ │ │ └── .gitignore
│ │ └── uploads
│ │ │ └── .gitignore
│ ├── models
│ │ ├── .gitignore
│ │ ├── mymodel.lua
│ │ └── myuser.lua
│ ├── monconfig.lua
│ ├── plugins
│ │ ├── .gitignore
│ │ └── ajaxpaginator
│ │ │ ├── ajaxpaginator.html
│ │ │ └── init.lua
│ ├── settings.lua
│ ├── tests
│ │ └── viewbench_tests.lua
│ └── views
│ │ ├── form.html
│ │ ├── index.html
│ │ ├── item.html
│ │ ├── paginator.html
│ │ ├── result (复件).html
│ │ ├── result.html
│ │ └── test.html
├── cache_test
│ ├── app
│ │ └── handler_entry.lua
│ ├── cachecall
│ │ └── callbacks.lua
│ ├── initial
│ │ └── .gitignore
│ ├── media
│ │ ├── css
│ │ │ └── .gitignore
│ │ ├── images
│ │ │ └── .gitignore
│ │ ├── js
│ │ │ └── .gitignore
│ │ ├── plugins
│ │ │ └── .gitignore
│ │ └── uploads
│ │ │ └── .gitignore
│ ├── models
│ │ ├── .gitignore
│ │ └── myuser.lua
│ ├── plugins
│ │ └── .gitignore
│ ├── settings.lua
│ └── views
│ │ ├── form.html
│ │ ├── index.html
│ │ └── result.html
├── core_test
│ ├── app
│ │ └── handler_entry.lua
│ ├── cachecall
│ │ └── callbacks.lua
│ ├── initial
│ │ └── .gitignore
│ ├── media
│ │ ├── css
│ │ │ └── .gitignore
│ │ ├── images
│ │ │ └── .gitignore
│ │ ├── js
│ │ │ ├── .gitignore
│ │ │ └── jquery.min.js
│ │ ├── plugins
│ │ │ └── .gitignore
│ │ └── uploads
│ │ │ └── .gitignore
│ ├── models
│ │ ├── .gitignore
│ │ ├── mymodel.lua
│ │ └── myuser.lua
│ ├── monconfig.lua
│ ├── plugins
│ │ ├── .gitignore
│ │ └── ajaxpaginator
│ │ │ ├── ajaxpaginator.html
│ │ │ └── init.lua
│ ├── settings.lua
│ ├── tests
│ │ └── core_tests.lua
│ └── views
│ │ ├── form.html
│ │ ├── index.html
│ │ ├── item.html
│ │ ├── paginator.html
│ │ ├── result (复件).html
│ │ ├── result.html
│ │ └── test.html
├── hashindex_test
│ ├── index.lua
│ └── settings.lua
└── paginator_test
│ ├── app
│ └── handler_entry.lua
│ ├── cachecall
│ └── callbacks.lua
│ ├── initial
│ └── .gitignore
│ ├── media
│ ├── css
│ │ └── .gitignore
│ ├── images
│ │ └── .gitignore
│ ├── js
│ │ ├── .gitignore
│ │ └── jquery.min.js
│ ├── plugins
│ │ └── .gitignore
│ └── uploads
│ │ └── .gitignore
│ ├── models
│ ├── .gitignore
│ └── myuser.lua
│ ├── monconfig.lua
│ ├── plugins
│ ├── .gitignore
│ └── paginator
│ │ ├── init.lua
│ │ └── paginator.html
│ ├── settings.lua
│ └── views
│ ├── form.html
│ ├── index.html
│ ├── item.html
│ ├── paginator.html
│ ├── result (复件).html
│ ├── result.html
│ └── test.html
└── tools
└── rebuild_hash_index.lua
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | AUTHORS
2 |
3 | Daogang Tang (daogangtang@gmail.com)
4 | Jianfeng Yang (littlehaker@gmail.com)
5 | Xiquan Feng (18602871035@163.com)
6 |
7 |
8 | CONTRIBUTORS
9 |
10 | Gang Chen (trustmore@163.com)
11 | Wenlong Lu (a88i99@gmail.com)
12 | Feng Gu (windkoo@gmail.com)
13 | renbenxingfu.com
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ## BSD 3-clause license
2 |
3 | Copyright (c) 2010, legerobot.com and Bamboo Project Contributors.
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are
8 | met:
9 |
10 | * Redistributions of source code must retain the above copyright
11 | notice, this list of conditions and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright
14 | notice, this list of conditions and the following disclaimer in the
15 | documentation and/or other materials provided with the distribution.
16 |
17 | * Neither the name of the Bamboo Project, legerobot.com, nor the names
18 | of its contributors may be used to endorse or promote products
19 | derived from this software without specific prior written
20 | permission.
21 |
22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
23 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 |
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | README
2 | ======
3 |
4 | ## Introduction
5 |
6 | Bamboo is a powerful web framework, written in lua. It is designed to be the most popular web framework in lua community, like Django in python, ROR in ruby.
7 |
8 | ## Features
9 |
10 | - Bamboo is a MVC framework;
11 | - cooperates with mongrel2, zeromq and redis;
12 | - stateless handler;
13 | - powerful views rendering engine;
14 | - a strict single inheritance OOP model style;
15 | - use a lua table as the main URL router;
16 | - in each module, there can be another URL router related to this module (URL moduled);
17 | - project init(), finish(); module init(), finish();
18 | - a whole set of filter system for handler function;
19 | - flexible ORM wrapper, specific to redis;
20 | - powerful MVM (model-to-view mapping) function;
21 | - decorators on database related actions, to reduce manual code;
22 | - builtin User, Group and Permission models and a set of permission checking procedure;
23 | - builtin test framework (based on telescope).
24 |
25 | ## What does it look like?
26 | ### Project Directory Structure
27 | One project # This project's directory
28 | ├── app # control code directory
29 | │ └── handler_entry.lua # entry file
30 | ├── initial # place where put db's initial data
31 | ├── media # static files directory
32 | │ ├── css
33 | │ ├── images
34 | │ ├── js
35 | │ ├── plugins
36 | │ └── uploads
37 | ├── models # place where put model defined files
38 | ├── plugins # plugins directory
39 | ├── settings.lua # project setting file
40 | └── views # place where put views (html) files
41 | └── index.html
42 |
43 |
44 | ### Entry file
45 | In Bamboo's project, file `app/handler_entry.lua` is the entry of this project. This file can contain the following parts:
46 |
47 | 1. require 'bamboo' (MUST)
48 | 2. require other classes and modules (MUST)
49 | 3. register permissions (Optional)
50 | 4. register filters (Optional)
51 | 5. register models used by this project (Optional)
52 | 6. register modules related by this project (Optional)
53 | 7. handlers of this project (Optional)
54 | 8. main URL router (MUST)
55 |
56 | This file looks usually like follows:
57 |
58 | ------------------------------------------------------------------------
59 | -- bamboo import
60 | require 'bamboo'
61 |
62 | -- Other Class and module required
63 | local Form = require 'bamboo.form'
64 | local View = require 'bamboo.view'
65 | local Session = require 'bamboo.session'
66 | local registerModel = bamboo.registerModel
67 | local registerModule = bamboo.registerModule
68 |
69 | ------------------------------------------------------------------------
70 | -- Model Registrations
71 | local User = require 'bamboo.models.user'
72 | registerModel(User)
73 | local IPUser = require 'models.ipuser'
74 | registerModel(IPUser)
75 | local Upload = require 'bamboo.models.upload'
76 | registerModel(Upload)
77 | local Image = require 'bamboo.models.image'
78 | registerModel(Image)
79 | local Permission = require 'bamboo.models.permission'
80 | registerModel(Permission)
81 |
82 | ------------------------------------------------------------------------
83 | -- Module Registrations
84 | local module1 = require 'app.module1'
85 | registerModule(module1)
86 | local module2 = require 'app.module2'
87 | registerModule(module2)
88 | local upload = require 'app.upload'
89 | registerModule(upload)
90 |
91 | ------------------------------------------------------------------------
92 | -- Some handlers
93 | local function index(web, req)
94 | web:page(View("index.html"){})
95 | end
96 |
97 | local function get(web, req)
98 | ...
99 | end
100 |
101 | local function put(web, req)
102 | ...
103 | end
104 |
105 | ------------------------------------------------------------------------
106 | -- URL router
107 | URLS = { '/',
108 | ['/'] = index,
109 | ['/index/'] = index,
110 | ['/get/'] = get,
111 | ['/put/'] = put,
112 | }
113 |
114 | ### How to start it
115 | - Start mongrel2;
116 |
117 | `cd ~/workspace/monserver`
118 | `bamboo startserver main`
119 | If mongrel2 server occupy this terminal, open a new terminal before next step;
120 |
121 | - Start redis server;
122 |
123 | `cd ~`
124 | `redis-server /etc/redis.conf`
125 |
126 | - Start bamboo project;
127 |
128 | `cd ~/workspace/your_project`
129 | `bamboo start`
130 | If this step goes smoothly, you will see what like follows:
131 | CONNECTING / 45564ef2-ca84-a0b5-9a60-0592c290ebd0 tcp://127.0.0.1:9999 tcp://127.0.0.1:9998
132 |
133 | - Open the web browser and input `http://localhost:6767` and enter, you will see your project page, if everything is OK;
134 | - END.
135 |
136 | ## Installation
137 | Please download [bamboo_installer-v1.1.1.tar.gz](https://github.com/downloads/daogangtang/bamboo_doc_cn/bamboo_installer-v1.1.1.tar.gz).
138 |
139 | ## Some Websites Using Bamboo Now
140 |
141 | - http://www.top-edu.cn
142 | - http://www.51jianzhiwang.cn
143 | - http://www.onewin.cn
144 | - http://www.artjia.cn
145 |
146 |
147 |
148 | ## Mailing List
149 | Bamboo use the following mailing list:
150 |
151 | bamboo-global@googlegroups.com # for global world people
152 | bamboo-cn@googlegroups.com # for Chinese people
153 |
154 | Welcome to discuss.
155 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 |
2 | v1.1
3 |
4 | 1. Extend query_set:sortBy(self, field, direction, sort_func) to query_set:sortBy(self, field, direction, sort_func, ...), to support second rank sort;
5 | 2. Dynamic field mechanism;
6 | 3. realize cache system, using model Cutsom API;
7 | 4. check global variables;
8 | 5. add Menu config, binding url, handler, template automatically, to reduce the code amount;
9 | 6. supply common parameters for rendering;
10 | 7. supply better bug report method for erroring in views rendering.
11 |
12 |
13 | v1.2
14 |
15 | 1. perfect plugins system;
16 | 2. a basic CMS;
17 | 3. data's permanent work and migration of redis;
18 |
--------------------------------------------------------------------------------
/doc/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/doc/.gitignore
--------------------------------------------------------------------------------
/doc/en/welcome:
--------------------------------------------------------------------------------
1 | Ready to OK.
2 |
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/.gitignore
--------------------------------------------------------------------------------
/examples/ajaxexample/app/handler_entry.lua:
--------------------------------------------------------------------------------
1 | require 'bamboo'
2 |
3 | local View = require 'bamboo.view'
4 | local Form = require 'bamboo.form'
5 |
6 | local function index(web, req)
7 | web:page(View("form.html"){})
8 | end
9 |
10 | local function ajax_submit(web, req)
11 | local params = Form:parse(req)
12 | DEBUG(params)
13 |
14 | web:json {
15 | success = true,
16 | htmls = View('result.html'){results = params},
17 | -- for play only
18 | now = os.time()
19 | }
20 | end
21 |
22 |
23 |
24 | URLS = { '/',
25 | ['/'] = index,
26 | ['/index/'] = index,
27 | ['/ajax_submit/'] = ajax_submit,
28 |
29 |
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/examples/ajaxexample/initial/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/ajaxexample/initial/.gitignore
--------------------------------------------------------------------------------
/examples/ajaxexample/media/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/ajaxexample/media/css/.gitignore
--------------------------------------------------------------------------------
/examples/ajaxexample/media/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/ajaxexample/media/images/.gitignore
--------------------------------------------------------------------------------
/examples/ajaxexample/media/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/ajaxexample/media/js/.gitignore
--------------------------------------------------------------------------------
/examples/ajaxexample/media/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/ajaxexample/media/plugins/.gitignore
--------------------------------------------------------------------------------
/examples/ajaxexample/media/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/ajaxexample/media/uploads/.gitignore
--------------------------------------------------------------------------------
/examples/ajaxexample/models/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/ajaxexample/models/.gitignore
--------------------------------------------------------------------------------
/examples/ajaxexample/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/ajaxexample/plugins/.gitignore
--------------------------------------------------------------------------------
/examples/ajaxexample/settings.lua:
--------------------------------------------------------------------------------
1 | project_name = "ajaxexample"
2 | host = 'ajaxexample'
3 |
4 | sender_id = '99edc750-f2bc-1f04-ffc7-cb0301efa240'
5 | io_threads = 1
6 | views = "views/"
7 | config_file = 'config.lua'
8 | WHICH_DB = 15
9 |
10 | debug_level = 1
11 |
--------------------------------------------------------------------------------
/examples/ajaxexample/views/form.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
5 |
11 |
12 | ]}
--------------------------------------------------------------------------------
/examples/ajaxexample/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
33 |
34 | Ajax Submit
35 |
36 |
37 |
38 |
39 |
40 | {[ 'page' ]}
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/examples/ajaxexample/views/result.html:
--------------------------------------------------------------------------------
1 |
2 | You submit some values, they are:
3 |
4 |
5 | {% for k, v in pairs(results) do %}
6 |
7 |
==> {{k}}: {{v}}
8 |
9 | {% end %}
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/apptest/app/handler_entry.lua:
--------------------------------------------------------------------------------
1 | require 'bamboo'
2 |
3 | local View = require 'bamboo.view'
4 |
5 | local function index(web, req)
6 | web:page(View("index.html"){})
7 | end
8 |
9 | URLS = { '/',
10 | ['/'] = index,
11 | ['index/'] = index,
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/examples/apptest/initial/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/apptest/initial/.gitignore
--------------------------------------------------------------------------------
/examples/apptest/media/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/apptest/media/css/.gitignore
--------------------------------------------------------------------------------
/examples/apptest/media/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/apptest/media/images/.gitignore
--------------------------------------------------------------------------------
/examples/apptest/media/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/apptest/media/js/.gitignore
--------------------------------------------------------------------------------
/examples/apptest/media/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/apptest/media/plugins/.gitignore
--------------------------------------------------------------------------------
/examples/apptest/media/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/apptest/media/uploads/.gitignore
--------------------------------------------------------------------------------
/examples/apptest/models/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/apptest/models/.gitignore
--------------------------------------------------------------------------------
/examples/apptest/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/apptest/plugins/.gitignore
--------------------------------------------------------------------------------
/examples/apptest/settings.lua:
--------------------------------------------------------------------------------
1 | project_name = "apptest"
2 | host = 'apptest'
3 | sender_id = '99edc750-f2bc-1f04-ffc7-cb0301efa242'
4 | io_threads = 1
5 | views = "views/"
6 | config_file = 'config.lua'
7 | WHICH_DB = 15
8 |
--------------------------------------------------------------------------------
/examples/apptest/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
14 |
15 | Bamboo
16 |
17 |
18 |
19 |
20 |
21 | Welcome to Bamboo.
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/formprocess/app/handler_entry.lua:
--------------------------------------------------------------------------------
1 | require 'bamboo'
2 |
3 | local View = require 'bamboo.view'
4 | local Form = require 'bamboo.form'
5 |
6 | local function index(web, req)
7 | web:page(View("form.html"){})
8 | end
9 |
10 | local function form_submit(web, req)
11 | local params = Form:parse(req)
12 | DEBUG(params)
13 |
14 | web:html("result.html", {results = params})
15 | end
16 |
17 |
18 | URLS = { '/',
19 | ['/'] = index,
20 | ['/index/'] = index,
21 | ['/form_submit/'] = form_submit,
22 |
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/examples/formprocess/initial/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/formprocess/initial/.gitignore
--------------------------------------------------------------------------------
/examples/formprocess/media/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/formprocess/media/css/.gitignore
--------------------------------------------------------------------------------
/examples/formprocess/media/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/formprocess/media/images/.gitignore
--------------------------------------------------------------------------------
/examples/formprocess/media/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/formprocess/media/js/.gitignore
--------------------------------------------------------------------------------
/examples/formprocess/media/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/formprocess/media/plugins/.gitignore
--------------------------------------------------------------------------------
/examples/formprocess/media/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/formprocess/media/uploads/.gitignore
--------------------------------------------------------------------------------
/examples/formprocess/models/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/formprocess/models/.gitignore
--------------------------------------------------------------------------------
/examples/formprocess/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/formprocess/plugins/.gitignore
--------------------------------------------------------------------------------
/examples/formprocess/settings.lua:
--------------------------------------------------------------------------------
1 | project_name = "formprocess"
2 | host = 'formprocess'
3 |
4 | debug_level = 1
5 | sender_id = '99edc750-f2bc-1f04-ffc7-cb0301efa240'
6 | io_threads = 1
7 | views = "views/"
8 | config_file = 'config.lua'
9 | WHICH_DB = 15
10 |
--------------------------------------------------------------------------------
/examples/formprocess/views/form.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
5 |
11 |
12 | ]}
--------------------------------------------------------------------------------
/examples/formprocess/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 | Form Process
15 |
16 |
17 |
18 |
19 |
20 | {[ 'page' ]}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/examples/formprocess/views/result.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
5 | You submit some values, they are:
6 |
7 |
8 | {% for k, v in pairs(results) do %}
9 |
10 |
==> {{k}}: {{v}}
11 |
12 | {% end %}
13 |
14 |
15 | Click here to return form page.
16 |
17 | ]}
--------------------------------------------------------------------------------
/examples/pagejump/app/handler_entry.lua:
--------------------------------------------------------------------------------
1 | require 'bamboo'
2 |
3 | local View = require 'bamboo.view'
4 |
5 | local function index(web, req)
6 | web:page(View("index.html"){})
7 | end
8 |
9 | local function pageb(web, req)
10 | web:html("index2.html")
11 | end
12 |
13 | local function pagec(web, req)
14 | web:html("index3.html", {})
15 | end
16 |
17 | URLS = { '/',
18 | ['/'] = index,
19 | ['/index/'] = index,
20 | ['/pageb/'] = pageb,
21 | ['/pagec/'] = pagec,
22 |
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/examples/pagejump/initial/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/pagejump/initial/.gitignore
--------------------------------------------------------------------------------
/examples/pagejump/media/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/pagejump/media/css/.gitignore
--------------------------------------------------------------------------------
/examples/pagejump/media/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/pagejump/media/images/.gitignore
--------------------------------------------------------------------------------
/examples/pagejump/media/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/pagejump/media/js/.gitignore
--------------------------------------------------------------------------------
/examples/pagejump/media/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/pagejump/media/plugins/.gitignore
--------------------------------------------------------------------------------
/examples/pagejump/media/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/pagejump/media/uploads/.gitignore
--------------------------------------------------------------------------------
/examples/pagejump/models/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/pagejump/models/.gitignore
--------------------------------------------------------------------------------
/examples/pagejump/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/pagejump/plugins/.gitignore
--------------------------------------------------------------------------------
/examples/pagejump/settings.lua:
--------------------------------------------------------------------------------
1 | project_name = "pagejump"
2 | host = 'pagejump'
3 | sender_id = '99edc750-f2bc-1f04-ffc7-cb0301efa240'
4 | io_threads = 1
5 | views = "views/"
6 | config_file = 'config.lua'
7 | WHICH_DB = 15
8 |
--------------------------------------------------------------------------------
/examples/pagejump/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 | PageJump
15 |
16 |
17 |
18 |
19 |
20 | Hello, this is page A. Click
here to jump to Page B.
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/examples/pagejump/views/index2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
14 |
15 | PageJump
16 |
17 |
18 |
19 |
20 |
21 | I am page B. Click
here to jump to Page C.
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/pagejump/views/index3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
14 |
15 | PageJump
16 |
17 |
18 |
19 |
20 |
21 | I am page C. Click
here to jump to Page A.
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/workwithdb/app/handler_entry.lua:
--------------------------------------------------------------------------------
1 | require 'bamboo'
2 |
3 | local View = require 'bamboo.view'
4 | local Form = require 'bamboo.form'
5 |
6 | local MYUser = require 'models.myuser'
7 | bamboo.registerModel(MYUser)
8 |
9 | local function index(web, req)
10 | web:page(View("form.html"){})
11 | end
12 |
13 | local function form_submit(web, req)
14 | local params = Form:parse(req)
15 | DEBUG(params)
16 |
17 | local person = MYUser(params)
18 | -- save person object to db
19 | person:save()
20 |
21 | -- retreive all person instance from db
22 | local all_persons = MYUser:all()
23 |
24 | web:html("result.html", {all_persons = all_persons})
25 | end
26 |
27 |
28 | URLS = { '/',
29 | ['/'] = index,
30 | ['/index/'] = index,
31 | ['/form_submit/'] = form_submit,
32 |
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/examples/workwithdb/initial/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/workwithdb/initial/.gitignore
--------------------------------------------------------------------------------
/examples/workwithdb/media/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/workwithdb/media/css/.gitignore
--------------------------------------------------------------------------------
/examples/workwithdb/media/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/workwithdb/media/images/.gitignore
--------------------------------------------------------------------------------
/examples/workwithdb/media/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/workwithdb/media/js/.gitignore
--------------------------------------------------------------------------------
/examples/workwithdb/media/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/workwithdb/media/plugins/.gitignore
--------------------------------------------------------------------------------
/examples/workwithdb/media/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/workwithdb/media/uploads/.gitignore
--------------------------------------------------------------------------------
/examples/workwithdb/models/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/workwithdb/models/.gitignore
--------------------------------------------------------------------------------
/examples/workwithdb/models/myuser.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local User = require 'bamboo.models.user'
4 |
5 | local MYUser = User:extend {
6 | __tag = 'Bamboo.Model.User.MYUser';
7 | __name = 'MYUser';
8 | __desc = 'Generitic MYUser definition';
9 | __indexfd = 'name';
10 | __fields = {
11 | ['name'] = {},
12 | ['age'] = {},
13 | ['gender'] = {},
14 |
15 | };
16 |
17 | init = function (self, t)
18 | if not t then return self end
19 |
20 | self.name = t.name
21 | self.age = t.age
22 | self.gender = t.gender
23 |
24 | return self
25 | end;
26 |
27 | }
28 |
29 | return MYUser
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/examples/workwithdb/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/examples/workwithdb/plugins/.gitignore
--------------------------------------------------------------------------------
/examples/workwithdb/settings.lua:
--------------------------------------------------------------------------------
1 | project_name = "workwithdb"
2 | host = 'workwithdb'
3 |
4 | debug_level = 1
5 | sender_id = '99edc750-f2bc-1f04-ffc7-cb0301efa240'
6 | io_threads = 1
7 | views = "views/"
8 | config_file = 'config.lua'
9 | WHICH_DB = 15
10 |
--------------------------------------------------------------------------------
/examples/workwithdb/views/form.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
5 |
11 |
12 | ]}
--------------------------------------------------------------------------------
/examples/workwithdb/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 | Form Process
15 |
16 |
17 |
18 |
19 |
20 | {[ 'page' ]}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/examples/workwithdb/views/result.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
9 |
10 |
11 | The database have the following data:
12 |
13 |
14 | Name Age Gender
15 | {% for _, v in ipairs(all_persons) do%}
16 | {{v.name}} {{v.age}} {{v.gender}}
17 | {% end %}
18 |
19 |
20 | Click here to return form page.
21 |
22 | ]}
--------------------------------------------------------------------------------
/src/bin/shell.lua:
--------------------------------------------------------------------------------
1 | #!/usr/bin/lua
2 | package.path = './?.lua;./?/init.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;' .. package.path
3 |
4 | require 'bamboo'
5 |
6 | local redis = require 'bamboo.db.redis'
7 | local utils = require 'bamboo.utils'
8 |
9 | -- load configuration
10 | local config = utils.readSettings(bamboo.config)
11 | ptable(config)
12 | config.is_shell = true
13 |
14 | local DB_HOST = config.DB_HOST or arg[1] or '127.0.0.1'
15 | local DB_PORT = config.DB_PORT or arg[2] or '6379'
16 | local WHICH_DB = config.WHICH_DB or arg[3] or 0
17 | local AUTH = config.AUTH
18 |
19 | db = redis.connect {host=DB_HOST, port=DB_PORT, which = WHICH_DB, auth=AUTH}
20 | -- make model.lua work
21 | BAMBOO_DB = db
22 |
23 | -- add this project's initial register work to global evironment
24 | setfenv(assert(loadfile('app/handler_entry.lua') or loadfile('../app/handler_entry.lua')), _G)()
25 |
26 | print('Entering bamboo shell.... OK')
27 | return true
28 |
29 |
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/app/handler_entry.lua:
--------------------------------------------------------------------------------
1 | require 'bamboo'
2 |
3 | local View = require 'bamboo.view'
4 |
5 | local function index(web, req)
6 | web:page(View("index.html"){})
7 | end
8 |
9 | URLS = {
10 | ['/'] = index,
11 | ['/index/'] = index,
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/initial/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/createapp/initial/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/media/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/createapp/media/css/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/media/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/createapp/media/images/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/media/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/createapp/media/js/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/media/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/createapp/media/plugins/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/media/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/createapp/media/uploads/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/models/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/createapp/models/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/createapp/plugins/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/settings.lua:
--------------------------------------------------------------------------------
1 | config_file = 'config.lua'
2 | WHICH_DB = 15
3 |
--------------------------------------------------------------------------------
/src/cmd_tmpls/createapp/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
14 |
15 | Bamboo
16 |
17 |
18 |
19 |
20 |
21 | Welcome to Bamboo.
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/cmd_tmpls/createcontroller/newcontroller.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local $MODEL = require "models.$CONTROLLER"
4 |
5 | local urlprefix = "$CONTROLLER"
6 |
7 | -- default methods
8 | newview = function (web, req)
9 |
10 | end
11 |
12 | editview = function (web, req)
13 |
14 | end
15 |
16 | delview = function (web, req)
17 |
18 | end
19 |
20 | item = function (web, req)
21 |
22 | end
23 |
24 | list = function (web, req)
25 |
26 | end
27 |
28 | create = function (web, req)
29 |
30 | end
31 |
32 | update = function (web, req)
33 |
34 | end
35 |
36 | delete = function (web, req)
37 |
38 | end
39 |
40 | URLS = {
41 | ["/" + urlprefix + "/newview/"] = newview,
42 | ["/" + urlprefix + "/editview/"] = editview,
43 | ["/" + urlprefix + "/delview/"] = delview,
44 | ["/" + urlprefix + "/item/"] = item,
45 | ["/" + urlprefix + "/list/"] = list,
46 | ["/" + urlprefix + "/create/"] = create,
47 | ["/" + urlprefix + "/update/"] = update,
48 | ["/" + urlprefix + "/delete/"] = delete,
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/cmd_tmpls/createmodel/newmodel.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local Model = require 'bamboo.model'
4 |
5 | return Model:extend {
6 | __name = '$MODEL';
7 | __primarykey = 'name',
8 | __fields = {
9 | ['name'] = {},
10 |
11 | };
12 |
13 | init = function (self, t)
14 | if isFalse(t) then return self end
15 |
16 | -- auto fill non-foreign fields with params t
17 | local fields = self.__fields
18 | for k, v in pairs(t) do
19 | if fields[k] and not fields[k].foreign then
20 | self[k] = tostring(v)
21 | end
22 | end
23 |
24 | return self
25 | end;
26 |
27 | }
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/cmd_tmpls/createplugin/init.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local View = require 'bamboo.view'
4 |
5 | function main()
6 | local i = 0
7 | local str = ''
8 | for i=1, 100, 10 do
9 | str = ('%s %s'):format(str, tostring(i))
10 | end
11 |
12 | return View('testplugin/testplugin.html'){ onetwo = str }
13 | end
14 |
15 | URLS = {
16 | ['xxxxx/xxxxx/'] = main
17 | }
18 |
--------------------------------------------------------------------------------
/src/cmd_tmpls/createplugin/testplugin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/cmd_tmpls/pluginmedia/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/pluginmedia/css/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/pluginmedia/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/pluginmedia/images/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/pluginmedia/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/pluginmedia/js/.gitignore
--------------------------------------------------------------------------------
/src/cmd_tmpls/pluginmedia/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/src/cmd_tmpls/pluginmedia/uploads/.gitignore
--------------------------------------------------------------------------------
/src/db/driver.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | local mongo_driver = require 'bamboo.db.mongo.mongo_driver'
4 |
5 | local default_driver = 'mongo'
6 | local driver_selected = bamboo.config.dbdriver or default_driver
7 |
8 |
9 |
10 | if driver_selected == 'mongo' then
11 | return mongo_driver
12 | else
13 | error('No db driver selected!')
14 | end
15 |
16 |
--------------------------------------------------------------------------------
/src/db/mongo.lua:
--------------------------------------------------------------------------------
1 | mongo_on_luamongo.lua
--------------------------------------------------------------------------------
/src/db/mongo_on_luamongo.lua:
--------------------------------------------------------------------------------
1 | --
2 | -- in this version, we use luamongo as our low level
3 | --
4 | module(..., package.seeall)
5 |
6 |
7 | local mongo = require "mongo"
8 |
9 | local QueryOption_SlaveOk = 4
10 |
11 |
12 |
13 |
14 |
15 | local function cursor_all(cursor)
16 | local results = {}
17 | for r in cursor:results() do
18 | table.insert(results, r)
19 | end
20 |
21 | return results
22 | end
23 |
24 | -- XXX: can this work?
25 | local addAttachMethod2Cursor = function (cursor)
26 | local cursor_mt = getmetatable(cursor)
27 | local up_mt = cursor_mt.__index
28 |
29 | -- add function all
30 | up_mt.all = cursor_all
31 |
32 | end
33 |
34 |
35 |
36 |
37 | local find = function (self, ns, query, fieldsToReturn, nToSkip, nToReturn, queryOptions, batchSize)
38 | assert(type(ns) == 'string')
39 |
40 | if self.secondary then
41 | local bit = require("bit")
42 | if queryOptions then
43 | queryOptions = bit.bor(queryOptions, QueryOption_SlaveOk)
44 | else
45 | queryOptions = QueryOption_SlaveOk
46 | end
47 | end
48 |
49 | local cursor, err = self.conn:query(self.db..'.'..ns, query, nToReturn, nToSkip, fieldsToReturn, queryOptions, batchSize)
50 | if err then print(err) end
51 |
52 | addAttachMethod2Cursor(cursor)
53 |
54 | return cursor
55 |
56 | end
57 |
58 | local findOne = function (self, ns, query, fieldsToReturn, nToSkip, queryOptions, batchSize)
59 |
60 | local cursor = find(self, ns, query, fieldsToReturn, nToSkip, queryOptions, batchSize)
61 | local result = cursor:next()
62 |
63 | -- XXX: need to notice whether it is nil when none found
64 | return result
65 |
66 | end
67 |
68 |
69 | local insert = function (self, ns, doc)
70 | local ns = self.db..'.'..ns
71 | local ok, err = self.conn:insert(ns, doc)
72 |
73 | -- XXX: need disturb or continue??
74 | if err then print(err) end
75 |
76 | return ok
77 |
78 | end
79 |
80 |
81 | local insert_batch = function (self, ns, docs)
82 | local ns = self.db..'.'..ns
83 | local ok, err = self.conn:insert_batch(ns, docs)
84 |
85 | -- XXX: need disturb or continue??
86 | if err then print(err) end
87 |
88 | return ok
89 |
90 | end
91 |
92 |
93 | local update = function (self, ns, query, modifier, upsert, multi)
94 | local ns = self.db..'.'..ns
95 | local ok, err = self.conn:update(ns, query, modifier, upsert, multi)
96 |
97 | -- XXX: need disturb or continue??
98 | if err then print(err) end
99 |
100 | return ok
101 | end
102 |
103 | local remove = function (self, ns, query, justOne)
104 | local ns = self.db..'.'..ns
105 | local ok, err = self.conn:remove(ns, query, justOne)
106 |
107 | -- XXX: need disturb or continue??
108 | if err then print(err) end
109 |
110 | return ok
111 | end
112 |
113 |
114 | local count = function (self, ns, query)
115 | local ns = self.db..'.'..ns
116 | local count, err = self.conn:count(ns, query)
117 |
118 | -- XXX: need disturb or continue??
119 | if err then print(err) end
120 |
121 | return count
122 | end
123 |
124 |
125 |
126 | -- useage:
127 | -- local mongo = require 'bamboo.db.mongo'
128 | -- local conn = mongo.connect(mongo_config)
129 | -- local db = conn:use('one_db_name')
130 | --
131 | function _connect(config)
132 | assert(type(config) == 'table', 'missing config in mongo.connect')
133 | local host = config.host
134 | local port = config.port
135 | local dbname = config.db
136 | local conn_str = host..':'..port
137 |
138 | -- Create a Connection object
139 | local conn = mongo.Connection.New({auto_reconnect = true})
140 | assert( conn ~= nil, 'unable to create mongo.Connection' )
141 | assert( conn:connect(conn_str), 'unable to forcefully connect to mongo instance' )
142 |
143 | if config.user and config.user ~= '' then
144 | --assert( conn:auth { dbname = 'admin', username = config.user, password = config.password } == true, "unable to auth to db" )
145 | assert( conn:auth { dbname = dbname, username = config.user, password = config.password } == true, "unable to auth to db" )
146 | end
147 |
148 | return conn
149 | end
150 |
151 | function _connect_replica_set(config)
152 | assert(type(config) == 'table', 'missing config in mongo.connect')
153 | local set = config.set
154 | assert(set, 'missing set in replica set connect.')
155 | local dbname = config.db
156 | -- ensure replicaset is a host:port array
157 | local replicaset = config.replicaset
158 |
159 | -- Create a Connection object
160 | local conn = mongo.ReplicaSet.New(set, replicaset)
161 | assert( conn ~= nil, 'unable to create mongo.ReplicaSetConnection' )
162 | assert( conn:connect(), 'unable to forcefully connect to mongo instance' )
163 |
164 | print(conn)
165 |
166 | if config.user and config.user ~= '' then
167 | --assert( conn:auth { dbname = 'admin', username = config.user, password = config.password } == true, "unable to auth to db" )
168 | assert( conn:auth { dbname = dbname, username = config.user, password = config.password } == true, "unable to auth to db" )
169 | end
170 | print('db ', dbname, config.user, config.password, 'authored success.')
171 |
172 | return conn
173 | end
174 |
175 |
176 | -- mongodb methods metatable
177 | local _mt_db = {
178 | find = find,
179 | findOne = findOne,
180 | insert = insert,
181 | insert_batch = insert_batch,
182 | update = update,
183 | remove = remove,
184 | count = count
185 | }
186 |
187 |
188 |
189 | function connect (config)
190 | assert(type(config) == 'table', 'missing config in mongo.connect')
191 | config.db = config.db or 'test'
192 | local replicaset = config.replicaset
193 |
194 | local db = {}
195 | -- attach all methods to db
196 | setmetatable(db, { __index = _mt_db })
197 | local conn
198 | if replicaset then
199 | conn = _connect_replica_set(config)
200 | else
201 | config.host = config.host or '127.0.0.1'
202 | config.port = config.port or '27017'
203 | conn = _connect(config)
204 | end
205 |
206 | for k, v in pairs(config) do
207 | db[k] = v
208 | end
209 | -- add db instance to db
210 | db.conn = conn
211 |
212 | return db
213 | end
214 |
215 | function connectReplSet(config, username, password, secondary)
216 | assert(config, "connectReplSet config null");
217 | assert(config.primary, "connectReplSet config.primary");
218 |
219 | local primary_str = config.primary.host..':'..config.primary.port
220 | local second_strs = {}
221 | if config.secondaries then
222 | for i, s in ipairs(config.secondaries) do
223 | second_strs[i] = s.host..':'..s.port
224 | end
225 | end
226 |
227 | local replicaset = {primary_str}
228 | for i=1, #second_strs do
229 | table.insert(replicaset, second_strs[i])
230 | end
231 |
232 | local newconfig = {
233 | set = config.set,
234 | db = config.dbname,
235 | user = username,
236 | password = password,
237 | replicaset = replicaset,
238 | secondary = secondary
239 | }
240 |
241 | return connect(newconfig)
242 | end
243 |
244 |
245 | function connectPrimary(config, username, password)
246 | assert(config, "connectReplSet config null");
247 | assert(config.primary, "connectReplSet config.primary");
248 |
249 |
250 | local newconfig = {
251 | host = config.primary.host,
252 | port = config.primary.port,
253 | db = config.dbname,
254 | user = username,
255 | password = password
256 | }
257 |
258 | return connect(newconfig)
259 | end
260 |
261 |
262 | function connectSecondary(config, username, password)
263 | assert(config, "connectReplSet config null");
264 | assert(config.primary, "connectReplSet config.primary");
265 |
266 | return connectReplSet(config, username, password, true);
267 | end
268 |
269 |
270 |
--------------------------------------------------------------------------------
/src/db/mongo_on_mongol.lua:
--------------------------------------------------------------------------------
1 | --
2 | -- Now this module can be used only in sync mode in persist connction
3 | -- and in async mode in each connection per request
4 | -- later will enhance it.
5 | --
6 | module(..., package.seeall)
7 |
8 |
9 | local mongol = require "mongol"
10 |
11 | -- For background tasks
12 | local SUSPENDED_TASKS = {}
13 | local SUSPENDED_SOCKETS = {}
14 |
15 | local mongoCoroDispatcher = function (loop, io_watcher, revents)
16 |
17 | -- wait for database return
18 | local state = SUSPENDED_TASKS[io_watcher]
19 | if state then
20 | coroutine.resume(state)
21 |
22 | if coroutine.status(state) == "dead" then
23 | SUSPENDED_TASKS[io_watcher] = nil
24 | SUSPENDED_SOCKETS[io_watcher].sock:close()
25 | SUSPENDED_SOCKETS[io_watcher] = nil
26 | io_watcher:stop(loop)
27 | end
28 | end
29 | end
30 |
31 | -- useage:
32 | -- local mongo = require 'bamboo.db.mongo'
33 | -- local conn = mongo.connect(mongo_config)
34 | -- local db = conn:use('one_db_name')
35 | --
36 | function connect(mongo_config)
37 | local config = mongo_config or { host="127.0.0.1", port=27017 }
38 |
39 | local conn
40 | -- async mode
41 | if config.async then
42 | conn = mongol(config.host, config.port, bamboo.internal.loop, mongoCoroDispatcher)
43 | else
44 | -- sync mode
45 | conn = mongol(config.host, config.port)
46 | end
47 | assert(conn, '[Error] connect to mongodb failed.')
48 | if config.user and config.password then
49 | local db = conn:use('admin')
50 | db:auth(config.user, config.password)
51 | end
52 | local db = conn:use(config.db)
53 |
54 | return db
55 | end
56 |
57 |
--------------------------------------------------------------------------------
/src/db/mysql.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | require 'luasql.mysql'
4 |
5 | local Model = require 'bamboo.model'
6 |
7 | local Mysql = Model:extend {
8 | __name = 'Mysql';
9 | __fields = {
10 | ['host'] = {};
11 | ['port'] = {};
12 | ['database'] = {};
13 | ['user'] = {};
14 | ['password'] = {};
15 |
16 | ['conn'] = {};
17 | };
18 |
19 |
20 | init = function(self,t)
21 | self.id = self:getCounter() + 1;
22 |
23 | self.host = t.host or '127.0.0.1';
24 | self.port = t.port or 3306;
25 | self.database = t.database;
26 | self.user = t.user;
27 | self.password = t.password;
28 |
29 | if t.conn ~= nil then
30 | self:connect();
31 | end
32 |
33 | return self;
34 | end;
35 |
36 | connect = function(self,password)
37 | local mysql_env = luasql.mysql();
38 | if password == nil then
39 | self.conn,err = mysql_env:connect( self.database,
40 | self.user,
41 | self.password,
42 | self.host,
43 | self.port);
44 | else
45 | self.conn,err = mysql_env:connect( self.database,
46 | self.user,
47 | password,
48 | self.host,
49 | self.port);
50 |
51 | end
52 |
53 | return err or "connected";
54 | end;
55 |
56 | retrieve = function(self, sqlstr)
57 | local cur = self.conn:execute(sqlstr);
58 |
59 | if type(cur) == 'number' or type(cur)== 'string' then
60 | return cur;
61 | end
62 |
63 | local records = List();
64 | local numrows = cur:numrows();
65 | local colnames = cur:getcolnames();
66 | for i=1,numrows,1 do
67 | local record = {};
68 | local temp = {};
69 | cur:fetch(temp);
70 | --print("temp");
71 | for k,v in ipairs(colnames) do
72 | -- print(k,v);
73 | record[v] = temp[k];
74 | end
75 |
76 | records:append(record);
77 | end
78 |
79 | cur:close();
80 |
81 | return records;
82 | end;
83 |
84 | close = function(self)
85 | self.conn:close();
86 | end;
87 |
88 |
89 | getFromFile = function(self,datFile,achFile)
90 | local fields = nil;
91 | if achFile then
92 | local file = io.open(achFile,"r");
93 | if file then
94 | fields = {};
95 | for line in file:lines() do
96 | table.insert(fields, string.split(line,'\t')[4]);
97 | end
98 | io.close(file);
99 | end
100 | end
101 |
102 | local records = {};
103 | if fields then
104 | local file = io.open(datFile,"r");
105 | if file then
106 | for line in file:lines() do
107 | local record = {};
108 | local temp = string.split(line,'\t');
109 | for k,field in ipairs(fields) do
110 | record[field] = temp[k];
111 | end
112 |
113 | table.insert(records, record);
114 | end
115 |
116 | io.close(file);
117 | end
118 | else
119 | local file = io.open(datFile,"r");
120 | if file then
121 | for line in file:lines() do
122 | local record = string.split(line,'\t');
123 | table.insert(records, record);
124 | end
125 |
126 | io.close(file);
127 | end
128 | end
129 |
130 | return records;
131 | end;
132 |
133 | writeDbToFile = function(self)
134 | local cur = self.conn:execute("show tables");
135 |
136 | local tables = {};
137 | local numrows = cur:numrows();
138 | for i=1,numrows,1 do
139 | local temp ={};
140 | cur:fetch(temp);
141 | tables[i] = temp[1];
142 | end
143 | cur:close();
144 |
145 | for i,v in ipairs(tables) do
146 | print(v,self.database)
147 | self.conn:execute("use information_schema");
148 | self.conn:execute("select * from columns where table_name='"..v.."' and table_schema='" ..self.database.. "' into outfile '/tmp/".. v .. ".ach'");
149 | self.conn:execute("use "..self.database);
150 | self.conn:execute("select * from ".. v .. " into outfile '/tmp/".. v .. ".dat'");
151 | end
152 | end;
153 |
154 | writeDataToFile = function(self, sqlStr, file)
155 | local cur = self.conn:execute(sqlStr.." into outfile '"..file.."'");
156 | return cur;
157 | end
158 | }
159 |
160 | return Mysql;
161 |
--------------------------------------------------------------------------------
/src/db/redis.lua:
--------------------------------------------------------------------------------
1 |
2 | local RDS = {}
3 | local redis = require 'bamboo-redis'
4 |
5 | function RDS.connect(config_t)
6 | -- local params = {
7 | local host = config_t.host or '127.0.0.1'
8 | local port = config_t.port or 6379
9 | -- }
10 | local db = config_t.db or 0
11 |
12 | local redis_db = redis.connect(host, port)
13 | assert(redis_db, '[Error] Redis database connect failed.')
14 |
15 | if config_t.auth then
16 | redis_db:auth(config_t.auth)
17 | end
18 | redis_db:select(db)
19 |
20 | return redis_db
21 | end
22 |
23 | function RDS.connectAll (config)
24 | -- connect redis, basic db
25 | config = config or {master={host="127.0.0.1", port=6379}, slaves={}}
26 |
27 | local DB_HOST = config.master.host
28 | local DB_PORT = config.master.port
29 | local WHICH_DB = config.master.db or 0
30 | local AUTH = config.AUTH or config.master.auth
31 |
32 | -- create a redis connection in this process
33 | -- we will create one redis connection for every process
34 | local master = RDS.connect {
35 | host=DB_HOST,
36 | port=DB_PORT,
37 | db = WHICH_DB,
38 | auth = AUTH}
39 | assert(master, '[Error] Redis master database connect failed.')
40 | -- try to connect slaves
41 | master._slaves = {}
42 | for i, slave in ipairs(config.slaves or {}) do
43 | local sdb = RDS.connect {
44 | host=slave.host,
45 | port=slave.port,
46 | db = slave.db,
47 | auth = slave.auth}
48 | if sdb then
49 | table.insert(master._slaves, sdb)
50 | end
51 | end
52 |
53 | -- keep compatible with old version
54 | -- _G['BAMBOO_DB'] = master
55 | return master
56 | end
57 |
58 | return RDS
59 |
--------------------------------------------------------------------------------
/src/db/redis/Scheme.md:
--------------------------------------------------------------------------------
1 | In bamboo's backend redis, there may be the following key pattern:
2 |
3 | Normal model record
4 | --------------------
5 | Model_name:[0-9]* : redis hash
6 | Model_name:__counter : redis string, number
7 | Model_name:__index : redis zset, score is xxx, member is xxxx
8 |
9 | Foreign field record
10 | --------------------
11 | Model_name:[0-9]*:field : zset (MANY), list (LIST, FIFO), zset (ZFIFO)
12 |
13 | Custom record
14 | --------------------
15 | Model_name:custom:custom_string : model custom key. type can be string, list, set, zset, hash
16 | Model_name:[0-9]*:custom:custom_string : instance custom key. type can be string, list, set, zset, hash
17 |
18 | Cache type
19 | --------------------
20 | Model_name:cache:cache_string : string, zset
21 | CACHETYPE:Model_name:cache:cache_string string
22 |
23 | Deleted Data
24 | --------------------
25 | DELETED:Model_name:[0-9]* : redis hash
26 | DELETED:Model_name:[0-9]*:field : foreign zset, list
27 |
28 | Session Data
29 | -------------------
30 | Session:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx : hash
31 | Session:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:field_name:[string|list|set|zset] : list, set, zset
32 |
33 | Dynamic fields
34 | -------------------
35 | Model_name:dynamic_field:field_string : hash
36 | Model_name:dynamic_field:__index : list
37 |
38 | Rule Index
39 | -------------------
40 | _index_manager:Model_name : zset, value is query_args_str, score is the counter number
41 | _RULE:Model_name:score_num : list, value is instance ids
42 |
43 | Fulltext Index
44 | -------------------
45 | _fulltext_words:Model_name : set, value is word
46 | _FT:Model_name:word : set, value is instance id
47 | _RFT:Model_name:[0-9]* : set, value is word
48 |
49 |
50 |
51 |
52 |
53 | In every redis wapper module, there must be a standard API:
54 |
55 | save : create, batch import
56 | update : update batch import
57 | retrieve : get all data once
58 | del : del the whole
59 | add (append) : add one element to object set
60 | remove (pop) : remove one element from object set
61 | num (len) : measure the number of length of object set
62 | has : check if it is in object set
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/db/redis/fifo.lua:
--------------------------------------------------------------------------------
1 |
2 | -- FIFO, we push element from left, pop from right,
3 | -- new element is at left, old element is at right
4 | -- because redis has no reversely retrieve method on list
5 | module(..., package.seeall)
6 |
7 | local List = require 'lglib.list'
8 | local rdlist = require 'bamboo.db.redis.list'
9 | local db = BAMBOO_DB
10 |
11 | function save (key, tbl, length)
12 | for i,v in ipairs(tbl) do
13 | push(key,v,length);
14 | end
15 | end
16 |
17 | function update (key, tbl, length)
18 | for i,v in ipairs(tbl) do
19 | push(key,v,length);
20 | end
21 | end
22 |
23 | function push (key, val, length)
24 | local len = db:llen(key)
25 |
26 | if len < length then
27 | db:lpush(key, val)
28 | else
29 | -- if FIFO is full, push this element from left, pop one old from right
30 | db:rpop(key)
31 | db:lpush(key, val)
32 | end
33 |
34 | end
35 |
36 | function pop( key )
37 |
38 | return rdlist.pop(key)
39 | end
40 |
41 | function remove( key, val )
42 |
43 | return rdlist.remove(key, val)
44 | end
45 |
46 | function retrieve( key )
47 |
48 | return rdlist.retrieve(key)
49 | end
50 |
51 | function len( key )
52 |
53 | return rdlist.len(key)
54 | end
55 |
56 | function del( key )
57 |
58 | return rdlist.del(key)
59 | end
60 |
61 | function fakedel(key)
62 | return db:rename(key, 'DELETED:' + key)
63 | end
64 |
65 | function has(key, obj)
66 | return rdlist.have(key, obj)
67 | end
68 |
69 |
--------------------------------------------------------------------------------
/src/db/redis/hash.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local db = BAMBOO_DB
4 |
5 | --- create a list
6 | --
7 | function save( key, tbl )
8 | -- if exist, remove it first
9 | if db:exists(key) then
10 | db:del(key)
11 | end
12 |
13 | -- push all elements in tbl to redis hash
14 | for k, v in pairs(tbl) do
15 | db:hset(key, k, tostring(v))
16 | end
17 | end
18 |
19 | --- update a list
20 | --
21 | function update( key, tbl )
22 | for k, v in pairs(tbl) do
23 | db:hset(key, k, tostring(v))
24 | end
25 | end
26 |
27 | function retrieve( key )
28 | return db:hgetall(key)
29 | end
30 |
31 | -- in hash store, passed into add function should be a table
32 | -- such as { good = true }
33 | function add( key, tbl )
34 | update(key, tbl)
35 | end
36 |
37 | -- in hash store, passed into remove function should be a table
38 | -- remove action will check the key and value's equality before real deletion
39 | function remove(key, tbl)
40 | for k, v in pairs(tbl) do
41 | if db:hget(key, k) == tostring(v) then
42 | db:hdel(key, k)
43 | end
44 | end
45 | end
46 |
47 | function has(key, keytocheck)
48 | if db:hget(key, keytocheck) then
49 | return true
50 | else
51 | return false
52 | end
53 | end
54 |
55 | function num(key)
56 | return db:hlen(key)
57 | end
58 |
--------------------------------------------------------------------------------
/src/db/redis/list.lua:
--------------------------------------------------------------------------------
1 | -- wapper to redis list structure
2 | -- new is at right, old is at left
3 | module(..., package.seeall)
4 |
5 | local db = BAMBOO_DB
6 | local List = require 'lglib.list'
7 |
8 | --- create a list
9 | --
10 | function save( key, tbl )
11 | -- if exist, remove it first
12 | if db:exists(key) then
13 | db:del(key)
14 | end
15 |
16 | -- push all elements in tbl to redis list
17 | for _, v in ipairs(tbl) do
18 | db:rpush(key, tostring(v))
19 | end
20 |
21 | end
22 |
23 | --- update a list
24 | --
25 | function update( key, tbl )
26 |
27 | local list = db:lrange(key, 0, -1)
28 |
29 | if #list == 0 then save(key, tbl) end
30 |
31 | if #list >= #tbl then
32 | for i, v in ipairs(tbl) do
33 | if list[i] ~= v then
34 | -- update different elements
35 | db:lset(key, i - 1, tostring(v))
36 | end
37 | end
38 | -- remove rest elements
39 | local delta = #list - #tbl
40 | for i = 1, delta do
41 | db:rpop(key)
42 | end
43 | else
44 | for i, v in ipairs(list) do
45 | if tbl[i] ~= v then
46 | db:lset(key, i - 1, tostring(tbl[i]))
47 | end
48 | end
49 |
50 | -- push more elements
51 | local delta = #tbl - #list
52 | for i = 1, delta do
53 | db:rpush(key, tostring(tbl[#list + i]))
54 | end
55 | end
56 | end
57 |
58 | function retrieve( key )
59 | -- if no element exists, return empty list
60 | local r = db:lrange(key, 0, -1)
61 | --print('r', r)
62 | if r and type(r) ~= 'table' then return List() end
63 | return List(r)
64 | end
65 |
66 | function append( key, val )
67 | -- if have no, create one
68 | -- if have, append to it
69 | return db:rpush(key, tostring(val))
70 | end
71 |
72 | function prepend( key, val )
73 | return db:lpush(key, tostring(val))
74 | end
75 |
76 | function pop( key )
77 | return db:rpop(key)
78 | end
79 |
80 | function remove( key, val )
81 |
82 | return db:lrem(key, 0, val)
83 | end
84 |
85 | function removeByIndex( key, index )
86 | local elem = db:lindex(key, index)
87 | if elem then
88 | return db:lrem(key, 0, elem)
89 | end
90 |
91 | return nil
92 | end
93 |
94 | function len( key )
95 |
96 | return db:llen(key)
97 | end
98 |
99 | function del( key )
100 |
101 | return db:del(key)
102 | end
103 |
104 | function has(key, obj)
105 | local len = db:llen(key)
106 | for i = 0, len-1 do
107 | local elem = db:lindex(i)
108 | if obj == elem then
109 | return true
110 | end
111 | end
112 |
113 | return false
114 | end
115 |
116 |
--------------------------------------------------------------------------------
/src/db/redis/set.lua:
--------------------------------------------------------------------------------
1 | local List = require 'lglib.list'
2 |
3 | module(..., package.seeall)
4 |
5 |
6 | local db = BAMBOO_DB
7 |
8 | -- @param tbl: a member list
9 | function save(key, tbl)
10 | for _, v in ipairs(tbl) do
11 | db:sadd(key, tostring(v))
12 | end
13 | end
14 |
15 | function update(key, tbl)
16 | save(key, tbl)
17 | end
18 |
19 |
20 | function add( key, val )
21 |
22 | return db:sadd(key, val)
23 | end
24 |
25 |
26 | function retrieve( key )
27 |
28 | return List(db:smembers(key))
29 | end
30 |
31 | function remove( key, val )
32 |
33 | return db:srem(key, val)
34 | end
35 |
36 | function num( key )
37 |
38 | return db:scard(key)
39 | end
40 |
41 | function del( key )
42 |
43 | return db:del(key)
44 | end
45 |
46 | function has(key, obj)
47 |
48 | return db:sismember(key, tostring(obj))
49 | end
50 |
51 |
--------------------------------------------------------------------------------
/src/db/redis/string.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 |
4 | local db = BAMBOO_DB
5 |
6 | -- @param tbl: a member list
7 | function save(key, val)
8 | db:set(key, val)
9 | end
10 |
11 | update = save
12 | add = save
13 |
14 | function retrieve(key)
15 | return db:get(key)
16 | end
17 |
18 | function remove(key, val)
19 | return db:set(key, '')
20 | end
21 |
22 | function num( key )
23 | return 1
24 | end
25 |
26 | function del( key )
27 | return db:del(key)
28 | end
29 |
30 | function has(key, obj)
31 | return db:get(key) == obj
32 | end
33 |
34 |
--------------------------------------------------------------------------------
/src/db/redis/zfifo.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | module(..., package.seeall)
4 |
5 | local List = require 'lglib.list'
6 | local rdzset = require 'bamboo.db.redis.zset'
7 | local db = BAMBOO_DB
8 |
9 | function save(key,tbl,length)
10 | for i,v in ipairs(tbl) do
11 | push(key,v,length)
12 | end
13 | end
14 |
15 | function update(key,tbl,length)
16 | for i,v in ipairs(tbl) do
17 | push(key,v,length)
18 | end
19 | end
20 |
21 |
22 | function push( key, val, length )
23 |
24 | local n = db:zcard(key)
25 | if n < length then
26 | if n == 0 then
27 | db:zadd(key, 1, val)
28 | else
29 | local _, scores = db:zrange(key, -1, -1, 'withscores')
30 | local lastscore = scores[1]
31 | db:zadd(key, lastscore + 1, val)
32 | end
33 | else
34 | local _, scores = db:zrange(key, -1, -1, 'withscores')
35 | local lastscore = scores[1]
36 |
37 | -- remove the oldest one
38 | db:zremrangebyrank(key, 0, 0)
39 | -- add the new one
40 | db:zadd(key, lastscore + 1, val)
41 | end
42 |
43 | end
44 |
45 | function pop( key )
46 | local n = db:zcard(key)
47 |
48 | if n >= 1 then
49 | --
50 | local _, scores = db:zrange(key, 0, 0, 'withscores')
51 | local score = scores[1]
52 | db:zremrangebyrank(key, 0, 0)
53 | return score
54 | else
55 | return nil
56 | end
57 | end
58 |
59 | function remove( key, val )
60 | return rdzset.remove(key, val)
61 | end
62 |
63 | function removeByScore(key, score)
64 | return rdzset.removeByScore(key, score)
65 | end
66 |
67 |
68 | function retrieve( key )
69 | -- reverse get
70 |
71 | return List(db:zrevrange(key, 0, -1))
72 | end
73 |
74 | function retrieveWithScores(key)
75 | -- every element, [1] is val, [2] is score
76 | local values, scores = db:zrevrange(key, 0, -1, 'withscores')
77 | return List(values), List(scores)
78 | end
79 |
80 | function num( key )
81 |
82 | return rdzset.num(key)
83 | end
84 |
85 | function del( key )
86 |
87 | return rdzset.del(key)
88 | end
89 |
90 | function fakedel(key)
91 | return db:rename(key, 'DELETED:' + key)
92 | end
93 |
94 | function has(key, obj)
95 |
96 | return rdzset.have(key, obj)
97 | end
98 |
--------------------------------------------------------------------------------
/src/db/redis/zset.lua:
--------------------------------------------------------------------------------
1 | local List = require 'lglib.list'
2 |
3 | module(..., package.seeall)
4 |
5 |
6 | local db = BAMBOO_DB
7 |
8 | function save(key, tbl, scores)
9 | db:del(key)
10 | if not scores then
11 | local n = 0
12 | for _, v in ipairs(tbl) do
13 | db:zadd(key, n + 1, tostring(v))
14 | n = n + 1
15 | end
16 | else
17 | checkType(scores, 'table')
18 | assert(#tbl == #scores, '[Error] the lengths of val and scores are not equal.')
19 |
20 | for i, v in ipairs(tbl) do
21 | local score = scores[i]
22 | assert(type(tonumber(score)) == 'number', '[Error] Some score in score list is not number.')
23 | db:zadd(key, score, tostring(v))
24 | end
25 | end
26 | end
27 |
28 | function update(key, tbl)
29 | local n = db:zcard(key)
30 |
31 | for _, v in ipairs(tbl) do
32 | db:zadd(key, n + 1, tostring(v))
33 | n = n + 1
34 | end
35 | end
36 |
37 |
38 |
39 | function add( key, val, score )
40 | -- local oscore = db:zscore(key, val)
41 | -- is exist, do nothing, else redis will update the score of val
42 | -- if oscore then return nil end
43 | if not score then
44 | -- get the current element in zset
45 | local n = db:zcard(key)
46 | if n == 0 then
47 | db:zadd(key, 1, val)
48 | else
49 | local _, scores = db:zrange(key, -1, -1, 'withscores')
50 | lastscore = scores[1]
51 | -- give the new added element score n+1
52 | db:zadd(key, lastscore + 1, val)
53 | end
54 | else
55 | -- checkType(score, 'number')
56 | db:zadd(key, score, val)
57 | end
58 |
59 | -- return the score
60 | return db:zscore(key, val)
61 | end
62 |
63 |
64 | function retrieve( key )
65 | -- only have members, no scores
66 | return List(db:zrange(key, 0, -1))
67 | end
68 |
69 | function retrieveReversely( key )
70 | return List(db:zrevrange(key, 0, -1))
71 | end
72 |
73 | function retrieveWithScores( key )
74 | -- [1] is member, [2] is score
75 | local value_list, score_list = db:zrange(key, 0, -1, 'withscores')
76 | return List(value_list), List(score_list)
77 | end
78 |
79 | function retrieveReverselyWithScores( key )
80 | -- [1] is member, [2] is score
81 | local value_list, score_list = db:zrevrange(key, 0, -1, 'withscores')
82 |
83 | return List(value_list), List(score_list)
84 | end
85 |
86 | function remove( key, val )
87 | return db:zrem(key, val)
88 | end
89 |
90 | function removeByScore(key, score)
91 | return db:zremrangebyscore(key, score, score)
92 | end
93 |
94 | function num( key )
95 |
96 | return db:zcard(key)
97 | end
98 |
99 | function del( key )
100 |
101 | return db:del(key)
102 | end
103 |
104 | function fakedel(key)
105 | return db:rename(key, 'DELETED:' + key)
106 | end
107 |
108 | function has(key, obj)
109 |
110 | local score = db:zscore(key, tostring(obj))
111 |
112 | return (score ~= nil)
113 | end
114 |
115 |
--------------------------------------------------------------------------------
/src/errors.lua:
--------------------------------------------------------------------------------
1 |
2 | module('bamboo.errors', package.seeall)
3 |
4 | local View = require 'bamboo.view'
5 |
6 | -- Reports errors back to the browser so the user has something to work with.
7 | function reportError(conn, request, err, state)
8 | local trace = debug.traceback(state.controller, err)
9 | local info
10 | local source = nil
11 |
12 | info = debug.getinfo(state.controller, state.main)
13 |
14 | if info.source:match("@.+$") then
15 | -- if code comes from file, display the code lines errored in that file
16 | source = io.loadLines(info.source:sub(2), info.linedefined, info.lastlinedefined)
17 | else
18 | -- if code doesn't come from file, it is a string
19 | source = info.source
20 | end
21 |
22 | local erroutput = ""
23 | local target = err:match("%[%w* *\"(%S+)\"%]:")
24 | local errorlinenum = tonumber(string.match(err, ":(%d+):"))
25 | if target and errorlinenum and bamboo.compiled_views_tmpls[target] then
26 | local elines = string.split(bamboo.compiled_views_tmpls[target], '\n')
27 | local errorline = elines[errorlinenum]
28 | if errorline then
29 | erroutput = "[Error] error occured at: " .. (errorline:match("_result%[%#_result%+1%] = (.+)$") or errorline)
30 | end
31 | end
32 | print(erroutput)
33 | print("[Error]", err)
34 |
35 | -- Error info template
36 | local ERROR_PAGE = View.compileView [[
37 | Bamboo Error
38 |
39 | There was an error processing your request.
40 | Stack Trace
41 |
42 | {{ erroutput }}
43 | {{ err }}
44 |
45 | Source Code
46 |
47 | {{ source }}
48 |
49 | Request
50 |
51 | {{ request }}
52 |
53 |
54 |
55 | ]]
56 |
57 | local pretty_req = "Request\n " + serialize(request or {})
58 | local page = ERROR_PAGE {err=trace, source=source, request=pretty_req, erroutput = erroutput}
59 | conn:reply_http(page, 500, "Internal Server Error", nil, nil, req.meta)
60 | end
61 |
62 |
63 | function basicError(conn, req, body, code, status, headers)
64 | headers = headers or {}
65 | headers['content-type'] = 'text/plain'
66 | headers['server'] = 'Bamboo on Monserver'
67 |
68 | conn:reply_http(body, code, status, headers, nil, req.meta)
69 | end
70 |
71 |
--------------------------------------------------------------------------------
/src/form.lua:
--------------------------------------------------------------------------------
1 |
2 | module(..., package.seeall)
3 |
4 | local http = require 'lglib.http'
5 |
6 |
7 | local ENCODING_MATCH = '^%s-([%w/%-]+);*(.*)$'
8 | local URL_ENCODED_FORM = 'application/x-www-form-urlencoded'
9 | local MULTIPART_ENCODED_FORM = 'multipart/form-data'
10 |
11 |
12 |
13 |
14 | --- parse http headers, transform it to lua table
15 | -- @param head: original HTTP header string
16 | -- @return result: lua table
17 | local parseHeaders = function (head)
18 | local result = {}
19 | head = ('%s\r\n'):format(head)
20 |
21 | for key, val in head:gmatch('%s*(.-):%s*(.-)\r\n') do
22 | result[key:lower()] = http.parseURL(val, ';')
23 | end
24 |
25 | return result
26 | end;
27 |
28 |
29 | --- parse multipart form string format
30 | -- now it's very simple, load whole file into memory
31 | -- @param body: original HTTP body string
32 | -- @param sepstr:
33 | -- @return result:
34 | local extractMultiparts = function (body, sepstr)
35 | sepstr = ('%s;'):format(sepstr)
36 | local boundary = ('%%-%%-%s'):format(sepstr:match('^.*boundary=(.-);.*$'):gsub('%-', '%%-'))
37 | local results = {}
38 |
39 | -- body use boundary to seperate each part,iterate them
40 | for part in body:gmatch(('(.-)%s'):format(boundary)) do
41 | -- in every part, head and piece are divided by two '\r\n',piece has one '\r\n' followed it
42 | local head, piece = part:match('^(.-)\r\n\r\n(.*)\r\n$')
43 |
44 | if head then
45 | head = parseHeaders(head)
46 |
47 | local cdisp = head['content-disposition']
48 | if cdisp and cdisp.name and cdisp[1] == 'form-data' and not head['content-type'] then
49 | -- store named variable in form,as a dict
50 | results[cdisp.name:match('"(.-)"')] = piece
51 | else
52 | head.body = piece
53 | -- store none named variable in form, as a list
54 | results[#results + 1] = head
55 | end
56 | end
57 | end
58 |
59 | return results
60 | end;
61 |
62 |
63 | local Form = Object:extend {
64 | parse = function (self, req)
65 | I_AM_CLASS(self)
66 | local headers = req.headers
67 | local method = req.method
68 | local query_string = req.query_string
69 | local body = req.body
70 | local params = {}
71 |
72 | if method == 'GET' then
73 | if query_string then
74 | -- params is the dictory of query
75 | params = http.parseURL(query_string)
76 | end
77 | --elseif method == 'POST' then
78 | else
79 | local ctype = headers['content-type'] or ""
80 | local encoding, encparams = ctype:match(ENCODING_MATCH)
81 | if encoding then encoding = encoding:lower() end
82 |
83 | if encoding == URL_ENCODED_FORM then
84 | if body then
85 | -- POST data is placed in body
86 | params = http.parseURL(body)
87 | end
88 | elseif encoding == MULTIPART_ENCODED_FORM then
89 | params = extractMultiparts(body, encparams)
90 | params.multipart = true
91 | else
92 | -- for other format case
93 | --print(("POST RECEIVED BUT NO CONTENT TYPE WE UNDERSTAND: %s."):format(ctype))
94 | end
95 | end
96 |
97 | return params
98 | end;
99 |
100 | parseQuery = function (self, req)
101 | I_AM_CLASS(self)
102 | if req.query_string then
103 | return http.parseURL(req.query_string)
104 | else
105 | return {}
106 | end
107 | end;
108 |
109 |
110 | encode = function (self, data, sep)
111 | I_AM_CLASS(self)
112 | local result = {}
113 |
114 | for k,v in pairs(data) do
115 | result[#result + 1] = ('%s=%s'):format(http.encodeURL(tostring(k)), http.encodeURL(tostring(v)))
116 | end
117 |
118 | return table.concat(result, sep or '&')
119 | end;
120 |
121 | }
122 |
123 | return Form
124 |
--------------------------------------------------------------------------------
/src/i18n.lua:
--------------------------------------------------------------------------------
1 |
2 | local _M = {}
3 |
4 |
5 | _M['translate'] = function (sentence)
6 | local ret
7 | local lang_code = _G.languageEnv or 'zh-cn'
8 | local tranelem = bamboo.i18n[sentence]
9 | if tranelem then
10 | ret = bamboo.i18n[sentence][lang_code]
11 | end
12 | if ret then
13 | return ret
14 | else
15 | return sentence
16 | end
17 |
18 | end
19 |
20 | _M['langcode'] = function (req)
21 | -- here, we like req is a local variable
22 | -- get the language specified
23 | local langenv = nil
24 | --
25 | local accept_language = req.headers['accept-language'] or req.headers['Accept-Language']
26 | if not accept_language then
27 | langenv = 'zh-cn'
28 | else
29 | -- such as zh-cn, en-us, zh-tw, zh-hk
30 | langenv = accept_language:match('(%a%a%-%a%a)')
31 | if langenv then
32 | langenv = langenv:lower()
33 | else
34 | langenv = 'zh-cn'
35 | end
36 | end
37 |
38 | -- currently, we define this language environment global variable
39 | _G.languageEnv = langenv
40 |
41 | return langenv
42 | end
43 |
44 |
45 | return _M
46 |
--------------------------------------------------------------------------------
/src/lib/gdutil.lua:
--------------------------------------------------------------------------------
1 |
2 | local gd = require 'gd'
3 |
4 | local getFormat = function(path)
5 | local file = io.open(path,"r");
6 | if not file then return nil; end
7 |
8 | local fileType = file:read(1):byte(1,1);
9 | io.close(file);
10 |
11 | --根据图片类型和路径生成gd对象
12 | local ext = nil;
13 | if fileType == 137 then -- png
14 | ext = ".png"
15 | elseif fileType == 71 then -- gif
16 | ext = ".gif"
17 | elseif fileType == 255 then--jpg
18 | ext = ".jpg"
19 | else
20 | end
21 |
22 | return ext;
23 | end;
24 |
25 | local getGdObj = function(path)
26 | local fileType = getFormat(path);
27 | if not fileType then
28 | return nil;
29 | end
30 |
31 | local gdObj = nil;
32 | if fileType == ".png" then -- png
33 | gdObj = gd.createFromPng(path);
34 | elseif fileType == ".gif" then -- gif
35 | gdObj = gd.createFromGif(path);
36 | elseif fileType == ".jpg" then--jpg
37 | gdObj = gd.createFromJpeg(path);
38 | else
39 |
40 | end
41 |
42 | return gdObj;
43 | end
44 |
45 | return {
46 | getFormat = getFormat,
47 | getGdObj = getGdObj
48 | }
49 |
--------------------------------------------------------------------------------
/src/mixins/fakedel.lua:
--------------------------------------------------------------------------------
1 | -- 假删
2 | -- 目前只提供删除功能,后面找个时间把删除的恢复功能加上
3 | -- 现在相当于是放到垃圾箱里面
4 |
5 | local driver = require 'bamboo.db.driver'
6 |
7 | return function (...)
8 |
9 |
10 | return {
11 | -- delete self instance object
12 | trueDelById = function (self, id)
13 | I_AM_CLASS(self)
14 | return driver.delById(self, id)
15 | end,
16 |
17 | fakeDelById = function (self, id)
18 | I_AM_CLASS(self)
19 | return driver.fakeDelById(self, id)
20 | end,
21 |
22 | delById = function (self, id)
23 | I_AM_CLASS(self)
24 | return driver.fakeDelById(self)
25 | end,
26 |
27 | -- delete self instance object
28 | trueDel = function (self)
29 | I_AM_INSTANCE(self)
30 | return driver.del(self)
31 | end,
32 |
33 | fakedel = function (self)
34 | I_AM_INSTANCE(self)
35 | return driver.fakedel(self)
36 | end,
37 |
38 | del = function (self)
39 | I_AM_INSTANCE(self)
40 | return driver.fakedel(self)
41 | end,
42 |
43 |
44 | }
45 |
46 | end
47 |
--------------------------------------------------------------------------------
/src/mixins/mvm.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | toHtml = function (self, params)
5 | I_AM_INSTANCE(self)
6 | params = params or {}
7 |
8 | if params.field and type(params.field) == 'string' then
9 | for k, v in pairs(params.attached) do
10 | if v == 'html_class' then
11 | self.__fields[params.field][k] = self.__fields[params.field][k] .. ' ' .. v
12 | else
13 | self.__fields[params.field][k] = v
14 | end
15 | end
16 |
17 | return (self.__fields[params.field]):toHtml(self, params.field, params.format)
18 | end
19 |
20 | params.attached = params.attached or {}
21 |
22 | local output = ''
23 | for field, fdt_old in pairs(self.__fields) do
24 | local fdt = table.copy(fdt_old)
25 | setmetatable(fdt, getmetatable(fdt_old))
26 | for k, v in pairs(params.attached) do
27 | if type(v) == 'table' then
28 | for key, val in pairs(v) do
29 | fdt[k] = fdt[k] or {}
30 | fdt[k][key] = val
31 | end
32 | else
33 | fdt[k] = v
34 | end
35 | end
36 |
37 | local flag = true
38 | params.filters = params.filters or {}
39 | for k, v in pairs(params.filters) do
40 | -- to redundant query condition, once meet, jump immediately
41 | if not fdt[k] then
42 | -- if k == 'vl' then self.__fields[field][k] = 0 end
43 | if k == 'vl' then fdt[k] = 0 end
44 | end
45 |
46 | if type(v) == 'function' then
47 | flag = v(fdt[k] or '')
48 | if not flag then break end
49 | else
50 | if fdt[k] ~= v then flag=false; break end
51 | end
52 | end
53 |
54 | if flag then
55 | output = output .. fdt:toHtml(self, field, params.format or nil)
56 | end
57 |
58 | end
59 |
60 | return output
61 | end
62 |
63 |
--------------------------------------------------------------------------------
/src/models/group.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 | local socket = require 'socket'
3 | local Model = require 'bamboo.model'
4 |
5 | local Group
6 | Group = Model:extend {
7 | __name = 'Group';
8 | __indexfd = "name";
9 | __fields = {
10 | ['name'] = {},
11 | ['desc'] = {},
12 | ['created_date'] = {},
13 |
14 | ['perms'] = { foreign="Permission", st="MANY" },
15 | ['owner'] = { foreign="User", st="ONE" },
16 | ['managers'] = { foreign="User", st="MANY" },
17 | };
18 |
19 | init = function (self, t)
20 | if not t then return self end
21 |
22 | self.name = t.name
23 | self.desc = self.desc
24 | self.created_date = socket.gettime()
25 |
26 |
27 | return self
28 | end;
29 |
30 | }
31 |
32 | return Group
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/models/image.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local Upload = require 'bamboo.models.upload'
4 | local gdutil = require 'bamboo.lib.gdutil'
5 |
6 | local Image = Upload:extend {
7 | __name = 'Image';
8 | __fields = {
9 | ['width'] = {},
10 | ['height'] = {},
11 |
12 | };
13 |
14 | init = function (self, t)
15 | if not t then return self end
16 | local imgobj = gdutil.getGdObj(self.innerpath)
17 | if imgobj then
18 | local width, height = imgobj:sizeXY()
19 | self.width = width
20 | self.height = height
21 | end
22 | return self
23 | end;
24 |
25 | }
26 |
27 | return Image
28 |
--------------------------------------------------------------------------------
/src/models/message.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 |
4 | local Model = require 'bamboo.model'
5 |
6 | local Message = Model:extend {
7 | __name = 'Message';
8 | __fields = {
9 | ['from'] = { foreign='User', required=true },
10 | ['to'] = { foreign='User' },
11 | ['subject'] = { foreign='UNFIXED', st='ONE' },
12 | ['type'] = {},
13 | -- ['uuid'] = {},
14 | ['author'] = {},
15 | ['content'] = {},
16 | ['timestamp'] = {}
17 | };
18 |
19 |
20 | init = function (self, t)
21 | if not t then return self end
22 |
23 | self.type = t.type
24 | -- self.uuid = t.uuid
25 | self.author = t.author
26 | self.content = t.content
27 | self.timestamp = t.timestamp or os.time()
28 |
29 | return self
30 | end;
31 |
32 | }
33 |
34 | return Message
35 |
--------------------------------------------------------------------------------
/src/models/node.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local Model = require 'bamboo.model'
4 |
5 | local getModelByName = bamboo.getModelByName
6 |
7 | local Node
8 | Node = Model:extend {
9 | __name = 'Node';
10 | __fields = {
11 | ['name'] = { newfield=true},
12 | ['rank'] = { newfield=true},
13 | ['title'] = { required=true, newfield=true},
14 | ['content'] = { required=true, newfield=true},
15 | ['status'] = { newfield=true},
16 |
17 | ['is_category'] = { newfield=true},
18 | ['parent'] = { st='ONE', foreign='Node', newfield=true, order=1.1},
19 | ['children'] = { st='MANY', foreign='Node', newfield=true},
20 | ['groups'] = { st='MANY', foreign='Node', newfield=true},
21 |
22 | ['comments'] = { st='MANY', foreign='Message', newfield=true},
23 | ['attachments'] = { st='MANY', foreign='Upload', newfield=true},
24 |
25 | ['created_date'] = { newfield=true},
26 | ['lastmodified_date'] = { newfield=true},
27 | ['creator'] = { st='ONE', foreign='User', newfield=true},
28 | ['owner'] = { st='ONE', foreign='User', newfield=true},
29 | ['lastmodifier'] = { st='ONE', foreign='User', newfield=true},
30 |
31 | };
32 | __callbacks = {
33 |
34 | onUpdate = function (args)
35 |
36 | end,
37 |
38 | onDelete = function (args)
39 |
40 | end,
41 |
42 | onGet = function (args)
43 |
44 | end,
45 |
46 | onAddForeign = function (args)
47 |
48 | end,
49 | onAddedAsForeign = function (args)
50 |
51 | end,
52 |
53 | onDeleteForeign = function (args)
54 |
55 | end,
56 | onDeletedAsForeign = function (args)
57 |
58 | end,
59 |
60 | onSave = function (args)
61 |
62 | end,
63 |
64 | };
65 |
66 | init = function (self, t)
67 | if not t then return self end
68 |
69 | self.name = t.name or self.name
70 | self.rank = t.rank
71 | self.title = t.title
72 | self.content = t.content
73 |
74 | self.is_category = t.is_category
75 | self.parent = t.parent
76 | self.children = t.children
77 | self.groups = t.groups
78 |
79 | self.comments = t.comments
80 | self.attachments = t.attachments
81 |
82 | self.created_date = os.time()
83 | self.lastmodified_date = self.created_date
84 |
85 | return self
86 | end;
87 |
88 |
89 | getComments = function (self)
90 | return self:getForeign ('comments')
91 | end;
92 |
93 | getPartialComments = function (self, start, stop)
94 | return self:getForeign ('comments', start, stop)
95 | end;
96 |
97 |
98 | getAttachments = function (self)
99 | return self:getForeign ('attachments')
100 | end;
101 |
102 |
103 | getChildren = function (self)
104 | if isFalse(self.children) then return {} end
105 | return self:getForeign ('children')
106 | end;
107 |
108 |
109 | getParent = function (self)
110 | if self.parent == '' then return nil end
111 | return self:getForeign('parent')
112 | end;
113 |
114 | }
115 |
116 | return Node
117 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/src/models/permission.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local Model = require 'bamboo.model'
4 |
5 | local Permission
6 | Permission = Model:extend {
7 | __name = 'Permission';
8 | __primarykey = 'name';
9 | __fields = {
10 | ['name'] = {},
11 | ['desc'] = {},
12 | };
13 |
14 | init = function (self, t)
15 | if not t then return self end
16 |
17 | self.name = t.name
18 | self.desc = t.desc
19 |
20 | return self
21 | end;
22 |
23 | add = function (self, name, desc)
24 | I_AM_CLASS(self)
25 | checkType(name, 'string')
26 | local desc = desc or ''
27 |
28 | local perm = Permission:getByIndex(name)
29 | if not perm then
30 | local new_perm = Permission {
31 | name = name,
32 | desc = desc
33 | }
34 | new_perm:save()
35 | elseif perm.desc ~= desc then
36 | perm:update('desc', desc)
37 | end
38 |
39 | end;
40 |
41 |
42 | }
43 |
44 | return Permission
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/models/upload_bak.lua:
--------------------------------------------------------------------------------
1 | --- A basic lower upload API
2 | --
3 | module(..., package.seeall)
4 |
5 | require 'posix'
6 | local http = require 'lglib.http'
7 | local normalizePath = require('lglib.path').normalize
8 |
9 | local Model = require 'bamboo.model'
10 | local Form = require 'bamboo.form'
11 |
12 | local function rename_func(oldname)
13 | return os.time() .. math.random(1000000, 9999999) .. oldname:match('^.+(%.%w+)$')
14 | end
15 |
16 |
17 | local function calcNewFilename(dir, oldname)
18 | -- separate the base name and extense name of a filename
19 | local main, ext = oldname:match('^(.+)(%.%w+)$')
20 | if not ext then
21 | main = oldname
22 | ext = ''
23 | end
24 | -- check if exists the same name file
25 | local tstr = ''
26 | local i = 0
27 | while posix.stat( dir + main + tstr + ext ) do
28 | i = i + 1
29 | tstr = '_' + tostring(i)
30 | end
31 | -- concat to new filename
32 | local newbasename = main + tstr
33 |
34 | return newbasename, ext
35 | end
36 |
37 | --- here, we temprorily only consider the file data is passed wholly by zeromq
38 | -- and save by bamboo
39 | -- @field t.req
40 | -- @field t.file_obj
41 | -- @field t.dest_dir
42 | -- @field t.prefix
43 | -- @field t.postfix
44 | --
45 | local function savefile(t)
46 | local req, file_obj = t.req, t.file_obj
47 |
48 |
49 | local export_dir = t.dest_dir and ('/uploads/' + t.dest_dir + '/') or '/uploads/'
50 | export_dir = normalizePath(export_dir)
51 | local dest_dir = 'media'.. export_dir
52 |
53 | local prefix = t.prefix or ''
54 | local postfix = t.postfix or ''
55 | local filename = ''
56 | local body = ''
57 | local rename_func = t.rename_func == 'default' and rename_func or t.rename_func
58 |
59 | -- if upload in html5 way
60 | if req.headers['x-requested-with'] then
61 | -- when html5 upload, we think of the name of that file was stored in header x-file-name
62 | -- if this info missing, we may consider the filename was put in the query string
63 | filename = req.headers['x-file-name'] or Form:parseQuery(req)['filename']
64 | -- TODO:
65 | -- req.body contains all file binary data
66 | body = req.body
67 | else
68 | checkType(file_obj, 'table')
69 | -- Notice: the filename in the form string are quoted by ""
70 | -- this pattern rule can deal with the windows style directory delimiter
71 | -- file_obj['content-disposition'] contains many file associated info
72 | filename = file_obj['content-disposition'].filename:sub(2, -2):match('\\?([^\\]-%.%w+)$')
73 |
74 | body = file_obj.body
75 | end
76 | if isFalse(filename) or isFalse(body) then return nil, nil end
77 | if not req.ajax then
78 | filename = http.encodeURL(filename)
79 | end
80 |
81 | if not posix.stat(dest_dir) then
82 | -- why posix have no command like " mkdir -p "
83 | os.execute('mkdir -p ' + dest_dir)
84 | end
85 |
86 | local newfilename = filename
87 | -- if passed in a rename function, use it to replace the orignial filename
88 | if rename_func and type(rename_func) == 'function' then
89 | newfilename = rename_func(filename)
90 | end
91 |
92 | local newbasename, ext = calcNewFilename(dest_dir, newfilename)
93 | local newname = prefix + newbasename + postfix + ext
94 |
95 | local export_path = export_dir + newname
96 | local path = dest_dir + newname
97 |
98 | -- write file to disk
99 | local fd = io.open(path, "wb")
100 | fd:write(body)
101 | fd:close()
102 |
103 | return newname, path, export_path, filename
104 | end
105 |
106 |
107 |
108 | local Upload = Model:extend {
109 | __name = 'Upload';
110 | __fields = {
111 | ['name'] = {},
112 | ['path'] = {unique=true},
113 | ['size'] = {},
114 | ['desc'] = {},
115 |
116 | };
117 | __decorators = {
118 | del = function (odel)
119 | return function (self, ...)
120 | I_AM_INSTANCE_OR_QUERY_SET(self)
121 | -- if self is query set
122 | if isQuerySet(self) then
123 | for _, v in ipairs(self) do
124 | -- remove file from disk
125 | os.execute('rm ' + v.path)
126 | end
127 | else
128 | -- remove file from disk
129 | os.execute('rm ' + self.path)
130 | end
131 |
132 | return odel(self, ...)
133 | end
134 | end
135 |
136 | };
137 |
138 | init = function (self, t)
139 | if not t then return self end
140 | self.oldname = t.oldname
141 | self.name = t.name
142 | self.path = t.export_path
143 | self.size = posix.stat(t.path).size
144 | --self.timestamp = os.time()
145 | -- according the current design, desc field is nil
146 | self.desc = t.desc or ''
147 |
148 | return self
149 | end;
150 |
151 | --- For traditional Html4 form upload
152 | --
153 | batch = function (self, req, params, dest_dir, prefix, postfix, rename_func)
154 | I_AM_CLASS(self)
155 | local file_objs = List()
156 | -- file data are stored as arraies in params
157 | for i, v in ipairs(params) do
158 | local name, path, export_path = savefile { req = req, file_obj = v, dest_dir = dest_dir, prefix = prefix, postfix = postfix, rename_func = rename_func }
159 | if not path or not name then return nil end
160 | -- create file instance
161 | local file_instance = self { name = name, path = path, export_path = export_path }
162 | if file_instance then
163 | -- store to db
164 | -- file_instance:save()
165 | -- fix the id sequence
166 | -- file_instance.id = file_instance.id + i - 1
167 | file_objs:append(file_instance)
168 | end
169 | end
170 |
171 | -- a file object list
172 | return file_objs
173 | end;
174 |
175 | process = function (self, web, req, dest_dir, prefix, postfix, rename_func)
176 | I_AM_CLASS(self)
177 | assert(web, '[Error] Upload input parameter: "web" must be not nil.')
178 | assert(req, '[Error] Upload input parameter: "req" must be not nil.')
179 | -- current scheme: for those larger than PRESIZE, send a ABORT signal, and abort immediately
180 | if req.headers['x-mongrel2-upload-start'] then
181 | print('return blank to abort upload.')
182 | web.conn:reply(req, '')
183 | return nil, 'Uploading file is too large.'
184 | end
185 |
186 | -- if upload in html5 way
187 | if req.headers['x-requested-with'] then
188 | -- stored to disk
189 | local name, path, export_path, oldname = savefile { req = req, dest_dir = dest_dir, prefix = prefix, postfix = postfix, rename_func = rename_func }
190 | if not path or not name then return nil, '[Error] empty file.' end
191 |
192 | local file_instance = self { name = name, path = path, export_path = export_path, oldname = oldname }
193 | if file_instance then
194 | -- file_instance:save()
195 | return file_instance, 'single'
196 | end
197 | else
198 | -- for uploading in html4 way
199 | -- here, in formal html4 form, req.POST always has value in,
200 | local params = req.POST -- or Form:parse(req)
201 | assert(#params > 0, '[Error] No valid file data contained.')
202 | local files = self:batch ( req, params, dest_dir, prefix, postfix, rename_func )
203 | if files:isEmpty() then return nil, '[Error] empty file.' end
204 |
205 | if #files == 1 then
206 | -- even only one file upload, batch function will return a list
207 | return files[1], 'single'
208 | else
209 | return files, 'multiple'
210 | end
211 | end
212 | end;
213 |
214 | calcNewFilename = function (self, dest_dir, oldname)
215 | return calcNewFilename(dest_dir, oldname)
216 | end;
217 |
218 | -- this function, encorage override by child model, to execute their own delete action
219 | -- specDelete = function (self)
220 | -- I_AM_INSTANCE(self)
221 | -- -- remove file from disk
222 | -- os.execute('rm ' + self.path)
223 | -- return self
224 | -- end;
225 |
226 | }
227 |
228 | return Upload
229 |
230 |
231 |
--------------------------------------------------------------------------------
/src/models/user.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 |
4 | local Model = require 'bamboo.model'
5 | local Session = require 'bamboo.session'
6 | local md5 = require 'md5'
7 | local socket = require 'socket'
8 | local Perm = require 'bamboo.models.permission'
9 |
10 | local User = Model:extend {
11 | __name = 'User';
12 | __primarykey = "username";
13 | __fields = {
14 | ['username'] = { required=true, unique=true },
15 | ['password'] = { required=true },
16 | ['salt'] = {},
17 | ['email'] = { required=true },
18 | ['nickname'] = {},
19 | ['created_date'] = {type="number"},
20 |
21 | ['perms'] = { foreign="Permission", st="MANY" },
22 | ['groups'] = { foreign="Group", st="MANY" },
23 | };
24 |
25 |
26 | init = function (self, t)
27 | if not t then return self end
28 |
29 | self.username = t.username
30 | self.email = t.email
31 | self.nickname = t.nickname
32 | self.created_date = socket.gettime()
33 |
34 | math.randomseed(os.time())
35 | self.salt = tostring(math.random(1, 1000000))
36 |
37 | if self.encrypt and type(self.encrypt) == 'function' then
38 | self.password = self:encrypt(t.password)
39 | end
40 |
41 | return self
42 | end;
43 |
44 | encrypt = function(self, password)
45 | return md5.sumhexa(password .. (self.salt or '')):lower()
46 | end;
47 |
48 | authenticate = function (self, params)
49 | I_AM_CLASS(self)
50 |
51 | local user = self:getByIndex(params.username)
52 | if not user then return false end
53 |
54 | if self.encrypt and type(self.encrypt) == 'function' then
55 | if user:encrypt(params.password) ~= user.password then
56 | return false
57 | end
58 | else
59 | if (params.password):lower() ~= user.password then
60 | return false
61 | end
62 | end
63 | return true, user
64 | end;
65 |
66 | login = function (self, params)
67 | I_AM_CLASS_OR_INSTANCE(self)
68 | -- make instance can use this login
69 | local user
70 | if isInstance(self) then
71 | user = self
72 | else
73 | if not params['username'] or not params['password'] then return nil end
74 | authed, user = self:authenticate(params)
75 | if not authed then return nil end
76 | end
77 |
78 | Session:setKey('user_id', self:classname() + ':' + user.id)
79 | Session:userHash(user, req.session_id)
80 |
81 | req.user = user
82 | return user
83 | end;
84 |
85 | logout = function (self)
86 | -- I_AM_CLASS(self)
87 | -- Class and instance can both call this function
88 | if req.user then
89 | Session:delUserHash(req.user)
90 | return Session:delKey('user_id')
91 | end
92 | end;
93 |
94 | register = function (self, params)
95 | I_AM_CLASS(self)
96 | if not params['username'] or not params['password'] then return nil, 101, 'less parameters.' end
97 |
98 | local user_id = self:getIdByIndex(params.username)
99 | if user_id then return nil, 103, 'the same name user exists.' end
100 |
101 | local user = self(params)
102 | user:save()
103 |
104 | return user
105 | end;
106 |
107 | set = function (self, req)
108 | I_AM_CLASS(self)
109 | local user_id = req.session['user_id']
110 | if user_id then
111 | req.user = self:getById(user_id)
112 | else
113 | req.user = nil
114 | end
115 | return self
116 | end;
117 |
118 | addPerm = function(self, ...)
119 | local perms = {...}
120 | for _, perm in ipairs(perms) do
121 | local ps = Perm:filter(function(u) return u.name:startsWith(perm) end)
122 | for _, p in ipairs(ps) do
123 | self:addForeign('perms', p)
124 | end
125 | end
126 | end;
127 |
128 | hasPerm = function(self, ...)
129 | local perms = {...}
130 | for _, perm in ipairs(perms) do
131 | local ps = Perm:filter(function(u) return u.name:startsWith(perm) end)
132 | for _, p in ipairs(ps) do
133 | if not self:hasForeign('perms', p) then
134 | return false
135 | end
136 | end
137 | end
138 | return true
139 | end;
140 |
141 | loginRequired = function (self, url)
142 | local url = url or '/'
143 | if isFalse(req.user) then web:redirect(url); return false end
144 | return true
145 | end;
146 |
147 | }
148 |
149 | return User
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/src/mvm/validate.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | function validateMax(value, field, limit_value)
4 | local num = tonumber(value)
5 | if num and num > limit_value then
6 | return false, ('The max input of $field is $limit_value'):gsub('$field', field):gsub('$limit_value', limit_value)
7 | end
8 | return true
9 | end
10 |
11 | function validateMin(value, field, limit_value)
12 | local num = tonumber(value)
13 | if num and num < limit_value then
14 | return false, ('The min input of $field is $limit_value'):gsub('$field', field):gsub('$limit_value', limit_value)
15 | end
16 | return true
17 | end
18 |
19 | function validateMinLength(value, field, limit_value)
20 | if #value < limit_value then
21 | return false, ('The minlength of $field is $limit_value'):gsub('$field', field):gsub('$limit_value', limit_value)
22 | end
23 | return true
24 | end
25 |
26 | function validateMaxLength(value, field, limit_value)
27 | if #value > limit_value then
28 | return false, ('The maxlength of $field is $limit_value'):gsub('$field', field):gsub('$limit_value', limit_value)
29 | end
30 | return true
31 | end
32 |
33 | function validateRange(value, field, limit_value)
34 | local min, max = unpack(limit_value)
35 | if validateMin(value, field, min) and validateMax(value, field, max) then
36 | return true
37 | else
38 | return false, ('The value of $field should be in the range of $min to $max'):gsub('$max', max):gsub('$min', min):gsub('$field', field)
39 | end
40 | end
41 |
42 | function validateRangeLength(value, field, limit_value)
43 | local min, max = unpack(limit_value)
44 | if validateMinLength(value, field, min) and validateMaxLength(value, field, max) then
45 | return true
46 | else
47 | return false, ('The length of $field should be in the range of $min to $max'):gsub('$max', max):gsub('$min', min):gsub('$field', field)
48 | end
49 | end
50 |
51 | function validateRequired(value, field, limit_value)
52 | if limit_value then
53 | if isFalse(value) then
54 | return false, ('Field $field is required'):gsub('$field', field)
55 | end
56 | end
57 | return true
58 | end
59 |
60 | function validateEmail(value, field, limit_value)
61 | if limit_value == true and value and value ~= '' then
62 | local regxp = '[%w_%.]+@%w+%.%w+'
63 | if not value:find(regxp) then
64 | return false, ('Please input an Email address to $field'):gsub('$field', field)
65 | end
66 | end
67 | return true
68 | end
69 |
70 | function validateDateISO(value, field, limit_value)
71 | if limit_value == true and value and value ~= '' then
72 | local regxp1 = '%d%d%d%d/%d%d/%d%d'
73 | local regxp2 = '%d%d%d%d%-%d%d%-%d%d'
74 | -- print(value)
75 | if not value:find(regxp1) and not value:find(regxp2) then
76 | return false, ('Please input a date to $field, the format is yyyy/mm/dd or yyyy-mm-dd'):gsub('$field', field)
77 | end
78 | end
79 | return true
80 | end
81 |
82 | validators = {
83 | max = validateMax,
84 | min = validateMin,
85 | range = validateRange,
86 | minlength = validateMinLength,
87 | maxlength = validateMaxLength,
88 | rangelength = validateRangeLength,
89 | required = validateRequired,
90 | email = validateEmail,
91 | dateISO = validateDateISO,
92 | }
93 |
94 | return validators
95 |
--------------------------------------------------------------------------------
/src/plugin.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local cmsgpack = require 'cmsgpack'
4 |
5 | local PLUGIN_ARGS_DBKEY = "_plugin_args:%s:%s"
6 |
7 |
8 | --[[
9 | local function collectUpvalues(func)
10 | local upvalues = {}
11 | for i=1, math.huge do
12 | local name, v = debug.getupvalue(func, i)
13 | if not name then break end
14 | local ctype = type(v)
15 | local table_has_metatable = false
16 | if ctype == 'table' then
17 | table_has_metatable = getmetatable(v) and true or false
18 | end
19 | -- because we could not collect the upvalues whose type is 'table', print warning here
20 | if type(v) == 'function' or table_has_metatable then
21 | print"[Warning] @collectUpvalues in plugin - bamboo has no ability to collect the function upvalue whose type is 'function' or 'table' with metatable."
22 | return false
23 | end
24 |
25 | if ctype == 'table' then
26 | upvalues[#upvalues + 1] = { name, serialize(v), type(v) }
27 | else
28 | upvalues[#upvalues + 1] = { name, tostring(v), type(v) }
29 | end
30 | end
31 |
32 | return upvalues
33 | end
34 |
35 | local function restoreFunction(func_str)
36 |
37 | local dstart, dstop = func_str:find(' |^|^| ')
38 | local func_part = func_str:sub(13, dstart - 1)
39 | local up_part = func_str:sub(dstop+1, -1)
40 | -- now fpart is the function binary string
41 | local func = loadstring(func_part)
42 | -- now query_args is query function
43 | if func and not isFalse(up_part) then
44 | -- item 1 is key, item 2 is value, item 3 is value type, item 4 is key ....
45 | local flat_upvalues = up_part:split(' ^_^ ')
46 | for i=1, #flat_upvalues / 3 do
47 | local vtype = flat_upvalues[3*i]
48 | local key = flat_upvalues[3*i - 2]
49 | local value = flat_upvalues[3*i - 1]
50 | if vtype == 'table' then
51 | value = deserialize(value)
52 | elseif vtype == 'number' then
53 | value = tonumber(value)
54 | elseif vtype == 'boolean' then
55 | value = loadstring('return ' .. value)()
56 | elseif vtype == 'nil' then
57 | value = nil
58 | end
59 | -- set upvalues
60 | debug.setupvalue(func, i, value)
61 | end
62 | end
63 |
64 | return func
65 | end
66 |
67 | function deepCopyWithModelName(self, seen)
68 | local res = {}
69 | seen = seen or {}
70 | seen[self] = res
71 |
72 | if self.__spectype then
73 | res.__spectype = self.__spectype
74 | else
75 | if self.classname then
76 | res.__name = self:classname()
77 | else
78 | if self.__typename then
79 | res.__typename = self.__typename
80 | end
81 | end
82 | end
83 |
84 | for k, v in pairs(self) do
85 | if "table" == type(v) then
86 | if seen[v] then
87 | res[k] = seen[v]
88 | else
89 | res[k] = deepCopyWithModelName(v, seen)
90 | end
91 | elseif "function" == type(v) then
92 | local upvalues = collectUpvalues(v)
93 | local tmp = {}
94 | for _, upvalue in ipairs(upvalues) do
95 | table.insert(tmp, table.concat(upvalue, ' ^_^ '))
96 | end
97 | local upvalue_str = table.concat(tmp, ' ^_^ ')
98 | res[k] = '__function__' .. string.dump(v) .. " |^|^| " .. upvalue_str
99 | else
100 | res[k] = v
101 | end
102 | end
103 | seen[self] = nil
104 |
105 | return res
106 | end
107 |
108 | function table2model(tbl)
109 | if tbl.__name then
110 | local model = bamboo.getModelByName(tbl.__name)
111 | --ptable(getmetatable(medel))
112 | tbl.__name = nil
113 | --tbl = model(tbl)
114 | setmetatable(tbl, {__index=model})
115 | end
116 | if tbl.__typename then
117 | tbl.__typename = nil
118 | tbl = List(tbl)
119 | end
120 | if tbl.__spectype then
121 | tbl.__spectype = nil
122 | tbl = QuerySet(tbl)
123 | end
124 | for k,v in pairs(tbl) do
125 | if type(v) == 'table' then
126 | tbl[k] = table2model(v)
127 | elseif type(v) == 'string' and v:startsWith('__function__') then
128 | tbl[k] = restoreFunction(v)
129 | end
130 | end
131 | return tbl
132 | end
133 |
134 | --]]
135 |
136 | function persist(plugin_name, tag, args)
137 | assert(plugin_name, "[Error] @ plugin persist - missing plugin_name.")
138 | assert(type(tag) == 'string', "[Error] @plugin persist - #2 tag should be string.")
139 | assert(type(args) == 'table', "[Error] @plugin persist - #3 args should be table.")
140 |
141 | -- use cmsgpack to persist
142 | -- here, must use deepCopy to remove all the metatables in args
143 | --local buf = cmsgpack.pack(deepCopyWithModelName(args))
144 | local buf = cmsgpack.pack(args)
145 | if not buf then
146 | return print(format('[Warning] plugin %s: arguments persisting failed.', plugin_name))
147 | end
148 | -- store to db
149 | local db = BAMBOO_DB
150 | db:hset(plugin_name, tag, buf)
151 |
152 | return true
153 | end
154 |
155 | function unpersist(plugin_name, tag)
156 | assert(plugin_name, "[Error] @ plugin unpersist - missing plugin_name.")
157 | assert(type(tag) == 'string', "[Error] @plugin unpersist - #2 tag should be string.")
158 |
159 | local db = BAMBOO_DB
160 | local buf = db:hget(plugin_name, tag)
161 | if not buf then return {} end
162 |
163 | local tbl = cmsgpack.unpack(buf)
164 | if not tbl then
165 | return print(format('[Warning] plugin %s: arguments unpersisting failed.', plugin_name))
166 | end
167 |
168 | assert(type(tbl) == 'table', "[Error] @plugin unpersist - unpersisted result should be table.")
169 | --tbl = table2model(tbl)
170 |
171 | return tbl
172 | end
173 |
--------------------------------------------------------------------------------
/src/queryset.lua:
--------------------------------------------------------------------------------
1 |
2 | local QuerySetMeta = {__spectype='QuerySet'}
3 |
4 | -- require neccessary methods
5 | local checkLogicRelation = bamboo.internal.checkLogicRelation
6 |
7 |
8 | QuerySetMeta.get = function (self, query_args, find_rev)
9 | I_AM_QUERY_SET(self)
10 |
11 | local checkRelation = function (obj)
12 | -- logic check
13 | local checkLogicRelation = bamboo.internal.checkLogicRelation
14 |
15 | flag = checkLogicRelation(obj, query_args, logic == 'and')
16 | if flag then return obj end
17 |
18 | return nil
19 | end
20 |
21 | local obj
22 | if find_rev == 'rev' then
23 | for i=#self, 1, -1 do
24 | return checkRelation(self[i])
25 | end
26 | else
27 | for i=1, #self do
28 | return checkRelation(self[i])
29 | end
30 |
31 | end
32 |
33 |
34 |
35 | end
36 |
37 | QuerySetMeta.filter = function (self, query_args, ...)
38 | I_AM_QUERY_SET(self)
39 | local objs = self
40 | if #objs == 0 then return QuerySet() end
41 |
42 | assert(type(query_args) == 'table' or type(query_args) == 'function',
43 | '[Error] the query_args passed to filter must be table or function.')
44 | local no_sort_rule
45 | -- regular the args
46 | local sort_field, sort_dir, sort_func, start, stop, is_rev
47 | local first_arg = select(1, ...)
48 | if type(first_arg) == 'function' then
49 | sort_func = first_arg
50 | start = select(2, ...)
51 | stop = select(3, ...)
52 | is_rev = select(4, ...)
53 | no_sort_rule = false
54 | elseif type(first_arg) == 'string' then
55 | sort_field = first_arg
56 | sort_dir = select(2, ...)
57 | start = select(3, ...)
58 | stop = select(4, ...)
59 | is_rev = select(5, ...)
60 | no_sort_rule = false
61 | elseif type(first_arg) == 'number' then
62 | start = first_arg
63 | stop = select(2, ...)
64 | is_rev = select(3, ...)
65 | no_sort_rule = true
66 | end
67 |
68 | if start then assert(type(start) == 'number', '[Error] @filter - start must be number.') end
69 | if stop then assert(type(stop) == 'number', '[Error] @filter - stop must be number.') end
70 | if is_rev then assert(type(is_rev) == 'string', '[Error] @filter - is_rev must be string.') end
71 |
72 | local is_args_table = (type(query_args) == 'table')
73 | local logic = 'and'
74 |
75 | -- create a query set
76 | local query_set = QuerySet()
77 |
78 | if is_args_table then
79 | assert( not query_args['id'],
80 | "[Error] query set doesn't support searching by id, please use getById.")
81 |
82 | -- if query table is empty, treate it as all action, or slice action
83 | if isFalse(query_args) and no_sort_rule then
84 | return self:slice(start, stop, is_rev)
85 | end
86 |
87 | if query_args[1] then
88 | -- normalize the 'and' and 'or' logic
89 | assert(query_args[1] == 'or' or query_args[1] == 'and',
90 | "[Error] The logic should be 'and' or 'or', rather than: " .. tostring(query_args[1]))
91 | if query_args[1] == 'or' then
92 | logic = 'or'
93 | end
94 | query_args[1] = nil
95 | end
96 |
97 | end
98 |
99 | local logic_choice = (logic == 'and')
100 | -- walkcheck can process full object and partial object
101 | local walkcheck = function (objs)
102 | for i, obj in ipairs(objs) do
103 | -- check the object's legalery, only act on valid object
104 | local checkLogicRelation = bamboo.internal.checkLogicRelation
105 | local flag = checkLogicRelation(obj, query_args, logic_choice)
106 |
107 | -- if walk to this line, means find one
108 | if flag then
109 | table.insert(query_set, obj)
110 | end
111 | end
112 | end
113 |
114 | walkcheck(objs)
115 | -- sort
116 | if not no_sort_rule then
117 | query_set = query_set:sortBy(sort_field or sort_func, sort_dir)
118 | end
119 | -- slice
120 | if start or stop then
121 | query_set = query_set:slice(start, stop, is_rev)
122 | end
123 |
124 | return query_set
125 | end
126 |
127 |
128 | QuerySetMeta.sortBy = function (self, ...)
129 | I_AM_QUERY_SET(self)
130 | local field, dir, sort_func, field2, dir2, sort_func2
131 | -- regular the args, 6 cases
132 | local first_arg = select(1, ...)
133 | if type(first_arg) == 'function' then
134 | sort_func = first_arg
135 | local second_arg = select(2, ...)
136 | if type(second_arg) == 'function' then
137 | sort_func2 = first_arg
138 | elseif type(second_arg) == 'string' then
139 | field2 = second_arg
140 | dir2 = select(3, ...)
141 | end
142 | elseif type(first_arg) == 'string' then
143 | field = first_arg
144 | dir = select(2, ...)
145 | local third_arg = select(3, ...)
146 | if type(third_arg) == 'function' then
147 | sort_func2 = third_arg
148 | elseif type(third_arg) == 'string' then
149 | filed2 = third_arg
150 | dir2 = select(4, ...)
151 | end
152 | end
153 |
154 |
155 | local dir = dir or 'asc'
156 | local byfield = field
157 | local sort_func = sort_func or function (a, b)
158 | local af = a[byfield]
159 | local bf = b[byfield]
160 | if af and bf then
161 | if dir == 'asc' then
162 | return af < bf
163 | elseif dir == 'desc' then
164 | return af > bf
165 | end
166 | else
167 | return nil
168 | end
169 | end
170 |
171 | table.sort(self, sort_func)
172 |
173 | -- secondary sort
174 | if field2 then
175 | checkType(field2, 'string')
176 |
177 | -- divide to parts
178 | local work_t = {{self[1]}, }
179 | for i = 2, #self do
180 | if self[i-1][field] == self[i][field] then
181 | -- insert to the last table element of the list
182 | table.insert(work_t[#work_t], self[i])
183 | else
184 | work_t[#work_t + 1] = {self[i]}
185 | end
186 | end
187 |
188 | -- sort each part
189 | local result = {}
190 | byfield = field2
191 | dir = dir2 or 'asc'
192 | sort_func = sort_func2 or sort_func
193 | for i, val in ipairs(work_t) do
194 | table.sort(val, sort_func)
195 | table.insert(result, val)
196 | end
197 |
198 | -- flatten to one rank table
199 | local flat = QuerySet()
200 | for i, val in ipairs(result) do
201 | for j, v in ipairs(val) do
202 | table.insert(flat, v)
203 | end
204 | end
205 |
206 | self = flat
207 | end
208 |
209 | return self
210 | end
211 |
212 |
213 | QuerySetMeta.querySetIds = function (self)
214 | I_AM_QUERY_SET(self)
215 | local ids = List()
216 | for _, v in ipairs(self) do
217 | ids:append(v.id)
218 | end
219 | return ids
220 | end
221 |
222 | QuerySetMeta.combineQuerySets = function (self, another)
223 | I_AM_QUERY_SET(self)
224 | I_AM_QUERY_SET(another)
225 | local ids = List()
226 | for _, v in ipairs(self) do
227 | ids:append(v.id)
228 | end
229 | local self_set = Set(ids)
230 |
231 | for _, v in ipairs(another) do
232 | -- if not duplicated, append it
233 | if not self_set[v.id] then
234 | self:append(v)
235 | end
236 | end
237 |
238 | return self
239 | end;
240 |
241 | QuerySetMeta.fakeDel = function (self)
242 | I_AM_QUERY_SET(self)
243 | local fakedelFromRedis = bamboo.internal.fakedelFromRedis
244 |
245 | for _, v in ipairs(self) do
246 | fakedelFromRedis(v)
247 | v = nil
248 | end
249 |
250 | self = nil
251 | end;
252 |
253 | QuerySetMeta.trueDel = function (self)
254 | I_AM_QUERY_SET(self)
255 | local delFromRedis = bamboo.internal.delFromRedis
256 |
257 | for _, v in ipairs(self) do
258 | delFromRedis(v)
259 | v = nil
260 | end
261 |
262 | self = nil
263 | end;
264 |
265 | QuerySetMeta.del = function (self)
266 | I_AM_QUERY_SET(self)
267 | if bamboo.config.use_fake_deletion == true then
268 | return self:fakeDel()
269 | else
270 | return self:trueDel()
271 | end
272 | end;
273 |
274 | QuerySet = function (list)
275 | local list = List(list)
276 | local query_set = setProto(list, QuerySetMeta)
277 |
278 | return query_set
279 | end
280 |
281 | _G['QuerySet'] = QuerySet
282 |
--------------------------------------------------------------------------------
/src/task.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local IO_THREADS = 1
4 | local zmq = require 'zmq'
5 |
6 | -- for task process main loop
7 | function start(config)
8 | assert(config.spec, "You need to at least set spec = to your 0MQ socket spec.")
9 | assert(config.main, "You must set a main function.")
10 |
11 | local ctx = assert(zmq.init(config.io_threads or IO_THREADS))
12 | local conn = assert(ctx:socket(config.socket_type or zmq.SUB))
13 | local main = config.main
14 | conn:setopt(zmq.SUBSCRIBE, config.subscribe or '')
15 | conn:bind(config.spec)
16 |
17 | if config.recv_ident then
18 | conn:setopt(zmq.IDENTITY, config.recv_ident)
19 | end
20 |
21 | print("BACKGROUND TASK " .. config.spec .. " STARTED.")
22 |
23 | if not config.custom_mainloop then
24 | while true do
25 | -- receive data from client
26 | local data = assert(conn:recv())
27 | main(conn, data)
28 | end
29 | else
30 | main(conn)
31 | end
32 |
33 | print('Task main loop terminated!')
34 | end
35 |
36 | -- used by client to connect the task server
37 | function connect(config)
38 | assert(config.spec, "You need to at least set spec = to your 0MQ socket spec.")
39 |
40 | local ctx = assert(zmq.init(config.io_threads or IO_THREADS))
41 | local conn = assert(ctx:socket(config.socket_type or zmq.PUB))
42 | conn:connect(config.spec)
43 |
44 | if config.send_ident then
45 | conn:setopt(zmq.IDENTITY, config.send_ident)
46 | end
47 |
48 | local TaskConn = {
49 | ctx = ctx,
50 | conn = conn,
51 | config = config
52 | }
53 |
54 | function TaskConn:send(data)
55 | self.conn:send(data, zmq.NOBLOCK)
56 | end
57 |
58 | -- conn_dispatcher is the default bamboo connecion dispatcher
59 | function TaskConn:wait()
60 | bamboo.poller:add(conn, zmq.POLLIN, bamboo.internal.connDispatcher)
61 | -- yield return data from task process
62 | return coroutine.yield()
63 | end
64 |
65 | -- callback(conn, revents)
66 | function TaskConn:send_and_wait(data, callback)
67 | self.conn:send(data, zmq.NOBLOCK)
68 | bamboo.poller:add(conn, zmq.POLLIN, callback)
69 | end
70 |
71 |
72 | function TaskConn:term()
73 | bamboo.SUSPENDED_TASKS[conn] = nil
74 | bamboo.poller:remove(conn)
75 |
76 | self.conn:close()
77 | self.ctx:term()
78 | end
79 |
80 | -- here, use global variable web
81 | bamboo.SUSPENDED_TASKS[conn] = web
82 | return TaskConn
83 | end
84 |
85 |
--------------------------------------------------------------------------------
/src/testing.lua:
--------------------------------------------------------------------------------
1 | -- prepare predefined structure
2 | dofile('/usr/local/bin/bamboo_handler')
3 |
4 | module(..., package.seeall)
5 |
6 | local json = require 'cjson'
7 |
8 | local Form = require 'bamboo.form'
9 | local borrowed = bamboo.EXPORT_FOR_TESTING
10 |
11 | local CONFIG_FILE = "settings.lua"
12 | local TEMPLATES = "views/"
13 |
14 | -- These globals are used to implement fake state for requests.
15 | local SESSION_ID = "d257873dfdc254ff6ff930a1c44aa6a9"
16 |
17 | local CONN_ID = 1
18 |
19 | local RUNNERS = {}
20 |
21 | local RESPONSES = {}
22 |
23 | local DEFAULT_UAGENT = "curl/7.19.7 (i486-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15"
24 |
25 | -- This constructs a fake mongrel2 connection that allows for running
26 | -- a handler but yields to receive a request and stuffs all the responses
27 | -- into RESPONSES for later inspection.
28 | local function makeFakeConnect(config)
29 | local conn = {config = config}
30 |
31 | function conn:recv()
32 | local req = coroutine.yield()
33 | assert(req.headers.PATH:match(self.config.route), ("[ERROR] Invalid request %q sent to handler: %q"):format(req.headers.PATH, self.config.route))
34 | return req
35 | end
36 |
37 | function conn:send(req, msg)
38 | RESPONSES[#RESPONSES + 1] = {
39 | type = "send",
40 | req = req,
41 | msg = msg
42 | }
43 | end
44 |
45 | function conn:close()
46 | CONN_ID = CONN_ID + 1
47 | end
48 |
49 | return conn
50 | end
51 |
52 | -- Replaces the base start with one that creates a fake m2 connection.
53 | local start = function(config)
54 | --config.methods = config.methods or borrowed.DEFAULT_ALLOWED_METHODS
55 |
56 | --config.ident = config.ident or borrowed.default_ident
57 |
58 | local conn = makeFakeConnect(config)
59 | config.conn = conn
60 | bamboo.conn = conn
61 |
62 | local runner = coroutine.wrap(borrowed.run)
63 | -- runner(conn, config)
64 | runner(config)
65 |
66 | -- This runner is used later to feed fake requests to the run loop.
67 | RUNNERS[config.route] = runner
68 | end
69 |
70 | -- Makes fake requests with all the right stuff in them.
71 | function makeFakeRequest(session, method, path, query, body, headers, data)
72 | local req = {
73 | path = path,
74 | body = body or "",
75 | data = data or {},
76 | }
77 |
78 | req.headers = {
79 | PATTERN = path,
80 | METHOD = method,
81 | QUERY = query,
82 | VERSION = "HTTP/1.1",
83 | ['x-forwarded-for'] = '127.0.0.1',
84 | host = "localhost:6767",
85 | PATH = path,
86 | ['user-agent'] = DEFAULT_UAGENT,
87 | cookie = session.COOKIE,
88 | URI = query and (path .. '?' .. query) or path,
89 | }
90 |
91 | table.update(req.headers, headers or {})
92 |
93 | -- add meta table for bamboo logic
94 | req.meta = {
95 | conn_id = CONN_ID
96 | }
97 |
98 | return req
99 | end
100 |
101 |
102 | function routeRequest(req)
103 | for pattern, runner in pairs(RUNNERS) do
104 | if req.headers.PATH:match(pattern) then
105 | return runner(req)
106 | end
107 | end
108 |
109 | assert(false, ("[ERROR] Request for %q path didn't match any loaded handlers."):format(req.headers.PATH))
110 | end
111 |
112 |
113 | -- Sets up a fake "browser" that is used in tests to pretend to send
114 | -- and receive requests and then analyze the results. It assumes a
115 | -- string request/response mode of operation and will throw errors if
116 | -- that's not followed.
117 | function browser(name, session_id, conn_id)
118 | CONN_ID = CONN_ID + 1
119 |
120 | local Browser = {
121 | RESPONSES = {},
122 | COOKIE = ('session="APP-%s"'):format(session_id or SESSION_ID),
123 | SESSION_ID = session_id or SESSION_ID,
124 | name = name,
125 | }
126 |
127 | function Browser:send(method, path, query, body, headers, data)
128 | routeRequest(makeFakeRequest(self, method, path, query, body, headers, data))
129 |
130 | local resp_count = #RESPONSES
131 |
132 | while #RESPONSES > 0 do
133 | local resp = table.remove(RESPONSES)
134 | if resp.req then
135 | self:extract_cookie(resp.req.headers)
136 | end
137 | self.RESPONSES[#self.RESPONSES] = resp
138 | end
139 |
140 | assert(resp_count > 0, ("[ERROR] Your application did not send a response to %q, that'll cause your browser to stall."):format(path))
141 |
142 | assert(resp_count == 1, ("[ERROR] A request for %q sent %d responses, that'll make the browser do really weird things."):format(path, resp_count))
143 |
144 | end
145 |
146 | function Browser:expect(needed)
147 | local last = self.RESPONSES[#self.RESPONSES]
148 |
149 | for k,v in pairs(last) do
150 | local pattern = needed[k]
151 |
152 | if pattern then
153 | if not tostring(v):match(tostring(pattern)) then
154 | error(("[ERROR] [%s] Failed expect: %q did not match %q but was %q:%q"
155 | ):format(self.name, k, pattern, v, last.body))
156 | end
157 | end
158 | end
159 |
160 | return last
161 | end
162 |
163 |
164 | function Browser:exited()
165 | return self.SESSION_ID -- and not .get_state(self.SESSION_ID)
166 | end
167 |
168 | function Browser:extract_cookie(headers)
169 | local cookie = headers['set-cookie']
170 |
171 | if cookie and cookie ~= self.COOKIE then
172 | self.COOKIE = cookie
173 | self.SESSION_ID = borrowed.parseSessionId(cookie)
174 | end
175 | end
176 |
177 | function Browser:click(path, expect)
178 | self:send("GET", path)
179 | return self:expect(expect or { code = 200 })
180 | end
181 |
182 | -- alias
183 | Browser.get = Browser.click
184 |
185 | function Browser:submit(path, form, expect, headers)
186 | local form = form or {}
187 | local body = Form:encode(form)
188 | headers = headers or {}
189 |
190 | expect = expect or {code = 200}
191 | if not expect.code then expect.code = 200 end
192 |
193 | headers['content-type'] = "application/x-www-form-urlencoded"
194 | headers['content-length'] = #body
195 |
196 | self:send("POST", path, nil, body, headers)
197 |
198 | return self:expect(expect)
199 | end
200 |
201 | Browser.post = Browser.submit
202 |
203 | function Browser:xhr(path, form, expect)
204 | local headers = {['x-requested-with'] = "XMLHttpRequest"}
205 | self:submit(path, form, headers)
206 | return self:expect(expect or { code = 200 })
207 | end
208 |
209 | function Browser:query(path, params, expect, headers)
210 | local params = params or {}
211 | local query = Form:encode(params)
212 | self:send("GET", path, query, nil, headers)
213 | return self:expect(expect or { code = 200 })
214 | end
215 |
216 | function Browser:ajaxGet(path, params, expect, headers)
217 | local headers = {['x-requested-with'] = "XMLHttpRequest"}
218 | local resp = self:query(path, params, expect, headers)
219 | local res = json.decode(resp.body)
220 | checkType(res, 'table')
221 | return res
222 | end
223 |
224 | function Browser:ajaxPost(path, form, expect, headers)
225 | local headers = {['x-requested-with'] = "XMLHttpRequest"}
226 | local resp = self:submit(path, form, expect, headers)
227 | local res = json.decode(resp.body)
228 | checkType(res, 'table')
229 | return res
230 | end
231 |
232 |
233 | return Browser
234 | end
235 |
236 | -------------------------------------------------------------------
237 | -- here, boot the testing server
238 | start(borrowed.config)
239 | -------------------------------------------------------------------
240 |
--------------------------------------------------------------------------------
/src/thread.lua:
--------------------------------------------------------------------------------
1 | local llthreads = require "llthreads"
2 |
3 | local _M = {}
4 |
5 | local new
6 | local _META = {
7 | __call = function (self, tcode, wait2ret, ...)
8 | return new(tcode, wait2ret, ...)
9 | end
10 | }
11 |
12 |
13 | new = function (tcode, wait2ret, ...)
14 | -- create child thread.
15 | local thread = llthreads.new(tcode, ...)
16 | if wait2ret then
17 | -- start joinable detached child thread.
18 | assert(thread:start())
19 |
20 | -- we need lua coroutine to swtich to other coroutine when wait child thread
21 | -- maybe we need modify join method, but it is not an easy thing.
22 | thread:join()
23 |
24 | else
25 | -- start non-joinable detached child thread.
26 | assert(thread:start(true))
27 | end
28 | end
29 |
30 |
31 |
32 |
33 | --[=[
34 | local Thread = require 'bamboo.thread'
35 |
36 | local ret1, ret2 = Thread( [[
37 |
38 | .... thread code ....
39 |
40 | ]], ...
41 |
42 | )
43 | ]=]
--------------------------------------------------------------------------------
/src/timer.lua:
--------------------------------------------------------------------------------
1 | --
2 | -- define setTimeout and setInterval functions like javascript
3 | --
4 | local _G = _G
5 | local ev = require'ev'
6 | local loop = bamboo.internal.loop
7 |
8 | local _M = {}
9 |
10 |
11 | local function timer_cb()
12 | zpub:send(tostring(msg_id))
13 | msg_id = msg_id + 1
14 | end
15 |
16 |
17 |
18 | _G.setTimeout = function (func, seconds)
19 | assert(type(func)=='function', '#1 of setTimeout must be function.')
20 | local timer = ev.Timer.new(func, seconds)
21 | timer:start(loop)
22 | end
23 |
24 |
25 | _G.setInterval = function (func, seconds)
26 | assert(type(func)=='function', '#1 of setInterval must be function.')
27 | local timer = ev.Timer.new(func, seconds, seconds)
28 | timer:start(loop)
29 | end
30 |
31 | return _M
32 |
--------------------------------------------------------------------------------
/src/utils.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | function simpleRender(tmpl_file, params)
4 | return function (web, req)
5 | web:html(tmpl_file, params)
6 | end
7 | end
8 |
9 | function simpleRedirect(rurl)
10 | local rulr = rurl or '/'
11 | return function (web, req)
12 | web:redirect(rurl)
13 | end
14 | end
15 |
16 |
17 | -- DEBUG level:
18 | -- 0, none;
19 | -- 1, verbose;
20 | -- 2, detailed;
21 | _G['DEBUG'] = function (...)
22 |
23 | local printout = function (level, ...)
24 | for i = 1, select('#', ...) do
25 | local arg = select (i, ...)
26 | if type(arg) == 'table' then
27 | if level >= 2 then
28 | print(table.tree(arg))
29 | else
30 | for k, v in pairs(arg) do
31 | print(k, v)
32 | end
33 | end
34 | else
35 | print(arg)
36 | end
37 | print('')
38 | end
39 | end
40 |
41 | local info = debug.getinfo(2, "nS")
42 | local debug_level = bamboo.config.debug_level
43 | if not isFalse(debug_level) then
44 | print('')
45 | print('-----------------------------------------------')
46 | print(('DEBUG @%s, @%s, @%s'):format(tostring(info.short_src), tostring(info.linedefined), tostring(info.name)))
47 | print('...............................................')
48 |
49 | local its_type = type(debug_level)
50 | if its_type == 'boolean' then
51 | printout(2, ...)
52 | elseif its_type == 'number' then
53 | printout(debug_level, ...)
54 | end
55 | print('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^')
56 | end
57 | end
58 |
59 |
60 | function readSettings(config)
61 | local config = config or {}
62 |
63 | -- try to load settings.lua
64 | local setting_file = loadfile('settings.lua') or loadfile('../settings.lua')
65 | if setting_file then
66 | setfenv(assert(setting_file), config)()
67 | end
68 | config.bamboo_dir = config.bamboo_dir or '/usr/local/share/lua/5.1/bamboo/'
69 |
70 | -- check whether have a global production setting
71 | local production = loadfile('/etc/bamboo_production')
72 | if production then
73 | config.PRODUCTION = true
74 | end
75 |
76 | return config
77 | end
78 |
--------------------------------------------------------------------------------
/src/web.lua:
--------------------------------------------------------------------------------
1 |
2 | module(..., package.seeall)
3 |
4 | local View = require 'bamboo.view'
5 | local json = require 'cjson'
6 | local cmsgpack = require 'cmsgpack'
7 |
8 | local function rawwrap(data, meta)
9 | local ret = {
10 | data = data or '', -- body string to reply
11 | meta = meta or {} -- some other info to webserver
12 | }
13 |
14 | return cmsgpack.pack(ret)
15 | end
16 |
17 |
18 | local function wrap(data, code, status, headers, meta)
19 | local ret = {
20 | data = data or '', -- body string to reply
21 | code = code or 200, -- http code to reply
22 | status = status or "OK", -- http status to reply
23 | headers = headers or {}, -- http headers to reply
24 | -- conns = conns or {}, -- http connections to receive this reply
25 | meta = meta or {} -- some other info to webserver
26 | }
27 |
28 | return cmsgpack.pack(ret)
29 | end
30 |
31 |
32 | local Web = Object:extend {
33 | __name = 'Web';
34 | init = function (self, main, req)
35 | self.req = req
36 | -- self.main = main
37 | -- for state programming
38 | self.controller = coroutine.create(main)
39 | self.dbs = {}
40 |
41 | return self
42 | end;
43 |
44 |
45 | setCookie = function (self, cookie)
46 | self.req.headers['set-cookie'] = cookie
47 | end;
48 |
49 | getCookie = function (self)
50 | return self.req.headers['cookie']
51 | end;
52 |
53 |
54 |
55 | json = function (self, data, conns)
56 | self:page(json.encode(data), 200, "OK", {['content-type'] = 'application/json'}, conns)
57 | end;
58 |
59 | jsonError = function (self, tbl)
60 | tbl = tbl or {}
61 | tbl['success'] = false
62 | self:json(tbl)
63 | end;
64 |
65 | jsonSuccess = function (self, tbl)
66 | tbl = tbl or {}
67 | tbl['success'] = true
68 | self:json(tbl)
69 | end;
70 |
71 | page = function (self, data, code, status, headers, conns)
72 | headers = headers or {}
73 |
74 | if self.req.headers and self.req.headers['set-cookie'] then
75 | headers['set-cookie'] = self.req.headers['set-cookie']
76 | end
77 |
78 | headers['server'] = 'Bamboo on lgserver'
79 | local ctype = headers['content-type']
80 |
81 | if ctype == nil then
82 | headers['content-type'] = 'text/html'
83 | elseif ctype == false then
84 | headers['content-type'] = nil
85 | end
86 |
87 | if bamboo.is_testing then
88 | -- for bamboo automatic test
89 | local msg = {
90 | body = data,
91 | code = code or 200,
92 | status = status or 'OK',
93 | headers = headers or {}
94 | }
95 | bamboo.conn:send(self.req, msg)
96 | else
97 | local meta = self.req.meta
98 | meta.conns = conns
99 | bamboo.ch_send:send(wrap(data, code, status, headers, meta))
100 | end
101 |
102 | return false
103 | end;
104 |
105 |
106 | redirect = function (self, url)
107 | return self:page("", 303, "See Other", {Location=url, ['content-type'] = false})
108 | end;
109 |
110 | error = function (self, data, code, status, headers)
111 | self:page(data or 'error', code or 500, status or 'Internal Error', headers or {['content-type'] = false})
112 | return self:close()
113 | end;
114 |
115 | ok = function (self, msg) self:page(msg or 'OK', 200, 'OK') end;
116 | notFound = function (self, msg) return self:error(msg or 'Not Found', 404, 'Not Found') end;
117 | unauthorized = function (self, msg) return self:error(msg or 'Unauthorized', 401, 'Unauthorized') end;
118 | forbidden = function (self, msg) return self:error(msg or 'Forbidden', 403, 'Forbidden') end;
119 | badRequest = function (self, msg) return self:error(msg or 'Bad Request', 400, 'Bad Request') end;
120 |
121 | send = function (self, data, meta)
122 | if bamboo.is_testing then
123 | -- for bamboo automatic test
124 | local msg = {
125 | data = data,
126 | meta = meta
127 | }
128 | bamboo.conn:send(self.req, msg)
129 | else
130 | bamboo.ch_send:send(rawwrap(data, meta))
131 | end
132 | end,
133 |
134 | close = function (self)
135 | self:send('', { cmd = 'close' })
136 | return false
137 | end,
138 |
139 | }
140 |
141 |
142 | return Web
143 |
144 |
--------------------------------------------------------------------------------
/src/webserver_driver.lua:
--------------------------------------------------------------------------------
1 | --module(..., package.seeall)
2 |
3 | local zmq = require'handler.zmq'
4 | local ev = require'ev'
5 | local loop = ev.Loop.default
6 | local json = require 'cjson'
7 | --local zmq = require 'zmq'
8 | local cmsgpack = require 'cmsgpack'
9 |
10 |
11 | local driver = {}
12 |
13 |
14 |
15 |
16 | local insert, concat = table.insert, table.concat
17 | local format = string.format
18 |
19 | local Connection = {}
20 | local Connection_meta = {__index = Connection}
21 |
22 | -- data structure return to webserver
23 | -- data is string
24 | -- extra is table
25 | -- conns: a list of connection keys
26 | local function wrap(data, code, status, headers, conns, meta)
27 | local ret = {
28 | data = data or '', -- body string to reply
29 | code = code or 200, -- http code to reply
30 | status = status or "OK", -- http status to reply
31 | headers = headers or {}, -- http headers to reply
32 | conns = conns or {}, -- http connections to receive this reply
33 | meta = meta or {} -- some other info to webserver
34 | }
35 |
36 | return cmsgpack.pack(ret)
37 | end
38 |
39 |
40 | --[[
41 | Receives a raw webserver.request object that you can then work with.
42 | Upon error while parsing the data, returns nil and an error message.
43 | ]]
44 | function Connection:recv()
45 | local reqstr, err = self.channel_req:recv()
46 | local req = cmsgpack.unpack(reqstr)
47 | return req
48 | end
49 |
50 | --[[
51 | Raw send to the given connection ID at the given uuid, mostly
52 | used internally.
53 | ]]
54 | function Connection:send(msg)
55 | return self.channel_res:send(msg)
56 | end
57 |
58 | -- req.meta.conn_id and conns[i] are all connection id
59 | function Connection:reply(data, code, status, headers, conns, meta)
60 | local msg = wrap(
61 | data,
62 | code,
63 | status,
64 | headers,
65 | conns,
66 | meta)
67 |
68 | return self:send(msg)
69 | end
70 |
71 |
72 | --[[
73 | Same as reply, but tries to convert data to JSON first.
74 | data: table
75 | ]]
76 | function Connection:reply_json(data, conns, meta)
77 | return self:reply(
78 | json.encode(data),
79 | 200,
80 | 'OK',
81 | {['content-type'] = 'application/json'},
82 | conns, meta )
83 | end
84 |
85 | --[[
86 | Basic HTTP response mechanism which will take your body,
87 | any headers you've made, and encode them so that the
88 | browser gets them.
89 | ]]
90 | function Connection:reply_http(body, code, status, headers, conns, meta)
91 | return self:reply(body, code, status, headers, conns, meta)
92 | end
93 |
94 |
95 | --[[
96 | -- Tells webserver to explicitly close the HTTP connection.
97 | --]]
98 | -- function Connection:close(req)
99 | -- return self:reply(req, "")
100 | -- end
101 |
102 |
103 | --[[
104 | -- Creates a new connection object.
105 | -- Internal use only, call ctx:new_context instead.
106 | --]]
107 | --local function newConnection(sender_id, sub_addr, pub_addr, cluster_addr)
108 | --
109 | -- local ctx = zmq.init(loop, 1)
110 | --
111 | -- -- local channel_req = ctx:socket(zmq.PULL)
112 | -- -- channel_req:bind(sub_addr)
113 | --
114 | -- -- local channel_res = ctx:socket(zmq.PUSH)
115 | -- -- channel_res:connect(pub_addr)
116 | --
117 | -- local channel_req = ctx:socket(zmq.PULL)
118 | -- channel_req:connect(sub_addr)
119 | --
120 | local channel_res = ctx:socket(zmq.PUSH)
121 | channel_res:connect(pub_addr)
122 | -- local channel_res = ctx:socket(zmq.PUB)
123 | -- -- channel_res:bind(pub_addr)
124 | -- channel_res:connect(pub_addr)
125 | --
126 | -- local cluster_channel_pub, cluster_channel_sub
127 | -- -- if set cluster channel
128 | -- if cluster_addr then
129 | -- cluster_channel_pub = ctx:socket(zmq.PUB)
130 | -- cluster_channel_pub:bind(cluster_addr)
131 | --
132 | -- cluster_channel_sub = ctx:socket(zmq.SUB)
133 | -- cluster_channel_sub:setopt(zmq.SUBSCRIBE, "")
134 | -- cluster_channel_sub:connect(cluster_addr)
135 | -- end
136 | --
137 | -- -- Build the object and give it a metatable.
138 | -- local obj = {
139 | -- ctx = ctx,
140 | -- sender_id = sender_id,
141 | --
142 | -- sub_addr = sub_addr,
143 | -- pub_addr = pub_addr,
144 | -- cluster_addr = cluster_addr,
145 | --
146 | -- channel_req = channel_req,
147 | -- channel_res = channel_res,
148 | -- cluster_channel_pub = cluster_channel_pub,
149 | -- cluster_channel_sub = cluster_channel_sub
150 | -- }
151 | --
152 | -- return setmetatable(obj, Connection_meta)
153 | --end
154 | --
155 | [[
156 | --local Request = {}
157 | --Request.__index = Request
158 | --
159 | Returns true if the request object is a disconnect event.
160 | --function Request:is_disconnect()
161 | -- return self.data.type == 'disconnect'
162 | --end
163 | --
164 | Checks if the request was for a connection close.
165 | --function Request:should_close()
166 | -- if self.headers['connection'] == 'close' then
167 | -- return true
168 | -- elseif self.headers['VERSION'] == 'HTTP/1.0' then
169 | -- return true
170 | -- else
171 | -- return false
172 | -- end
173 | --end
174 | --
175 | ]]
176 | --
177 | --local function findHandler(webserver_config, host_name, route )
178 | -- local server = webserver_config.server
179 | -- local host_name = host_name or server.default_host
180 | --
181 | -- for _, host in ipairs(server.hosts) do
182 | -- if host.name == host_name then
183 | -- return host.routes[route]
184 | -- end
185 | -- end
186 | --
187 | -- return nil
188 | --end
189 | --
190 | -- load configuration from webserver's config.sqlite
191 |
192 | ----------------------------------------------------------------------
193 | --function driver.loadConfig(config)
194 | -- config.webserver_config = {}
195 | -- print('Ready to load config file: ', config.config_file)
196 | -- local config_file = loadfile(config.config_file)
197 | -- -- release the global variables to config table
198 | -- setfenv(assert(config_file, "Failed to load webserver config file."),
199 | -- config.webserver_config)()
200 | --
201 | -- local handler = findHandler(config.webserver_config, config.host, config.route)
202 | -- assert(handler, "Failed to find route: " .. config.route ..
203 | -- ". Make sure you set config.host to a host in your config.lua.")
204 | --
205 | -- config.sub_addr = handler.send_spec
206 | -- config.pub_addr = handler.recv_spec
207 | -- config.sender_id = handler.sender_id
208 | --
209 | -- return config
210 | --end
211 | --
212 | --
213 | --
214 | -- create a new connection between bamboo and mognrel2 (via zeromq)
215 | -- @return conn: new created connection
216 | ----------------------------------------------------------------------
217 | --function driver.connect(config)
218 | -- local sub_addr, pub_addr = config.sub_addr, config.pub_addr
219 | -- math.randomseed(os.time())
220 | -- local sender_id = config.sender_id or 'bamboo_handler_'..math.random(100000, 999999)
221 | -- print("CONNECTING", config.route, sender_id, sub_addr, pub_addr)
222 | --
223 | -- local cluster_addr = config.cluster_addr or 'tcp://127.0.0.1:12315'
224 | --
225 | -- local conn = newConnection(sender_id, sub_addr, pub_addr, cluster_addr)
226 | --
227 | -- assert(conn, "Failed to start webserver connection.")
228 | --
229 | -- return conn
230 | --end
231 | --
232 |
233 | return driver
234 |
--------------------------------------------------------------------------------
/src/widget.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 |
4 |
5 | local TEXT_TMPL = [[ ]]
6 | text = function (args)
7 | local name = args.name or 'text'
8 | local class = args.class or name
9 | local value = args.value or ''
10 | local id = args.id
11 |
12 | local htmls = (TEXT_TMPL % {
13 | name = name,
14 | value = value,
15 | class = class,
16 | id = id and format('id="%s"', id) or ''
17 | })
18 |
19 | return htmls
20 | end
21 |
22 |
23 |
24 |
25 | local CHECKBOX_TMPL = [[ ${caption} ]]
26 | checkbox = function (args)
27 | local htmls = {}
28 | local name = args.name or 'checkbox'
29 | local class = args.class or name
30 | local value = args.value or {}
31 | local value_field = args.value_field
32 | local caption_field = args.caption_field
33 | local id = args.id
34 |
35 | local checked = args.checked or {}
36 | local checked_set, flag = false
37 | if type(checked) == 'table' then
38 | checked_set = Set(checked)
39 | end
40 |
41 | if value_field and caption_field then
42 | -- here, value is datasource
43 | for _, item in ipairs(value) do
44 | if type(checked) == 'string' then
45 | flag = checked == item[value_field]
46 | else
47 | flag = checked_set:has(item[value_field])
48 | end
49 |
50 | table.insert(htmls, (CHECKBOX_TMPL % {
51 | id = id and format('id="%s"', id) or '',
52 | class = class,
53 | name = name,
54 | value = item[value_field],
55 | caption = item[caption_field] or '',
56 | checked = flag and 'checked="checked"' or ''
57 | }))
58 | end
59 | else
60 | for _, item in ipairs(value) do
61 |
62 | if type(checked) == 'string' then
63 | flag = checked == item[1]
64 | else
65 | flag = checked_set:has(item[1])
66 | end
67 | table.insert(htmls, (CHECKBOX_TMPL % {
68 | id = id and format('id="%s"', id) or '',
69 | name = name,
70 | value = item[1],
71 | class = class,
72 | caption = item[2] or item[1] or '',
73 | checked = flag and 'checked="checked"' or ''
74 | }))
75 | end
76 | end
77 |
78 | return table.concat(htmls)
79 | end
80 |
81 | local RADIO_TMPL = [[ ${caption} ${layout}]]
82 | radio = function (args)
83 | local htmls = {}
84 | local name = args.name or 'radio'
85 | local class = args.class or name
86 | local value = args.value or {}
87 | local checked = args.checked or ''
88 | local value_field = args.value_field
89 | local caption_field = args.caption_field
90 | local layout = args.layout or ''
91 | if layout == 'vertical' then
92 | layout=" "
93 | end
94 |
95 | if value_field and caption_field then
96 | for _, item in ipairs(value) do
97 | table.insert(htmls, (RADIO_TMPL % {
98 | id = id and format('id="%s"', id) or '',
99 | class = class,
100 | name = name,
101 | value = item[value_field],
102 | caption = item[caption_field] or '',
103 | checked = (checked == item[value_field]) and 'checked="checked"' or '',
104 | layout = layout,
105 | }))
106 | end
107 | else
108 | for _, item in ipairs(value) do
109 | table.insert(htmls, (RADIO_TMPL % {
110 | id = id and format('id="%s"', id) or '',
111 | name = name,
112 | value = item[1],
113 | class = class,
114 | caption = item[2] or item[1] or'',
115 | checked = (checked == item[1]) and 'checked="checked"' or '',
116 | layout = layout,
117 | }))
118 | end
119 | end
120 |
121 | return table.concat(htmls)
122 | end
123 |
124 |
125 |
126 | local SELECT_TMPL0 = [[]]
127 | local SELECT_TMPL1 = [[${caption} ]]
128 | local SELECT_TMPL2 = [[ ]]
129 | select = function (args)
130 | local htmls = {}
131 | local name = args.name or 'select'
132 | local class = args.class or name
133 | local value = args.value or {}
134 | local selected = args.selected or {}
135 |
136 | local value_field = args.value_field
137 | local caption_field = args.caption_field
138 |
139 | local selected_set
140 | local flag = false
141 | if type(selected) == 'table' then
142 | selected_set = Set(selected)
143 | end
144 |
145 | table.insert(htmls, SELECT_TMPL0 % {class=class, name=name, id=id and format('id="%s"', id) or ''})
146 | local inval, incap
147 |
148 | -- if specify value field and caption field
149 | if value_field and caption_field then
150 | -- here, value is datasource
151 | for _, item in ipairs(value) do
152 | if type(selected) == 'string' then
153 | flag = selected == item[value_field]
154 | else
155 | flag = selected_set:has(item[value_field])
156 | end
157 |
158 | table.insert(htmls, (SELECT_TMPL1 % {
159 | value = item[value_field],
160 | caption = item[caption_field] or '',
161 | selected = flag and 'selected="selected"' or ''
162 | }))
163 | end
164 | else
165 | -- if not specify value field and caption field
166 | for _, item in ipairs(value) do
167 | if type(item) == 'string' then
168 | inval = item
169 | incap = item
170 | else
171 | inval = item[1]
172 | incap = item[2] or inval
173 | end
174 |
175 | if type(selected) == 'string' then
176 | flag = selected == inval
177 | else
178 | flag = selected_set:has(inval)
179 | end
180 | table.insert(htmls, (SELECT_TMPL1 % {
181 | value = inval,
182 | caption = incap,
183 | selected = flag and 'selected="selected"' or ''
184 | }))
185 | end
186 | end
187 | table.insert(htmls, SELECT_TMPL2)
188 |
189 | return table.concat(htmls)
190 | end
191 |
192 |
193 |
194 | bamboo.WIDGETS['text'] = text
195 | bamboo.WIDGETS['checkbox'] = checkbox
196 | bamboo.WIDGETS['radio'] = radio
197 | bamboo.WIDGETS['select'] = select
198 |
--------------------------------------------------------------------------------
/tests/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/.gitignore
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/app/handler_entry.lua:
--------------------------------------------------------------------------------
1 | require 'bamboo'
2 |
3 | local View = require 'bamboo.view'
4 | local Form = require 'bamboo.form'
5 |
6 | local MYUser = require 'models.myuser'
7 | bamboo.registerModel(MYUser)
8 | local plugin_paginator = require "plugins.ajaxpaginator"
9 | bamboo.registerPlugin('paginator', plugin_paginator)
10 |
11 |
12 |
13 | local function index(web, req)
14 | web:page(View("form.html"){})
15 | end
16 |
17 |
18 | local function form_submit(web, req)
19 | local params = req.PARAMS
20 | DEBUG(params)
21 |
22 | local person = MYUser(params)
23 | -- save person object to db
24 | person:save()
25 |
26 |
27 | web:redirect("/page/getInstance/")
28 | end
29 |
30 | local function show(web, req)
31 |
32 | return web:page(View("result.html"){})
33 | end
34 |
35 | local function form_submit2(web, req, starti, endi)
36 |
37 | local all_persons
38 | if not MYUser:existCache('persons_list') then
39 | -- retreive all person instance from db
40 | all_persons = MYUser:all():sortBy('name')
41 | MYUser:setCache('persons_list', all_persons)
42 | all_persons = all_persons:slice(starti, endi)
43 | else
44 | -- DEBUG('entering cache block.')
45 | all_persons = MYUser:getCache('persons_list', starti, endi)
46 |
47 | end
48 | -- fptable(all_persons)
49 | local total = MYUser:numCache('persons_list')
50 |
51 | return View("item.html"){all_persons = all_persons}, total
52 | end
53 |
54 | bamboo.registerPluginCallback('page_callback', form_submit2)
55 |
56 | function test (web, req)
57 | local x, y, z = 10, 100, 1000
58 |
59 | return web:page(View("test.html"){"locals"})
60 | end
61 |
62 |
63 | URLS = { '/',
64 | ['/'] = index,
65 | ['/index/'] = index,
66 | ['/form_submit/'] = form_submit,
67 | ['/page/getInstance/'] = show,
68 | ['/page/getInstances/'] = plugin_paginator.jsons,
69 | ['/test/'] = test
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/cachecall/callbacks.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | function onSave(self)
4 | -- here, we need to add new instance's id to cache
5 | DEBUG('ready to add new member to cache.')
6 | self:addToCacheAndSortBy('persons_list', 'name')
7 |
8 | return self
9 | end
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/initial/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/ajaxpaginator_test/initial/.gitignore
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/media/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/ajaxpaginator_test/media/css/.gitignore
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/media/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/ajaxpaginator_test/media/images/.gitignore
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/media/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/ajaxpaginator_test/media/js/.gitignore
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/media/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/ajaxpaginator_test/media/plugins/.gitignore
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/media/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/ajaxpaginator_test/media/uploads/.gitignore
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/models/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/ajaxpaginator_test/models/.gitignore
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/models/mymodel.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local Model = require 'bamboo.model'
4 |
5 | local Mymodel = Model:extend {
6 | __tag = 'Bamboo.Model.Mymodel';
7 | __name = 'Mymodel';
8 | __desc = 'Generitic Mymodel definition';
9 | __indexfd = 'name',
10 | __fields = {
11 | ['name'] = {},
12 |
13 | };
14 |
15 | init = function (self, t)
16 | if isFalse(t) then return self end
17 |
18 | -- auto fill non-foreign fields with params t
19 | local fields = self.__fields
20 | for k, v in pairs(t) do
21 | if fields[k] and not fields[k].foreign then
22 | self[k] = tostring(v)
23 | end
24 | end
25 |
26 | return self
27 | end;
28 |
29 |
30 |
31 | }
32 |
33 | return Mymodel
34 |
35 |
36 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/models/myuser.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local User = require 'bamboo.models.user'
4 |
5 | local cache_callbacks = require 'cachecall.callbacks'
6 |
7 | local MYUser = User:extend {
8 | __tag = 'Bamboo.Model.User.MYUser';
9 | __name = 'MYUser';
10 | __desc = 'Generitic MYUser definition';
11 | __indexfd = 'name';
12 | __fields = {
13 | ['name'] = {},
14 | ['age'] = {},
15 | ['gender'] = {},
16 |
17 | };
18 | __decorators = {
19 | save = function(osave)
20 | return function(self, ...)
21 | self = osave(self, ...)
22 | if not self then return nil end
23 |
24 | self = cache_callbacks.onSave(self)
25 |
26 | return self
27 | end
28 | end;
29 |
30 | };
31 |
32 | init = function (self, t)
33 | if not t then return self end
34 |
35 | self.name = t.name
36 | self.age = t.age
37 | self.gender = t.gender
38 |
39 | return self
40 | end;
41 |
42 | }
43 |
44 | return MYUser
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/monconfig.lua:
--------------------------------------------------------------------------------
1 | static_ajaxpaginator_test = { type="dir", base='sites/ajaxpaginator_test/', index_file='index.html', default_ctype='text/plain' }
2 |
3 | handler_ajaxpaginator_test = { type="handler", send_spec='tcp://127.0.0.1:10001',
4 | send_ident='ba06f707-8647-46b9-b7f7-e641d6419909',
5 | recv_spec='tcp://127.0.0.1:10002', recv_ident=''}
6 |
7 | main = {
8 | bind_addr = "0.0.0.0",
9 | uuid="505417b8-1de4-454f-98b6-07eb9225cca1",
10 | access_log="logs/access.log",
11 | error_log="logs/error.log",
12 | chroot="./",
13 | pid_file="run/mongrel2.pid",
14 | default_host="ajaxpaginator_test",
15 | name="main",
16 | port=6767,
17 | hosts= {
18 | {
19 | name="ajaxpaginator_test",
20 | matching = "xxxxxx",
21 | routes={
22 | ['/'] = handler_ajaxpaginator_test,
23 | ['/media/'] = static_ajaxpaginator_test
24 | }
25 | },
26 | }
27 | }
28 |
29 |
30 | settings = {
31 | ['zeromq.threads'] = 1,
32 | ['limits.content_length'] = 20971520,
33 | ['upload.temp_store'] = '/tmp/monserver.upload.XXXXXX'
34 | }
35 |
36 | mimetypes = {}
37 |
38 | servers = { main }
39 |
40 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/ajaxpaginator_test/plugins/.gitignore
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/plugins/ajaxpaginator/ajaxpaginator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
72 |
73 |
74 |
{{htmlcontent}}
75 |
76 |
77 |
首页
78 |
上一页
79 |
80 | {% for i=1, totalpages do %}
81 | {{i}}/{{totalpages}}
82 | {% end %}
83 |
84 |
89 |
90 |
下一页
91 |
末页
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/plugins/ajaxpaginator/init.lua:
--------------------------------------------------------------------------------
1 | module (..., package.seeall)
2 |
3 | local _args = {}
4 |
5 | function helper()
6 | local params = req.PARAMS
7 |
8 | local thepage = tonumber(params.thepage) or 1
9 | if thepage < 1 then thepage = 1 end
10 | local totalpages = tonumber(params.totalpages) or 1
11 | if totalpages and thepage > totalpages then thepage = totalpages end
12 | local npp = tonumber(params.npp) or tonumber(_args.npp) or 5
13 | local starti = (thepage-1) * npp + 1
14 | local endi = thepage * npp
15 | local pageurl = params.pageurl or _args.pageurl
16 | local callback = _args.callback
17 |
18 | -- the callback should return 2 values: html fragment and totalnum
19 | local htmlcontent, totalnum = bamboo.getPluginCallbackByName(callback)(web, req, starti, endi)
20 |
21 | if totalnum then
22 | totalpages = math.ceil(totalnum/npp)
23 | if thepage > totalpages then thepage = totalpages end
24 | end
25 |
26 | local prevpage = thepage - 1
27 | if prevpage < 1 then prevpage = 1 end
28 | local nextpage = thepage + 1
29 | print(nextpage, totalpages)
30 | if nextpage > totalpages then nextpage = totalpages end
31 |
32 | return {
33 | ['htmlcontent'] = htmlcontent,
34 | ['totalpages'] = totalpages,
35 | ['npp'] = npp,
36 | ['pageurl'] = pageurl,
37 | ['thepage'] = thepage,
38 | ['prevpage'] = prevpage,
39 | ['nextpage'] = nextpage
40 | }
41 | end
42 |
43 |
44 | function main(args)
45 |
46 | assert(type(args.pageurl) == 'string', '[Error] pageurl missing in plugin paginator.')
47 | assert(type(args.callback) == 'string' and type(bamboo.getPluginCallbackByName(args.callback)) == 'function', '[Error] callback missing in plugin paginator.')
48 | _args = args
49 |
50 | return View('../plugins/ajaxpaginator/ajaxpaginator.html') (helper())
51 |
52 | end
53 |
54 | function jsons(web, req)
55 | -- fptable(req.PARAMS)
56 | return web:jsonSuccess(helper())
57 | end
58 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/settings.lua:
--------------------------------------------------------------------------------
1 | project_name = "ajaxpaginator_test"
2 | host = "ajaxpaginator_test"
3 | sender_id = 'c62c1bda-0816-2205-eef9-811c8e055fd1'
4 | io_threads = 1
5 | views = "views/"
6 | config_file = 'config.lua'
7 | WHICH_DB = 15
8 | debug_level = 1
9 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/tests/viewbench_tests.lua:
--------------------------------------------------------------------------------
1 | local testing = require "bamboo.testing"
2 | local json = require 'json'
3 | local socket = require "socket"
4 |
5 | context("View performance benchmark", function ()
6 | context("test1", function ()
7 | local tester = testing.browser("tester")
8 | local t1 = socket.gettime()
9 | local ret
10 | for i=1, 10000 do
11 | ret = tester:click("/test")
12 | end
13 | local t2 = socket.gettime()
14 | print(t2 - t1)
15 | end)
16 | end)
17 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/views/form.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
5 |
11 |
12 | ]}
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Form Process
13 |
14 |
15 |
16 |
17 |
18 | {[ 'page' ]}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/views/item.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Name Age Gender
4 | {% for _, v in ipairs(all_persons) do %}
5 | {{v.name}} {{v.age}} {{v.gender}}
6 | {% end %}
7 |
8 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/views/paginator.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
首页
16 |
上一页
17 |
18 | {% for i=1, pages do %}
19 | {{i}}/{{pages}}
20 | {% end %}
21 |
22 |
25 |
26 |
下一页
27 |
末页
28 |
29 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/views/result (复件).html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
24 | The database have the following data:
25 |
26 |
27 | Name Age Gender
28 | {% for _, v in ipairs(all_persons) do%}
29 | {{v.name}} {{v.age}} {{v.gender}}
30 | {% end %}
31 |
32 |
33 |
34 |
35 |
首页
36 |
上一页
37 |
38 | {% for i=1, pages do %}
39 | {{i}}/{{pages}}
40 | {% end %}
41 |
42 |
45 |
46 |
下一页
47 |
末页
48 |
49 |
50 |
51 |
52 | Click here to return form page.
53 |
54 | ]}
55 |
56 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/views/result.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
9 |
10 |
19 |
20 | The database have the following data:
21 |
22 | {^ paginator callback="page_callback", pageurl="/page/getInstances/", htmlfile="item.html" ^}
23 |
24 | Click here to return form page.
25 |
26 | ]}
27 |
28 |
--------------------------------------------------------------------------------
/tests/ajaxpaginator_test/views/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | I am here.
4 | {{ x }}
5 | {{ x }}
6 | {{ x }}
7 | {{ x }}
8 |
9 |
10 | {% if y > x then %}
11 | {% for i=1, 100 do %}
12 | hahaha
13 |
14 | {% end %}
15 |
16 | {% end %}
17 |
18 |
19 |
20 |
21 | lastit.
--------------------------------------------------------------------------------
/tests/cache_test/app/handler_entry.lua:
--------------------------------------------------------------------------------
1 | require 'bamboo'
2 |
3 | local View = require 'bamboo.view'
4 | local Form = require 'bamboo.form'
5 |
6 | local MYUser = require 'models.myuser'
7 | bamboo.registerModel(MYUser)
8 |
9 | local function index(web, req)
10 | web:page(View("form.html"){})
11 | end
12 |
13 | local function form_submit(web, req)
14 | local params = Form:parse(req)
15 | DEBUG(params)
16 |
17 | local person = MYUser(params)
18 | -- save person object to db
19 | person:save()
20 |
21 | local all_persons
22 | if not MYUser:existCache('aapersons_list') then
23 | -- retreive all person instance from db
24 | all_persons = MYUser:all():sortBy('name')
25 | MYUser:setCache('aapersons_list', all_persons)
26 | DEBUG('-----vvvv---------')
27 | else
28 | DEBUG('entering cache block.')
29 | all_persons = MYUser:getCache('aapersons_list')
30 |
31 | end
32 |
33 | web:html("result.html", {all_persons = all_persons or {} })
34 | end
35 |
36 |
37 | URLS = { '/',
38 | ['/'] = index,
39 | ['/index/'] = index,
40 | ['/form_submit/'] = form_submit,
41 |
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/tests/cache_test/cachecall/callbacks.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | function onSave(self)
4 | -- here, we need to add new instance's id to cache
5 | DEBUG('ready to add new member to cache.')
6 | self:addToCacheAndSortBy('persons_list', 'name')
7 |
8 | return self
9 | end
--------------------------------------------------------------------------------
/tests/cache_test/initial/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/cache_test/initial/.gitignore
--------------------------------------------------------------------------------
/tests/cache_test/media/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/cache_test/media/css/.gitignore
--------------------------------------------------------------------------------
/tests/cache_test/media/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/cache_test/media/images/.gitignore
--------------------------------------------------------------------------------
/tests/cache_test/media/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/cache_test/media/js/.gitignore
--------------------------------------------------------------------------------
/tests/cache_test/media/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/cache_test/media/plugins/.gitignore
--------------------------------------------------------------------------------
/tests/cache_test/media/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/cache_test/media/uploads/.gitignore
--------------------------------------------------------------------------------
/tests/cache_test/models/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/cache_test/models/.gitignore
--------------------------------------------------------------------------------
/tests/cache_test/models/myuser.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local User = require 'bamboo.models.user'
4 |
5 | local cache_callbacks = require 'cachecall.callbacks'
6 |
7 | local MYUser = User:extend {
8 | __tag = 'Bamboo.Model.User.MYUser';
9 | __name = 'MYUser';
10 | __desc = 'Generitic MYUser definition';
11 | __indexfd = 'name';
12 | __fields = {
13 | ['name'] = {},
14 | ['age'] = {},
15 | ['gender'] = {},
16 |
17 | };
18 | __decorators = {
19 | save = function(osave)
20 | return function(self, ...)
21 | self = osave(self, ...)
22 | if not self then return nil end
23 |
24 | self = cache_callbacks.onSave(self)
25 |
26 | return self
27 | end
28 | end;
29 |
30 | };
31 |
32 | init = function (self, t)
33 | if not t then return self end
34 |
35 | self.name = t.name
36 | self.age = t.age
37 | self.gender = t.gender
38 |
39 | return self
40 | end;
41 |
42 | }
43 |
44 | return MYUser
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/tests/cache_test/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/cache_test/plugins/.gitignore
--------------------------------------------------------------------------------
/tests/cache_test/settings.lua:
--------------------------------------------------------------------------------
1 | project_name = "cache_test"
2 | host = "cache_test"
3 | sender_id = 'c62c1bda-0816-2205-eef9-811c8e055fd1'
4 | io_threads = 1
5 | views = "views/"
6 | config_file = 'config.lua'
7 | WHICH_DB = 15
8 |
9 | debug_level = 1
--------------------------------------------------------------------------------
/tests/cache_test/views/form.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
5 |
11 |
12 | ]}
--------------------------------------------------------------------------------
/tests/cache_test/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 | Form Process
15 |
16 |
17 |
18 |
19 |
20 | {[ 'page' ]}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/tests/cache_test/views/result.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
9 |
10 |
11 | The database have the following data:
12 |
13 |
14 | Name Age Gender
15 | {% for _, v in ipairs(all_persons) do%}
16 | {{v.name}} {{v.age}} {{v.gender}}
17 | {% end %}
18 |
19 |
20 | Click here to return form page.
21 |
22 | ]}
--------------------------------------------------------------------------------
/tests/core_test/app/handler_entry.lua:
--------------------------------------------------------------------------------
1 | require 'bamboo'
2 |
3 | local View = require 'bamboo.view'
4 | local Form = require 'bamboo.form'
5 |
6 | local MYUser = require 'models.myuser'
7 | bamboo.registerModel(MYUser)
8 | local plugin_paginator = require "plugins.ajaxpaginator"
9 | bamboo.registerPlugin('paginator', plugin_paginator)
10 |
11 |
12 |
13 | local function index(web, req)
14 | web:page(View("form.html"){})
15 | end
16 |
17 |
18 | local function form_submit(web, req)
19 | local params = req.PARAMS
20 | DEBUG(params)
21 |
22 | local person = MYUser(params)
23 | -- save person object to db
24 | person:save()
25 |
26 |
27 | web:redirect("/page/getInstance/")
28 | end
29 |
30 | local function show(web, req)
31 |
32 | return web:page(View("result.html"){})
33 | end
34 |
35 | local function form_submit2(web, req, starti, endi)
36 |
37 | local all_persons
38 | if not MYUser:existCache('persons_list') then
39 | -- retreive all person instance from db
40 | all_persons = MYUser:all():sortBy('name')
41 | MYUser:setCache('persons_list', all_persons)
42 | all_persons = all_persons:slice(starti, endi)
43 | else
44 | -- DEBUG('entering cache block.')
45 | all_persons = MYUser:getCache('persons_list', starti, endi)
46 |
47 | end
48 | -- fptable(all_persons)
49 | local total = MYUser:numCache('persons_list')
50 |
51 | return View("item.html"){all_persons = all_persons}, total
52 | end
53 |
54 | bamboo.registerPluginCallback('page_callback', form_submit2)
55 |
56 | function test (web, req)
57 | local x, y, z = 10, 100, 1000
58 |
59 | return web:page(View("test.html"){"locals"})
60 | end
61 |
62 | function i_am_query_set(web, req)
63 | local query_set = MYUser:all()
64 |
65 | return isQuerySet(query_set)
66 | end
67 |
68 |
69 | URLS = { '/',
70 | ['/'] = index,
71 | ['/index/'] = index,
72 | ['/form_submit/'] = form_submit,
73 | ['/page/getInstance/'] = show,
74 | ['/page/getInstances/'] = plugin_paginator.jsons,
75 |
76 | -- below for auto test
77 | ['/test/'] = test,
78 | ['/test/i_am_query_set/'] = i_am_query_set,
79 |
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/tests/core_test/cachecall/callbacks.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | function onSave(self)
4 | -- here, we need to add new instance's id to cache
5 | DEBUG('ready to add new member to cache.')
6 | self:addToCacheAndSortBy('persons_list', 'name')
7 |
8 | return self
9 | end
--------------------------------------------------------------------------------
/tests/core_test/initial/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/core_test/initial/.gitignore
--------------------------------------------------------------------------------
/tests/core_test/media/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/core_test/media/css/.gitignore
--------------------------------------------------------------------------------
/tests/core_test/media/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/core_test/media/images/.gitignore
--------------------------------------------------------------------------------
/tests/core_test/media/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/core_test/media/js/.gitignore
--------------------------------------------------------------------------------
/tests/core_test/media/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/core_test/media/plugins/.gitignore
--------------------------------------------------------------------------------
/tests/core_test/media/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/core_test/media/uploads/.gitignore
--------------------------------------------------------------------------------
/tests/core_test/models/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/core_test/models/.gitignore
--------------------------------------------------------------------------------
/tests/core_test/models/mymodel.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local Model = require 'bamboo.model'
4 |
5 | local Mymodel = Model:extend {
6 | __tag = 'Bamboo.Model.Mymodel';
7 | __name = 'Mymodel';
8 | __desc = 'Generitic Mymodel definition';
9 | __indexfd = 'name',
10 | __fields = {
11 | ['name'] = {},
12 |
13 | };
14 |
15 | init = function (self, t)
16 | if isFalse(t) then return self end
17 |
18 | -- auto fill non-foreign fields with params t
19 | local fields = self.__fields
20 | for k, v in pairs(t) do
21 | if fields[k] and not fields[k].foreign then
22 | self[k] = tostring(v)
23 | end
24 | end
25 |
26 | return self
27 | end;
28 |
29 |
30 |
31 | }
32 |
33 | return Mymodel
34 |
35 |
36 |
--------------------------------------------------------------------------------
/tests/core_test/models/myuser.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local User = require 'bamboo.models.user'
4 |
5 | local cache_callbacks = require 'cachecall.callbacks'
6 |
7 | local MYUser = User:extend {
8 | __tag = 'Bamboo.Model.User.MYUser';
9 | __name = 'MYUser';
10 | __desc = 'Generitic MYUser definition';
11 | __indexfd = 'name';
12 | __fields = {
13 | ['name'] = {},
14 | ['age'] = {},
15 | ['gender'] = {},
16 |
17 | };
18 | __decorators = {
19 | save = function(osave)
20 | return function(self, ...)
21 | self = osave(self, ...)
22 | if not self then return nil end
23 |
24 | self = cache_callbacks.onSave(self)
25 |
26 | return self
27 | end
28 | end;
29 |
30 | };
31 |
32 | init = function (self, t)
33 | if not t then return self end
34 |
35 | self.name = t.name
36 | self.age = t.age
37 | self.gender = t.gender
38 |
39 | return self
40 | end;
41 |
42 | }
43 |
44 | return MYUser
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/tests/core_test/monconfig.lua:
--------------------------------------------------------------------------------
1 | static_core_test = { type="dir", base='sites/core_test/', index_file='index.html', default_ctype='text/plain' }
2 |
3 | handler_core_test = { type="handler", send_spec='tcp://127.0.0.1:10001',
4 | send_ident='ba06f707-8647-46b9-b7f7-e641d6419909',
5 | recv_spec='tcp://127.0.0.1:10002', recv_ident=''}
6 |
7 | main = {
8 | bind_addr = "127.0.0.1",
9 | uuid="505417b8-1de4-454f-98b6-07eb9225cca1",
10 | access_log="logs/access.log",
11 | error_log="logs/error.log",
12 | chroot="./",
13 | pid_file="run/mongrel2.pid",
14 | default_host="core_test",
15 | name="main",
16 | port=6767,
17 | hosts= {
18 | {
19 | name="core_test",
20 | matching = "xxxxxx",
21 | routes={
22 | ['/'] = handler_core_test,
23 | ['/media/'] = static_core_test
24 | }
25 | },
26 | }
27 | }
28 |
29 |
30 | settings = {
31 | ['zeromq.threads'] = 1,
32 | ['limits.content_length'] = 20971520,
33 | ['upload.temp_store'] = '/tmp/monserver.upload.XXXXXX'
34 | }
35 |
36 | mimetypes = {}
37 |
38 | servers = { main }
39 |
40 |
--------------------------------------------------------------------------------
/tests/core_test/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/core_test/plugins/.gitignore
--------------------------------------------------------------------------------
/tests/core_test/plugins/ajaxpaginator/ajaxpaginator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
72 |
73 |
74 |
{{htmlcontent}}
75 |
76 |
77 |
首页
78 |
上一页
79 |
80 | {% for i=1, totalpages do %}
81 | {{i}}/{{totalpages}}
82 | {% end %}
83 |
84 |
89 |
90 |
下一页
91 |
末页
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/tests/core_test/plugins/ajaxpaginator/init.lua:
--------------------------------------------------------------------------------
1 | module (..., package.seeall)
2 |
3 | local _args = {}
4 |
5 | function helper()
6 | local params = req.PARAMS
7 |
8 | local thepage = tonumber(params.thepage) or 1
9 | if thepage < 1 then thepage = 1 end
10 | local totalpages = tonumber(params.totalpages)
11 | if totalpages and thepage > totalpages then thepage = totalpages end
12 | local npp = tonumber(params.npp) or tonumber(_args.npp) or 5
13 | local starti = (thepage-1) * npp + 1
14 | local endi = thepage * npp
15 | local pageurl = params.pageurl or _args.pageurl
16 | local callback = _args.callback
17 |
18 | -- the callback should return 2 values: html fragment and totalnum
19 | local htmlcontent, totalnum = bamboo.getPluginCallbackByName(callback)(web, req, starti, endi)
20 |
21 | if totalnum then
22 | totalpages = math.ceil(totalnum/npp)
23 | if thepage > totalpages then thepage = totalpages end
24 | end
25 |
26 | local prevpage = thepage - 1
27 | if prevpage < 1 then prevpage = 1 end
28 | local nextpage = thepage + 1
29 | if nextpage > totalpages then nextpage = totalpages end
30 |
31 | return {
32 | ['htmlcontent'] = htmlcontent,
33 | ['totalpages'] = totalpages,
34 | ['npp'] = npp,
35 | ['pageurl'] = pageurl,
36 | ['thepage'] = thepage,
37 | ['prevpage'] = prevpage,
38 | ['nextpage'] = nextpage
39 | }
40 | end
41 |
42 |
43 | function main(args)
44 |
45 | assert(type(args.pageurl) == 'string', '[Error] pageurl missing in plugin paginator.')
46 | assert(type(args.callback) == 'string' and type(bamboo.getPluginCallbackByName(args.callback)) == 'function', '[Error] callback missing in plugin paginator.')
47 | _args = args
48 |
49 | return View('../plugins/ajaxpaginator/ajaxpaginator.html') (helper())
50 |
51 | end
52 |
53 | function jsons(web, req)
54 | -- fptable(req.PARAMS)
55 | return web:jsonSuccess(helper())
56 | end
57 |
--------------------------------------------------------------------------------
/tests/core_test/settings.lua:
--------------------------------------------------------------------------------
1 | project_name = "core_test"
2 | host = "core_test"
3 | sender_id = 'c62c1bda-0816-2205-eef9-811c8e055fd1'
4 | io_threads = 1
5 | views = "views/"
6 | config_file = 'config.lua'
7 | WHICH_DB = 15
8 |
9 | debug_level = 1
10 |
--------------------------------------------------------------------------------
/tests/core_test/tests/core_tests.lua:
--------------------------------------------------------------------------------
1 | local testing = require "bamboo.testing"
2 | local socket = require "socket"
3 |
4 | context("Bamboo Core Functions Testing", function ()
5 |
6 | context("Session - session.lua", function ()
7 |
8 | end)
9 |
10 | context("Web & Request - web.lua", function ()
11 |
12 | end)
13 |
14 | context("Form - form.lua", function ()
15 |
16 | end)
17 |
18 |
19 | context("Model - model.lua", function ()
20 |
21 | math.randomseed(os.time())
22 | local MYUser = require 'models.myuser'
23 | local u = MYUser {
24 | username = tostring(math.random()),
25 | password = '123456',
26 | name = 'Tang Gang', --tostring(math.random()),
27 | age = 20,
28 | gender = 'male',
29 | email = 'xxxx',
30 | }
31 | u:save()
32 |
33 | context("Global Functions Defined in model.lua", function ()
34 | test("isClass()", function ()
35 | assert_equal(isClass(MYUser), true)
36 | assert_equal(isClass({}), false)
37 | local instance = MYUser:getById(1)
38 | assert_equal(isClass(instance), false)
39 | end)
40 | test("isInstance()", function ()
41 | local instance = MYUser:getById(1)
42 | assert_equal(isInstance(instance), true)
43 | end)
44 | test("isQuerySet()", function ()
45 | local query_set = MYUser:all()
46 | assert_equal(isQuerySet(query_set), true)
47 | end)
48 | test("isValidInstance()", function ()
49 | local obj = MYUser:getById(1)
50 | assert_equal(isValidInstance(obj), true)
51 | end)
52 |
53 |
54 | --[[
55 | local tester = testing.browser("tester")
56 | local t1 = socket.gettime()
57 | local ret
58 | for i=1, 10000 do
59 | ret = tester:click("/test")
60 | end
61 | local t2 = socket.gettime()
62 | print(t2 - t1)
63 | --]]
64 | end)
65 |
66 | context("Assertions", function ()
67 | test("I_AM_CLASS()", function ()
68 | local ret, err = pcall(I_AM_CLASS, MYUser)
69 | assert_equal(ret, true)
70 | end)
71 | test("I_AM_INSTANCE()", function ()
72 | local instance = MYUser:getById(1)
73 | local ret, err = pcall(I_AM_INSTANCE, instance)
74 | assert_equal(ret, true)
75 | end)
76 | test("I_AM_QUERY_SET()", function ()
77 | local query_set = MYUser:all()
78 | local ret, err = pcall(I_AM_QUERY_SET, query_set)
79 | assert_equal(ret, true)
80 | end)
81 |
82 | end)
83 |
84 | context("Basic API", function ()
85 |
86 | end)
87 |
88 | context("Custom API", function ()
89 |
90 | end)
91 |
92 | context("Cache API", function ()
93 |
94 | end)
95 |
96 | context("Foreign API", function ()
97 |
98 | end)
99 |
100 | context("Dynamic Field API", function ()
101 |
102 | end)
103 |
104 | end)
105 |
106 | context("Views - view.lua", function ()
107 |
108 | end)
109 |
110 | context("Utils - util.lua", function ()
111 |
112 | end)
113 |
114 | context("Redis Wrapper - redis.lua and redis/*", function ()
115 |
116 | end)
117 |
118 | context("MySQL Driver - mysql.lua", function ()
119 |
120 | end)
121 |
122 |
123 | end)
124 |
--------------------------------------------------------------------------------
/tests/core_test/views/form.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
5 |
11 |
12 | ]}
--------------------------------------------------------------------------------
/tests/core_test/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Form Process
13 |
14 |
15 |
16 |
17 |
18 | {[ 'page' ]}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/tests/core_test/views/item.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Name Age Gender
4 | {% for _, v in ipairs(all_persons) do %}
5 | {{v.name}} {{v.age}} {{v.gender}}
6 | {% end %}
7 |
8 |
--------------------------------------------------------------------------------
/tests/core_test/views/paginator.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
首页
16 |
上一页
17 |
18 | {% for i=1, pages do %}
19 | {{i}}/{{pages}}
20 | {% end %}
21 |
22 |
25 |
26 |
下一页
27 |
末页
28 |
29 |
--------------------------------------------------------------------------------
/tests/core_test/views/result (复件).html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
24 | The database have the following data:
25 |
26 |
27 | Name Age Gender
28 | {% for _, v in ipairs(all_persons) do%}
29 | {{v.name}} {{v.age}} {{v.gender}}
30 | {% end %}
31 |
32 |
33 |
34 |
35 |
首页
36 |
上一页
37 |
38 | {% for i=1, pages do %}
39 | {{i}}/{{pages}}
40 | {% end %}
41 |
42 |
45 |
46 |
下一页
47 |
末页
48 |
49 |
50 |
51 |
52 | Click here to return form page.
53 |
54 | ]}
55 |
56 |
--------------------------------------------------------------------------------
/tests/core_test/views/result.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
9 |
10 |
19 |
20 | The database have the following data:
21 |
22 | {^ paginator callback="page_callback", pageurl="/page/getInstances/", htmlfile="item.html" ^}
23 |
24 | Click here to return form page.
25 |
26 | ]}
27 |
28 |
--------------------------------------------------------------------------------
/tests/core_test/views/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | I am here.
4 | {{ x }}
5 | {{ x }}
6 | {{ x }}
7 | {{ x }}
8 |
9 |
10 | {% if y > x then %}
11 | {% for i=1, 100 do %}
12 | hahaha
13 |
14 | {% end %}
15 |
16 | {% end %}
17 |
18 |
19 |
20 |
21 | lastit.
--------------------------------------------------------------------------------
/tests/hashindex_test/settings.lua:
--------------------------------------------------------------------------------
1 | url = 'http://localhost'
2 | project_name = "lgcms"
3 | host = "lgcms"
4 | sender_id = 'c2405186-5c67-8b7e-e584-915901e5a02c'
5 | io_threads = 1
6 | views = "views/"
7 | config_file = 'config.lua'
8 | WHICH_DB = 27
9 |
10 | rule_index_support = false
11 |
12 | debug_level = 1
13 | PRODUCTION =false
14 | prof =false
15 |
16 | index_hash = true
17 | mmseg_dict_path = 'dict/mmseg'
18 | sensitive_dict_path = 'dict/sensitive'
19 |
20 | auto_reload_when_code_change = true
21 |
22 |
23 | --全文搜索
24 | fulltext_index_support = true
25 | search_threshold = 20 --搜索门限值
26 |
27 | --审核时间
28 | verify_time_start = 24
29 | verify_time_end = 24
30 |
31 | --假删
32 | use_fake_deletion = true
33 |
34 | --每页显示数目
35 | forum_npp = 10
36 | article_npp = 10
37 |
38 | --初始界面(0 = 正常, 1 = 只看帖, 2 = 只聊天)
39 | oo_style_status = 2
40 |
--------------------------------------------------------------------------------
/tests/paginator_test/app/handler_entry.lua:
--------------------------------------------------------------------------------
1 | require 'bamboo'
2 |
3 | local View = require 'bamboo.view'
4 | local Form = require 'bamboo.form'
5 |
6 | local MYUser = require 'models.myuser'
7 | bamboo.registerModel(MYUser)
8 |
9 | bamboo.registerPlugin('paginator', require "plugins.paginator")
10 |
11 |
12 |
13 | local function index(web, req)
14 | web:page(View("form.html"){})
15 | end
16 |
17 | function paginator(list, npp)
18 |
19 | local length = #list
20 | local pages = math.ceil(length/npp)
21 |
22 | return npp, pages
23 |
24 | end
25 |
26 | local function form_submit(web, req)
27 | local params = req.PARAMS
28 | DEBUG(params)
29 |
30 | local person = MYUser(params)
31 | -- save person object to db
32 | person:save()
33 |
34 | web:redirect('/result/')
35 | end
36 |
37 | local function show(web, req)
38 | web:page(View('result.html'){})
39 | end
40 |
41 | local function paginator_callback(web, req, starti, endi)
42 |
43 | local all_persons
44 | if not MYUser:existCache('aa_persons_list') then
45 | -- retreive all person instance from db
46 | all_persons = MYUser:all():sortBy('name')
47 | MYUser:setCache('aa_persons_list', all_persons)
48 | all_persons = all_persons:slice(starti, endi)
49 | else
50 | DEBUG('entering cache block.')
51 | all_persons = MYUser:getCache('aa_persons_list', starti, endi)
52 |
53 | end
54 | fptable(all_persons)
55 | local total = MYUser:numCache('aa_persons_list')
56 |
57 | return View("item.html"){all_persons = all_persons}, total
58 | end
59 |
60 | bamboo.registerPluginCallback('page_callback', paginator_callback)
61 |
62 | URLS = { '/',
63 | ['/'] = index,
64 | ['/index/'] = index,
65 | ['/result/'] = show,
66 | ['/form_submit/'] = form_submit,
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/tests/paginator_test/cachecall/callbacks.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | function onSave(self)
4 | -- here, we need to add new instance's id to cache
5 | DEBUG('ready to add new member to cache.')
6 | self:addToCacheAndSortBy('aa_persons_list', 'name')
7 |
8 | return self
9 | end
10 |
--------------------------------------------------------------------------------
/tests/paginator_test/initial/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/paginator_test/initial/.gitignore
--------------------------------------------------------------------------------
/tests/paginator_test/media/css/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/paginator_test/media/css/.gitignore
--------------------------------------------------------------------------------
/tests/paginator_test/media/images/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/paginator_test/media/images/.gitignore
--------------------------------------------------------------------------------
/tests/paginator_test/media/js/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/paginator_test/media/js/.gitignore
--------------------------------------------------------------------------------
/tests/paginator_test/media/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/paginator_test/media/plugins/.gitignore
--------------------------------------------------------------------------------
/tests/paginator_test/media/uploads/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/paginator_test/media/uploads/.gitignore
--------------------------------------------------------------------------------
/tests/paginator_test/models/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/paginator_test/models/.gitignore
--------------------------------------------------------------------------------
/tests/paginator_test/models/myuser.lua:
--------------------------------------------------------------------------------
1 | module(..., package.seeall)
2 |
3 | local User = require 'bamboo.models.user'
4 |
5 | local cache_callbacks = require 'cachecall.callbacks'
6 |
7 | local MYUser = User:extend {
8 | __tag = 'Bamboo.Model.User.MYUser';
9 | __name = 'MYUser';
10 | __desc = 'Generitic MYUser definition';
11 | __indexfd = 'name';
12 | __fields = {
13 | ['name'] = {},
14 | ['age'] = {},
15 | ['gender'] = {},
16 |
17 | };
18 | __decorators = {
19 | save = function(osave)
20 | return function(self, ...)
21 | self = osave(self, ...)
22 | if not self then return nil end
23 |
24 | self = cache_callbacks.onSave(self)
25 |
26 | return self
27 | end
28 | end;
29 |
30 | };
31 |
32 | init = function (self, t)
33 | if not t then return self end
34 |
35 | self.name = t.name
36 | self.age = t.age
37 | self.gender = t.gender
38 |
39 | return self
40 | end;
41 |
42 | }
43 |
44 | return MYUser
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/tests/paginator_test/monconfig.lua:
--------------------------------------------------------------------------------
1 | static_paginator_test = { type="dir", base='sites/paginator_test/', index_file='index.html', default_ctype='text/plain' }
2 |
3 | handler_paginator_test = { type="handler", send_spec='tcp://127.0.0.1:10001',
4 | send_ident='ba06f707-8647-46b9-b7f7-e641d6419909',
5 | recv_spec='tcp://127.0.0.1:10002', recv_ident=''}
6 |
7 | main = {
8 | bind_addr = "127.0.0.1",
9 | uuid="505417b8-1de4-454f-98b6-07eb9225cca1",
10 | access_log="logs/access.log",
11 | error_log="logs/error.log",
12 | chroot="./",
13 | pid_file="run/mongrel2.pid",
14 | default_host="paginator_test",
15 | name="main",
16 | port=6767,
17 | hosts= {
18 | {
19 | name="paginator_test",
20 | matching = "xxxxxx",
21 | routes={
22 | ['/'] = handler_paginator_test,
23 | ['/media/'] = static_paginator_test
24 | }
25 | },
26 | }
27 | }
28 |
29 |
30 | settings = {
31 | ['zeromq.threads'] = 1,
32 | ['limits.content_length'] = 20971520,
33 | ['upload.temp_store'] = '/tmp/monserver.upload.XXXXXX'
34 | }
35 |
36 | mimetypes = {}
37 |
38 | servers = { main }
39 |
40 |
--------------------------------------------------------------------------------
/tests/paginator_test/plugins/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/miketang84/bamboo/ac50c6a527eea34a4d9c625d932c6e9eb38ca376/tests/paginator_test/plugins/.gitignore
--------------------------------------------------------------------------------
/tests/paginator_test/plugins/paginator/init.lua:
--------------------------------------------------------------------------------
1 | module (..., package.seeall)
2 |
3 | function main(args)
4 |
5 | assert(type(args.pageurl) == 'string', '[Error] pageurl missing in plugin paginator.')
6 | assert(type(args.callback) == 'string' and type(bamboo.getPluginCallbackByName(args.callback)) == 'function', '[Error] callback missing in plugin paginator.')
7 |
8 | local params = req.PARAMS
9 |
10 | local thepage = tonumber(params.thepage) or 1
11 | if thepage < 1 then thepage = 1 end
12 | local totalpages = tonumber(params.totalpages)
13 | if totalpages and thepage > totalpages then thepage = totalpages end
14 | local npp = tonumber(params.npp) or tonumber(args.npp) or 5
15 | local starti = (thepage-1) * npp + 1
16 | local endi = thepage * npp
17 | local pageurl = args.pageurl
18 | local callback = args.callback
19 |
20 | -- the callback should return 2 values: html fragment and totalnum
21 | local htmlcontent, totalnum = bamboo.getPluginCallbackByName(callback)(web, req, starti, endi)
22 |
23 | if totalnum then
24 | totalpages = math.ceil(totalnum/npp)
25 | if thepage > totalpages then thepage = totalpages end
26 | end
27 |
28 | local prevpage = thepage - 1
29 | if prevpage < 1 then prevpage = 1 end
30 | local nextpage = thepage + 1
31 | if nextpage > totalpages then nextpage = totalpages end
32 |
33 |
34 | return View('../plugins/paginator/paginator.html') {
35 | htmlcontent = htmlcontent,
36 | totalpages = totalpages, npp = npp, pageurl = pageurl,
37 | thepage = thepage, prevpage = prevpage, nextpage = nextpage
38 | }
39 |
40 | end
41 |
--------------------------------------------------------------------------------
/tests/paginator_test/plugins/paginator/paginator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
26 |
27 |
28 | {{htmlcontent}}
29 |
30 |
31 |
首页
32 |
上一页
33 |
34 | {% for i=1, totalpages do %}
35 | {{i}}/{{totalpages}}
36 | {% end %}
37 |
38 |
43 |
44 |
下一页
45 |
末页
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/tests/paginator_test/settings.lua:
--------------------------------------------------------------------------------
1 | project_name = "paginator_test"
2 | host = "paginator_test"
3 | sender_id = 'c62c1bda-0816-2205-eef9-811c8e055fd1'
4 | io_threads = 1
5 | views = "views/"
6 | config_file = 'config.lua'
7 | WHICH_DB = 14
8 |
9 | debug_level = 1
10 |
--------------------------------------------------------------------------------
/tests/paginator_test/views/form.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
5 |
11 |
12 | ]}
--------------------------------------------------------------------------------
/tests/paginator_test/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Form Process
13 |
14 |
15 |
16 |
17 |
18 | {[ 'page' ]}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/tests/paginator_test/views/item.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Name Age Gender
4 | {% for _, v in ipairs(all_persons) do %}
5 | {{v.name}} {{v.age}} {{v.gender}}
6 | {% end %}
7 |
8 |
--------------------------------------------------------------------------------
/tests/paginator_test/views/paginator.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
首页
16 |
上一页
17 |
18 | {% for i=1, pages do %}
19 | {{i}}/{{pages}}
20 | {% end %}
21 |
22 |
25 |
26 |
下一页
27 |
末页
28 |
29 |
--------------------------------------------------------------------------------
/tests/paginator_test/views/result (复件).html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
24 | The database have the following data:
25 |
26 |
27 | Name Age Gender
28 | {% for _, v in ipairs(all_persons) do%}
29 | {{v.name}} {{v.age}} {{v.gender}}
30 | {% end %}
31 |
32 |
33 |
34 |
35 |
首页
36 |
上一页
37 |
38 | {% for i=1, pages do %}
39 | {{i}}/{{pages}}
40 | {% end %}
41 |
42 |
45 |
46 |
下一页
47 |
末页
48 |
49 |
50 |
51 |
52 | Click here to return form page.
53 |
54 | ]}
55 |
56 |
--------------------------------------------------------------------------------
/tests/paginator_test/views/result.html:
--------------------------------------------------------------------------------
1 | {: 'index.html' :}
2 |
3 | {[ ======= 'page' ========
4 |
9 | {- {( "test.html" )} -}
10 |
11 | {- {% x= {1,2,3} %} -}
12 | The database have the following data:
13 | {- for test error report -}
14 | {- {{ x[3].a }} -}
15 |
16 | {^ paginator callback="page_callback", pageurl="/result/", htmlfile="item.html" ^}
17 |
18 | Click here to return form page.
19 |
20 | ]}
21 |
22 |
--------------------------------------------------------------------------------
/tests/paginator_test/views/test.html:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/tools/rebuild_hash_index.lua:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env luajit
2 | -- this is a tool for rebuilding hash index for all the objects of the
3 | -- specify Model. First , it delete all the data of the hash index of
4 | -- the Model, and second, it build the new hash index of the objects
5 | -- one by one.
6 | -- you must specify the db and model name
7 |
8 |
9 | redis = require 'redis'
10 | require 'lglib'
11 |
12 | if arg[1] == nil or arg[2] == nil then
13 | print("HAS no DB args or the Models args ");
14 | return;
15 | end
16 |
17 | if not tonumber(arg[1]) then
18 | print("First argment [WHICH DB] must be number ");
19 | return;
20 | end
21 |
22 |
23 |
24 | local DB_HOST = '127.0.0.1'
25 | local DB_PORT = 6379
26 | local WHICH_DB = tonumber(arg[1])
27 | local AUTH = nil
28 | -- create a redis connection in this process
29 | -- we will create one redis connection for every process
30 | local db = redis.connect(DB_HOST, DB_PORT)
31 | assert(db, '[Error] Database connection is failed.')
32 | if AUTH then assert(db:command("auth",AUTH)); end
33 | assert(db:select(WHICH_DB));
34 |
35 |
36 | BAMBOO_DB = db
37 | require 'bamboo'
38 | local Model = require 'bamboo.model'
39 | local mih = require 'bamboo.model-indexhash'
40 |
41 |
42 | local modelName = "models." .. string.lower(arg[2]);
43 |
44 | local TagModel = require(modelName)
45 | if TagModel == nil then
46 | print("HAS no the Models: " .. arg[2]);
47 | return;
48 | end
49 |
50 | --del all hash index
51 | local fields = TagModel.__fields;
52 | for k,v in pairs(fields) do
53 | if v.index_type == "string" then
54 | local setKeys = db:keys(arg[2] .. ":" .. k .. ":*__set");
55 | for _,setKey in pairs(setKeys) do
56 | db:del(setKey);
57 | end
58 | db:del(arg[2] .. ":" .. k .. ":__hash");
59 | elseif v.index_type == 'number' then
60 | db:del(arg[2] .. ":" .. k .. ":__zset");
61 | else
62 | end
63 | end
64 |
65 | local all = TagModel:all();
66 | local count = 0;
67 | for i,obj in pairs(all) do
68 | -- print(obj.title, obj.id )
69 | mih.index(obj,true);
70 | count = count +1 ;
71 | end
72 |
73 |
74 | print("make hash index for all " .. count .. " objects of " .. modelName);
75 |
76 |
77 |
--------------------------------------------------------------------------------