├── .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 |
6 | Input 1:
7 | Input 2:
8 | Input 3:
9 | 10 |
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 |
6 | Input 1:
7 | Input 2:
8 | Input 3:
9 | 10 |
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 |
6 | Name:
7 | Age:
8 | Gender:
9 | 10 |
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 | 15 | {% for _, v in ipairs(all_persons) do%} 16 | 17 | {% end %} 18 |
NameAgeGender
{{v.name}}{{v.age}}{{v.gender}}
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 = [[]] 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 | 84 |
85 | 86 | 87 | 88 |
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 |
6 | Name:
7 | Age:
8 | Gender:
9 | 10 |
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 | 4 | {% for _, v in ipairs(all_persons) do %} 5 | 6 | {% end %} 7 |
NameAgeGender
{{v.name}}{{v.age}}{{v.gender}}
8 | -------------------------------------------------------------------------------- /tests/ajaxpaginator_test/views/paginator.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 |
15 | 首页 16 | 上一页 17 | 22 |
23 | 24 |
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 | 28 | {% for _, v in ipairs(all_persons) do%} 29 | 30 | {% end %} 31 |
NameAgeGender
{{v.name}}{{v.age}}{{v.gender}}
32 | 33 | 34 |
35 | 首页 36 | 上一页 37 | 42 |
43 | 44 |
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 | 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 |
6 | Name:
7 | Age:
8 | Gender:
9 | 10 |
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 | 15 | {% for _, v in ipairs(all_persons) do%} 16 | 17 | {% end %} 18 |
NameAgeGender
{{v.name}}{{v.age}}{{v.gender}}
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 | 84 |
85 | 86 | 87 | 88 |
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 |
6 | Name:
7 | Age:
8 | Gender:
9 | 10 |
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 | 4 | {% for _, v in ipairs(all_persons) do %} 5 | 6 | {% end %} 7 |
NameAgeGender
{{v.name}}{{v.age}}{{v.gender}}
8 | -------------------------------------------------------------------------------- /tests/core_test/views/paginator.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 |
15 | 首页 16 | 上一页 17 | 22 |
23 | 24 |
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 | 28 | {% for _, v in ipairs(all_persons) do%} 29 | 30 | {% end %} 31 |
NameAgeGender
{{v.name}}{{v.age}}{{v.gender}}
32 | 33 | 34 |
35 | 首页 36 | 上一页 37 | 42 |
43 | 44 |
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 | 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 | 38 |
39 | 40 | 41 | 42 |
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 |
6 | Name:
7 | Age:
8 | Gender:
9 | 10 |
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 | 4 | {% for _, v in ipairs(all_persons) do %} 5 | 6 | {% end %} 7 |
NameAgeGender
{{v.name}}{{v.age}}{{v.gender}}
8 | -------------------------------------------------------------------------------- /tests/paginator_test/views/paginator.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 |
15 | 首页 16 | 上一页 17 | 22 |
23 | 24 |
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 | 28 | {% for _, v in ipairs(all_persons) do%} 29 | 30 | {% end %} 31 |
NameAgeGender
{{v.name}}{{v.age}}{{v.gender}}
32 | 33 | 34 |
35 | 首页 36 | 上一页 37 | 42 |
43 | 44 |
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 |
2 | Name:
3 | Age:
4 | Gender:
5 | 6 |
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 | --------------------------------------------------------------------------------