├── src ├── sailor │ ├── version.lua │ ├── blank-app │ │ ├── conf │ │ │ ├── .htaccess │ │ │ ├── conf.lua │ │ │ ├── nginx.conf │ │ │ └── mime.types │ │ ├── models │ │ │ └── .htaccess │ │ ├── runtime │ │ │ ├── .htaccess │ │ │ └── tmp │ │ │ │ └── .htaccess │ │ ├── tests │ │ │ ├── .htaccess │ │ │ ├── fixtures │ │ │ │ └── .htaccess │ │ │ ├── unit │ │ │ │ └── dummy.lua │ │ │ ├── functional │ │ │ │ └── dummy.lua │ │ │ ├── bootstrap.lua │ │ │ ├── helper.lua │ │ │ └── bootstrap_resty.lua │ │ ├── views │ │ │ ├── .htaccess │ │ │ ├── error │ │ │ │ ├── 404.lp │ │ │ │ └── inspect.lp │ │ │ └── main │ │ │ │ └── index.lp │ │ ├── controllers │ │ │ ├── .htaccess │ │ │ └── main.lua │ │ ├── index.lua │ │ ├── themes │ │ │ └── default │ │ │ │ ├── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ │ ├── js │ │ │ │ └── npm.js │ │ │ │ ├── config.json │ │ │ │ ├── css │ │ │ │ └── sticky-footer-navbar.css │ │ │ │ └── main.lp │ │ ├── .htaccess │ │ ├── index-magnet.lua │ │ └── start-server.lua │ ├── db.lua │ ├── cookie.lua │ ├── session.lua │ ├── access.lua │ ├── test.lua │ ├── form.lua │ └── cli.lua └── web_utils │ ├── lp_ex.lua │ ├── utils.lua │ └── serialize.lua ├── test └── dev-app │ ├── views │ ├── test │ │ ├── error.lp │ │ ├── _included.lp │ │ ├── include.lp │ │ ├── upload.lp │ │ ├── mailer.lp │ │ ├── client_module_js.lp │ │ ├── runat_both.lp │ │ ├── client_module.lp │ │ ├── runat_client.lp │ │ ├── form.lp │ │ ├── realtime.lp │ │ ├── index.lp │ │ ├── starlight.lp │ │ ├── luavmjs.lp │ │ ├── lua51js.lp │ │ └── frontend_performance.lp │ ├── .htaccess │ ├── error │ │ ├── 404.lp │ │ └── inspect.lp │ ├── blog │ │ ├── comments.lp │ │ └── index.lp │ ├── category │ │ ├── view.lp │ │ ├── index.lp │ │ ├── update.lp │ │ └── create.lp │ └── main │ │ └── index.lp │ ├── conf │ ├── .htaccess │ ├── nginx.conf │ ├── conf.lua │ └── mime.types │ ├── models │ ├── .htaccess │ ├── comment.lua │ ├── category.lua │ ├── post.lua │ └── user.lua │ ├── sql │ ├── .htaccess │ ├── sqlite3.sql │ ├── pgsql.sql │ └── mysql.sql │ ├── tests │ ├── .htaccess │ ├── fixtures │ │ ├── category.lua │ │ ├── post.lua │ │ └── user.lua │ ├── unit │ │ ├── other.lua │ │ ├── openresty.lua │ │ ├── form.lua │ │ ├── access.lua │ │ ├── db.lua │ │ └── page.lua │ ├── .busted.lua │ ├── bootstrap.lua │ ├── helper.lua │ ├── bootstrap_resty.lua │ └── functional │ │ ├── autogen.lua │ │ └── category.lua │ ├── controllers │ ├── .htaccess │ ├── main.lua │ ├── blog.lua │ └── category.lua │ ├── runtime │ └── .htaccess │ ├── pub │ ├── lua │ │ ├── mymodule.lua │ │ └── mymodule.js │ └── moonshine │ │ └── DOMAPI.moonshine.min.js │ ├── themes │ └── default │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── config.json │ │ ├── css │ │ └── sticky-footer-navbar.css │ │ └── main.lp │ ├── .luacov │ ├── index.lua │ ├── .htaccess │ ├── index-magnet.lua │ └── start-server.lua ├── .gitignore ├── THANKYOU.md ├── docs ├── manual_sailor_functions.md ├── manual_access_module.md ├── tutorial_controllers.md ├── manual_validation_module.md ├── create_app.md ├── INSTALL_LINUX_ARCH.md ├── manual_lua_at_client.md ├── CONFIG.md ├── tutorial_views.md ├── manual_page_object.md ├── manual_form_module.md ├── INSTALL_MAC.md └── INSTALL_WIN.md ├── LICENSE ├── .luacheckrc ├── CODE_OF_CONDUCT.md ├── .travis.yml ├── sailor ├── CHANGELOG.md └── rockspecs ├── sailor-0.3-1.rockspec ├── sailor-0.3-2.rockspec ├── sailor-0.2.1-1.rockspec ├── sailor-0.2.1-2.rockspec ├── sailor-0.3-3.rockspec └── sailor-0.3-4.rockspec /src/sailor/version.lua: -------------------------------------------------------------------------------- 1 | return "0.5.3" -------------------------------------------------------------------------------- /test/dev-app/views/test/error.lp: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /test/dev-app/conf/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /test/dev-app/models/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /test/dev-app/sql/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /test/dev-app/tests/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /test/dev-app/views/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /test/dev-app/controllers/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /test/dev-app/runtime/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | .floo* 3 | *.swp 4 | *.swo 5 | test/db/ 6 | -------------------------------------------------------------------------------- /src/sailor/blank-app/conf/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /src/sailor/blank-app/models/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /src/sailor/blank-app/runtime/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /src/sailor/blank-app/tests/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /src/sailor/blank-app/views/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /src/sailor/blank-app/controllers/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /src/sailor/blank-app/runtime/tmp/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /src/sailor/blank-app/tests/fixtures/.htaccess: -------------------------------------------------------------------------------- 1 | order allow,deny 2 | deny from all -------------------------------------------------------------------------------- /src/sailor/blank-app/index.lua: -------------------------------------------------------------------------------- 1 | local sailor = require "sailor" 2 | sailor.launch() 3 | -------------------------------------------------------------------------------- /test/dev-app/views/test/_included.lp: -------------------------------------------------------------------------------- 1 | This is inside an included page! 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/dev-app/pub/lua/mymodule.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M.printh1(s) 4 | print('

'..s..'

') 5 | end 6 | 7 | return M -------------------------------------------------------------------------------- /test/dev-app/tests/fixtures/category.lua: -------------------------------------------------------------------------------- 1 | -- category fixtures 2 | 3 | return { 4 | { name = 'General' }, 5 | { name = 'Literature' } 6 | } -------------------------------------------------------------------------------- /test/dev-app/views/error/404.lp: -------------------------------------------------------------------------------- 1 | Error 404: Page not found. 2 |
3 | You can customize this page under /views/error/404.lp -------------------------------------------------------------------------------- /test/dev-app/views/test/include.lp: -------------------------------------------------------------------------------- 1 | Include test :) This is located at include.lp 2 |
3 | 4 | -------------------------------------------------------------------------------- /src/sailor/blank-app/views/error/404.lp: -------------------------------------------------------------------------------- 1 | Error 404: Page not found. 2 |
3 | You can customize this page under /views/error/404.lp -------------------------------------------------------------------------------- /src/sailor/blank-app/controllers/main.lua: -------------------------------------------------------------------------------- 1 | local main = {} 2 | 3 | function main.index(page) 4 | page:render('index') 5 | end 6 | 7 | return main 8 | -------------------------------------------------------------------------------- /test/dev-app/tests/unit/other.lua: -------------------------------------------------------------------------------- 1 | describe("#AnotherTest", function() 2 | 3 | it("should have another success", function() 4 | assert.is_equal(1,1) 5 | end) 6 | 7 | end) -------------------------------------------------------------------------------- /test/dev-app/views/test/upload.lp: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
-------------------------------------------------------------------------------- /test/dev-app/tests/fixtures/post.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | author_id = 1, 4 | body = "Hello World" 5 | }, 6 | { 7 | author_id = 1, 8 | body = "Hello 2" 9 | } 10 | } -------------------------------------------------------------------------------- /src/sailor/blank-app/tests/unit/dummy.lua: -------------------------------------------------------------------------------- 1 | describe("#DummyUnitTest", function() 2 | 3 | it("should have a success", function() 4 | assert.is_equal(1,1) 5 | end) 6 | 7 | end) 8 | -------------------------------------------------------------------------------- /test/dev-app/themes/default/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sailorproject/sailor/HEAD/test/dev-app/themes/default/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /test/dev-app/themes/default/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sailorproject/sailor/HEAD/test/dev-app/themes/default/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /test/dev-app/views/test/mailer.lp: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |
-------------------------------------------------------------------------------- /test/dev-app/themes/default/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sailorproject/sailor/HEAD/test/dev-app/themes/default/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /test/dev-app/themes/default/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sailorproject/sailor/HEAD/test/dev-app/themes/default/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/sailor/blank-app/tests/functional/dummy.lua: -------------------------------------------------------------------------------- 1 | describe("#DummyFunctionalTest", function() 2 | 3 | it("should have a success", function() 4 | assert.is_equal(1,1) 5 | end) 6 | 7 | end) 8 | -------------------------------------------------------------------------------- /src/sailor/blank-app/themes/default/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sailorproject/sailor/HEAD/src/sailor/blank-app/themes/default/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/sailor/blank-app/themes/default/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sailorproject/sailor/HEAD/src/sailor/blank-app/themes/default/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/sailor/blank-app/themes/default/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sailorproject/sailor/HEAD/src/sailor/blank-app/themes/default/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/sailor/blank-app/themes/default/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sailorproject/sailor/HEAD/src/sailor/blank-app/themes/default/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /test/dev-app/.luacov: -------------------------------------------------------------------------------- 1 | return { 2 | modules = { 3 | sailor = "src/sailor.lua", 4 | ['sailor.*'] = "src", 5 | ['sailor.db.*'] = "src", 6 | ['web_utils.*'] = "src" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /THANKYOU.md: -------------------------------------------------------------------------------- 1 | Sailor Supporters: 2 | 3 | * [Jan Krueger Siqueira](https://github.com/wallysalami) 4 | * Silent donor 0x01 5 | * [Jonathan Barronville] (https://twitter.com/jonathanmarvens) 6 | * Justin Cormack 7 | -------------------------------------------------------------------------------- /src/sailor/blank-app/tests/bootstrap.lua: -------------------------------------------------------------------------------- 1 | -- Test Bootstrap file 2 | -- This file will run before running tests 3 | sailor = require "sailor" 4 | 5 | -- local t = require "sailor.test" 6 | -- load fixtures 7 | -- t.load_fixtures() -------------------------------------------------------------------------------- /test/dev-app/index.lua: -------------------------------------------------------------------------------- 1 | local base_dir = ((debug.getinfo(1).source):match('^@?(.-)index.lua$') or '') 2 | 3 | package.path = base_dir..'../../src/?.lua;'..package.path 4 | 5 | local sailor = require "sailor" 6 | sailor.launch() 7 | -------------------------------------------------------------------------------- /src/sailor/blank-app/tests/helper.lua: -------------------------------------------------------------------------------- 1 | -- Test helper module 2 | -- This module contains functions to be shared with among tests 3 | 4 | local helper = {} 5 | 6 | function helper.example() 7 | -- do something 8 | end 9 | 10 | return helper -------------------------------------------------------------------------------- /test/dev-app/tests/.busted.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | 4 | default = { 5 | ROOT = {"tests/unit/*"}, 6 | verbose = true 7 | }, 8 | apiUnit = { 9 | 10 | ROOT = {"tests/unit/*"}, 11 | verbose = true 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /test/dev-app/controllers/main.lua: -------------------------------------------------------------------------------- 1 | local main = {} 2 | 3 | function main.index(page) 4 | page:render('index') 5 | end 6 | 7 | function main.cors(page) 8 | page:enable_cors() 9 | page:json({1, 2, 3}) 10 | end 11 | 12 | return main 13 | -------------------------------------------------------------------------------- /test/dev-app/tests/bootstrap.lua: -------------------------------------------------------------------------------- 1 | -- Test Bootstrap file 2 | -- This file will run before running tests 3 | sailor = require "sailor" 4 | local t = require "sailor.test" 5 | 6 | t.load_fixtures('user') 7 | t.load_fixtures('post') 8 | t.load_fixtures('category') 9 | 10 | --t.load_fixtures() 11 | 12 | -------------------------------------------------------------------------------- /test/dev-app/views/test/client_module_js.lp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/dev-app/views/blog/comments.lp: -------------------------------------------------------------------------------- 1 | Comments from post "<%= post.body %>": 2 |

3 | 4 | 7 | <%= c.body %>
8 | By: <%= c.author.username %>

9 | 11 | No Comments 12 | -------------------------------------------------------------------------------- /test/dev-app/pub/lua/mymodule.js: -------------------------------------------------------------------------------- 1 | // Generated by lua5.1.js-file-packer 2 | (function(Lua5_1) { 3 | Lua5_1.provide_file("/pub/lua/", "mymodule.lua", 4 | "local M = {}\n" 5 | +"function M.printh1(s)\n" 6 | +"print('

'..s..'

')\n" 7 | +"end\n" 8 | +"return M\n", 9 | true, false); 10 | // End of /mymodule.lua 11 | })(Lua5_1); -------------------------------------------------------------------------------- /test/dev-app/tests/fixtures/user.lua: -------------------------------------------------------------------------------- 1 | --user fixtures 2 | 3 | return { 4 | { 5 | username = 'serena', 6 | password = '123456' 7 | }, 8 | 9 | { 10 | username = 'lua', 11 | password = '1234' 12 | }, 13 | 14 | { 15 | username = 'arnold', 16 | password = '12345678' 17 | }, 18 | 19 | { 20 | username = 'harry', 21 | password = '12345678' 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /test/dev-app/views/test/runat_both.lp: -------------------------------------------------------------------------------- 1 | 8 | 9 |

10 |

11 | 12 | 15 | 16 |

17 | This page mixes both client and server-side Lua code. -------------------------------------------------------------------------------- /test/dev-app/views/blog/index.lp: -------------------------------------------------------------------------------- 1 | 2 | <%= p.body %> 3 |
4 | By: <%= p.author.username %> 5 |
6 | Categories: 7 | 8 | <%= c.name %>; 9 | 10 |
11 | 12 | Comments (<%= #p.comments %>) 13 | 14 |
15 |
16 | -------------------------------------------------------------------------------- /test/dev-app/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | AddHandler lua-script .lua 3 | 4 | DirectoryIndex index.lua 5 | 6 | #Uncomment these lines to enable friendly urls on apache 7 | 8 | #RewriteEngine On 9 | #RewriteCond %{REQUEST_FILENAME} !-f 10 | #RewriteRule ^([^/]+)/([^/]+)/([^.]*)$ index.lua?r=$1/$2&q=$3 [L] 11 | #RewriteRule ^([^/]+)/([^/]+)$ index.lua?r=$1&q=$3 [L] 12 | #RewriteRule ^([^.]+)$ index.lua?r=$1 [L] 13 | -------------------------------------------------------------------------------- /src/sailor/blank-app/.htaccess: -------------------------------------------------------------------------------- 1 | LuaPackagePath "{{path}}/?.lua" 2 | 3 | AddHandler lua-script .lua 4 | 5 | DirectoryIndex index.lua 6 | 7 | #Uncomment these lines to enable friendly urls on apache 8 | 9 | #RewriteEngine On 10 | #RewriteCond %{REQUEST_FILENAME} !-f 11 | #RewriteRule ^([^/]+)/([^/]+)/([^.]*)$ index.lua?r=$1/$2&q=$3 [L] 12 | #RewriteRule ^([^/]+)/([^/]+)$ index.lua?r=$1&q=$3 [L] 13 | #RewriteRule ^([^.]+)$ index.lua?r=$1 [L] 14 | -------------------------------------------------------------------------------- /test/dev-app/models/comment.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | -- Attributes and their validation rules 4 | M.attributes = { 5 | {id = "safe"}, 6 | {body = "safe"}, 7 | {author_id = "safe"}, 8 | {post_id = "safe"} 9 | } 10 | 11 | M.db = { 12 | key = 'id', 13 | table = 'comment' 14 | } 15 | 16 | M.relations = { 17 | post = {relation = "BELONGS_TO", model = "post", attribute = "post_id"}, 18 | author = {relation = "BELONGS_TO", model = "user", attribute = "author_id"}, 19 | } 20 | 21 | return M 22 | -------------------------------------------------------------------------------- /test/dev-app/controllers/blog.lua: -------------------------------------------------------------------------------- 1 | local sailor = require "sailor" 2 | 3 | local blog = {} 4 | 5 | function blog.index (page) 6 | local posts = sailor.model("post"):find_all() 7 | page:inspect(posts,"Content of posts") 8 | page:render('index',{posts=posts}) 9 | end 10 | 11 | function blog.comments (page) 12 | local post = sailor.model("post"):find_by_id( page.GET.pid ) 13 | if not post then 14 | return 404 15 | end 16 | page:render('comments',{post = post}) 17 | end 18 | 19 | return blog 20 | -------------------------------------------------------------------------------- /test/dev-app/views/category/view.lp: -------------------------------------------------------------------------------- 1 |

2 | View category # 3 | (update) 4 | (delete) 5 |

6 | 7 | 8 | 9 |
id
name
10 |
11 | <- Back to View All 12 | -------------------------------------------------------------------------------- /src/sailor/blank-app/themes/default/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /test/dev-app/views/category/index.lp: -------------------------------------------------------------------------------- 1 | 6 |

View all

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
idname
19 |
20 | Create new category 21 | -------------------------------------------------------------------------------- /test/dev-app/views/test/client_module.lp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 |

12 | Note: If you see a 'pub.lua.mymodule' not found error message you need to configure the LuaMapHandler directive - this is explained here. -------------------------------------------------------------------------------- /test/dev-app/models/category.lua: -------------------------------------------------------------------------------- 1 | -- Uncomment this to use validation rules 2 | -- local val = require "valua" 3 | local M = {} 4 | 5 | -- Attributes and their validation rules 6 | M.attributes = { 7 | -- { = } 8 | -- Ex. {id = val:new().integer()} 9 | { id = "safe" }, 10 | { name = "safe" }, 11 | } 12 | 13 | M.db = { 14 | key = 'id', 15 | table = 'category' 16 | } 17 | 18 | M.relations = { 19 | posts = {relation = "MANY_MANY", model = "post", table = "post_category", attributes = {"category_id","post_id"}} 20 | } 21 | 22 | return M 23 | -------------------------------------------------------------------------------- /test/dev-app/views/test/runat_client.lp: -------------------------------------------------------------------------------- 1 | ') 4 | js.document:write('Your User-Agent is: '..js.navigator.userAgent..'
') 5 | if js.navigator.cookieEnabled == true then 6 | js.document:write('Cookie support is enabled.
') 7 | else 8 | js.document:write('Cookie support is disabled.
') 9 | end 10 | 11 | function say_bye() 12 | js.document:write('Bye! :)') 13 | end 14 | 15 | ?> 16 | 17 | 20 | 21 |

22 | The text above was generated using client-side Lua. -------------------------------------------------------------------------------- /test/dev-app/views/main/index.lp: -------------------------------------------------------------------------------- 1 | 4 |

This is the default view file, you will find it under /views/main/index.lp. 5 | It is rendered by a controller, you will find it under /controllers/main.lua. This layout is provided by Twitter Bootstrap, an open source front-end framework. You can use this layout as is, modify it, or get new layouts and place them under /layouts/. Further documentation of Sailor MVC can be found here.

-------------------------------------------------------------------------------- /src/sailor/blank-app/views/main/index.lp: -------------------------------------------------------------------------------- 1 | 4 |

This is the default view file, you will find it under /views/main/index.lp. 5 | It is rendered by a controller, you will find it under /controllers/main.lua. This layout is provided by Twitter Bootstrap, an open source front-end framework. You can use this layout as is, modify it, or get new layouts and place them under /layouts/. Further documentation of Sailor MVC can be found here.

-------------------------------------------------------------------------------- /test/dev-app/models/post.lua: -------------------------------------------------------------------------------- 1 | local post = {} 2 | 3 | -- Attributes and their validation rules 4 | post.attributes = { 5 | -- = { = {}, = {}...} 6 | {id = "safe"}, 7 | {author_id = "safe" }, 8 | {body = "safe"}, 9 | } 10 | 11 | post.relations = { 12 | author = {relation = "BELONGS_TO", model = "user", attribute = "author_id"}, 13 | categories = {relation = "MANY_MANY", model = "category", table = "post_category", attributes = {"post_id","category_id"}}, 14 | comments = {relation = "HAS_MANY", model = "comment", attribute = "post_id"} 15 | } 16 | 17 | post.db = { 18 | key = 'id', 19 | table = 'post' 20 | } 21 | 22 | return post 23 | -------------------------------------------------------------------------------- /test/dev-app/views/test/form.lp: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |

5 | 6 |

7 | 8 |

9 | 10 |

11 | 12 |

13 | 14 |

15 | 16 |

17 | 18 |
19 | -------------------------------------------------------------------------------- /test/dev-app/views/category/update.lp: -------------------------------------------------------------------------------- 1 | 2 |

Update category

3 | 4 | There was an error while saving. 5 | 6 |
7 |
8 | 9 | 10 |
11 |
12 | 13 | 14 |
15 | 16 |
17 |
18 | <- Back to View All 19 | -------------------------------------------------------------------------------- /test/dev-app/views/error/inspect.lp: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 |
25 | <%= (string.gsub(page:tostring(v),"
","",1)) %> 26 |
30 | -------------------------------------------------------------------------------- /src/sailor/blank-app/views/error/inspect.lp: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 |
25 | <%= (string.gsub(page:tostring(v),"
","",1)) %> 26 |
30 | -------------------------------------------------------------------------------- /test/dev-app/tests/helper.lua: -------------------------------------------------------------------------------- 1 | -- A helper file contains functions to be shared among all tests 2 | -- Needs to be required 3 | 4 | local M = {} 5 | 6 | function M.tostring (val, indent, sep, ln, inspect) 7 | indent = indent or 0 8 | inspect = inspect or '' 9 | sep = sep or ' ' 10 | ln = ln or "\n" 11 | 12 | if type(val) ~= "table" then 13 | inspect = inspect .. tostring(val) 14 | else 15 | for k, v in pairs(val) do 16 | if(k ~= "__newindex") then 17 | local formatting = ln..string.rep(sep, indent) .. k .. ": " 18 | inspect = inspect.. formatting 19 | inspect = M.tostring(v, indent+8, sep, ln, inspect) 20 | end 21 | end 22 | end 23 | return inspect 24 | end 25 | 26 | return M -------------------------------------------------------------------------------- /src/web_utils/lp_ex.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- lp_ex.lua, v0.2 - Extension to lua pages, bridge with lua at client 3 | -- This file is a part of Sailor project 4 | -- Copyright (c) 2014 Etiene Dalcol 5 | -- License: MIT 6 | -- http://sailorproject.org 7 | -------------------------------------------------------------------------------- 8 | 9 | local lp = require"web_utils.lp" 10 | local lat = require"latclient" 11 | 12 | local M = { 13 | lat = lat 14 | } 15 | 16 | function M.translate (s,skip_lat) 17 | if not skip_lat then 18 | s = lat.translate(s) 19 | end 20 | return lp.translate(s) 21 | end 22 | 23 | function M.setoutfunc (f) 24 | lp.setoutfunc(f) 25 | end 26 | 27 | return M 28 | -------------------------------------------------------------------------------- /test/dev-app/views/test/realtime.lp: -------------------------------------------------------------------------------- 1 | hi! 2 |
3 | This is done with JavaScript: 4 |
5 | 0 6 |
7 | 8 | 16 | 17 | 18 | 19 | hi! 20 | <br/> 21 | This is done with JavaScript: 22 | <div id="time-js"> 23 | 0 24 | </div> 25 | 26 | <script> 27 | var start = (new Date()).getTime(); 28 | function timer(){ 29 | var t = document.getElementById("time-js"); 30 | t.innerText = Math.floor(((new Date()).getTime()-start)/1000); 31 | } 32 | window.setInterval(timer,1000); 33 | </script> 34 | 35 | -------------------------------------------------------------------------------- /test/dev-app/views/category/create.lp: -------------------------------------------------------------------------------- 1 | 2 |

Create category

3 | 4 | There was an error while saving. 5 | 6 |
7 |
8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 |
17 | 18 |
19 |
20 | <- Back to View All 21 | -------------------------------------------------------------------------------- /docs/manual_sailor_functions.md: -------------------------------------------------------------------------------- 1 | ###Reference Manual 2 | 3 | ###Sailor built-in functions 4 | Can be used anywhere in a Sailor web application. 5 | 6 | ####sailor.make_url( route [*, params*] ) 7 | Creates a url for an internal app route depending on friendly url configuration. 8 | 9 | * route: string, controller/action or controller. 10 | 11 | * params: [optional] table, vars and values to be sent via GET. 12 | 13 | Example 1: `sailor.make_url( 'post/view', {id = 5, title = 'Manual'} )` 14 | 15 | ####sailor.model( model_name ) 16 | Creates a sailor model that can be instantiated in objects with :new(). 17 | 18 | * model_name: string, model's name. There must be a .lua file with the model's name under /model. 19 | 20 | Example 1: 21 | 22 | -- In this case there must be a file inside /model called user.lua 23 | local User = sailor.model( 'user' ) 24 | local u = User:new( ) -------------------------------------------------------------------------------- /src/sailor/db.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- db.lua, v0.3.2: DB module for connecting and querying through different DB modules 3 | -- This file is a part of Sailor project 4 | -- Copyright (c) 2014 Etiene Dalcol 5 | -- License: MIT 6 | -- http://sailorproject.org 7 | -------------------------------------------------------------------------------- 8 | 9 | local main_conf = require "conf.conf" 10 | local conf = main_conf.db[main_conf.sailor.environment] 11 | local remy = require "remy" 12 | 13 | local function detect() 14 | if conf == nil then error("DB environment not found.") return end 15 | local m 16 | if remy.detect() == remy.MODE_NGINX and conf.driver == "mysql" then 17 | m = require "sailor.db.resty_mysql" 18 | else 19 | m = require "sailor.db.luasql_common" 20 | end 21 | m.detect = detect 22 | return m 23 | end 24 | 25 | return detect() 26 | -------------------------------------------------------------------------------- /test/dev-app/index-magnet.lua: -------------------------------------------------------------------------------- 1 | -- Alternative index and launcher for LightTPD's mod_magnet 2 | if lighty ~= nil then 3 | -- Note: package.loaded.lighty ~= nil will not work because lighty is a 4 | -- local variable and not a package 5 | local docroot = lighty.env['physical.doc-root'] 6 | package.path = package.path..";"..docroot.."/../lib/lua/?.lua" 7 | package.cpath = package.cpath..";"..docroot.."/../lib/clibs/?.dll" 8 | package.path = package.path..";"..docroot.."/sailor/?.lua" 9 | 10 | -- FIXME: os.tmpname(), used by web_utils\utils.lua not 11 | -- working in LightTPD (affects Windows build only?) 12 | -- This breaks every script using "session" 13 | function os.tmpname() 14 | return 'tmp' 15 | end 16 | 17 | -- Makes lighty global so it can be accessed by Remy or controllers 18 | _G["lighty"] = lighty 19 | local sailor = require "sailor" 20 | sailor.launch() 21 | return sailor.r.status 22 | end 23 | 24 | -------------------------------------------------------------------------------- /src/sailor/blank-app/index-magnet.lua: -------------------------------------------------------------------------------- 1 | -- Alternative index and launcher for LightTPD's mod_magnet 2 | if lighty ~= nil then 3 | -- Note: package.loaded.lighty ~= nil will not work because lighty is a 4 | -- local variable and not a package 5 | local docroot = lighty.env['physical.doc-root'] 6 | package.path = package.path..";"..docroot.."/../lib/lua/?.lua" 7 | package.cpath = package.cpath..";"..docroot.."/../lib/clibs/?.dll" 8 | package.path = package.path..";"..docroot.."/sailor/?.lua" 9 | 10 | -- FIXME: os.tmpname(), used by web_utils\utils.lua not 11 | -- working in LightTPD (affects Windows build only?) 12 | -- This breaks every script using "session" 13 | function os.tmpname() 14 | return 'tmp' 15 | end 16 | 17 | -- Makes lighty global so it can be accessed by Remy or controllers 18 | _G["lighty"] = lighty 19 | local sailor = require "sailor" 20 | sailor.launch() 21 | return sailor.r.status 22 | end 23 | 24 | -------------------------------------------------------------------------------- /test/dev-app/sql/sqlite3.sql: -------------------------------------------------------------------------------- 1 | -- SQLite3 2 | drop table if exists users; 3 | create table users( 4 | 'id' integer primary key autoincrement, 5 | 'username' varchar(20), 6 | 'password' varchar(64) 7 | ); 8 | 9 | drop table if exists post; 10 | create table post( 11 | `id` INTEGER PRIMARY KEY AUTOINCREMENT, 12 | `body` TEXT, 13 | `author_id` INTEGER REFERENCES users (id) 14 | ); 15 | 16 | drop table if exists comment; 17 | create table comment( 18 | id integer primary key autoincrement, 19 | body text, 20 | author_id integer references users (id), 21 | post_id integer references post (id) 22 | ); 23 | 24 | drop table if exists category; 25 | create table category( 26 | id integer primary key autoincrement, 27 | name text 28 | ); 29 | 30 | drop table if exists post_category; 31 | create table post_category( 32 | post_id integer references post (id), 33 | category_id integer references category(id), 34 | primary key(post_id,category_id) 35 | ); 36 | -------------------------------------------------------------------------------- /test/dev-app/themes/default/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "vars": {}, 3 | "css": [ 4 | "print.less", 5 | "type.less", 6 | "code.less", 7 | "grid.less", 8 | "tables.less", 9 | "forms.less", 10 | "buttons.less", 11 | "glyphicons.less", 12 | "button-groups.less", 13 | "input-groups.less", 14 | "navs.less", 15 | "navbar.less", 16 | "breadcrumbs.less", 17 | "pagination.less", 18 | "pager.less", 19 | "labels.less", 20 | "badges.less", 21 | "jumbotron.less", 22 | "thumbnails.less", 23 | "alerts.less", 24 | "progress-bars.less", 25 | "media.less", 26 | "list-group.less", 27 | "panels.less", 28 | "wells.less", 29 | "close.less", 30 | "dropdowns.less", 31 | "tooltip.less", 32 | "popovers.less", 33 | "modals.less", 34 | "carousel.less", 35 | "utilities.less", 36 | "responsive-utilities.less", 37 | "component-animations.less" 38 | ], 39 | "js": [] 40 | } -------------------------------------------------------------------------------- /src/sailor/blank-app/themes/default/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "vars": {}, 3 | "css": [ 4 | "print.less", 5 | "type.less", 6 | "code.less", 7 | "grid.less", 8 | "tables.less", 9 | "forms.less", 10 | "buttons.less", 11 | "glyphicons.less", 12 | "button-groups.less", 13 | "input-groups.less", 14 | "navs.less", 15 | "navbar.less", 16 | "breadcrumbs.less", 17 | "pagination.less", 18 | "pager.less", 19 | "labels.less", 20 | "badges.less", 21 | "jumbotron.less", 22 | "thumbnails.less", 23 | "alerts.less", 24 | "progress-bars.less", 25 | "media.less", 26 | "list-group.less", 27 | "panels.less", 28 | "wells.less", 29 | "close.less", 30 | "dropdowns.less", 31 | "tooltip.less", 32 | "popovers.less", 33 | "modals.less", 34 | "carousel.less", 35 | "utilities.less", 36 | "responsive-utilities.less", 37 | "component-animations.less" 38 | ], 39 | "js": [] 40 | } -------------------------------------------------------------------------------- /test/dev-app/models/user.lua: -------------------------------------------------------------------------------- 1 | local user = {} 2 | local val = require "valua" 3 | local access = require "sailor.access" 4 | -- Attributes and their validation rules 5 | user.attributes = { 6 | --{ = < "safe" or validation function, requires valua >} 7 | {id = "safe"}, 8 | {username = val:new().not_empty() }, 9 | {password = val:new().not_empty().len(6,10) } 10 | } 11 | 12 | user.db = { 13 | key = 'id', 14 | table = 'users' 15 | } 16 | 17 | user.relations = { 18 | posts = {relation = "HAS_MANY", model = "post", attribute = "author_id"}, 19 | comments = {relation = "HAS_MANY", model = "comment", attribute = "author_id"} 20 | } 21 | 22 | -- Public Methods 23 | function user.test() return "test" end 24 | 25 | function user.authenticate(login,password,use_hashing) 26 | if use_hashing == nil then use_hashing = true end 27 | access.settings({model = 'user', hashing = use_hashing}) 28 | return access.login(login,password) 29 | end 30 | 31 | function user.logout() 32 | return access.logout() 33 | end 34 | 35 | return user 36 | -------------------------------------------------------------------------------- /test/dev-app/views/test/index.lp: -------------------------------------------------------------------------------- 1 | 2 | 3 | SAILOR! 4 | 5 | 6 |
7 | This comes from a .lp file. "LP" stands for Lua Page. 8 |
9 | You can use lua scripts here inside tags, everything else looks like regular HTML. 10 |
11 | More info about it here. 12 |
13 | 14 | 23 |
24 | 25 | 26 | 28 | 29 | 30 | it will never print this 31 | 32 |
33 | 34 | this is in a loop 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /test/dev-app/themes/default/css/sticky-footer-navbar.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer styles 2 | -------------------------------------------------- */ 3 | 4 | html, 5 | body { 6 | height: 100%; 7 | /* The html and body elements cannot have any padding or margin. */ 8 | } 9 | 10 | /* Wrapper for page content to push down footer */ 11 | #wrap { 12 | min-height: 100%; 13 | height: auto; 14 | /* Negative indent footer by its height */ 15 | margin: 0 auto -60px; 16 | /* Pad bottom by footer height */ 17 | padding: 0 0 60px; 18 | } 19 | 20 | /* Set the fixed height of the footer here */ 21 | #footer { 22 | height: 60px; 23 | background-color: #f5f5f5; 24 | } 25 | 26 | 27 | /* Custom page CSS 28 | -------------------------------------------------- */ 29 | /* Not required for template or sticky footer method. */ 30 | 31 | #wrap > .container { 32 | padding: 60px 15px 0; 33 | } 34 | .container .text-muted { 35 | margin: 20px 0; 36 | } 37 | 38 | #footer > .container { 39 | padding-left: 15px; 40 | padding-right: 15px; 41 | } 42 | 43 | code { 44 | font-size: 80%; 45 | } 46 | -------------------------------------------------------------------------------- /src/sailor/blank-app/themes/default/css/sticky-footer-navbar.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer styles 2 | -------------------------------------------------- */ 3 | 4 | html, 5 | body { 6 | height: 100%; 7 | /* The html and body elements cannot have any padding or margin. */ 8 | } 9 | 10 | /* Wrapper for page content to push down footer */ 11 | #wrap { 12 | min-height: 100%; 13 | height: auto; 14 | /* Negative indent footer by its height */ 15 | margin: 0 auto -60px; 16 | /* Pad bottom by footer height */ 17 | padding: 0 0 60px; 18 | } 19 | 20 | /* Set the fixed height of the footer here */ 21 | #footer { 22 | height: 60px; 23 | background-color: #f5f5f5; 24 | } 25 | 26 | 27 | /* Custom page CSS 28 | -------------------------------------------------- */ 29 | /* Not required for template or sticky footer method. */ 30 | 31 | #wrap > .container { 32 | padding: 60px 15px 0; 33 | } 34 | .container .text-muted { 35 | margin: 20px 0; 36 | } 37 | 38 | #footer > .container { 39 | padding-left: 15px; 40 | padding-right: 15px; 41 | } 42 | 43 | code { 44 | font-size: 80%; 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013-2014 Etiene Dalcol 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /src/sailor/cookie.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- cookie.lua, v1.1: lib for cookies 3 | -- This file is a part of Sailor project 4 | -- Copyright (c) 2014 Etiene Dalcol 5 | -- License: MIT 6 | -- http://sailorproject.org 7 | -------------------------------------------------------------------------------- 8 | 9 | local cookie = {} 10 | local remy = require "remy" 11 | 12 | function cookie.set(r, key, value, path) 13 | path = path or "/" 14 | if remy.detect(r) == remy.MODE_CGILUA then 15 | local ck = require "cgilua.cookies" 16 | return ck.set(key,value,{path=path}) 17 | end 18 | r.headers_out['Set-Cookie'] = ("%s=%s;Path=%s;"):format(key, value, path) 19 | end 20 | 21 | function cookie.get(r, key) 22 | local remy_mode = remy.detect(r) 23 | if remy_mode == remy.MODE_CGILUA then 24 | local ck = require "cgilua.cookies" 25 | return ck.get(key) 26 | elseif remy_mode == remy.MODE_LWAN then 27 | return r.native_request:cookie(key) 28 | end 29 | return (r.headers_in['Cookie'] or ""):match(key .. "=([^;]+)") or "" 30 | end 31 | 32 | return cookie 33 | -------------------------------------------------------------------------------- /test/dev-app/sql/pgsql.sql: -------------------------------------------------------------------------------- 1 | -- PostgreSQL 2 | drop table if exists users cascade; 3 | drop table if exists post cascade; 4 | drop table if exists comment cascade; 5 | drop table if exists post_category cascade; 6 | 7 | drop table if exists users cascade; 8 | create table users( 9 | id serial primary key, 10 | username varchar(20), 11 | password varchar(64) 12 | ); 13 | 14 | drop table if exists post cascade; 15 | create table post( 16 | id serial primary key, 17 | body text, 18 | author_id int references users ON DELETE set null 19 | ); 20 | 21 | drop table if exists comment cascade; 22 | create table comment( 23 | id serial primary key, 24 | body text, 25 | author_id int references users ON DELETE set null, 26 | post_id int references post ON DELETE CASCADE 27 | ); 28 | 29 | drop table if exists category cascade; 30 | create table category( 31 | id serial primary key, 32 | name text 33 | ); 34 | 35 | drop table if exists post_category cascade; 36 | create table post_category( 37 | post_id int references post ON DELETE CASCADE, 38 | category_id int references category ON DELETE CASCADE, 39 | primary key(post_id,category_id) 40 | ); -------------------------------------------------------------------------------- /src/web_utils/utils.lua: -------------------------------------------------------------------------------- 1 | local os_tmpname, gsub = os.tmpname, string.gsub 2 | 3 | local M = { 4 | } 5 | 6 | -- Default function for temporary names 7 | -- @returns a temporay name using os.tmpname 8 | function M.tmpname () 9 | local tempname = os_tmpname() 10 | -- Lua os.tmpname returns a full path in Unix, but not in Windows 11 | -- so we strip the eventual prefix 12 | tempname = gsub(tempname, "(/tmp/)", "") 13 | return tempname 14 | end 15 | 16 | -- deep copies a table 17 | function M.deepcopy(orig) 18 | local orig_type = type(orig) 19 | local copy 20 | if orig_type == 'table' then 21 | copy = {} 22 | for orig_key, orig_value in next, orig, nil do 23 | copy[M.deepcopy(orig_key)] = M.deepcopy(orig_value) 24 | end 25 | setmetatable(copy, M.deepcopy(getmetatable(orig))) 26 | else -- number, string, boolean, etc 27 | copy = orig 28 | end 29 | return copy 30 | end 31 | 32 | -- performs string split 33 | function M.split(str,sep) 34 | sep = sep or ":" 35 | local fields = {} 36 | local pattern = string.format("([^%s]+)", sep) 37 | str:gsub(pattern, function(c) fields[#fields+1] = c end) 38 | return fields 39 | end 40 | 41 | return M 42 | -------------------------------------------------------------------------------- /test/dev-app/views/test/starlight.lp: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

hey

5 |
6 |
7 | 8 | 20 | 21 | 43 | 44 | 45 | 51 | -------------------------------------------------------------------------------- /test/dev-app/tests/unit/openresty.lua: -------------------------------------------------------------------------------- 1 | --[[local remy = require "remy" 2 | --remy.forced_mode = remy.MODE_NGINX 3 | --local mysql = require "resty.mysql" 4 | --local rest = 5 | local helper = require "tests.helper" 6 | --local remy = require 'remy' 7 | fakengx = require 'fake.fakengx' 8 | ngx = fakengx.new() 9 | ngx.config = {} 10 | ngx.config.ngx_lua_version = 10000 11 | local old_tcp = ngx.socket.tcp 12 | ngx.socket.tcp = function() 13 | local sock,err = old_tcp() 14 | sock.getreusedtimes = function() 15 | return 0 16 | end 17 | return sock,err 18 | end 19 | 20 | 21 | local db 22 | describe("Testing #openresty", function() 23 | 24 | setup(function() 25 | remy.force_mode(remy.MODE_NGINX) 26 | 27 | db = require "sailor.db" 28 | db = db.detect() 29 | end) 30 | 31 | it("should do nothing", function() 32 | --print(tstring(remy,0,' ','\n')) 33 | print("mode:",remy.detect()) 34 | assert.is_equal(1,1) 35 | 36 | end) 37 | 38 | it("should find 1 result",function() 39 | db.connect() 40 | local res = db.query("select * from post where id = 1") 41 | print(helper.tostring(res)) 42 | db.close() 43 | assert.is_equal(1,#res) 44 | end) 45 | 46 | teardown(function() 47 | 48 | end) 49 | --remy.forced_mode = nil 50 | --ngx = nil 51 | end) 52 | ]] -------------------------------------------------------------------------------- /src/sailor/blank-app/conf/conf.lua: -------------------------------------------------------------------------------- 1 | local conf = { 2 | sailor = { 3 | app_name = 'Sailor! A Lua MVC Framework', 4 | default_static = nil, -- If defined, default page will be a rendered lp as defined. 5 | -- Example: 'maintenance' will render /views/maintenance.lp 6 | default_controller = 'main', 7 | default_action = 'index', 8 | theme = 'default', 9 | layout = 'main', 10 | route_parameter = 'r', 11 | default_error404 = 'error/404', 12 | enable_autogen = false, -- default is false, should be true only in development environment 13 | friendly_urls = false, 14 | max_upload = 1024 * 1024, 15 | environment = "development", -- this will use db configuration named development 16 | hide_stack_trace = false -- false recommended for development, true recommended for production 17 | }, 18 | 19 | db = { 20 | development = { -- current environment 21 | driver = 'mysql', 22 | host = '', 23 | user = '', 24 | pass = '', 25 | dbname = '' 26 | } 27 | }, 28 | 29 | smtp = { 30 | server = '', 31 | user = '', 32 | pass = '', 33 | from = '' 34 | }, 35 | 36 | lua_at_client = { 37 | vm = "starlight", -- starlight is default. Other options are moonshine, lua51js and luavmjs. They need to be downloaded. 38 | }, 39 | 40 | debug = { 41 | inspect = false 42 | } 43 | } 44 | 45 | return conf 46 | -------------------------------------------------------------------------------- /src/sailor/blank-app/tests/bootstrap_resty.lua: -------------------------------------------------------------------------------- 1 | sailor = require "sailor" 2 | local t = require "sailor.test" 3 | local busted = require 'busted.runner' 4 | local lfs = require 'lfs' 5 | 6 | -- load fixtures 7 | -- t.load_fixtures() 8 | 9 | -- prepare busted 10 | busted() 11 | 12 | local busted_lib = { 13 | describe = describe, 14 | it = it, 15 | setup = setup, 16 | assert = assert, 17 | teardown = teardown, 18 | before_each = before_each, 19 | finally = finally, 20 | pending = pending, 21 | spy = spy, 22 | stub = stub, 23 | mock = mock, 24 | } 25 | 26 | local test_dirs = { 27 | "tests/unit", 28 | "tests/functional", 29 | } 30 | 31 | -- Get busted vars to pass them ahead when loading each test file 32 | 33 | local env = {} 34 | for k,v in pairs(_G) do env[k] = v end 35 | for name,f in pairs(busted_lib) do 36 | env[name] = f 37 | end 38 | 39 | -- Looping through test dirs and loading them with environment with busted vars 40 | 41 | for _,dir in pairs(test_dirs) do 42 | dir = sailor.path..'/'..dir 43 | for file in lfs.dir(dir) do 44 | if file ~= '.' and file ~= '..' then 45 | local test 46 | if _VERSION == "Lua 5.1" then 47 | test = assert(loadfile(dir..'/'..file)) 48 | setfenv(test,env) 49 | else 50 | test = assert(loadfile(dir..'/'..file, env)) 51 | end 52 | test() 53 | end 54 | end 55 | end 56 | 57 | -------------------------------------------------------------------------------- /test/dev-app/conf/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | error_log stderr notice; 3 | 4 | events { 5 | worker_connections 16384; 6 | } 7 | http { 8 | server { 9 | listen 8080; 10 | include mime.types; 11 | default_type application/octet-stream; 12 | root .; 13 | sendfile on; 14 | access_log off; 15 | location / { 16 | lua_need_request_body on; 17 | lua_code_cache on; 18 | content_by_lua_file index.lua; 19 | index index.lua index.lp; 20 | } 21 | 22 | location ~ ^/[^\.]+$ { 23 | lua_need_request_body on; 24 | lua_code_cache off; 25 | content_by_lua_file index.lua; 26 | index index.lp index.lua; 27 | rewrite_by_lua_block { 28 | local conf = require "conf.conf" 29 | if conf.sailor.friendly_urls then 30 | local args = ngx.req.get_uri_args() 31 | args[conf.sailor.route_parameter] = ngx.var.uri:sub(2) 32 | 33 | ngx.req.set_uri_args(args) 34 | ngx.req.set_uri("/index.lua") 35 | end 36 | } 37 | } 38 | 39 | location ~ \.(css|eot|js|json|gif|jpg|png|svg|ttf|woff|map)$ { 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/sailor/blank-app/conf/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | error_log stderr notice; 3 | 4 | events { 5 | worker_connections 16384; 6 | } 7 | http { 8 | server { 9 | listen 8080; 10 | include mime.types; 11 | default_type application/octet-stream; 12 | root .; 13 | sendfile on; 14 | access_log off; 15 | location / { 16 | lua_need_request_body on; 17 | lua_code_cache on; 18 | content_by_lua_file index.lua; 19 | index index.lua index.lp; 20 | } 21 | 22 | location ~ ^/[^\.]+$ { 23 | lua_need_request_body on; 24 | lua_code_cache on; 25 | content_by_lua_file index.lua; 26 | index index.lp index.lua; 27 | rewrite_by_lua_block { 28 | local conf = require "conf.conf" 29 | if conf.sailor.friendly_urls then 30 | local args = ngx.req.get_uri_args() 31 | args[conf.sailor.route_parameter] = ngx.var.uri:sub(2) 32 | 33 | ngx.req.set_uri_args(args) 34 | ngx.req.set_uri("/index.lua") 35 | end 36 | } 37 | } 38 | 39 | location ~ \.(css|eot|js|json|gif|jpg|png|svg|ttf|woff|map)$ { 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /test/dev-app/tests/bootstrap_resty.lua: -------------------------------------------------------------------------------- 1 | sailor = require "sailor" 2 | local t = require "sailor.test" 3 | local busted = require 'busted.runner' 4 | local lfs = require 'lfs' 5 | 6 | -- load fixtures 7 | 8 | t.load_fixtures('user') 9 | t.load_fixtures('post') 10 | t.load_fixtures('category') 11 | 12 | -- prepare busted 13 | busted() 14 | 15 | local busted_lib = { 16 | describe = describe, 17 | it = it, 18 | setup = setup, 19 | assert = assert, 20 | teardown = teardown, 21 | before_each = before_each, 22 | finally = finally, 23 | pending = pending, 24 | spy = spy, 25 | stub = stub, 26 | mock = mock, 27 | } 28 | 29 | local test_dirs = { 30 | "tests/unit", 31 | "tests/functional", 32 | } 33 | 34 | -- Get busted vars to pass them ahead when loading each test file 35 | 36 | local env = {} 37 | for k,v in pairs(_G) do env[k] = v end 38 | for name,f in pairs(busted_lib) do 39 | env[name] = f 40 | end 41 | 42 | -- Looping through test dirs and loading them with environment with busted vars 43 | 44 | for _,dir in pairs(test_dirs) do 45 | dir = sailor.path..'/'..dir 46 | for file in lfs.dir(dir) do 47 | if file ~= '.' and file ~= '..' then 48 | local test 49 | if _VERSION == "Lua 5.1" then 50 | test = assert(loadfile(dir..'/'..file)) 51 | setfenv(test,env) 52 | else 53 | test = assert(loadfile(dir..'/'..file, env)) 54 | end 55 | test() 56 | end 57 | end 58 | end 59 | 60 | -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | 2 | codes = true 3 | 4 | std = 'max' 5 | files['src/sailor/blank-app/tests'].std = 'max+busted' 6 | files['test/dev-app/tests'].std = 'max+busted' 7 | 8 | ignore = { '11./sailor', -- global variable 9 | '113/lighty', -- global variable 10 | '211', -- unused variable 11 | '6..' } -- formatting 12 | 13 | files['src/sailor.lua'].ignore = { '111/handle', -- setting non-standard global variable handle 14 | '113/apr_table', -- accessing undefined variable apr_table 15 | '113/apache2', -- accessing undefined variable apache2 16 | '231/GETMULTI', -- variable GETMULTI is never accessed 17 | '321/res', -- accessing uninitialized variable res 18 | '431/autogen' } -- shadowing upvalue autogen 19 | files['src/sailor/blank-app/index-magnet.lua'].ignore = { '122' } -- setting read-only field tmpname of global os 20 | files['src/sailor/model.lua'].ignore = { '431/model' } -- shadowing upvalue model 21 | files['src/sailor/page.lua'].ignore = { '212/self' } -- unused argument self 22 | files['src/sailor/db/luasql_common.lua'].ignore = { '212/key' } -- unused argument key 23 | files['test/dev-app/index-magnet.lua'].ignore = { '122' } -- setting read-only field tmpname of global os 24 | files['test/dev-app/tests/unit/model.lua'].ignore = { '431/u' } -- shadowing upvalue u 25 | 26 | -------------------------------------------------------------------------------- /test/dev-app/controllers/category.lua: -------------------------------------------------------------------------------- 1 | local sailor = require "sailor" 2 | 3 | local M = {} 4 | 5 | function M.index(page) 6 | local categorys = sailor.model("category"):find_all() 7 | page:render('index',{categorys = categorys}) 8 | end 9 | 10 | function M.create(page) 11 | local category = sailor.model("category"):new() 12 | local saved 13 | if next(page.POST) then 14 | category:get_post(page.POST) 15 | saved = category:save() 16 | if saved then 17 | page:redirect('category/index') 18 | end 19 | end 20 | page:render('create',{category = category, saved = saved}) 21 | end 22 | 23 | function M.update(page) 24 | local category = sailor.model("category"):find_by_id(page.GET.id) 25 | if not category then 26 | return 404 27 | end 28 | local saved 29 | if next(page.POST) then 30 | category:get_post(page.POST) 31 | saved = category:update() 32 | if saved then 33 | page:redirect('category/index') 34 | end 35 | end 36 | page:render('update',{category = category, saved = saved}) 37 | end 38 | 39 | function M.view(page) 40 | local category = sailor.model("category"):find_by_id(page.GET.id) 41 | if not category then 42 | return 404 43 | end 44 | page:render('view',{category = category}) 45 | end 46 | 47 | function M.delete(page) 48 | local category = sailor.model("category"):find_by_id(page.GET.id) 49 | if not category then 50 | return 404 51 | end 52 | 53 | if category:delete() then 54 | page:redirect('category/index') 55 | end 56 | end 57 | 58 | return M 59 | -------------------------------------------------------------------------------- /test/dev-app/conf/conf.lua: -------------------------------------------------------------------------------- 1 | 2 | local db_driver, db_user, db_pass, db_name 3 | 4 | if os.getenv("TRAVIS") == "true" then 5 | db_driver = assert(os.getenv("DB_DRIVER")) 6 | db_user = assert(os.getenv("DB_USER")) 7 | db_pass = os.getenv("DB_PASS") 8 | db_name = os.getenv("DB_NAME") 9 | end 10 | 11 | local conf = { 12 | sailor = { 13 | app_name = 'Sailor! A Lua MVC Framework', 14 | default_static = nil, -- If defined, default page will be a rendered lp as defined. 15 | -- Example: 'maintenance' will render /views/maintenance.lp 16 | default_controller = 'main', 17 | default_action = 'index', 18 | theme = 'default', 19 | layout = 'main', 20 | route_parameter = 'r', 21 | default_error404 = 'error/404', 22 | enable_autogen = true, -- default is false, should be true only in development environment 23 | friendly_urls = false, 24 | max_upload = 1024 * 1024, 25 | environment = "test", -- this will use db configuration named test 26 | }, 27 | 28 | db = { 29 | test = { -- current environment 30 | driver = db_driver or 'mysql', 31 | host = 'localhost', 32 | user = db_user or '', 33 | pass = db_pass or '', 34 | dbname = db_name or '' 35 | } 36 | }, 37 | 38 | smtp = { 39 | server = '', 40 | user = '', 41 | pass = '', 42 | from = '' 43 | }, 44 | 45 | lua_at_client = { 46 | vm = "starlight", -- starlight is default. Other options are: "lua51js", "luavmjs" and "moonshine" 47 | }, 48 | 49 | debug = { 50 | inspect = true 51 | } 52 | } 53 | 54 | return conf 55 | -------------------------------------------------------------------------------- /test/dev-app/tests/unit/form.lua: -------------------------------------------------------------------------------- 1 | local form = require "sailor.form" 2 | local model = require "sailor.model" 3 | local User = model("user") 4 | 5 | describe("Testing form generator", function() 6 | local u = User:new() 7 | 8 | it("should not create fields that don't exist", function () 9 | assert.has_error( 10 | function() form.blah(u,'password') end 11 | ) 12 | end) 13 | 14 | it("should create a generic field accordingly", function() 15 | local html = form.text(u,'password') 16 | assert.equal('',html) 17 | end) 18 | 19 | it("should create textarea field accordingly", function() 20 | local html = form.textarea(u,'password') 21 | assert.equal(html,'') 22 | end) 23 | 24 | it("should create a dropdown list accordingly", function() 25 | local html = form.dropdown(u,'password',{'a','b'},'Select...') 26 | assert.equal(html,'') 27 | end) 28 | 29 | it("should create a radio list accordingly", function() 30 | local html = form.radio_list(u,'password',{'a','b'},1,'vertical') 31 | assert.equal(html,' a
b') 32 | end) 33 | 34 | it("should create a checkbox accordingly", function() 35 | local html = form.checkbox(u,'password',nil, true) 36 | assert.equal(html,' password') 37 | end) 38 | 39 | end) 40 | -------------------------------------------------------------------------------- /src/sailor/session.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- session.lua, v0.2.1: cgilua session abstraction 3 | -- This file is a part of Sailor project 4 | -- Copyright (c) 2014 Etiene Dalcol 5 | -- License: MIT 6 | -- http://sailorproject.org 7 | -------------------------------------------------------------------------------- 8 | 9 | local sailor = require "sailor" 10 | local utils = require "web_utils.utils" 11 | local session = require "web_utils.session" 12 | local cookie = require "sailor.cookie" 13 | 14 | local ID_NAME = "SAILORSESSID" 15 | 16 | session.id = nil 17 | session.setsessiondir (sailor.path..'/runtime/tmp') 18 | 19 | function session.open (r) 20 | if session.id then 21 | return session.id 22 | end 23 | 24 | local id = cookie.get(r,ID_NAME ) 25 | if not id then 26 | session.new(r) 27 | else 28 | session.data = session.load(id) 29 | if session.data then 30 | session.id = id 31 | else 32 | session.new(r) 33 | end 34 | end 35 | 36 | session.cleanup() 37 | return id 38 | end 39 | 40 | function session.destroy (r) 41 | local id = session.id or cookie.get(r,ID_NAME ) 42 | if id then 43 | session.data = {} 44 | session.delete (id) 45 | end 46 | session.id = nil 47 | return true 48 | end 49 | 50 | local new = session.new 51 | function session.new(r) 52 | session.id = new() 53 | session.data = {} 54 | cookie.set(r,ID_NAME,session.id) 55 | end 56 | 57 | local save = session.save 58 | function session.save(data) 59 | save(session.id,data) 60 | session.data = data 61 | return true 62 | end 63 | 64 | function session.is_active() 65 | return session.id ~= nil 66 | end 67 | 68 | 69 | return session 70 | -------------------------------------------------------------------------------- /test/dev-app/views/test/luavmjs.lp: -------------------------------------------------------------------------------- 1 | 2 |
Hello
3 |
Hello
4 | 17 | max then return false,"should have "..min.."-"..max.." characters" end 48 | return true 49 | end 50 | local test = {} 51 | table.insert(test,len) 52 | for i=1,10000 do 53 | for k, v in ipairs(test) do 54 | v('oi',3,5) 55 | end 56 | -- Something is up with ipairs? 57 | end 58 | t() 59 | 60 | 61 | t = timer("Function 8 Insert on table") 62 | test = {} 63 | for i=1,1000 do 64 | test[i] = i 65 | end 66 | t() 67 | 68 | t = timer("Function 9 Read from table using ipairs") 69 | test = {} 70 | for i=1,10000 do 71 | for k, v in ipairs(test) do 72 | test[k] = k + 1 73 | end 74 | end 75 | t() 76 | 77 | t = timer("Function 10 Read from table using pairs") 78 | test = {} 79 | for i=1,10000 do 80 | for k, v in ipairs(test) do 81 | test[k] = k + 1 82 | end 83 | end 84 | t() 85 | 86 | ?> -------------------------------------------------------------------------------- /test/dev-app/views/test/lua51js.lp: -------------------------------------------------------------------------------- 1 | 2 |
Hello
3 |
Hello
4 | 17 | max then return false,"should have "..min.."-"..max.." characters" end 48 | return true 49 | end 50 | local test = {} 51 | table.insert(test,len) 52 | for i=1,10000 do 53 | for k, v in ipairs(test) do 54 | v('oi',3,5) 55 | end 56 | -- Something is up with ipairs? 57 | end 58 | t() 59 | 60 | 61 | t = timer("Function 8 Insert on table") 62 | test = {} 63 | for i=1,1000 do 64 | test[i] = i 65 | end 66 | t() 67 | 68 | t = timer("Function 9 Read from table using ipairs") 69 | test = {} 70 | for i=1,10000 do 71 | for k, v in ipairs(test) do 72 | test[k] = k + 1 73 | end 74 | end 75 | t() 76 | 77 | t = timer("Function 10 Read from table using pairs") 78 | test = {} 79 | for i=1,10000 do 80 | for k, v in ipairs(test) do 81 | test[k] = k + 1 82 | end 83 | end 84 | t() 85 | 86 | ?> -------------------------------------------------------------------------------- /test/dev-app/sql/mysql.sql: -------------------------------------------------------------------------------- 1 | -- MySQL 2 | drop table if exists users; 3 | create table users( 4 | id int primary key auto_increment, 5 | username varchar(20), 6 | password varchar(64) 7 | ); 8 | 9 | drop table if exists post; 10 | create table post( 11 | id int primary key auto_increment, 12 | body text, 13 | author_id int references users (id) 14 | ); 15 | 16 | drop table if exists comment; 17 | create table comment( 18 | id int primary key auto_increment, 19 | body text, 20 | author_id int references users (id), 21 | post_id int references post (id) 22 | ); 23 | 24 | drop table if exists category; 25 | create table category( 26 | id int primary key auto_increment, 27 | name text 28 | ); 29 | 30 | drop table if exists post_category; 31 | create table post_category( 32 | post_id int references post (id), 33 | category_id int references category(id), 34 | primary key(post_id,category_id) 35 | ); 36 | 37 | insert into users values (1,'etiene','geronimo'); 38 | insert into users values (2,'pedro','fantastic'); 39 | 40 | insert into post values (1,'This is a post',1); 41 | insert into post values (2,'This is another post',2); 42 | insert into post values (3,'This is yet anoter post',1); 43 | 44 | insert into category values (1,'Politics'); 45 | insert into category values (2,'Internet'); 46 | insert into category values (3,'Programming'); 47 | insert into category values (4,'Personal'); 48 | insert into category values (5,'Other'); 49 | 50 | insert into post_category values (1,1); 51 | insert into post_category values (1,2); 52 | insert into post_category values (1,3); 53 | insert into post_category values (2,5); 54 | insert into post_category values (2,4); 55 | insert into post_category values (3,3); 56 | insert into post_category values (3,4); 57 | 58 | insert into comment values(1,"This is a comment",2,1); 59 | insert into comment values(2,"This is another comment",2,1); 60 | insert into comment values(3,"This is yet another comment",1,2); 61 | -------------------------------------------------------------------------------- /docs/manual_access_module.md: -------------------------------------------------------------------------------- 1 | ##Reference Manual 2 | ###The access module 3 | The access module is useful if you want to have a login system and pages that are not visible to guests. It needs to be required from either the controller or view: `local access = require "sailor.access"`. If you want to deactive the default login and password, you must setup a model to act as a User model. 4 | 5 | This is the default configuration of the access module 6 | 7 | local settings = { 8 | default_login = 'admin', -- Default login details 9 | default_password = 'demo', 10 | grant_time = 604800, -- 1 week 11 | model = nil, -- Setting this field will deactivate default login details and activate below fields 12 | login_attributes = {'username'},-- Allows multiple options, for example, username or email. The one used in the hash of the password should come first. 13 | password_attribute = 'password', 14 | salt_attribute = 'salt', 15 | hashing = true -- setting to false will deactivate hashing passwords 16 | } 17 | 18 | 19 | 20 | ####access.is_guest() 21 | Verifies if there is session data and returns a boolean. 22 | 23 | Example: ` if access.is_guest( ) return 404 end ` 24 | 25 | 26 | ####access.login( username, password ) 27 | Tries to login with the given username and password (raw). If using a User model, it will encrypt the given password string before making the comparison. Returns a boolean and an error string. 28 | 29 | * username, string. 30 | * password, string. 31 | 32 | Example 1: `local ok, err = access.login('admin','notdemo') -- false, Invalid username or password.` 33 | 34 | Example 2: `local ok, err = access.login('admin','demo') -- true, nil` 35 | 36 | ####access.logout() 37 | Logs user out and erases session. 38 | 39 | Example: `access.logout()` 40 | 41 | ####access.settings(s) 42 | Changes settings of the access module. 43 | 44 | * s: table with new settings. 45 | 46 | Example: ` access.settings{ default_login = 'IamGod', default_password = 'StairwayToHeaven' } ` 47 | 48 | -------------------------------------------------------------------------------- /test/dev-app/tests/functional/autogen.lua: -------------------------------------------------------------------------------- 1 | local test = require "sailor.test" 2 | local lfs = require "lfs" 3 | local conf = require "conf.conf" 4 | local model = require "sailor.model" 5 | 6 | 7 | local db = require "sailor.db" 8 | local helper = require "tests.helper" 9 | 10 | 11 | 12 | describe("Testing #Autogen", function() 13 | 14 | it("should not open autogen page", function() 15 | conf.sailor.enable_autogen = false 16 | local res = test.request('autogen') 17 | assert.truthy(res.body:match('Error')) 18 | end) 19 | 20 | it("should open autogen page", function() 21 | conf.sailor.enable_autogen = true 22 | local res = test.request('autogen') 23 | assert.same(200,res.status) 24 | assert.truthy(res.body:match('Generate CRUD')) 25 | end) 26 | 27 | it("should not generate model", function() 28 | local res = test.request('autogen',{post={table_name ='asdasd'}}) 29 | assert.falsy(res.body:match('Model generated with success')) 30 | end) 31 | 32 | it("should generate model", function() 33 | local path = 'models/category.lua' 34 | os.remove(path) 35 | local res = test.request('autogen',{post={table_name ='category'}}) 36 | assert.truthy(res.body:match('Model generated with success')) 37 | assert.truthy(lfs.attributes(path)) 38 | end) 39 | 40 | it("should not generate CRUD", function() 41 | local res = test.request('autogen',{post={model_name ='asdasd'}}) 42 | assert.falsy(res.body:match('CRUD generated with success')) 43 | end) 44 | 45 | it("should generate CRUD", function() 46 | local paths = { 47 | 'controllers/category.lua', 48 | 'views/category/create.lp', 49 | 'views/category/index.lp', 50 | 'views/category/update.lp', 51 | 'views/category/view.lp' 52 | } 53 | for _,f in ipairs(paths) do 54 | os.remove(f) 55 | end 56 | 57 | local res = test.request('autogen',{post={model_name ='category'}}) 58 | assert.truthy(res.body:match('CRUD generated with success')) 59 | for _,f in ipairs(paths) do 60 | assert.truthy(lfs.attributes(f)) 61 | end 62 | end) 63 | 64 | end) -------------------------------------------------------------------------------- /docs/tutorial_controllers.md: -------------------------------------------------------------------------------- 1 | ##Controllers 2 | 3 | When you access a Sailor application, you will be routed to your page depending on the url. If there is nothing to route, sailor will load the default action specified in your conf.lua. Suppose the accessed url is `localhost/your_application/?r=main/index` (no friendly urls) or `localhost/your_application/main/index` (friendly urls activated). That means Sailor will try to find an action named 'index' inside a controller named 'main' on your application and run that function. 4 | 5 | A controller is a .lua file inside `/controllers` on your app's directory tree named after your controller's name. This is the basic structure of our `main.lua`: 6 | 7 | local main = {} --a table 8 | 9 | --Your actions are functions indexed by this table. They receive the 'page' object. 10 | function main.index(page) 11 | --Let's send a message to our view 12 | page:render("index", { msg = "Hello"}) 13 | end 14 | 15 | return main --don't forget to return your controller at the end of the file. 16 | 17 | 18 | You can have multiple actions inside your controller, all of them are functions indexed to a table you will return at the end of your controller. The action receives the 'page' object as an argument and this object contains attributes and methods for interacting with your page. The first method we will learn is `page:render()`. For other methods of the page object, please consult the reference manual. 19 | 20 | The render method has two parameters, a string with the view file to be rendered and a table with the vars that are being passed ahead. Sailor will look for your view file inside `/views/controller_name/file_name.lp`. So, a `page:render('index',{msg="Hello"})` executed from the main controller will render `/views/main/index.lp` and from that view you will have access to a variable named 'msg' containing the string 'Hello' (and the page object, which is also passed ahead by default). If you have configured a theme and a layout or are using the default theme and layout, Sailor will first render the layout and then render your view wrapped inside it, otherwise it will render your view directly. 21 | -------------------------------------------------------------------------------- /test/dev-app/tests/unit/access.lua: -------------------------------------------------------------------------------- 1 | local test = require "sailor.test" 2 | local access = require "sailor.access" 3 | local User = require "sailor.model"('user') 4 | local fixtures = require "tests.fixtures.user" or {} 5 | 6 | describe("Testing #UserController", function() 7 | 8 | it("should know no one has logged in", function() 9 | assert.is_true(access.is_guest()) 10 | end) 11 | 12 | it("should not login with wrong pass", function() 13 | assert.is_false(User.authenticate(fixtures[1].username,"dummy",true)) 14 | end) 15 | 16 | it("should not login with wrong username", function() 17 | assert.is_false(User.authenticate("meh",fixtures[1].password,false)) 18 | end) 19 | 20 | it("should not login with default settings", function() 21 | assert.is_false(access.login('admin','demo')) 22 | end) 23 | 24 | it("should login", function() 25 | assert.is_true(User.authenticate(fixtures[1].username,fixtures[1].password,false)) 26 | end) 27 | 28 | it("should know the user is logged in", function() 29 | assert.is_false(access.is_guest()) 30 | end) 31 | 32 | it("should logout", function() 33 | assert.is_true(User.logout()) 34 | end) 35 | 36 | it("should know the user is logged out", function() 37 | assert.is_true(access.is_guest()) 38 | end) 39 | 40 | it("should login with encrypted pass", function() 41 | local u = User:new() 42 | u.username = 'Hermione' 43 | local raw_pass = 'freeelf54' 44 | u.password = access.hash(u.username,raw_pass) 45 | u:save(false) 46 | assert.is_true(User.authenticate(u.username,raw_pass,true)) 47 | assert.is_false(access.is_guest()) 48 | User.logout() 49 | u:delete() 50 | end) 51 | 52 | it("should with default settings", function() 53 | access.settings({model = false, hashing = false, default_login = 'admin', default_password = 'demo'}) 54 | assert.is_true(access.login('admin','demo')) 55 | assert.is_false(access.is_guest()) 56 | assert.is_true(User.logout()) 57 | assert.is_true(access.is_guest()) 58 | 59 | end) 60 | 61 | it("should not login with default settings", function() 62 | assert.is_false(access.login('admin','demon')) 63 | assert.is_true(access.is_guest()) 64 | end) 65 | 66 | end) 67 | -------------------------------------------------------------------------------- /test/dev-app/start-server.lua: -------------------------------------------------------------------------------- 1 | local xavante = require "xavante" 2 | local filehandler = require "xavante.filehandler" 3 | local cgiluahandler = require "xavante.cgiluahandler" 4 | local redirect = require "xavante.redirecthandler" 5 | local conf = (require "conf.conf").sailor 6 | 7 | -- Define here where Xavante HTTP documents scripts are located 8 | local webDir = "." 9 | 10 | local uri_map 11 | 12 | if conf.friendly_urls then 13 | uri_map = { -- URI remapping 14 | match = "^[^%./]*/?([^%.]*)$", 15 | with = redirect, 16 | params = { 17 | "/", 18 | function(req,_,cap) 19 | local vars = {} 20 | 21 | for var in string.gmatch(cap[1], '([^/]+)') do 22 | table.insert(vars,var) 23 | end 24 | 25 | if #vars > 0 then 26 | local mod = (#vars % 2) - 1 27 | local get = "" 28 | 29 | if #vars > 1 - mod then 30 | for i = 2 - mod, #vars, 2 do 31 | get = get.."&"..vars[i].."="..vars[i+1] 32 | end 33 | end 34 | 35 | if mod == -1 then 36 | get = vars[2]..get 37 | end 38 | 39 | req.cmd_url = "/index.lua?"..conf.route_parameter.."="..vars[1].."/"..get 40 | else 41 | req.cmd_url = "/index.lua" 42 | end 43 | 44 | return "reparse" 45 | end 46 | } 47 | } 48 | else 49 | uri_map = { 50 | match = "^[^%./]*/$", 51 | with = redirect, 52 | params = {"index.lua"} 53 | } 54 | end 55 | 56 | local simplerules = { 57 | 58 | uri_map, 59 | 60 | { -- cgiluahandler example 61 | match = {"%.lp$", "%.lp/.*$", "%.lua$", "%.lua/.*$" }, 62 | with = cgiluahandler.makeHandler (webDir,{ reload = true }) 63 | }, 64 | 65 | { -- filehandler example 66 | match = ".", 67 | with = filehandler, 68 | params = {baseDir = webDir} 69 | }, 70 | } 71 | 72 | xavante.HTTP{ 73 | server = {host = "*", port = 8080}, 74 | 75 | defaultHost = { 76 | rules = simplerules 77 | } 78 | } 79 | xavante.start() 80 | -------------------------------------------------------------------------------- /src/sailor/blank-app/start-server.lua: -------------------------------------------------------------------------------- 1 | local xavante = require "xavante" 2 | local filehandler = require "xavante.filehandler" 3 | local cgiluahandler = require "xavante.cgiluahandler" 4 | local redirect = require "xavante.redirecthandler" 5 | local conf = (require "conf.conf").sailor 6 | 7 | -- Define here where Xavante HTTP documents scripts are located 8 | local webDir = "." 9 | 10 | local uri_map 11 | 12 | if conf.friendly_urls then 13 | uri_map = { -- URI remapping 14 | match = "^[^%./]*/?([^%.]*)$", 15 | with = redirect, 16 | params = { 17 | "/", 18 | function(req,_,cap) 19 | local vars = {} 20 | 21 | for var in string.gmatch(cap[1], '([^/]+)') do 22 | table.insert(vars,var) 23 | end 24 | 25 | if #vars > 0 then 26 | local mod = (#vars % 2) - 1 27 | local get = "" 28 | 29 | if #vars > 1 - mod then 30 | for i = 2 - mod, #vars, 2 do 31 | get = get.."&"..vars[i].."="..vars[i+1] 32 | end 33 | end 34 | 35 | if mod == -1 then 36 | get = vars[2]..get 37 | end 38 | 39 | req.cmd_url = "/index.lua?"..conf.route_parameter.."="..vars[1].."/"..get 40 | else 41 | req.cmd_url = "/index.lua" 42 | end 43 | 44 | return "reparse" 45 | end 46 | } 47 | } 48 | else 49 | uri_map = { 50 | match = "^[^%./]*/$", 51 | with = redirect, 52 | params = {"index.lua"} 53 | } 54 | end 55 | 56 | local simplerules = { 57 | 58 | uri_map, 59 | 60 | { -- cgiluahandler example 61 | match = {"%.lp$", "%.lp/.*$", "%.lua$", "%.lua/.*$" }, 62 | with = cgiluahandler.makeHandler (webDir, { reload = true }) 63 | }, 64 | 65 | { -- filehandler example 66 | match = ".", 67 | with = filehandler, 68 | params = {baseDir = webDir} 69 | }, 70 | } 71 | 72 | xavante.HTTP{ 73 | server = {host = "*", port = 8080}, 74 | 75 | defaultHost = { 76 | rules = simplerules 77 | } 78 | } 79 | xavante.start() 80 | -------------------------------------------------------------------------------- /docs/manual_validation_module.md: -------------------------------------------------------------------------------- 1 | ##Reference Manual 2 | ###Valua: the validation module 3 | This is a module external to Sailor that is useful for setting validation rules to model attributes but it can also be used elsewhere. It needs to be required: `local valua = require "valua"`. It works in chains. First you need to create your validation object then you chain the validation functions you wish in the order you wish. If a test fails, it will break the chain. More info: Valua - validation for Lua 4 | ####Examples 5 | Example 1 - Just create, chain and use: 6 | 7 | valua:new().type("string").len(3,5)("test string!") 8 | -- false, "should have 3-5 characters"` 9 | 10 | Example 2 - Create, chain and later use it multiple times: 11 | 12 | local reusable_validation = valua:new().type("string").len(3,5) 13 | reusable_validation("test string!") -- false, "should have 3-5 characters" 14 | reusable_validation("test!") -- true` 15 | 16 | Example 3 - On a model: 17 | 18 | local user.attributes = { 19 | { username = valua:new().not_empty().len(6,20) } 20 | } 21 | 22 | 23 | ####Validation functions 24 | 25 | `alnum()` - Checks if string is alphanumeric. 26 | 27 | `boolean()` - Checks if value is a boolean. 28 | 29 | `compare(another_value)` - Checks if value is equal to another value. 30 | 31 | `contains(substr)` - Checks if a string contains a substring. 32 | 33 | `date()` or `date(format)` - Checks if a string is a valid date. Default format is UK (dd/mm/yyyy). Also checks for US and ISO formats. 34 | 35 | `email()` - Checks if a string is a valid email address. 36 | 37 | `empty()` - Checks if a value is empty. 38 | 39 | `integer()` - Checks if a number is an integer. 40 | 41 | `in_list(list)` - Checks if a value is inside an array. 42 | 43 | `len(min,max)` - Checks if a string's length is between min and max. 44 | 45 | `match(pattern)` - Checks if a string matches a given pattern. 46 | 47 | `max(n)` - Checks if a number is equal or less than n. 48 | 49 | `min(n)` - Checks if a number is equal or greater than n. 50 | 51 | `not_empty()` - Checks if a value is not empty. 52 | 53 | `no_white()` - Checks if a string contains no white spaces. 54 | 55 | `number()` - Checks if a value is a number. 56 | 57 | `string()` - Checks if a value is a string. 58 | 59 | `type(t)` - Checks if a value is of type t. 60 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Sailor Contributor Code of Conduct 2 | 3 | As contributors and maintainers of Sailor, and in the interest of 4 | fostering an open and welcoming community, we pledge to respect all people who 5 | contribute through reporting issues, posting feature requests, updating 6 | documentation, submitting pull requests or patches, and other activities. 7 | 8 | We are committed to making participation in this project a harassment-free 9 | experience for everyone, regardless of level of experience, gender, gender 10 | identity and expression, sexual orientation, disability, personal appearance, 11 | body size, race, ethnicity, age, religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * The use of sexualized language or imagery 16 | * Personal attacks 17 | * Trolling or insulting/derogatory comments 18 | * Public or private harassment 19 | * Publishing other's private information, such as physical or electronic 20 | addresses, without explicit permission 21 | * Other unethical or unprofessional conduct 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or 24 | reject comments, commits, code, wiki edits, issues, and other contributions 25 | that are not aligned to this Code of Conduct, or to ban temporarily or 26 | permanently any contributor for other behaviors that they deem inappropriate, 27 | threatening, offensive, or harmful. 28 | 29 | By adopting this Code of Conduct, project maintainers commit themselves to 30 | fairly and consistently applying these principles to every aspect of managing 31 | this project. Project maintainers who do not follow or enforce the Code of 32 | Conduct may be permanently removed from the project team. 33 | 34 | This code of conduct applies both within project spaces and in public spaces 35 | when an individual is representing the project or its community. 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 38 | reported by contacting the project maintainer at dalcol@etiene.net. All 39 | complaints will be reviewed and investigated and will result in a response that 40 | is deemed necessary and appropriate to the circumstances. Maintainers are 41 | obligated to maintain confidentiality with regard to the reporter of an 42 | incident. 43 | 44 | 45 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 46 | version 1.3.0, available at 47 | [http://contributor-covenant.org/version/1/3/0/][version] 48 | 49 | [homepage]: http://contributor-covenant.org 50 | [version]: http://contributor-covenant.org/version/1/3/0/ 51 | -------------------------------------------------------------------------------- /test/dev-app/tests/unit/db.lua: -------------------------------------------------------------------------------- 1 | local db = require "sailor.db" 2 | local helper = require "tests.helper" 3 | 4 | describe("Testing db module", function() 5 | 6 | local fixtures = require "tests.fixtures.post" 7 | local count = #fixtures 8 | 9 | it("should find 1 result",function() 10 | db.connect() 11 | local res = db.query("select * from post where id = 1") 12 | db.close() 13 | assert.is_equal(1,#res) 14 | end) 15 | 16 | it("should insert and give me the id",function() 17 | db.connect() 18 | local res = db.query_insert("insert into post(author_id,body) values(2,'Hello darkness my old friend')") 19 | db.close() 20 | count = count + 1 21 | -- should be posts already inserted by the fixtures plus this one 22 | assert.is_equal(count,res) 23 | end) 24 | 25 | it("should find multiple results",function() 26 | db.connect() 27 | local res = db.query("select * from post limit 2") 28 | db.close() 29 | assert.is_equal(2,#res) 30 | end) 31 | 32 | it("should begin transaction and commit",function() 33 | db.begin_transaction() 34 | local res = db.query_insert("insert into post(author_id,body) values(2,'Transaction 1')") 35 | db.commit() 36 | count = count + 1 37 | assert.is_equal(count,res) 38 | 39 | db.connect() 40 | local res2 = db.query_one("select count(*) from post") 41 | db.close() 42 | 43 | assert.is_equal(count,tonumber(res2)) 44 | end) 45 | 46 | it("should begin transaction and rollback",function() 47 | db.begin_transaction() 48 | local res = db.query_insert("insert into post(author_id,body) values(2,'Transaction 2')") 49 | db.rollback() 50 | count = count + 1 51 | assert.is_equal(count,res) 52 | 53 | db.connect() 54 | local res2 = db.query_one("select count(*) from post") 55 | db.close() 56 | count = count -1 57 | assert.is_equal(count,tonumber(res2)) 58 | end) 59 | 60 | it("should see if a table exists",function() 61 | db.connect() 62 | local res = db.table_exists("category") 63 | local res2 = db.table_exists("asdasda") 64 | db.close() 65 | assert.is_true(res) 66 | assert.is_false(res2) 67 | end) 68 | 69 | it("should get columns and key of a table",function() 70 | db.connect() 71 | local columns,key = db.get_columns("category") 72 | db.close() 73 | 74 | local col_id,col_name = false 75 | 76 | for _,v in ipairs(columns) do 77 | if v == 'id' then col_id = true end 78 | if v == 'name' then col_name = true end 79 | end 80 | 81 | assert.same(2,#columns) 82 | assert.is_true(col_id) 83 | assert.is_true(col_name) 84 | assert.same('id',key) 85 | end) 86 | 87 | it("should not get columns ",function() 88 | db.connect() 89 | local columns,key = db.get_columns("asdas") 90 | db.close() 91 | assert.same(0,#columns) 92 | assert.same(nil,key) 93 | end) 94 | 95 | end) -------------------------------------------------------------------------------- /docs/create_app.md: -------------------------------------------------------------------------------- 1 | ##App Creation 2 | Once Sailor is installed, there are various ways to generate your app. 3 | 4 | ####Sailor with luarocks in Unix system 5 | An installation via luarocks will install a binary called `sailor`. You can use it to generate your apps by typing: 6 | 7 | sailor create [] 8 | 9 | 10 | The directory is optional, if not set, it will create your app in the same directory you are. Example: 11 | 12 | sailor create 'Hey arnold' /var/www 13 | 14 | 15 | This will create the directory /var/www/hey_arnold with the base of your app. I'll tell more about this later. 16 | 17 | 18 | ####Sailor by direct download / git clone in Unix system 19 | Sailor comes bundled with a lua file called sailor. To create your app, on terminal, you cd to where Sailor is and execute `sailor create` using the app name and the installation dir as parameters. Example: 20 | 21 | 22 | 23 | ./sailor create 'Hey arnold' /var/www 24 | 25 | 26 | This will create the directory `/var/www/hey_arnold` with the base of your app. I'll tell more about this later. 27 | 28 | 29 | ####Other 30 | Under src/sailor you will find a dir called `blank-app`. You can just copy paste this folder, rename it and use it as a blank base app. 31 | 32 | 33 | ##App structure 34 | Sailor values convention over configuration to make our tasks easier. That's why our base app has a default structure. 35 | 36 | 37 | `/conf` - Contains configuration files, open and edit them to suit your needs. 38 | 39 | 40 | `/controllers` - Contains a default controller and will contain other controllers you will make! 41 | 42 | 43 | `/themes` - Contains themes. Themes are folders that contain regular HTML + CSS + Javascript. Except that pages are named .lp instead of .html and you will place one {{content}} where you want your app views to be rendered. 44 | 45 | 46 | `/models` - Will contain models you will make! 47 | 48 | 49 | `/pub` - Contains public files (js libraries, for example) 50 | 51 | 52 | `/runtime` - Contains temporary files generated by sailor during runtime. You don't need to touch this. 53 | 54 | 55 | `/tests` - Contains unit and functional tests for your app. 56 | 57 | 58 | `/views` - This is where your view, Lua pages in .lp, will go. 59 | 60 | 61 | `index.lua` - This is the file that will run your app. You don't need to touch this either (unless you want to). 62 | 63 | `index-magnet.lua` - This is the file that will run your app in case you are using Lighttpd mod_magnet. 64 | 65 | 66 | `start-server.lua` - This file will be used for those who want to run Sailor using Xavante server. If that's the case you only need to install Xavante using luarocks and execute this file to put your app up. 67 | 68 | 69 | `.htaccess` - This files sets some stuff for those who use Apache2 as a webserver. 70 | -------------------------------------------------------------------------------- /test/dev-app/views/test/frontend_performance.lp: -------------------------------------------------------------------------------- 1 | 2 |
Hello
3 |
Hello
4 | 17 | max then return false,"should have "..min.."-"..max.." characters" end 84 | return true 85 | end 86 | local test = {} 87 | table.insert(test,len) 88 | for i=1,10000 do 89 | for k, v in ipairs(test) do 90 | v('oi',3,5) 91 | end 92 | -- Something is up with ipairs? 93 | end 94 | t() 95 | 96 | 97 | t = timer("Function 8 Insert on table") 98 | test = {} 99 | for i=1,1000 do 100 | test[i] = i 101 | end 102 | t() 103 | 104 | t = timer("Function 9 Read from table using ipairs") 105 | test = {} 106 | for i=1,10000 do 107 | for k, v in ipairs(test) do 108 | test[k] = k + 1 109 | end 110 | end 111 | t() 112 | 113 | t = timer("Function 10 Read from table using pairs") 114 | test = {} 115 | for i=1,10000 do 116 | for k, v in ipairs(test) do 117 | test[k] = k + 1 118 | end 119 | end 120 | t() 121 | ?> -------------------------------------------------------------------------------- /src/sailor/access.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- access.lua, v0.5: controls user login on sailor apps 3 | -- This file is a part of Sailor project 4 | -- Copyright (c) 2014 Etiene Dalcol 5 | -- License: MIT 6 | -- http://sailorproject.org 7 | -------------------------------------------------------------------------------- 8 | 9 | local sailor = require "sailor" 10 | local session = require "sailor.session" 11 | local bcrypt = require( "bcrypt" ) 12 | local log_rounds = 11 13 | 14 | local access = {} 15 | 16 | session.open(sailor.r) 17 | 18 | local INVALID = "Invalid username or password." 19 | 20 | local function default_settings() 21 | return { 22 | default_login = 'admin', -- Default login details 23 | default_password = 'demo', 24 | grant_time = 604800, -- 1 week 25 | model = nil, -- Setting this field will deactivate default login details and activate below fields 26 | login_attributes = {'username'},-- Allows multiple options, for example, username or email. The one used to hash the 27 | password_attribute = 'password',-- password should come first. 28 | hashing = true 29 | } 30 | end 31 | 32 | local settings = default_settings() 33 | 34 | -- Changes settings 35 | function access.settings(s) 36 | settings = default_settings() 37 | for k, v in pairs(s) do 38 | settings[k] = v 39 | end 40 | end 41 | 42 | -- Simple hashing algorithm for encrypting passworsd 43 | function access.hash(username, password) 44 | return bcrypt.digest( username .. password, log_rounds ) 45 | end 46 | 47 | function access.verify_hash(username, password, model_password) 48 | if not settings.hashing then return model_password == password end 49 | return bcrypt.verify( username..password, model_password ) 50 | end 51 | 52 | 53 | function access.is_guest() 54 | if not access.data then 55 | access.data = session.data 56 | end 57 | return not access.data.login 58 | end 59 | 60 | function access.grant(data,time) 61 | session.setsessiontimeout (time or 604800) -- 1 week 62 | if not data.login then return false end 63 | access.data = data 64 | return session.save(data) 65 | end 66 | 67 | function access.find_object(login) 68 | local Model = sailor.model(settings.model) 69 | local o 70 | for _, attr in pairs(settings.login_attributes) do 71 | local a = {} 72 | a[attr] = login 73 | o = Model:find_by_attributes(a) 74 | if o ~= nil then return o end 75 | end 76 | return false 77 | end 78 | 79 | function access.login(login,password) 80 | local id 81 | if settings.model then 82 | local model = access.find_object(login) 83 | if not model then 84 | return false, INVALID 85 | end 86 | if not access.verify_hash(model[settings.login_attributes[1]], password, model[settings.password_attribute]) then 87 | return false, INVALID 88 | end 89 | 90 | id = model.id 91 | else 92 | if login ~= settings.default_login or password ~= settings.default_password then 93 | return false, INVALID 94 | end 95 | id = 1 96 | end 97 | return access.grant({login=login,id=id}) 98 | end 99 | 100 | 101 | function access.logout() 102 | session.data = {} 103 | access.data = {} 104 | return session.destroy(sailor.r) 105 | end 106 | 107 | return access 108 | -------------------------------------------------------------------------------- /test/dev-app/pub/moonshine/DOMAPI.moonshine.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Moonshine - a Lua virtual machine. 3 | * 4 | * Email: moonshine@gamesys.co.uk 5 | * http://moonshinejs.org 6 | * 7 | * Copyright (c) 2013-2015 Gamesys Limited. All rights reserved. 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining 10 | * a copy of this software and associated documentation files (the 11 | * "Software"), to deal in the Software without restriction, including 12 | * without limitation the rights to use, copy, modify, merge, publish, 13 | * distribute, sublicense, and/or sell copies of the Software, and to 14 | * permit persons to whom the Software is furnished to do so, subject to 15 | * the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | !function(shine){function jsToLua(obj){var t,mt;return mt=new shine.Table({__index:function(t,key){var property=obj[key],i,children,child;if("function"==typeof property||property&&property.prototype&&"function"==typeof property.prototype.constructor){var f=function(){var e=convertArguments(arguments,luaToJS),t=property.apply(e.shift(),e);return"object"==typeof t?jsToLua(t):[t]};if(Object.getOwnPropertyNames)for(children=Object.getOwnPropertyNames(property),i=0;child=children[i];i++)"caller"!=child&&"callee"!=child&&"arguments"!=child&&(f[child]=property[child]);return f["new"]=function(){var args=convertArguments(arguments,luaToJS),argStr,obj,i,l;for(argStr=(l=args.length)?"args[0]":"",i=1;l>i;i++)argStr+=",args["+i+"]";return obj=eval("new property("+argStr+")"),jsToLua(obj)},f}return"object"==typeof property?jsToLua(property):property},__newindex:function(e,t,r){obj[t]=luaToJS(r)}}),mt.source=obj,t=new shine.Table,shine.gc.incrRef(t),shine.lib.setmetatable(t,mt)}function luaToJS(e){var t;if(e instanceof shine.Function)return function(){return jsToLua(e.apply(void 0,convertArguments(arguments,jsToLua)))};if(e instanceof shine.Table){if((t=shine.lib.getmetatable(e))&&t.source)return t.source;var r,n=shine.lib.table.getn(e)>0,o=shine.gc["create"+(n?"Array":"Object")](),i=e.__shine.numValues,a=i.length;for(r=1;a>r;r++)o[r-1]=(i[r]||shine.EMPTY_OBJ)instanceof shine.Table?luaToJS(i[r]):i[r];for(r in e)!e.hasOwnProperty(r)||r in shine.Table.prototype||"__shine"===r||(o[r]=(e[r]||shine.EMPTY_OBJ)instanceof shine.Table?luaToJS(e[r]):e[r]);return o}return"object"==typeof e?shine.utils.toObject(e):e}function convertArguments(e,t){var r,n,o=[];for(r=0,n=e.length;n>r;r++)o.push(t(e[r]));return o}shine.DOMAPI={window:jsToLua(window)},shine.DOMAPI.window.extract=function(){var e=shine.getCurrentVM(),t=Object.getOwnPropertyNames&&Object.getOwnPropertyNames(window);for(var r in t||window)t&&(r=t[r]),"print"!==r&&"window"!==r&&null!==window[r]&&e.setGlobal(r,shine.DOMAPI.window.getMember(r))}}(shine||{}); 30 | -------------------------------------------------------------------------------- /test/dev-app/tests/functional/category.lua: -------------------------------------------------------------------------------- 1 | --[-[ 2 | local model = require "sailor.model" 3 | local form = require "sailor.form"('category') 4 | local test = require "sailor.test" 5 | local helper = require "tests.helper" 6 | 7 | describe("Testing #CategoryController", function() 8 | local Category = model('category') 9 | local fixtures = require "tests.fixtures.category" 10 | 11 | setup(function() 12 | test.load_fixtures('category') 13 | end) 14 | 15 | it("should open index", function() 16 | local res = test.request('category') 17 | assert.same(200,res.status) 18 | assert.truthy(res.body:match('View all')) 19 | end) 20 | 21 | it("should open create page", function() 22 | local res = test.request('category/create') 23 | assert.same(200,res.status) 24 | assert.truthy(res.body:match('Create category')) 25 | end) 26 | 27 | it("should create new category", function() 28 | local count_before = Category:count() 29 | local res = test.request('category/create', {post = form.ify(fixtures[1])}) 30 | assert.same(Category:count(), count_before + 1) 31 | assert.is_true(res:redirected('category/index')) 32 | end) 33 | 34 | it("should open update page with a category", function() 35 | local res = test.request('category/update', {get = {id = 1}}) 36 | assert.same(200,res.status) 37 | assert.truthy(res.body:match('Update category')) 38 | end) 39 | 40 | it("should not open update page without a category", function() 41 | local res = test.request('category/update') 42 | assert.truthy(res.body:match('Error')) 43 | end) 44 | 45 | it("should update category", function() 46 | local res = test.request('category/update', {get = {id = 1}, post = form.ify(fixtures[2])}) 47 | assert.same(fixtures[2].name, Category:find_by_id(1).name) 48 | assert.is_true(res:redirected('category/index')) 49 | end) 50 | 51 | it("should get category", function() 52 | local res = test.request('category/view', {get = {id = 2}}) 53 | assert.same(200,res.status) 54 | assert.truthy(res.body:match(fixtures[2].name)) 55 | end) 56 | 57 | it("should not get category if id not found", function() 58 | local res = test.request('category/view', {get = {id = 42}}) 59 | assert.same(404,res.status) 60 | assert.truthy(res.body:match('Error')) 61 | end) 62 | it("should not get category without id", function() 63 | local res = test.request('category/view') 64 | assert.same(404,res.status) 65 | assert.truthy(res.body:match('Error')) 66 | end) 67 | 68 | it("should delete category", function() 69 | local count_before = Category:count() 70 | local res = test.request('category/delete', {get = {id = 1}}) 71 | assert.same(count_before - 1, Category:count()) 72 | assert.falsy(Category:find_by_id(1)) 73 | assert.is_true(res:redirected('category/index')) 74 | end) 75 | 76 | it("should not delete category without id", function() 77 | local count_before = Category:count() 78 | local res = test.request('category/delete') 79 | assert.same(count_before, Category:count()) 80 | assert.truthy(res.body:match('Error')) 81 | end) 82 | 83 | it("should not delete category if id not found", function() 84 | local count_before = Category:count() 85 | local res = test.request('category/delete',{get={id=42}}) 86 | assert.same(count_before, Category:count()) 87 | assert.truthy(res.body:match('Error')) 88 | end) 89 | 90 | end) 91 | --]] -------------------------------------------------------------------------------- /test/dev-app/themes/default/main.lp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <%=page.title%> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 60 | 61 | 62 |
63 | {{content}} 64 |
65 |
66 | 67 | 75 | 76 | 77 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/sailor/blank-app/themes/default/main.lp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <%=page.title%> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 60 | 61 | 62 |
63 | {{content}} 64 |
65 |
66 | 67 | 75 | 76 | 77 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/web_utils/serialize.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------- 2 | -- Serialize tables. 3 | -- It works only for tables without cycles and without functions or 4 | -- userdata inside it. 5 | -- @release $Id: serialize.lua,v 1.7 2007/04/16 14:01:32 tomas Exp $ 6 | ---------------------------------------------------------------------------- 7 | 8 | local ipairs, pairs, type = ipairs, pairs, type 9 | local format = string.format 10 | local sort, tinsert = table.sort, table.insert 11 | 12 | -- 13 | local value = nil 14 | 15 | ---------------------------------------------------------------------------- 16 | -- Serializes a table. 17 | -- @param tab Table representing the session. 18 | -- @param outf Function used to generate the output. 19 | -- @param ind String with indentation pattern (default = ""). 20 | -- @param pre String with indentation prefix (default = ""). 21 | ---------------------------------------------------------------------------- 22 | local function tabledump (tab, outf, ind, pre) 23 | local sep_n, sep, _n = ",\n", ", ", "\n" 24 | if (not ind) or (ind == "") then ind = ""; sep_n = ", "; _n = "" end 25 | if not pre then pre = "" end 26 | outf ("{") 27 | local p = pre..ind 28 | -- prepare list of keys 29 | local keys = { boolean = {}, number = {}, string = {} } 30 | local total = 0 31 | for key in pairs (tab) do 32 | total = total + 1 33 | local t = type(key) 34 | if t == "string" then 35 | tinsert (keys.string, key) 36 | else 37 | keys[t][key] = true 38 | end 39 | end 40 | local many = total > 5 41 | if not many then sep_n = sep; _n = " " end 42 | outf (_n) 43 | -- serialize entries with numeric keys 44 | if many then 45 | local _f,_s,_v = ipairs(tab) 46 | if _f(_s,_v) then outf (p) end 47 | end 48 | local num = keys.number 49 | local ok = false 50 | -- entries with automatic index 51 | for key, val in ipairs (tab) do 52 | value (val, outf, ind, p) 53 | outf (sep) 54 | num[key] = nil 55 | ok = true 56 | end 57 | if ok and many then outf (_n) end 58 | -- entries with explicit index 59 | for key in pairs (num) do 60 | if many then outf (p) end 61 | outf ("[") 62 | outf (key) 63 | outf ("] = ") 64 | value (tab[key], outf, ind, p) 65 | outf (sep_n) 66 | end 67 | -- serialize entries with boolean keys 68 | local tr = keys.boolean[true] 69 | if tr then 70 | outf (format ("%s[true] = ", many and p or '')) 71 | value (tab[true], outf, ind, p) 72 | outf (sep_n) 73 | end 74 | local fa = keys.boolean[false] 75 | if fa then 76 | outf (format ("%s[false] = ", many and p or '')) 77 | value (tab[false], outf, ind, p) 78 | outf (sep_n) 79 | end 80 | -- serialize entries with string keys 81 | sort (keys.string) 82 | for _, key in ipairs (keys.string) do 83 | outf (format ("%s[%q] = ", many and p or '', key)) 84 | value (tab[key], outf, ind, p) 85 | outf (sep_n) 86 | end 87 | if many then outf (pre) end 88 | outf ("}") 89 | end 90 | 91 | 92 | -- 93 | -- Serializes a value. 94 | -- 95 | value = function (v, outf, ind, pre) 96 | local t = type (v) 97 | if t == "string" then 98 | outf (format ("%q", v)) 99 | elseif t == "number" then 100 | outf (tostring(v)) 101 | elseif t == "boolean" then 102 | outf (tostring(v)) 103 | elseif t == "table" then 104 | tabledump (v, outf, ind, pre) 105 | else 106 | outf (format ("%q", tostring(v))) 107 | end 108 | end 109 | 110 | ---------------------------------------------------------------------------- 111 | return { 112 | serialize = tabledump, 113 | } -------------------------------------------------------------------------------- /test/dev-app/tests/unit/page.lua: -------------------------------------------------------------------------------- 1 | local conf = require "conf.conf" 2 | local test = require "sailor.test" 3 | local page = test.page 4 | 5 | describe("Testing Sailor core functions", function() 6 | local base_path 7 | local parms = {id = 5, name = 'a_test'} 8 | 9 | it("should create URLs accordingly without friendly urls", function() 10 | conf.sailor.friendly_urls = false 11 | 12 | 13 | local url = page:make_url('test') 14 | assert.is_equal('?r=test',url) 15 | 16 | url = page:make_url('test/etc') 17 | assert.is_equal('?r=test/etc',url) 18 | 19 | url = page:make_url('test', parms) 20 | local exp = '?r=test' 21 | for k,v in pairs(parms) do exp = exp .. '&' .. k .. '=' .. v end 22 | assert.is_equal(exp,url) 23 | 24 | url = page:make_url('test/etc', parms) 25 | exp = '?r=test/etc' 26 | for k,v in pairs(parms) do exp = exp .. '&' .. k .. '=' .. v end 27 | assert.is_equal(exp,url) 28 | 29 | base_path, page.base_path = page.base_path, '/sailor/test/dev-app' 30 | url = page:make_url('test/etc') 31 | assert.is_equal('/sailor/test/dev-app/?r=test/etc',url) 32 | 33 | url = page:make_url('test', parms) 34 | exp = '/sailor/test/dev-app/?r=test' 35 | for k,v in pairs(parms) do exp = exp .. '&' .. k .. '=' .. v end 36 | assert.is_equal(exp,url) 37 | 38 | url = page:make_url('test/etc') 39 | assert.is_equal('/sailor/test/dev-app/?r=test/etc',url) 40 | 41 | url = page:make_url('test/etc', parms) 42 | exp = '/sailor/test/dev-app/?r=test/etc' 43 | for k,v in pairs(parms) do exp = exp .. '&' .. k .. '=' .. v end 44 | assert.is_equal(exp,url) 45 | base_path, page.base_path = page.base_path, base_path 46 | end) 47 | 48 | it("should create URLs accordingly with friendly urls", function() 49 | conf.sailor.friendly_urls = true 50 | page.base_path = '' 51 | local url = page:make_url('test') 52 | assert.is_equal('/test',url) 53 | 54 | url = page:make_url('test/etc') 55 | assert.is_equal('/test/etc',url) 56 | 57 | url = page:make_url('test',parms) 58 | local exp = '/test' 59 | for k,v in pairs(parms) do exp = exp .. '/' .. k .. '/' .. v end 60 | assert.is_equal(exp,url) 61 | 62 | url = page:make_url('test/etc',parms) 63 | exp = '/test/etc' 64 | for k,v in pairs(parms) do exp = exp .. '/' .. k .. '/' .. v end 65 | assert.is_equal(exp,url) 66 | 67 | base_path, page.base_path = page.base_path, '/sailor/test/dev-app' 68 | url = page:make_url('test/etc') 69 | assert.is_equal('/sailor/test/dev-app/test/etc',url) 70 | 71 | url = page:make_url('test') 72 | assert.is_equal('/sailor/test/dev-app/test',url) 73 | 74 | url = page:make_url('test',parms) 75 | exp = '/sailor/test/dev-app/test' 76 | for k,v in pairs(parms) do exp = exp .. '/' .. k .. '/' .. v end 77 | assert.is_equal(exp,url) 78 | 79 | url = page:make_url('test/etc',parms) 80 | exp = '/sailor/test/dev-app/test/etc' 81 | for k,v in pairs(parms) do exp = exp .. '/' .. k .. '/' .. v end 82 | assert.is_equal(exp,url) 83 | base_path, page.base_path = page.base_path, base_path 84 | end) 85 | 86 | it("can render a table as JSON", function() 87 | assert.has_no.errors(function() 88 | page:json(parms) 89 | end) 90 | end) 91 | 92 | it("can send CORS headers", function() 93 | assert.has_no.errors(function() 94 | page:enable_cors() 95 | end) 96 | 97 | assert.has_no.errors(function() 98 | page:enable_cors({allow_origin = "http://sailorproject.org"}) 99 | end) 100 | end) 101 | end) 102 | -------------------------------------------------------------------------------- /src/sailor/test.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- test.lua, v0.5: Helper functions for testing functionality 3 | -- This file is a part of Sailor project 4 | -- Copyright (c) 2014-2015 Etiene Dalcol 5 | -- License: MIT 6 | -- http://sailorproject.org 7 | -------------------------------------------------------------------------------- 8 | 9 | local sailor = require "sailor" 10 | local lfs = require "lfs" 11 | local db = require "sailor.db" 12 | local main_conf = require "conf.conf" 13 | local db_conf = main_conf.db[main_conf.sailor.environment] 14 | 15 | local M = {req = {}, page = nil} 16 | 17 | local body 18 | local function write(_,data) body = (body or "") .. data end 19 | 20 | -- Prepares for making a request 21 | function M:prepare(headers_in) 22 | headers_in = headers_in or {} 23 | self.req = { uri = '', write = write, puts = write, headers_in = headers_in, headers_out = {} } 24 | self.page = sailor.init(M.req) 25 | return self 26 | end 27 | 28 | -- Loads tests fixtures into the database 29 | -- Warning, this will truncate the table, make sure you have configured a test database 30 | -- Returns table with objects created 31 | local function load_fixtures(model_name) 32 | local Model = sailor.model(model_name) 33 | local fixtures = require("tests.fixtures."..model_name) or {} 34 | local objects = {} 35 | db.connect() 36 | db.truncate(Model.db.table) -- Reseting current state 37 | db.close() 38 | 39 | 40 | for _,v in pairs(fixtures) do -- loading fixtures 41 | local o = Model:new(v) 42 | o:save(false) 43 | table.insert(objects, o) 44 | end 45 | return objects 46 | 47 | end 48 | 49 | function M.load_fixtures(model_name) 50 | -- if model name is specific, load fixtures from a file 51 | if model_name then 52 | return load_fixtures(model_name) 53 | end 54 | 55 | -- if not specified, loads all fixtures in fixtures dir 56 | for filename,_ in lfs.dir(sailor.path..'/tests/fixtures') do 57 | model_name = string.match(filename,'(%w*).lua') 58 | if model_name then 59 | load_fixtures(model_name) 60 | end 61 | end 62 | end 63 | 64 | -- Function that is a part of the request response. 65 | -- Verifies if the request redirected somewhere 66 | -- Requires path: string 67 | -- Returns boolean 68 | local function redirected(response, path) 69 | if response.status ~= 302 then return false end 70 | return (response.headers['Location']):match(path) and true or false 71 | end 72 | 73 | -- Makes a mockup request to an internal sailor path 74 | -- path: string. The path you want to make a request to, such as a controller or controller/action. Example: 'user/view' 75 | -- data: table. A table containing some data you want to send to the request, such as get or post. Example: {get = {id = 1}} 76 | -- additional_headers: A table containing additional headers you may want to send. Example: {ACCEPT = 'application/json'} 77 | -- Returns a table with the following fields: 78 | -- status: number. The status of the response. 79 | -- body: string. The body of the response. 80 | -- headers: table. Any headers out that were set. 81 | -- redirected: function. The above redirect function 82 | 83 | 84 | function M.request(path, data, additional_headers) 85 | local conf = require "conf.conf" 86 | data = data or {} 87 | body = '' 88 | conf.sailor.friendly_urls = false 89 | 90 | M:prepare(additional_headers) 91 | M.page.POST = data.post or {} 92 | M.page.GET = data.get or {} 93 | M.page.GET[conf.sailor.route_parameter] = path 94 | local status = sailor.route(M.page) 95 | 96 | return {status = status, body = body, headers = M.req.headers_out, redirected = redirected} 97 | end 98 | 99 | return M:prepare() 100 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | 4 | addons: 5 | apt: 6 | packages: 7 | - libmysqlclient-dev 8 | - libpq-dev 9 | - libsqlite3-dev 10 | 11 | services: 12 | - mysql 13 | - postgresql 14 | 15 | env: 16 | # Lua 5.1 on different DBs 17 | - LUA="lua 5.1" DB_DRIVER=mysql DB_USER=travis DB_NAME=sailor_test 18 | - LUA="lua 5.1" DB_DRIVER=postgres DB_USER=postgres DB_NAME=sailor_test 19 | - LUA="lua 5.1" DB_DRIVER=sqlite3 DB_USER="" DB_NAME=$TRAVIS_BUILD_DIR/sailor_test 20 | # Lua 5.2 on different DBs 21 | - LUA="lua 5.2" DB_DRIVER=mysql DB_USER=travis DB_NAME=sailor_test 22 | - LUA="lua 5.2" DB_DRIVER=postgres DB_USER=postgres DB_NAME=sailor_test 23 | - LUA="lua 5.2" DB_DRIVER=sqlite3 DB_USER="" DB_NAME=$TRAVIS_BUILD_DIR/sailor_test 24 | # Lua 5.3 on different DBs 25 | - LUA="lua 5.3" DB_DRIVER=mysql DB_USER=travis DB_NAME=sailor_test 26 | - LUA="lua 5.3" DB_DRIVER=postgres DB_USER=postgres DB_NAME=sailor_test 27 | - LUA="lua 5.3" DB_DRIVER=sqlite3 DB_USER="" DB_NAME=$TRAVIS_BUILD_DIR/sailor_test 28 | # LuaJIT 2.0.x on different DBs 29 | - LUA="luajit 2.0" DB_DRIVER=mysql DB_USER=travis DB_NAME=sailor_test 30 | - LUA="luajit 2.0" DB_DRIVER=postgres DB_USER=postgres DB_NAME=sailor_test 31 | - LUA="luajit 2.0" DB_DRIVER=sqlite3 DB_USER="" DB_NAME=$TRAVIS_BUILD_DIR/sailor_test 32 | # Openresty + LuaJIT 2.1.x + mysql 33 | - LUA="luajit 2.1" DB_DRIVER=mysql DB_USER=travis DB_NAME=sailor_test SERVER=openresty 34 | 35 | before_install: 36 | - pip install hererocks 37 | - hererocks HERE --$LUA --no-readline --luarocks latest --verbose 38 | - hererocks HERE --show 39 | - source HERE/bin/activate 40 | 41 | install: 42 | - luarocks install luacheck 43 | - luarocks install luacov 44 | - luarocks install luacov-coveralls 45 | - luarocks make rockspecs/sailor-current-1.rockspec 46 | - if [ "$DB_DRIVER" == "mysql" ]; then luarocks install luasql-mysql MYSQL_INCDIR=/usr/include/mysql; fi 47 | - if [ "$DB_DRIVER" == "postgres" ]; then luarocks install luasql-postgres PGSQL_INCDIR=/usr/include/postgresql; fi 48 | - if [ "$DB_DRIVER" == "sqlite3" ]; then luarocks install luasql-sqlite3; fi 49 | - luarocks list 50 | - | 51 | if [ "$SERVER" == "openresty" ]; then 52 | OPENRESTY_VERSION="1.11.2.5" 53 | wget https://openresty.org/download/openresty-$OPENRESTY_VERSION.tar.gz 54 | tar xzf openresty-$OPENRESTY_VERSION.tar.gz 55 | cd openresty-$OPENRESTY_VERSION/ 56 | ln -s $TRAVIS_BUILD_DIR/HERE/include $TRAVIS_BUILD_DIR/HERE/include/luajit-2.1 57 | ./configure --prefix=$TRAVIS_BUILD_DIR/HERE --with-luajit=$TRAVIS_BUILD_DIR/HERE 58 | make 59 | make install 60 | ln -s $TRAVIS_BUILD_DIR/HERE/nginx/sbin/nginx $TRAVIS_BUILD_DIR/HERE/bin/nginx 61 | cd .. 62 | rm -rf openresty-$OPENRESTY_VERSION 63 | nginx -v 64 | resty -V 65 | fi 66 | 67 | before_script: 68 | - mysql -e 'create database sailor_test;' 69 | - mysql sailor_test < test/dev-app/sql/mysql.sql 70 | - psql -c 'create database sailor_test;' -U postgres 71 | - psql sailor_test < test/dev-app/sql/pgsql.sql 72 | - sqlite3 $TRAVIS_BUILD_DIR/sailor_test < test/dev-app/sql/sqlite3.sql 73 | 74 | script: 75 | - luacheck src test 76 | - | 77 | cd test/dev-app 78 | if [ "$SERVER" != "openresty" ]; then 79 | sailor test -- --verbose --coverage 80 | else 81 | sailor test --resty 82 | fi 83 | 84 | after_success: | 85 | if [ "$SERVER" != "openresty" ]; then 86 | mv luacov.stats.out ../.. 87 | cd ../.. 88 | luacov-coveralls -c test/dev-app/.luacov 89 | fi 90 | 91 | notifications: 92 | email: 93 | on_success: change 94 | on_failure: always 95 | -------------------------------------------------------------------------------- /docs/INSTALL_LINUX_ARCH.md: -------------------------------------------------------------------------------- 1 | ## Installation for Linux 2 | Sailor is compatible with a variety of operating systems and webservers. On this example, we will use Arch Linux and Apache 2.4.12. 3 | 4 | ### Installing Lua 5 | 6 | If you don't have it already, install Lua. Sailor is compatible with both 5.1 and 5.2. 7 | 8 | sudo pacman -Sy 9 | sudo pacman -S lua-{sec,socket} 10 | 11 | ### Installing Apache 2.4 12 | 13 | Use the following command to install Apache 2.4.x: 14 | 15 | sudo pacman -S apache 16 | 17 | Enable mod_lua: 18 | 19 | Edit httpd.conf and enable the mod_lua.so module: 20 | 21 | sudo yourtexteditor /etc/httpd/conf/httpd.conf 22 | 23 | The following line must be uncommented: 24 | 25 | LoadModule lua_module modules/mod_lua.so 26 | 27 | Change the DirectoryIndex directive to: 28 | 29 | DirectoryIndex index.lua index.html 30 | 31 | Add the SetHandler directive: 32 | 33 | 34 | SetHandler lua-script 35 | 36 | 37 | Enable Apache: 38 | 39 | systemctl enable httpd.service 40 | 41 | If it is already enabled, restart it: 42 | 43 | systemctl restart httpd.service 44 | 45 | ### Installing Sailor 46 | You can either clone it directly from the repository, download the zip containing the master branch or download and install it through LuaRocks. We will go through LuaRocks since it will also download and install almost all the required dependencies except for luasql-mysql if you want to persist your models. 47 | 48 | sudo pacman -S luarocks 49 | sudo luarocks install sailor 50 | 51 | We are almost done! You can now use `sailor` to create your web applications. In this example, we will create an app called "Hey Arnold" on the directory Apache is reading from (usually /var/www). After you're done, you can open your browser and access it on . 52 | 53 | sailor create 'Hey Arnold' /srv/http 54 | 55 | Alternatively, you can manually copy the files in the /src/sailor/blank-app directory of this repository to /srv/http/hey_arnold and access it at and if you didn't install sailor through LuaRocks, you must open .htaccess and replace {{path}} with the full path on your system to Sailor's src directory. 56 | 57 | #### Dependencies 58 | If you want to persist your models you need luasql. Sailor could work with other drivers but so far we've only tested with mysql and don't offer support for others. 59 | 60 | sudo luarocks install luasql-mysql 61 | 62 | If you installed Sailor through LuaRocks, there is no need to worry, all next dependencies will be installed with it and you can ignore the rest of this section. If you just cloned the repository or downloaded the zip, you should install these dependencies: 63 | 64 | Lua File System and valua are required. 65 | 66 | sudo luarocks install luafilesystem 67 | sudo luarocks install valua 68 | 69 | If you want to save your models in a database, you will need LuaSQL. I believe it should work with every database LuaSQL supports, but so far I have only tested with MySQL. LuaSQL-MySQL requires you to have mysql installed. 70 | 71 | sudo luarocks install LuaSQL-MySQL 72 | 73 | If you want to use our mailer module, get these rocks so we are able to send stuff via SMTP. 74 | 75 | sudo luarocks install LuaSocket 76 | sudo luarocks install LuaSec 77 | 78 | LuaSec requires OpenSSL as a dependency, if you don't have it already please install it and try getting LuaSec again. Remember to install "devel" packages, if your distro has them, to get the header files! If LuaSec can't find your OpenSSL dir, try using these flags, depending on your system's architecture (the examples below work on some Linux distros). 79 | 80 | sudo luarocks install LuaSec OPENSSL_LIBDIR=/usr/lib/x86_64-linux-gnu 81 | or 82 | 83 | sudo luarocks install LuaSec OPENSSL_LIBDIR=/usr/lib/i386-linux-gnu 84 | -------------------------------------------------------------------------------- /sailor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -------------------------------------------------------------------------------- 4 | -- sailor v0.6.2: Command line utility for sailor 5 | -- This file is a part of Sailor project 6 | -- Copyright (c) 2014 Etiene Dalcol 7 | -- License: MIT 8 | -- http://sailorproject.org 9 | -------------------------------------------------------------------------------- 10 | 11 | local argparse = require "argparse" 12 | local colors = require "ansicolors" 13 | local actions = require "sailor.cli" 14 | 15 | 16 | local parser = argparse("script", colors("%{bright yellow}Sailor command line utility")) 17 | :name(string.match(arg[0], "/*(%w+)/*$")) -- this file name, usually will be "sailor" 18 | :epilog("For more info see http://sailorproject.org") 19 | :require_command(false) 20 | 21 | local create_cmd = parser:command("c create", "Generates web application in a directory.") 22 | :action(actions.create) 23 | create_cmd:usage(colors("%{bright red}Usage: sailor create NAME [PATH]")) 24 | create_cmd:argument("name", "The name of your application.") 25 | create_cmd:argument("path", "The path to where you wish your app to be created."):default(".") 26 | create_cmd:epilog(colors([[ 27 | Example: %{bright red} sailor create 'Hey Arnold' /var/www %{reset} 28 | This will create your web app under /var/www/hey_arnold.]])) 29 | 30 | local test_cmd = parser:command("t test", "Runs the tests specified for an application. Must be called from the base dir of the application.") 31 | :action(actions.test) 32 | test_cmd:usage(colors"%{bright red}Usage: sailor test [--resty] [-- EXTRA_FLAGS]") 33 | test_cmd:argument("EXTRA_FLAGS", "Flags that will be passed to the Busted library."):args("*") 34 | test_cmd:flag("--resty", "Run the tests using the resty bootstrap.", false) 35 | 36 | local enable_cmd = parser:command("e enable", "Installs an extension to Sailor and copy necessary files to your app. Must be called from the base dir of the application.") 37 | :action(actions.enable) 38 | enable_cmd:usage(colors("%{bright red}Usage: sailor enable NAME")) 39 | enable_cmd:argument("name", "The name of the extension to be enabled.") 40 | 41 | 42 | local gen_cmd = parser:command("g gen", "Generates some scaffolding for your app.") 43 | gen_cmd:usage(colors("%{bright red}Usage: sailor gen COMMAND ARG")) 44 | gen_cmd:epilog(colors([[ 45 | Example: %{bright red} sailor gen model users%{reset} 46 | Given a table called 'users' exist in the database, this will generate a model based on it.]])) 47 | 48 | local model_cmd = gen_cmd:command("m model", "Generates a basic model based on an existing table on the database.") 49 | :action(actions.gen_model) 50 | model_cmd:argument("table_name", "The name of the database table that the model should reflect.") 51 | model_cmd:usage(colors("%{bright red}Usage: sailor gen model TABLE_NAME")) 52 | 53 | local crud_cmd = gen_cmd:command("crud c", "Generates Create-Read-Update-Delete scaffolding for a given model name.") 54 | :action(actions.gen_crud) 55 | crud_cmd:argument("model_name", "The name of the model table that CRUD should act on.") 56 | crud_cmd:usage(colors("%{bright red}Usage: sailor gen crud MODEL_NAME")) 57 | 58 | local all_cmd = gen_cmd:command("a all", "Generates both the model and a CRUD for an existing table name.") 59 | :action(actions.gen_all) 60 | all_cmd:argument("table_name", "The name of the database table that the model and the CRUD should reflect.") 61 | all_cmd:usage(colors("%{bright red}Usage: sailor gen all TABLE_NAME")) 62 | 63 | local start_cmd = parser:command("s start", "Runs a Sailor app starting Xavante webserver.") 64 | :action(actions.start) 65 | enable_cmd:usage(colors("%{bright red}Usage: sailor start")) 66 | 67 | parser:flag("-v --version", "Show the current Sailor version installed and exit.", false):action(actions.version) 68 | 69 | parser:usage(parser:get_help()) 70 | 71 | local args = parser:parse() 72 | -------------------------------------------------------------------------------- /test/dev-app/conf/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /src/sailor/blank-app/conf/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /docs/manual_lua_at_client.md: -------------------------------------------------------------------------------- 1 | ##Reference Manual 2 | 3 | ###Lua at client 4 | 5 | With Sailor, you can not only write the backend of your app with Lua, but you can also write Lua code that will run on the browser. This is possible because Sailor will get this piece of code and give it to a Virtual Machine that will handle it and run Javascript behind the scenes. Sailor is compatible with different implementations of Lua-to-Javascript virtual machines. The default is starlight. In case you want to use a different VM, you will have to download the folder with the libraries, put it inside the `pub` folder of your Sailor app and edit your `conf/conf.lua` to configure your app to use this VM instead. 6 | 7 | Here is a small comparative table of the compatible VMs: 8 | 9 | | | starlight | moonshine | lua51js | luavmjs | 10 | |---------------------------------------------------------------------------|:---------------------:|:---------------------:|:------------------------:|:------------------------:| 11 | | The code is pre-processed on the server and bytecode is sent to the JS VM | | ✓ | | | 12 | | The code is sent as a string to the JS VM | ✓ | | ✓ | ✓ | 13 | | Compatible Lua version of the client written code | 5.1 | 5.1 | 5.1 | 5.2.3 | 14 | | Works with Sailor on LuaJIT based servers, such as openresty | ✓ | | ✓ | ✓ | 15 | | DOM manipulation | ✓ | ✓ | incomplete | ✓ | 16 | | Can require Lua modules | ✓ | ✓ | Only on Apache | | 17 | | Supports Lua callbacks | ✓ | ✓ | x | ✓ | 18 | | Supports Lua script tags | ✓ | x | ✓ | ✓ | 19 | | Can call JS methods like eval() from Lua | ? | ? | x | ✓ | 20 | | Supports the Lua C API | x | x | ✓ | incomplete | 21 | | How to print "hello" to the console | print("hello") | print("hello") | print("hello") | print("hello") | 22 | | How to pop an alert message with "hello" | window:alert("hello") | window:alert("hello") | js.window:alert("hello") | js.global:alert("hello") | 23 | 24 | 25 | You can find more information about them here: 26 | 27 | Starlight: Examples, Github Repo 28 | 29 | Moonshine: Official website, Github Repo 30 | 31 | Lua5.1.js: Examples, Github Repo 32 | 33 | Lua.vm.js: Examples, Official website, Github Repo 34 | 35 | 36 | -------------------------------------------------------------------------------- /docs/CONFIG.md: -------------------------------------------------------------------------------- 1 | ##Configuring your Sailor Application 2 | 3 | You will find Sailor's config file under `conf/conf.lua`. This file contains a table with config strings for core Sailor control, database server configuration and SMTP server configuration for mailer module. 4 | 5 | ###`sailor.app_name` 6 | Used as a default title tag for your pages and can be invoked at any time from the rest of your app accessing the conf table. 7 | 8 | ###`sailor.default_static` 9 | Is about flow control. If defined, Sailor will render a specific view as defined, disabling routing. It's useful, for example, for serving a very simple page instead of a complex app or for loading a temporary maintenance page. 10 | 11 | ###`sailor.default_controller` and `default_action` 12 | Will define which action from which controller will run by default, when no route is called, a.k.a. your index. 13 | 14 | ###`sailor.theme` 15 | Will define the basic and default theme used for all pages. Sailor is integrated with [Twitter Bootstrap](http://getbootstrap.com) for an enhanced feel. You can add more themes on `/themes` and set a default here. Themes can be changed for specific pages by setting `page.theme` to something else on controller before rendering pages. It can be deactivated by setting it to nil. 16 | 17 | ###`sailor.layout` 18 | Will define the basic and default layout used for all pages. A layout is the exact .lp file inside your theme folder that will be rendered. For example, one theme can come with many possible different layouts, like front and 'inside' pages, or 1-column and 2-columns. Layout can be changed for specific pages by setting `page.layout` to something else on controller before rendering pages. It can be deactivated by setting it to nil. 19 | 20 | ###`sailor.route_parameter` 21 | Is for page routing when friendly urls are deactivated, which is done accessing a get variable, the default is 'r', but you can change it here if you wish. 22 | 23 | ###`sailor.default_error404` 24 | Sets a default view for 404 errors. It can be deactivated by setting it to nil. 25 | 26 | ###`sailor.enable_autogen` 27 | Turns Sailor's autogen on or off by setting it to true or false. The autogen is a tool to autogenerate models and CRUDs. You can find more about it under the Autogen section. The default value is false for avoiding accidental overwriting of files. 28 | 29 | ###`sailor.friendly_urls` 30 | turns friendly urls on or off by setting it to true or false. Normally, to access the view action of the article controller passing 4 as id via get variables, you would have to type in your address bar `http://your_url.com/?r=article/view&id=4`. However, when friendly urls are set to true, you can access it on `http://your_url.com/article/view/id/4` which is way cleaner. If you want to personalize it, for Xavante, you need to edit /start-server.lua and for Apache2, .htaccess. 31 | 32 | ###`sailor.max_upload` 33 | Sets, in bytes, the max size of files to be uploaded using file inputs on forms. 34 | 35 | ###`environment` 36 | Is a string with the name of your current environment. Example: 'test'. 37 | 38 | ###`hide_stack_trace` 39 | Is a boolean that if set to true will make Sailor not throw the full stack trace on errors but a default 500 Internal Server Error message. 40 | 41 | ###`db` 42 | Contains a table with your different database setups. They must contain the following fields: 43 | 44 | ####`driver` 45 | Is your database driver, example: 'mysql'. 46 | 47 | ####`host` 48 | Is your database host, example: 'localhost'. 49 | 50 | ####`user` and `db.pass` 51 | Are the login information for your database. 52 | 53 | ####`dbname` 54 | Is the database name. You can leave db configs blank if you are not persisting models. 55 | 56 | ###`smtp.server` 57 | Is the server for sending emails. You can leave smtp configs blank if you are not using your application for sending emails. 58 | 59 | ###`smtp.user` and `smtp.server` 60 | Are the login information for your smtp server. 61 | 62 | ###`smtp.from` 63 | Is the email address who is sending emails. 64 | 65 | ###`lua_at_client.vm` 66 | Is the virtual machine that will translate Lua to Javascript in case you'd like to write Lua code for the browser as well. `starlight` is the default. Other options are `moonshine`, `lua51js` and `luavmjs`. You can find more details about them on the reference manual. 67 | 68 | ###`debug.inspect` 69 | Toggles on/off the exhibition of debug messages on the bottom of the page made with `page.inspect`. 70 | 71 | -------------------------------------------------------------------------------- /docs/tutorial_views.md: -------------------------------------------------------------------------------- 1 | ##Views 2 | Sailor's views are Lua Page files (.lp) contained inside your `/views` folder. They are valid HTML files in all aspects, except for the .lp extension and the fact that you can also use `` tags inside it to execute lua scripts. 3 | 4 | Let's create our `/views/main/index.lp`: 5 | 6 | This is valid HTML! 7 | 8 | You can: 9 | 10 | * Use regular HTML 11 | `tags` 12 | 13 | * Run Javascript on your browser 14 | `
` 15 | 16 | * Run Lua scripts from your server 17 | 18 | 21 | <%= msg %> 22 |
23 | 24 | * Intercalate Lua code with HTML 25 | 26 | 28 | This message will appear 10 times! This is number <%= i %>. 29 | 30 | 31 | * Use the page object and all its functions, for example, page:include(). 32 | 33 | 34 | 35 | 36 | 37 | ###Lua at client 38 | You can also write Lua code that will run on the browser, if you want to. This is possible because Sailor will get this piece of code and give it to a Virtual Machine that will handle it and run Javascript behind the scenes. There are different Virtual Machines available, however, `starlight` is the default. Other options are `moonshine`, `lua51js` and `luavmjs`. They have slight differences on features, performance and how they handle DOM manipulation. Since starlight is the default, the following example will work on it, but also on moonshine. You can find more details about these differences on the reference manual. 39 | 40 | 41 | To write Lua code on a view that will run on the browser, you need to annotate your `` tag. Code inside `` will run on the browser, code inside `` will run on the server side, and code inside `` will run both on the server side and on the browser. Since `` is the default, it can be simply written as ``. 42 | 43 | Here are some examples of Lua code that will run on the browser using the `starlight` virtual machine: 44 | 45 | #####Manipulation of the DOM 46 | 47 |
48 | 57 | 58 | 59 | #####Accessing Javascript functions and passing callbacks 60 | 61 | 70 | 71 | 81 | 82 | #####Exporting Lua modules to the browser 83 | 84 | Remember that this code will run on the browser and some Lua modules won't make sense being used in this context! Attention: this feature is still under tests. 85 | 86 | 87 | 95 | 96 | #####Accessing Javascript modules such as JQuery 97 | 98 | 99 | 105 | 106 |
107 | 108 | 113 | 114 | -------------------------------------------------------------------------------- /src/sailor/form.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- form.lua, v0.6: generates html for forms 3 | -- This file is a part of Sailor project 4 | -- Copyright (c) 2014 Etiene Dalcol 5 | -- License: MIT 6 | -- http://sailorproject.org 7 | -------------------------------------------------------------------------------- 8 | 9 | local form = {} 10 | local tinsert, tconcat = table.insert, table.concat 11 | local model_name 12 | 13 | local meta = {} 14 | meta.__call = function(_,mname) 15 | model_name = mname 16 | return form 17 | end 18 | setmetatable(form,meta) 19 | 20 | local generic_input_types = { 21 | file = true, 22 | text = true, 23 | hidden = true, 24 | password = true, 25 | color = true, 26 | date = true, 27 | datetime = true, 28 | email = true, 29 | month = true, 30 | number = true, 31 | range = true, 32 | search = true, 33 | tel = true, 34 | time = true, 35 | url = true, 36 | week = true 37 | } 38 | 39 | 40 | local function defaults(model,attribute,html_options) 41 | local value = model[attribute] and 'value="'..tostring(model[attribute])..'"' 42 | local name = model['@name']..':'..attribute 43 | local id = 'id="'..name..'"' 44 | name = 'name="'..name..'"' 45 | html_options = html_options 46 | 47 | return name, id, html_options, value 48 | end 49 | 50 | local pack = function(...) 51 | if not table.pack then 52 | return {n=select('#',...),...} 53 | end 54 | return table.pack(...) 55 | end 56 | 57 | -- Inputs that work exactly the same changing only the type 58 | meta.__index = function(_,key) 59 | if generic_input_types[key] then 60 | return function(...) 61 | local t = pack(defaults(...)) 62 | tinsert(t,1,'') 65 | return tconcat(t,' ') 66 | end 67 | end 68 | return nil 69 | end 70 | 71 | -- More form inputs 72 | function form.textarea(model,attribute,html_options) 73 | local t = pack(defaults(model,attribute,html_options)) 74 | tinsert(t,1,'' .. (model[attribute] or '') .. '') 76 | 77 | return tconcat(t,' ') 78 | end 79 | 80 | function form.dropdown(model,attribute,list,prompt,html_options) 81 | local html = pack(defaults(model,attribute,html_options)) 82 | local value = model[attribute] 83 | list = list or {} 84 | 85 | tinsert(html,1,'') 87 | if prompt then 88 | local s = value and '' or 'selected' 89 | tinsert(html,'') 90 | end 91 | for k,v in pairs(list) do 92 | local selected = '' 93 | if k == value then 94 | selected = 'selected' 95 | end 96 | tinsert(html,'') 97 | end 98 | tinsert(html,'') 99 | 100 | return tconcat(html,' ') 101 | end 102 | 103 | 104 | 105 | -- layout: horizontal(default) or vertical 106 | function form.radio_list(model,attribute,list,default,layout,html_options) 107 | local _ 108 | _, _, html_options = defaults(model,attribute,html_options) 109 | local value = model[attribute] 110 | list = list or {} 111 | 112 | local t = {} 113 | for k,v in pairs(list) do 114 | local html = {} 115 | tinsert(html, '') 125 | tinsert(html,v) 126 | 127 | tinsert(t,tconcat(html,' ')) 128 | end 129 | 130 | if layout == 'vertical' then 131 | return tconcat(t,'
') 132 | end 133 | return tconcat(t,' ') 134 | end 135 | 136 | -- checked: boolean 137 | function form.checkbox(model,attribute,label,checked,html_options) 138 | local t = pack(defaults(model,attribute,html_options)) 139 | local value = model[attribute] 140 | 141 | tinsert(t,1,'') 146 | tinsert(t,(label or attribute)) 147 | 148 | return tconcat(t,' ') 149 | end 150 | 151 | function form.ify(data) 152 | local new_data = {} 153 | for k,v in pairs(data) do 154 | new_data[model_name .. ':' .. k] = v 155 | end 156 | return new_data 157 | end 158 | 159 | return form 160 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.5 alpha - Pluto 2 | 3 | 4 | #### Bug Fixes 5 | 6 | 7 | * **autogen** Fixes autogen page not opening ([5347acb3](https://github.com/sailorproject/sailor/commit/5347acb3)) 8 | * **db** Changes query for postgres compatibility on table_exists ([85cc1f59](https://github.com/sailorproject/sailor/commit/85cc1f59)) 9 | * **tests** Adds posts fixtures to dev-app tests ([e53b0646](https://github.com/sailorproject/sailor/commit/e53b0646)) 10 | * **travis integration** Using a different rockspec for travis ci ([cbd48b2b](https://github.com/sailorproject/sailor/commit/cbd48b2b)) 11 | * **latclient** 12 | * Updates starlight version ([ff0d6642](https://github.com/sailorproject/sailor/commit/ff0d6642)) 13 | * Changes string delimiters to multiline strings ([5fe91827](https://github.com/sailorproject/sailor/commit/5fe91827)) 14 | * Load latclient settings from sailor.lua ([ec058470](https://github.com/sailorproject/sailor/commit/ec058470)) 15 | * Make set_application_path() simple again ([d1bc6fb7](https://github.com/sailorproject/sailor/commit/d1bc6fb7)) 16 | * Add compatibility with 4 different Lua to JS VMs for lua at client ([9d772384](https://github.com/sailorproject/sailor/commit/9d772384)) 17 | * Makes latclient not load the same module again ([4d7cc852](https://github.com/sailorproject/sailor/commit/4d7cc852)) 18 | * **remy** 19 | * Make Lighttpd support work again ([e9190523](https://github.com/sailorproject/sailor/commit/e9190523)) 20 | * Fixes openresty environment issue ([42545098](https://github.com/sailorproject/sailor/commit/42545098)) 21 | * **core** 22 | * Allow hiding stack trace ([b75c9181](https://github.com/sailorproject/sailor/commit/b75c9181)) 23 | * Path fixes on set_application_path and make_url. ([7d4528d5](https://github.com/sailorproject/sailor/commit/7d4528d5)) 24 | * Fixing set application path ([9d9a1ab6](https://github.com/sailorproject/sailor/commit/9d9a1ab6)) 25 | * Fixing route paths and status code for Xavante and Apache ([e2369138](https://github.com/sailorproject/sailor/commit/e2369138)) 26 | * **blank-app** Adds test bootstraps to blank app ([eef903f9](https://github.com/sailorproject/sailor/commit/eef903f9)) 27 | * **page** 28 | * Fixes typo on page.redirect ([2fe112eb](https://github.com/sailorproject/sailor/commit/2fe112eb)) 29 | * Adjusts path on page.include ([27376166](https://github.com/sailorproject/sailor/commit/27376166)) 30 | * Page inspect printing number of gsubs, Fixes #36 ([0c848a7c](https://github.com/sailorproject/sailor/commit/0c848a7c)) 31 | * **model** Adds missing db.close on model:fild_all ([e07960fb](https://github.com/sailorproject/sailor/commit/e07960fb)) 32 | 33 | #### Features 34 | 35 | * **page** 36 | * Adds page:to_browser method ([bd5d95db](https://github.com/sailorproject/sailor/commit/bd5d95db)) 37 | * Adds page:tostring() function ([77c8f848](https://github.com/sailorproject/sailor/commit/77c8f848)) 38 | * **transactions** Supporting transactions for major databases ([27ccf960](https://github.com/sailorproject/sailor/commit/27ccf960)) 39 | * **autogen** Adds autogen compatibility with sqlite3 and postgresql ([e0417418](https://github.com/sailorproject/sailor/commit/e0417418)) 40 | * **latclient** 41 | * Support Lua script tag when using starlight VM ([69402cfb](https://github.com/sailorproject/sailor/commit/69402cfb)) 42 | * Improve latclient support in persistent environments ([92d5d579](https://github.com/sailorproject/sailor/commit/92d5d579)) 43 | * **binary** Adds sailor enable command on binary to install extensions ([9efa2a34](https://github.com/sailorproject/sailor/commit/9efa2a34)) 44 | * **controllers** Allows controller to set a custom view path ([dbddf85f](https://github.com/sailorproject/sailor/commit/dbddf85f)) 45 | * **remy** 46 | * Adds suport to redirect when using openresty, Closes #47 ([ebb30c1e](https://github.com/sailorproject/sailor/commit/ebb30c1e)) 47 | * Improves openresty compatibility ([3d4aebd0](https://github.com/sailorproject/sailor/commit/3d4aebd0)) 48 | * **tests** 49 | * Adds openresty simulation on automated tests ([a5f4cd06](https://github.com/sailorproject/sailor/commit/a5f4cd06)) 50 | * **db** Adds support to openresty internal MySQL API. ([7e0bd062](https://github.com/sailorproject/sailor/commit/7e0bd062)) 51 | 52 | ### Breaking changes 53 | * `sailor` is no longer on global namespace, if you are accessing sailor attributes now you need to require 54 | 55 | ### Deprecations 56 | * sailor.make_url has been copied to page:make_url and will be removed on next versions 57 | * sailor.model has been copied to a call metamethod on the model module and will be removed on next version 58 | -------------------------------------------------------------------------------- /docs/manual_page_object.md: -------------------------------------------------------------------------------- 1 | ##Reference Manual 2 | 3 | ###The Page object 4 | The `page` object is being passed around controllers and views (implicitly) and it encapsulates several methods and attributes that may vary upon webservers. It can be used inside the actions of a controller and inside views. 5 | 6 | ####page:render( filename [*, parms*] ) 7 | Renders a view from a controller action, if there's a default layout configured, it will render the layout first and then render your view. 8 | 9 | * filename: string, filename without ".lp". The file must be inside /views/controller_name 10 | * parms: [optional] table, vars being passed ahead (env). 11 | 12 | Example: `page:render( 'index', {msg = 'hello'} )` 13 | 14 | ####page:include( path [*, parms*] ) 15 | Includes a .lp file from a .lp file. 16 | 17 | * path: string, full file path 18 | 19 | * parms: [optional] table, vars being passed ahead (env). 20 | 21 | Example: `page:include( '/views/incs/_defaultmenu' )` 22 | 23 | ####page:redirect( route [*, args*] ) 24 | Redirects to another action if the route string doesn't start with 'http://' or another address if it does. 25 | 26 | * route: string, "controller_name/action_name" or some website address. 27 | 28 | * args: [optional] table, vars being passed as get parameters (only for internal redirect). 29 | 30 | Example 1: `page:redirect( 'user/update', {id = 2} )` 31 | 32 | Example 2: `page:redirect( 'http://google.com' )` 33 | 34 | ####page:json( data, [*, parms*] ) 35 | Serializes data as JSON and sends it to the response body. 36 | 37 | * data: any serializable value (table, string, number). 38 | 39 | * parms: [optional] table, vars being passed to the dkjson.encode function. Refer to the [dkjson documentation](http://dkolf.de/src/dkjson-lua.fsl/wiki?name=Documentation) for details. Useful parameters are (indent, keyorder, level). 40 | 41 | ####page:write( data ) 42 | Writes a single string to the response body. 43 | 44 | * data: string. 45 | 46 | Example 1: `page:write( "Hello world!")` 47 | 48 | ####page:print( data ) 49 | (Apache2): Sends one or more variables to the response body. 50 | 51 | (Other webservers): Does the same as page:write does and writes a single string to the response body. 52 | 53 | * data: string. 54 | 55 | Example 1: `page:print( "Hello world!")` 56 | 57 | Example 2 (Apache2): `page:print( "Hello", " world", "!")` 58 | 59 | ####page:inspect( var ) 60 | Will inspect a var and print the result on the bottom of the page. It can be toggled on/off by setting conf.debug.inspect to true/false. Very useful for seeing the contents of a table, for example. 61 | * var: any type that you want to be stringfied. 62 | 63 | Example 1: `page:inspect( page.POST )` 64 | 65 | ####page:enable_cors([headers] ) 66 | Sends [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#The_HTTP_response_headers) to the client. 67 | 68 | * headers: [optional] table, contains which headers to pass, if nil then `Access-Control-Allow-Origin = "*"` is used. The keys are (allow_origin, expose_headers, max_age, allow_credentials, allow_methods, allow_headers). 69 | 70 | Example 1: `page:enable_cors()` 71 | 72 | Example 2: `page:enable_cors({allow_origin = "http://sailorproject.org"})` 73 | 74 | ####page.POST 75 | * table: contains the POST data, empty table if there is no POST data. 76 | 77 | ####page.GET 78 | * table: contains the GET data, empty table if there is no GET data. 79 | 80 | ####page.title 81 | * string *writeable*: By default it is set to the same as conf.sailor.app_name, but you can modify it before rendering a page, for example. 82 | 83 | ####page.theme 84 | * string *writeable*: Sailor comes bundled with a default theme, using Bootstrap. This is the name of a folder inside /themes folder. You can modify the default theme of your application by editing conf.sailor.theme. You can modify the theme of just one page by setting page.theme to something else on the controller action before rendering your page. You can set it to nil for displaying no theme at all, like for serving a JSON, for example. 85 | 86 | ####page.layout 87 | * string *writeable*: Sailor default's theme only comes with one layout, main. This refers to a .lp file inside the theme folder. You can create multiple layouts inside one same theme, like 1-column and 2-columns, for example. You can modify the default layout of your application by editing conf.sailor.layout. You can modify the layout of just one page by setting page.layout to something else on the controller action before rendering your page. 88 | 89 | ####page.r 90 | * userdata: request_rec structure that varies according to the webserver, comes with built-in functions. 91 | 92 | (Apache2): [The request_rec structure](http://modlua.org/api/request). 93 | -------------------------------------------------------------------------------- /docs/manual_form_module.md: -------------------------------------------------------------------------------- 1 | ##Reference Manual 2 | ###The form module 3 | This module will generate html to be used on forms that capture data for a model. It is handy and recommended. This module needs to be required on the view: `local form = require "sailor.form"`. Also please note that on views, `<%= var %>` is equal to ``. 4 | ####form.text(model,attribute,html_options) 5 | Generates a text field input. 6 | 7 | * model: object, an instantiated object from a model. 8 | 9 | * attribute: string, name of the attribute to which the value of this attribute will be sent to. 10 | 11 | * html_options: string, other things to be added inside the input tag. 12 | 13 | Example 1: 14 | 15 | <%= form.text(user, 'username', 'class="cute-form-input" width="300"') %> 16 | 17 | 18 | ####form.textarea(model,attribute,html_options) 19 | Generates a text area input. 20 | 21 | * model: object, an instantiated object from a model. 22 | 23 | * attribute: string, name of the attribute to which the value of this attribute will be sent to. 24 | 25 | * html_options: string, other things to be added inside the input tag. 26 | 27 | Example 1: 28 | 29 | <%= form.textarea(user, 'description', 'class="cute-form-input" width="300"') %> 30 | 31 | ####form.file(model,attribute,html_options) 32 | Generates a file input. 33 | 34 | * model: object, an instantiated object from a model. 35 | 36 | * attribute: string, name of the attribute to which the value of this attribute will be sent to. 37 | 38 | * html_options: string, other things to be added inside the input tag. 39 | 40 | Example 1: 41 | 42 | <%= form.file(user, 'profile_picture', 'class="cute-form-input" width="300"') %> 43 | 44 | ####form.dropdown(model,attribute,list,prompt,html_options) 45 | Generates a dropdown list. 46 | 47 | * model: object, an instantiated object from a model. 48 | 49 | * attribute: string, name of the attribute to which the value of this attribute will be sent to. 50 | 51 | * list: table, contains lists of options to be selected. 52 | 53 | * prompt: string, first option that contains a nil value. 54 | 55 | * html_options: string, other things to be added inside the select tag. 56 | 57 | Example 1: 58 | 59 | <%= form.dropdown( 60 | user, 61 | 'newsletter', 62 | { weekly = 'Receive weekly', monthly = 'Receive Monthly' }, 63 | 'Please select newsletter...', 64 | 'class="cute-form-input" width="300"' 65 | ) 66 | %> 67 | 68 | ####form.password(model,attribute,html_options) 69 | Generates a password input. 70 | 71 | * model: object, an instantiated object from a model. 72 | 73 | * attribute: string, name of the attribute to which the value of this attribute will be sent to. 74 | 75 | * html_options: string, other things to be added inside the input tag. 76 | 77 | Example 1: 78 | 79 | <%= form.password(user, 'password', 'class="cute-form-input" width="300"') %> 80 | 81 | ####form.radio_list(model,attribute,list,default,layout,html_options) 82 | Generates a set of radio buttons. 83 | 84 | * model: object, an instantiated object from a model. 85 | 86 | * attribute: string, name of the attribute to which the value of this attribute will be sent to. 87 | 88 | * list: table, contains lists of radios to be selected. 89 | 90 | * default: string or nil, which value should be selected by default. 91 | 92 | * layout: string or nil, 'vertical' or 'horizontal' (default when nil). 93 | 94 | * html_options: string, other things to be added inside the input tag. 95 | 96 | Example 1: 97 | 98 | <%= form.radio_list( 99 | user, 100 | 'newsletter', 101 | { weekly = 'Receive weekly', monthly = 'Receive Monthly' }, 102 | 'weekly', 103 | 'vertical' , 104 | 'class="cute-form-input" width="300"' 105 | ) 106 | %> 107 | 108 | ####form.checkbox(model,attribute,label,checked,html_options) 109 | Generates a checkbox. 110 | 111 | * model: object, an instantiated object from a model. 112 | 113 | * attribute: string, name of the attribute to which the value of this attribute will be sent to. 114 | 115 | * label: string or nil, text that will go next to the checkbox, defaults to attribute name when nil. 116 | 117 | * checked: boolean, whether or not the checkbox is checked by default. 118 | 119 | * html_options: string, other things to be added inside the input tag. 120 | 121 | Example 1: 122 | 123 | <%= form.checkbox( 124 | user, 125 | 'likes_puppies', 126 | "Do you like puppies?", 127 | true, 128 | 'class="cute-checkbox"' 129 | ) 130 | %> 131 | -------------------------------------------------------------------------------- /rockspecs/sailor-0.3-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Sailor" 2 | version = "0.3-1" 3 | source = { 4 | url = "git://github.com/Etiene/sailor", 5 | tag = "v0.3" 6 | } 7 | description = { 8 | summary = "A Lua MVC Framework", 9 | detailed = [[ 10 | Sailor is a web framework written in Lua that follows the MVC design pattern. 11 | ]], 12 | homepage = "http://sailorproject.org", 13 | license = "MIT" 14 | } 15 | dependencies = { 16 | "lua >= 5.1, < 5.3", 17 | 'datafile >= 0.1', 18 | 'luafilesystem >= 1.6.2', 19 | 'valua >= 0.2.2', 20 | 'lbase64 >= 20120807', 21 | 'cgilua >= 5.1.4', 22 | 'xavante >= 2.3', 23 | 'wsapi-xavante >= 1.6.1' 24 | } 25 | build = { 26 | type = "builtin", 27 | modules = { 28 | latclient = "src/latclient.lua", 29 | ['latclient.lp_handler'] = "src/latclient/lp_handler.lua", 30 | remy = "src/remy.lua", 31 | ['remy.cgilua'] = "src/remy/cgilua.lua", 32 | ['remy.mod_plua'] = "src/remy/mod_plua.lua", 33 | ['remy.nginx'] = "src/remy/nginx.lua", 34 | sailor = "src/sailor.lua", 35 | ['sailor.access'] = "src/sailor/access.lua", 36 | ['sailor.cookie'] = "src/sailor/cookie.lua", 37 | ['sailor.db'] = "src/sailor/db.lua", 38 | ['sailor.demo-app.conf.conf'] = "src/sailor/demo-app/conf/conf.lua", 39 | ['sailor.demo-app.start-server'] = "src/sailor/demo-app/start-server.lua", 40 | ['sailor.demo-app.controllers.main'] = "src/sailor/demo-app/controllers/main.lua", 41 | ['sailor.demo-app.index'] = "src/sailor/demo-app/index.lua", 42 | ['sailor.form'] = "src/sailor/form.lua", 43 | ['sailor.model'] = "src/sailor/model.lua", 44 | ['sailor.session'] = "src/sailor/session.lua", 45 | ['web_utils.lp'] = "src/web_utils/lp.lua", 46 | ['web_utils.lp_ex'] = "src/web_utils/lp_ex.lua", 47 | ['web_utils.serialize'] = "src/web_utils/serialize.lua", 48 | ['web_utils.session'] = "src/web_utils/session.lua", 49 | ['web_utils.utils'] = "src/web_utils/utils.lua" 50 | }, 51 | install = { 52 | lua = { 53 | ["sailor.demo-app.htaccess"] = "src/sailor/demo-app/.htaccess", 54 | ["sailor.demo-app.conf.htaccess"] = "src/sailor/demo-app/conf/.htaccess", 55 | ["sailor.demo-app.controllers.htaccess"] = "src/sailor/demo-app/controllers/.htaccess", 56 | ["sailor.demo-app.models.htaccess"] = "src/sailor/demo-app/models/.htaccess", 57 | ["sailor.demo-app.runtime.tmp.htaccess"] = "src/sailor/demo-app/runtime/tmp/.htaccess", 58 | ["sailor.demo-app.views.main.index"] = "src/sailor/demo-app/views/main/index.lp", 59 | ["sailor.demo-app.pub.thirdparty.latclient.js.js-lua"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/js-lua.js", 60 | ["sailor.demo-app.pub.thirdparty.latclient.js.latclient"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/latclient.js", 61 | ["sailor.demo-app.pub.thirdparty.latclient.js.lib.lua51"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/lib/lua5.1.5.min.js", 62 | ["sailor.demo-app.themes.default.css.bootstrap-theme"] = "src/sailor/demo-app/themes/default/css/bootstrap-theme.css", 63 | ["sailor.demo-app.themes.default.css.bootstrap"] = "src/sailor/demo-app/themes/default/css/bootstrap.css", 64 | ["sailor.demo-app.themes.default.css.bootstrap-thememin"] = "src/sailor/demo-app/themes/default/css/bootstrap-theme.min.css", 65 | ["sailor.demo-app.themes.default.css.bootstrapmin"] = "src/sailor/demo-app/themes/default/css/bootstrap.min.css", 66 | ["sailor.demo-app.themes.default.css.sticky-footer-navbar"] = "src/sailor/demo-app/themes/default/css/sticky-footer-navbar.css", 67 | ["sailor.demo-app.themes.default.js.jquery"] = "src/sailor/demo-app/themes/default/js/jquery-1.10.2.min.js", 68 | ["sailor.demo-app.themes.default.js.bootstrap"] = "src/sailor/demo-app/themes/default/js/bootstrap.js", 69 | ["sailor.demo-app.themes.default.js.bootstrapmin"] = "src/sailor/demo-app/themes/default/js/bootstrap.min.js", 70 | ["sailor.demo-app.themes.default.fonts.glysvg"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.svg", 71 | ["sailor.demo-app.themes.default.fonts.glyttf"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.ttf", 72 | ["sailor.demo-app.themes.default.fonts.glyeot"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.eot", 73 | ["sailor.demo-app.themes.default.fonts.glywoff"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.woff", 74 | ["sailor.demo-app.themes.default.config"] = "src/sailor/demo-app/themes/default/config.json", 75 | ["sailor.demo-app.themes.default.main"] = "src/sailor/demo-app/themes/default/main.lp", 76 | }, 77 | bin = { 78 | sailor_create = "sailor_create" 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /rockspecs/sailor-0.3-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "Sailor" 2 | version = "0.3-2" 3 | source = { 4 | url = "git://github.com/Etiene/sailor", 5 | tag = "v0.3" 6 | } 7 | description = { 8 | summary = "A Lua MVC Framework", 9 | detailed = [[ 10 | Sailor is a web framework written in Lua that follows the MVC design pattern. 11 | ]], 12 | homepage = "http://sailorproject.org", 13 | license = "MIT" 14 | } 15 | dependencies = { 16 | "lua >= 5.1, < 5.3", 17 | 'datafile >= 0.1', 18 | 'luafilesystem >= 1.6.2', 19 | 'valua >= 0.2.2', 20 | 'lbase64 >= 20120807', 21 | 'cgilua >= 5.1.4, < 5.2', 22 | 'xavante >= 2.3', 23 | 'wsapi-xavante >= 1.6.1' 24 | } 25 | build = { 26 | type = "builtin", 27 | modules = { 28 | latclient = "src/latclient.lua", 29 | ['latclient.lp_handler'] = "src/latclient/lp_handler.lua", 30 | remy = "src/remy.lua", 31 | ['remy.cgilua'] = "src/remy/cgilua.lua", 32 | ['remy.mod_plua'] = "src/remy/mod_plua.lua", 33 | ['remy.nginx'] = "src/remy/nginx.lua", 34 | sailor = "src/sailor.lua", 35 | ['sailor.access'] = "src/sailor/access.lua", 36 | ['sailor.cookie'] = "src/sailor/cookie.lua", 37 | ['sailor.db'] = "src/sailor/db.lua", 38 | ['sailor.demo-app.conf.conf'] = "src/sailor/demo-app/conf/conf.lua", 39 | ['sailor.demo-app.start-server'] = "src/sailor/demo-app/start-server.lua", 40 | ['sailor.demo-app.controllers.main'] = "src/sailor/demo-app/controllers/main.lua", 41 | ['sailor.demo-app.index'] = "src/sailor/demo-app/index.lua", 42 | ['sailor.form'] = "src/sailor/form.lua", 43 | ['sailor.model'] = "src/sailor/model.lua", 44 | ['sailor.session'] = "src/sailor/session.lua", 45 | ['web_utils.lp'] = "src/web_utils/lp.lua", 46 | ['web_utils.lp_ex'] = "src/web_utils/lp_ex.lua", 47 | ['web_utils.serialize'] = "src/web_utils/serialize.lua", 48 | ['web_utils.session'] = "src/web_utils/session.lua", 49 | ['web_utils.utils'] = "src/web_utils/utils.lua" 50 | }, 51 | install = { 52 | lua = { 53 | ["sailor.demo-app.htaccess"] = "src/sailor/demo-app/.htaccess", 54 | ["sailor.demo-app.conf.htaccess"] = "src/sailor/demo-app/conf/.htaccess", 55 | ["sailor.demo-app.controllers.htaccess"] = "src/sailor/demo-app/controllers/.htaccess", 56 | ["sailor.demo-app.models.htaccess"] = "src/sailor/demo-app/models/.htaccess", 57 | ["sailor.demo-app.runtime.tmp.htaccess"] = "src/sailor/demo-app/runtime/tmp/.htaccess", 58 | ["sailor.demo-app.views.main.index"] = "src/sailor/demo-app/views/main/index.lp", 59 | ["sailor.demo-app.pub.thirdparty.latclient.js.js-lua"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/js-lua.js", 60 | ["sailor.demo-app.pub.thirdparty.latclient.js.latclient"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/latclient.js", 61 | ["sailor.demo-app.pub.thirdparty.latclient.js.lib.lua51"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/lib/lua5.1.5.min.js", 62 | ["sailor.demo-app.themes.default.css.bootstrap-theme"] = "src/sailor/demo-app/themes/default/css/bootstrap-theme.css", 63 | ["sailor.demo-app.themes.default.css.bootstrap"] = "src/sailor/demo-app/themes/default/css/bootstrap.css", 64 | ["sailor.demo-app.themes.default.css.bootstrap-thememin"] = "src/sailor/demo-app/themes/default/css/bootstrap-theme.min.css", 65 | ["sailor.demo-app.themes.default.css.bootstrapmin"] = "src/sailor/demo-app/themes/default/css/bootstrap.min.css", 66 | ["sailor.demo-app.themes.default.css.sticky-footer-navbar"] = "src/sailor/demo-app/themes/default/css/sticky-footer-navbar.css", 67 | ["sailor.demo-app.themes.default.js.jquery"] = "src/sailor/demo-app/themes/default/js/jquery-1.10.2.min.js", 68 | ["sailor.demo-app.themes.default.js.bootstrap"] = "src/sailor/demo-app/themes/default/js/bootstrap.js", 69 | ["sailor.demo-app.themes.default.js.bootstrapmin"] = "src/sailor/demo-app/themes/default/js/bootstrap.min.js", 70 | ["sailor.demo-app.themes.default.fonts.glysvg"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.svg", 71 | ["sailor.demo-app.themes.default.fonts.glyttf"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.ttf", 72 | ["sailor.demo-app.themes.default.fonts.glyeot"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.eot", 73 | ["sailor.demo-app.themes.default.fonts.glywoff"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.woff", 74 | ["sailor.demo-app.themes.default.config"] = "src/sailor/demo-app/themes/default/config.json", 75 | ["sailor.demo-app.themes.default.main"] = "src/sailor/demo-app/themes/default/main.lp", 76 | }, 77 | bin = { 78 | sailor_create = "sailor_create" 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /rockspecs/sailor-0.2.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Sailor" 2 | version = "0.2.1-1" 3 | source = { 4 | url = "git://github.com/Etiene/sailor", 5 | tag = "v0.2.1" 6 | } 7 | description = { 8 | summary = "A Lua MVC Framework", 9 | detailed = [[ 10 | Sailor is a web framework written in Lua that follows the MVC design pattern. 11 | ]], 12 | homepage = "http://sailorproject.org", 13 | license = "MIT" 14 | } 15 | dependencies = { 16 | "lua >= 5.1, < 5.3", 17 | 'datafile >= 0.1', 18 | 'luafilesystem >= 1.6.2', 19 | 'valua >= 0.2.2', 20 | 'lbase64 >= 20120807', 21 | 'cgilua >= 5.1.4', 22 | 'xavante >= 2.3', 23 | 'wsapi-xavante >= 1.6.1' 24 | } 25 | build = { 26 | type = "builtin", 27 | modules = { 28 | latclient = "src/latclient.lua", 29 | ['latclient.lp_handler'] = "src/latclient/lp_handler.lua", 30 | remy = "src/remy.lua", 31 | ['remy.cgilua'] = "src/remy/cgilua.lua", 32 | ['remy.mod_plua'] = "src/remy/mod_plua.lua", 33 | ['remy.nginx'] = "src/remy/nginx.lua", 34 | sailor = "src/sailor.lua", 35 | ['sailor.access'] = "src/sailor/access.lua", 36 | ['sailor.cookie'] = "src/sailor/cookie.lua", 37 | ['sailor.db'] = "src/sailor/db.lua", 38 | ['sailor.demo-app.conf.conf'] = "src/sailor/demo-app/conf/conf.lua", 39 | ['sailor.demo-app.start-server'] = "src/sailor/demo-app/start-server.lua", 40 | ['sailor.demo-app.controllers.main'] = "src/sailor/demo-app/controllers/main.lua", 41 | ['sailor.demo-app.index'] = "src/sailor/demo-app/index.lua", 42 | ['sailor.form'] = "src/sailor/form.lua", 43 | ['sailor.model'] = "src/sailor/model.lua", 44 | ['sailor.session'] = "src/sailor/session.lua", 45 | ['web_utils.lp'] = "src/web_utils/lp.lua", 46 | ['web_utils.lp_ex'] = "src/web_utils/lp_ex.lua", 47 | ['web_utils.serialize'] = "src/web_utils/serialize.lua", 48 | ['web_utils.session'] = "src/web_utils/session.lua", 49 | ['web_utils.utils'] = "src/web_utils/utils.lua" 50 | }, 51 | install = { 52 | lua = { 53 | ["sailor.demo-app.htaccess"] = "src/sailor/demo-app/.htaccess", 54 | ["sailor.demo-app.conf.htaccess"] = "src/sailor/demo-app/conf/.htaccess", 55 | ["sailor.demo-app.controllers.htaccess"] = "src/sailor/demo-app/controllers/.htaccess", 56 | ["sailor.demo-app.models.htaccess"] = "src/sailor/demo-app/models/.htaccess", 57 | ["sailor.demo-app.runtime.tmp.htaccess"] = "src/sailor/demo-app/runtime/tmp/.htaccess", 58 | ["sailor.demo-app.views.main.index"] = "src/sailor/demo-app/views/main/index.lp", 59 | ["sailor.demo-app.pub.thirdparty.latclient.js.js-lua"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/js-lua.js", 60 | ["sailor.demo-app.pub.thirdparty.latclient.js.latclient"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/latclient.js", 61 | ["sailor.demo-app.pub.thirdparty.latclient.js.lib.lua51"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/lib/lua5.1.5.min.js", 62 | ["sailor.demo-app.layouts.default.css.bootstrap-theme"] = "src/sailor/demo-app/layouts/default/css/bootstrap-theme.css", 63 | ["sailor.demo-app.layouts.default.css.bootstrap"] = "src/sailor/demo-app/layouts/default/css/bootstrap.css", 64 | ["sailor.demo-app.layouts.default.css.bootstrap-thememin"] = "src/sailor/demo-app/layouts/default/css/bootstrap-theme.min.css", 65 | ["sailor.demo-app.layouts.default.css.bootstrapmin"] = "src/sailor/demo-app/layouts/default/css/bootstrap.min.css", 66 | ["sailor.demo-app.layouts.default.css.sticky-footer-navbar"] = "src/sailor/demo-app/layouts/default/css/sticky-footer-navbar.css", 67 | ["sailor.demo-app.layouts.default.js.jquery"] = "src/sailor/demo-app/layouts/default/js/jquery-1.10.2.min.js", 68 | ["sailor.demo-app.layouts.default.js.bootstrap"] = "src/sailor/demo-app/layouts/default/js/bootstrap.js", 69 | ["sailor.demo-app.layouts.default.js.bootstrapmin"] = "src/sailor/demo-app/layouts/default/js/bootstrap.min.js", 70 | ["sailor.demo-app.layouts.default.fonts.glysvg"] = "src/sailor/demo-app/layouts/default/fonts/glyphicons-halflings-regular.svg", 71 | ["sailor.demo-app.layouts.default.fonts.glyttf"] = "src/sailor/demo-app/layouts/default/fonts/glyphicons-halflings-regular.ttf", 72 | ["sailor.demo-app.layouts.default.fonts.glyeot"] = "src/sailor/demo-app/layouts/default/fonts/glyphicons-halflings-regular.eot", 73 | ["sailor.demo-app.layouts.default.fonts.glywoff"] = "src/sailor/demo-app/layouts/default/fonts/glyphicons-halflings-regular.woff", 74 | ["sailor.demo-app.layouts.default.config"] = "src/sailor/demo-app/layouts/default/config.json", 75 | ["sailor.demo-app.layouts.default.index"] = "src/sailor/demo-app/layouts/default/index.lp", 76 | }, 77 | bin = { 78 | sailor_create = "sailor_create" 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /rockspecs/sailor-0.2.1-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "Sailor" 2 | version = "0.2.1-2" 3 | source = { 4 | url = "git://github.com/sailorproject/sailor", 5 | tag = "v0.2.1" 6 | } 7 | description = { 8 | summary = "A Lua MVC Framework", 9 | detailed = [[ 10 | Sailor is a web framework written in Lua that follows the MVC design pattern. 11 | ]], 12 | homepage = "http://sailorproject.org", 13 | license = "MIT" 14 | } 15 | dependencies = { 16 | "lua >= 5.1, < 5.3", 17 | 'datafile >= 0.1', 18 | 'luafilesystem >= 1.6.2', 19 | 'valua >= 0.2.2', 20 | 'lbase64 >= 20120807', 21 | 'cgilua >= 5.1.4', 22 | 'xavante >= 2.3', 23 | 'wsapi-xavante >= 1.6.1' 24 | } 25 | build = { 26 | type = "builtin", 27 | modules = { 28 | latclient = "src/latclient.lua", 29 | ['latclient.lp_handler'] = "src/latclient/lp_handler.lua", 30 | remy = "src/remy.lua", 31 | ['remy.cgilua'] = "src/remy/cgilua.lua", 32 | ['remy.mod_plua'] = "src/remy/mod_plua.lua", 33 | ['remy.nginx'] = "src/remy/nginx.lua", 34 | sailor = "src/sailor.lua", 35 | ['sailor.access'] = "src/sailor/access.lua", 36 | ['sailor.cookie'] = "src/sailor/cookie.lua", 37 | ['sailor.db'] = "src/sailor/db.lua", 38 | ['sailor.demo-app.conf.conf'] = "src/sailor/demo-app/conf/conf.lua", 39 | ['sailor.demo-app.start-server'] = "src/sailor/demo-app/start-server.lua", 40 | ['sailor.demo-app.controllers.main'] = "src/sailor/demo-app/controllers/main.lua", 41 | ['sailor.demo-app.index'] = "src/sailor/demo-app/index.lua", 42 | ['sailor.form'] = "src/sailor/form.lua", 43 | ['sailor.model'] = "src/sailor/model.lua", 44 | ['sailor.session'] = "src/sailor/session.lua", 45 | ['web_utils.lp'] = "src/web_utils/lp.lua", 46 | ['web_utils.lp_ex'] = "src/web_utils/lp_ex.lua", 47 | ['web_utils.serialize'] = "src/web_utils/serialize.lua", 48 | ['web_utils.session'] = "src/web_utils/session.lua", 49 | ['web_utils.utils'] = "src/web_utils/utils.lua" 50 | }, 51 | install = { 52 | lua = { 53 | ["sailor.demo-app.htaccess"] = "src/sailor/demo-app/.htaccess", 54 | ["sailor.demo-app.conf.htaccess"] = "src/sailor/demo-app/conf/.htaccess", 55 | ["sailor.demo-app.controllers.htaccess"] = "src/sailor/demo-app/controllers/.htaccess", 56 | ["sailor.demo-app.models.htaccess"] = "src/sailor/demo-app/models/.htaccess", 57 | ["sailor.demo-app.runtime.tmp.htaccess"] = "src/sailor/demo-app/runtime/tmp/.htaccess", 58 | ["sailor.demo-app.views.main.index"] = "src/sailor/demo-app/views/main/index.lp", 59 | ["sailor.demo-app.pub.thirdparty.latclient.js.js-lua"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/js-lua.js", 60 | ["sailor.demo-app.pub.thirdparty.latclient.js.latclient"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/latclient.js", 61 | ["sailor.demo-app.pub.thirdparty.latclient.js.lib.lua51"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/lib/lua5.1.5.min.js", 62 | ["sailor.demo-app.layouts.default.css.bootstrap-theme"] = "src/sailor/demo-app/layouts/default/css/bootstrap-theme.css", 63 | ["sailor.demo-app.layouts.default.css.bootstrap"] = "src/sailor/demo-app/layouts/default/css/bootstrap.css", 64 | ["sailor.demo-app.layouts.default.css.bootstrap-thememin"] = "src/sailor/demo-app/layouts/default/css/bootstrap-theme.min.css", 65 | ["sailor.demo-app.layouts.default.css.bootstrapmin"] = "src/sailor/demo-app/layouts/default/css/bootstrap.min.css", 66 | ["sailor.demo-app.layouts.default.css.sticky-footer-navbar"] = "src/sailor/demo-app/layouts/default/css/sticky-footer-navbar.css", 67 | ["sailor.demo-app.layouts.default.js.jquery"] = "src/sailor/demo-app/layouts/default/js/jquery-1.10.2.min.js", 68 | ["sailor.demo-app.layouts.default.js.bootstrap"] = "src/sailor/demo-app/layouts/default/js/bootstrap.js", 69 | ["sailor.demo-app.layouts.default.js.bootstrapmin"] = "src/sailor/demo-app/layouts/default/js/bootstrap.min.js", 70 | ["sailor.demo-app.layouts.default.fonts.glysvg"] = "src/sailor/demo-app/layouts/default/fonts/glyphicons-halflings-regular.svg", 71 | ["sailor.demo-app.layouts.default.fonts.glyttf"] = "src/sailor/demo-app/layouts/default/fonts/glyphicons-halflings-regular.ttf", 72 | ["sailor.demo-app.layouts.default.fonts.glyeot"] = "src/sailor/demo-app/layouts/default/fonts/glyphicons-halflings-regular.eot", 73 | ["sailor.demo-app.layouts.default.fonts.glywoff"] = "src/sailor/demo-app/layouts/default/fonts/glyphicons-halflings-regular.woff", 74 | ["sailor.demo-app.layouts.default.config"] = "src/sailor/demo-app/layouts/default/config.json", 75 | ["sailor.demo-app.layouts.default.index"] = "src/sailor/demo-app/layouts/default/index.lp", 76 | }, 77 | bin = { 78 | sailor_create = "sailor_create" 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/sailor/cli.lua: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -- cli.lua v0.1.2: Functions for sailor's command line utility 3 | -- This file is a part of Sailor project 4 | -- Copyright (c) 2014 Etiene Dalcol 5 | -- License: MIT 6 | -- http://sailorproject.org 7 | -------------------------------------------------------------------------------- 8 | 9 | local lfs = require "lfs" 10 | 11 | local cli = {} 12 | 13 | local function get_sailor_path(current_dir) 14 | local sailor_path = ((debug.getinfo(1).source):match("^@?(.-)/sailor$")) 15 | 16 | local f = sailor_path and io.open(sailor_path.."/src/sailor.lua", "r") 17 | if not f then 18 | local datafile = require("datafile") 19 | sailor_path = datafile.path("sailor/cookie.lua"):match("^@?(.-)/sailor/cookie.lua$") 20 | else 21 | f:close() 22 | if sailor_path == '.' then 23 | sailor_path = current_dir.."/src" 24 | elseif sailor_path:match("^.") then 25 | local path = sailor_path:match(".(.-)") 26 | sailor_path = current_dir.."/sailor"..tostring(path).."/src" 27 | elseif not sailor_path:match("^/") then 28 | sailor_path = current_dir.."/src/"..sailor_path 29 | else 30 | sailor_path = sailor_path.."/src" 31 | end 32 | end 33 | return sailor_path 34 | end 35 | 36 | function cli.create(args, _) 37 | local name = string.gsub(args.name:lower(),' ','_') 38 | local current_dir = lfs.currentdir() 39 | local destiny = args.path or current_dir 40 | 41 | local sailor_path = get_sailor_path(current_dir) 42 | 43 | local raw_app = sailor_path.."/sailor/blank-app" 44 | local new_app = destiny.."/"..name 45 | assert(os.execute("cp -a '"..raw_app.."' '"..new_app.."'")) 46 | 47 | local htaccess = assert(io.open (new_app.."/.htaccess" , "r")) 48 | local src = htaccess:read("*a") 49 | htaccess:close() 50 | 51 | htaccess = assert(io.open (new_app.."/.htaccess" , "w")) 52 | src = string.gsub(src,"{{path}}",sailor_path) 53 | htaccess:write(src) 54 | htaccess:close() 55 | 56 | local conf = assert(io.open (new_app.."/conf/conf.lua" , "r")) 57 | src = conf:read("*a") 58 | conf:close() 59 | conf = assert(io.open (new_app.."/conf/conf.lua" , "w")) 60 | src = string.gsub(src,"Sailor! A Lua MVC Framework", args.name) 61 | conf:write(src) 62 | conf:close() 63 | 64 | print("done!") 65 | os.exit(0) 66 | end 67 | 68 | function cli.enable(args, _) 69 | local name = 'sailor-'..args.name 70 | local current_dir = lfs.currentdir() 71 | local sailor_path = get_sailor_path(current_dir) 72 | 73 | assert(os.execute('luarocks install '..name)) 74 | 75 | local ext_app = sailor_path.."/sailor/"..name.."/app" 76 | assert(os.execute("mkdir -p extensions")) 77 | local err = assert(os.execute("cp -a '"..ext_app.."' extensions/"..name)) 78 | if err == 0 then 79 | local index_file = assert(io.open ("index.lua" , "r")) 80 | local src = index_file:read("*a") 81 | index_file:close() 82 | 83 | local package_path = "package.path = base_dir..'extensions/"..name.."/?.lua;'..package.path" 84 | src = string.gsub(src,"\nlocal sailor",package_path.."\n\nlocal sailor") 85 | index_file = assert(io.open ("index.lua" , "w")) 86 | index_file:write(src) 87 | index_file:close() 88 | print("New files:") 89 | print("extensions/"..name.."/*\n") 90 | print("Files modified:") 91 | print("index.lua\n") 92 | print("done!") 93 | os.exit(0) 94 | end 95 | end 96 | 97 | function cli.gen_all(args) 98 | local model = require "sailor.model" 99 | model.generate_model(args.table_name) 100 | model.generate_crud(args.table_name) 101 | end 102 | 103 | function cli.gen_crud(args) 104 | local model = require "sailor.model" 105 | model.generate_crud(args.model_name) 106 | end 107 | 108 | function cli.gen_model(args) 109 | local model = require "sailor.model" 110 | model.generate_model(args.table_name) 111 | end 112 | 113 | function cli.start() 114 | local ok, _ = pcall(require, "start-server") 115 | if not ok then 116 | print("Start script not found. Please run this command from the root dir of your Sailor app.") 117 | os.exit(1, true) 118 | end 119 | end 120 | 121 | function cli.test(args, _) 122 | local ok 123 | 124 | local flags = table.concat(args.EXTRA_FLAGS, " ") 125 | 126 | if args.resty then 127 | ok = os.execute('resty tests/bootstrap_resty.lua') 128 | else 129 | ok = os.execute('busted --helper=tests/bootstrap.lua '..flags..' tests/unit/* tests/functional/*') 130 | end 131 | 132 | if type(ok) == "number" then return ok end -- Lua 5.1 just returns the status code 133 | local exit_code = ok and 0 or 1 -- don't care about actual value 134 | 135 | if exit_code and exit_code ~= 0 then 136 | -- exit code sometimes is > 255 and fails to be propagated 137 | os.exit(1, true) 138 | end 139 | end 140 | 141 | function cli.version() 142 | print("Sailor: version " .. require "sailor.version") 143 | os.exit(0) 144 | end 145 | 146 | return cli 147 | -------------------------------------------------------------------------------- /rockspecs/sailor-0.3-3.rockspec: -------------------------------------------------------------------------------- 1 | package = "Sailor" 2 | version = "0.3-3" 3 | source = { 4 | url = "git://github.com/Etiene/sailor", 5 | tag = "v0.3" 6 | } 7 | description = { 8 | summary = "A Lua MVC Framework", 9 | detailed = [[ 10 | Sailor is a web framework written in Lua that follows the MVC design pattern. 11 | ]], 12 | homepage = "http://sailorproject.org", 13 | license = "MIT" 14 | } 15 | dependencies = { 16 | "lua >= 5.1, < 5.3", 17 | 'datafile >= 0.1', 18 | 'luafilesystem >= 1.6.2', 19 | 'valua >= 0.2.2', 20 | 'lbase64 >= 20120807', 21 | 'cgilua >= 5.1.4, < 5.2', 22 | 'xavante >= 2.3', 23 | 'wsapi-xavante >= 1.6.1' 24 | } 25 | build = { 26 | type = "builtin", 27 | modules = { 28 | latclient = "src/latclient.lua", 29 | ['latclient.lp_handler'] = "src/latclient/lp_handler.lua", 30 | remy = "src/remy.lua", 31 | ['remy.cgilua'] = "src/remy/cgilua.lua", 32 | ['remy.mod_plua'] = "src/remy/mod_plua.lua", 33 | ['remy.mod_magnet'] = "src/remy/mod_magnet.lua", 34 | ['remy.lwan'] = "src/remy/lwan.lua", 35 | ['remy.nginx'] = "src/remy/nginx.lua", 36 | sailor = "src/sailor.lua", 37 | ['sailor.access'] = "src/sailor/access.lua", 38 | ['sailor.autogen'] = "src/sailor/autogen.lua", 39 | ['sailor.cookie'] = "src/sailor/cookie.lua", 40 | ['sailor.db'] = "src/sailor/db.lua", 41 | ['sailor.demo-app.conf.conf'] = "src/sailor/demo-app/conf/conf.lua", 42 | ['sailor.demo-app.start-server'] = "src/sailor/demo-app/start-server.lua", 43 | ['sailor.demo-app.controllers.main'] = "src/sailor/demo-app/controllers/main.lua", 44 | ['sailor.demo-app.index'] = "src/sailor/demo-app/index.lua", 45 | ['sailor.demo-app.index-magnet'] = "src/sailor/demo-app/index-magnet.lua", 46 | ['sailor.form'] = "src/sailor/form.lua", 47 | ['sailor.model'] = "src/sailor/model.lua", 48 | ['sailor.session'] = "src/sailor/session.lua", 49 | ['web_utils.lp'] = "src/web_utils/lp.lua", 50 | ['web_utils.lp_ex'] = "src/web_utils/lp_ex.lua", 51 | ['web_utils.serialize'] = "src/web_utils/serialize.lua", 52 | ['web_utils.session'] = "src/web_utils/session.lua", 53 | ['web_utils.utils'] = "src/web_utils/utils.lua" 54 | }, 55 | install = { 56 | lua = { 57 | ["sailor.demo-app.htaccess"] = "src/sailor/demo-app/.htaccess", 58 | ["sailor.demo-app.conf.htaccess"] = "src/sailor/demo-app/conf/.htaccess", 59 | ["sailor.demo-app.controllers.htaccess"] = "src/sailor/demo-app/controllers/.htaccess", 60 | ["sailor.demo-app.models.htaccess"] = "src/sailor/demo-app/models/.htaccess", 61 | ["sailor.demo-app.runtime.tmp.htaccess"] = "src/sailor/demo-app/runtime/tmp/.htaccess", 62 | ["sailor.demo-app.views.main.index"] = "src/sailor/demo-app/views/main/index.lp", 63 | ["sailor.demo-app.pub.thirdparty.latclient.js.js-lua"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/js-lua.js", 64 | ["sailor.demo-app.pub.thirdparty.latclient.js.latclient"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/latclient.js", 65 | ["sailor.demo-app.pub.thirdparty.latclient.js.lib.lua51"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/lib/lua5.1.5.min.js", 66 | ["sailor.demo-app.themes.default.css.bootstrap-theme"] = "src/sailor/demo-app/themes/default/css/bootstrap-theme.css", 67 | ["sailor.demo-app.themes.default.css.bootstrap"] = "src/sailor/demo-app/themes/default/css/bootstrap.css", 68 | ["sailor.demo-app.themes.default.css.bootstrap-thememin"] = "src/sailor/demo-app/themes/default/css/bootstrap-theme.min.css", 69 | ["sailor.demo-app.themes.default.css.bootstrapmin"] = "src/sailor/demo-app/themes/default/css/bootstrap.min.css", 70 | ["sailor.demo-app.themes.default.css.sticky-footer-navbar"] = "src/sailor/demo-app/themes/default/css/sticky-footer-navbar.css", 71 | ["sailor.demo-app.themes.default.js.jquery"] = "src/sailor/demo-app/themes/default/js/jquery-1.10.2.min.js", 72 | ["sailor.demo-app.themes.default.js.bootstrap"] = "src/sailor/demo-app/themes/default/js/bootstrap.js", 73 | ["sailor.demo-app.themes.default.js.bootstrapmin"] = "src/sailor/demo-app/themes/default/js/bootstrap.min.js", 74 | ["sailor.demo-app.themes.default.fonts.glysvg"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.svg", 75 | ["sailor.demo-app.themes.default.fonts.glyttf"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.ttf", 76 | ["sailor.demo-app.themes.default.fonts.glyeot"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.eot", 77 | ["sailor.demo-app.themes.default.fonts.glywoff"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.woff", 78 | ["sailor.demo-app.themes.default.config"] = "src/sailor/demo-app/themes/default/config.json", 79 | ["sailor.demo-app.themes.default.main"] = "src/sailor/demo-app/themes/default/main.lp", 80 | }, 81 | bin = { 82 | sailor_create = "sailor_create" 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /rockspecs/sailor-0.3-4.rockspec: -------------------------------------------------------------------------------- 1 | package = "Sailor" 2 | version = "0.3-4" 3 | source = { 4 | url = "git://github.com/sailorproject/sailor", 5 | tag = "v0.3" 6 | } 7 | description = { 8 | summary = "A Lua MVC Framework", 9 | detailed = [[ 10 | Sailor is a web framework written in Lua that follows the MVC design pattern. 11 | ]], 12 | homepage = "http://sailorproject.org", 13 | license = "MIT" 14 | } 15 | dependencies = { 16 | "lua >= 5.1, < 5.3", 17 | 'datafile >= 0.1', 18 | 'luafilesystem >= 1.6.2', 19 | 'valua >= 0.2.2', 20 | 'lbase64 >= 20120807', 21 | 'cgilua >= 5.1.4, < 5.2', 22 | 'xavante >= 2.3', 23 | 'wsapi-xavante >= 1.6.1' 24 | } 25 | build = { 26 | type = "builtin", 27 | modules = { 28 | latclient = "src/latclient.lua", 29 | ['latclient.lp_handler'] = "src/latclient/lp_handler.lua", 30 | remy = "src/remy.lua", 31 | ['remy.cgilua'] = "src/remy/cgilua.lua", 32 | ['remy.mod_plua'] = "src/remy/mod_plua.lua", 33 | ['remy.mod_magnet'] = "src/remy/mod_magnet.lua", 34 | ['remy.lwan'] = "src/remy/lwan.lua", 35 | ['remy.nginx'] = "src/remy/nginx.lua", 36 | sailor = "src/sailor.lua", 37 | ['sailor.access'] = "src/sailor/access.lua", 38 | ['sailor.autogen'] = "src/sailor/autogen.lua", 39 | ['sailor.cookie'] = "src/sailor/cookie.lua", 40 | ['sailor.db'] = "src/sailor/db.lua", 41 | ['sailor.demo-app.conf.conf'] = "src/sailor/demo-app/conf/conf.lua", 42 | ['sailor.demo-app.start-server'] = "src/sailor/demo-app/start-server.lua", 43 | ['sailor.demo-app.controllers.main'] = "src/sailor/demo-app/controllers/main.lua", 44 | ['sailor.demo-app.index'] = "src/sailor/demo-app/index.lua", 45 | ['sailor.demo-app.index-magnet'] = "src/sailor/demo-app/index-magnet.lua", 46 | ['sailor.form'] = "src/sailor/form.lua", 47 | ['sailor.model'] = "src/sailor/model.lua", 48 | ['sailor.session'] = "src/sailor/session.lua", 49 | ['web_utils.lp'] = "src/web_utils/lp.lua", 50 | ['web_utils.lp_ex'] = "src/web_utils/lp_ex.lua", 51 | ['web_utils.serialize'] = "src/web_utils/serialize.lua", 52 | ['web_utils.session'] = "src/web_utils/session.lua", 53 | ['web_utils.utils'] = "src/web_utils/utils.lua" 54 | }, 55 | install = { 56 | lua = { 57 | ["sailor.demo-app.htaccess"] = "src/sailor/demo-app/.htaccess", 58 | ["sailor.demo-app.conf.htaccess"] = "src/sailor/demo-app/conf/.htaccess", 59 | ["sailor.demo-app.controllers.htaccess"] = "src/sailor/demo-app/controllers/.htaccess", 60 | ["sailor.demo-app.models.htaccess"] = "src/sailor/demo-app/models/.htaccess", 61 | ["sailor.demo-app.runtime.tmp.htaccess"] = "src/sailor/demo-app/runtime/tmp/.htaccess", 62 | ["sailor.demo-app.views.main.index"] = "src/sailor/demo-app/views/main/index.lp", 63 | ["sailor.demo-app.pub.thirdparty.latclient.js.js-lua"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/js-lua.js", 64 | ["sailor.demo-app.pub.thirdparty.latclient.js.latclient"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/latclient.js", 65 | ["sailor.demo-app.pub.thirdparty.latclient.js.lib.lua51"] = "src/sailor/demo-app/pub/thirdparty/latclient/js/lib/lua5.1.5.min.js", 66 | ["sailor.demo-app.themes.default.css.bootstrap-theme"] = "src/sailor/demo-app/themes/default/css/bootstrap-theme.css", 67 | ["sailor.demo-app.themes.default.css.bootstrap"] = "src/sailor/demo-app/themes/default/css/bootstrap.css", 68 | ["sailor.demo-app.themes.default.css.bootstrap-thememin"] = "src/sailor/demo-app/themes/default/css/bootstrap-theme.min.css", 69 | ["sailor.demo-app.themes.default.css.bootstrapmin"] = "src/sailor/demo-app/themes/default/css/bootstrap.min.css", 70 | ["sailor.demo-app.themes.default.css.sticky-footer-navbar"] = "src/sailor/demo-app/themes/default/css/sticky-footer-navbar.css", 71 | ["sailor.demo-app.themes.default.js.jquery"] = "src/sailor/demo-app/themes/default/js/jquery-1.10.2.min.js", 72 | ["sailor.demo-app.themes.default.js.bootstrap"] = "src/sailor/demo-app/themes/default/js/bootstrap.js", 73 | ["sailor.demo-app.themes.default.js.bootstrapmin"] = "src/sailor/demo-app/themes/default/js/bootstrap.min.js", 74 | ["sailor.demo-app.themes.default.fonts.glysvg"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.svg", 75 | ["sailor.demo-app.themes.default.fonts.glyttf"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.ttf", 76 | ["sailor.demo-app.themes.default.fonts.glyeot"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.eot", 77 | ["sailor.demo-app.themes.default.fonts.glywoff"] = "src/sailor/demo-app/themes/default/fonts/glyphicons-halflings-regular.woff", 78 | ["sailor.demo-app.themes.default.config"] = "src/sailor/demo-app/themes/default/config.json", 79 | ["sailor.demo-app.themes.default.main"] = "src/sailor/demo-app/themes/default/main.lp", 80 | }, 81 | bin = { 82 | sailor_create = "sailor_create" 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /docs/INSTALL_MAC.md: -------------------------------------------------------------------------------- 1 | ## Installation for Mac 2 | 3 | Sailor is compatible with a variety of operating systems and webservers. On this example, we will use OS X 10.10 Yosemite 4 | 5 | ### Basic installation through LuaRocks (Xavante web server) 6 | 7 | For the purpose of this guide, it will be assumed that you already have Homebrew installed. Sailor is compatible with both Lua 5.1 and Lua 5.2 and either of them can be installed through brew. 8 | 9 | Let's begin by installing Lua (by default brew will install the version 5.1) and LuaRocks. LuaRocks is a package manager for Lua modules and if you don't have it already you will find it very useful. If you already have these, you may skip this step. 10 | 11 | brew update 12 | brew install lua luarocks 13 | 14 | 15 | Now we need to install Sailor: 16 | 17 | luarocks install sailor 18 | 19 | When installing Sailor through LuaRocks most of the dependencies will be installed with it, including Xavante, a lightweight webserver also written in Lua. However, libraries for manipulating data on a database will not be installed automatically because which database to use when developing a web application will be up to the developer. Once you have installed and configured your database of choice, you must also install the apropriate LuaSQL driver to allow your Sailor apps to communicate with it. So, supposed you are using MySQL, you must install LuaSQL-MySQL 20 | 21 | luarocks install luasql-mysql 22 | 23 | Now everything is ready to create and run your app! 24 | 25 | sailor create "My app" 26 | cd my_app 27 | lua start-server.lua 28 | 29 | You can view your app by opening your browser and navigating to `http://localhost:8080`. 30 | 31 | If you wish to use different web servers, please, read on. 32 | 33 | 34 | ### Alternative setup with Apache 2.4 and mod_lua 35 | 36 | This guide assumes that you have already completed Sailor's basic installation through LuaRocks. Now we need Apache, that can be installed view homebrew. The default apache build however, does not come with Lua module by default, so we must edit the brew install file. Also, Apache is not in the default repository of Homebrew formulas, nor are some dependencies, so we use the "brew tap" command to add other formula repositories. 37 | 38 | brew tap djl/homebrew-apache2 39 | brew install djl/apache2/apache24 40 | brew edit apache24 41 | 42 | Find the list of enabled flags, after `args = [`, add `--enable-lua` and save the file. You can add other flags to the argument list if necessary. The file is ususally localted at "/usr/local/Library/Taps/djl/homebrew-apache2/apache24.rb". 43 | 44 | brew install apache24 45 | 46 | Alternatively, if you are having issues downloading Apache via homebrew, you can download it directly from [apache.org](http://apache.org) and compile your own build. Remember to add the `--enable-lua` flag when running `./configure` along with other flags you may wish. 47 | 48 | Now, you need to enable `mod_lua` in Apache's configuration file. The file will probably be located at `/usr/local/etc/apache2/httpd.conf`. Add or uncomment the following directive: 49 | 50 | LoadModule lua_module modules/mod_lua.so 51 | 52 | Change the DirectoryIndex directive to: 53 | 54 | DirectoryIndex index.lua index.html 55 | 56 | Add the SetHandler directive: 57 | 58 | 59 | SetHandler lua-script 60 | 61 | 62 | It's also necessary to allow `.htaccess` files. Look for the `AllowOverride` directive in the configuration file and change from `None` (the default) to `All`. Now you can either add a VirtualHost for Apache to read from the directory of your previously created app or simply create your apps inside the directory Apache is reading from (for example /usr/local/var/www/htdocs). It is also necessary to 63 | 64 | sailor create "My app" /usr/local/var/www/htdocs 65 | 66 | Now, you should start Apache. You can use the following command for that: 67 | 68 | sudo apachectl start 69 | 70 | You can view your app by opening your browser and navigating to `http://localhost/my_app`. 71 | 72 | Keep in mind that this guide was made with a specific version of Apache (2.4.12) and in future releases some details might change. Other details like the location of apache's configuration files might also change according to the installation method used. It is recommended to always install the latest version of Apache. 73 | 74 | 75 | ### Alternative setup using Nginx and [OpenResty](http://openresty.org/) 76 | 77 | This guide assumes that you have already completed Sailor's basic installation through LuaRocks. Now we need OpenResty. Please follow the guide at [openresty.org](http://openresty.org/#Download) to download and install it. It is recommended to always install the latest version. 78 | 79 | Additionally, it might be necessary to add nginx binary to the PATH: 80 | 81 | PATH=/usr/local/openresty/nginx/sbin/:$PATH 82 | 83 | Then you can create your app and run it! 84 | 85 | sailor create "My app" 86 | cd my_app 87 | nginx -p `pwd`/ -c conf/nginx.conf 88 | 89 | And a small useful information, if you want to restart the server, run this same last comment but with an additional reload command: 90 | 91 | nginx -p `pwd`/ -c conf/nginx.conf -s reload 92 | 93 | -------------------------------------------------------------------------------- /docs/INSTALL_WIN.md: -------------------------------------------------------------------------------- 1 | ##Installation for Windows 2 | 3 | ### Installing Lua 4 | 5 | First, download and install Lua for Windows. You can get it from: 6 | 7 | 8 | 9 | Copy the files in the `src` directory of this repository to C:/Program Files/Lua/5.1/lua/. 10 | 11 | You will also need the LuaSec's ssl library, which is included in this ZIP: 12 | 13 | 14 | 15 | Unzip it and copy the `ssl.dll` file in the bin\clibs directory of the archive to C:/Program Files/Lua/5.1/clibs/. 16 | 17 | Copy the `ssl.lua` file and the `ssl` subdir in the lualibs directory to C:/Program Files/Lua/5.1/lua/. 18 | 19 | Alternatively, you may want to try to install it via luarocks: 20 | 21 | luarocks install LuaSec 22 | 23 | ###Installation for Apache 2 24 | 25 | Download Apache 2.4 according to your Windows version: 26 | 27 | Windows 7, 8/8.1, Vista, Server 2008, Server 2012 - 28 | 29 | Windows XP - 30 | 31 | Unzip the package (eg: httpd-2.4.9-win32.zip) to `C:\Apache24\`. 32 | 33 | Copy the files in the `src/sailor/blank-app` directory of this repository to C:/Apache24/htdocs/sailor/. 34 | 35 | ####Configuring mod_lua 36 | 37 | Edit `\conf\httpd.conf` and uncomment the following line to enable mod_lua: 38 | 39 | LoadModule lua_module modules/mod_lua.so 40 | 41 | Change the DirectoryIndex directive to: 42 | 43 | DirectoryIndex index.lua index.html 44 | 45 | Add the SetHandler directive: 46 | 47 | 48 | SetHandler lua-script 49 | 50 | 51 | Optionally, tweak mod_lua for high performance: 52 | 53 | LuaScope thread 54 | LuaCodeCache stat 55 | 56 | Add the LuaPackage* directives: 57 | 58 | 59 | LuaPackageCPath "C:/Program Files/Lua/5.1/clibs/?.dll" 60 | LuaPackagePath "C:/Program Files/Lua/5.1/lua/?.lua" 61 | LuaPackagePath "C:/Program Files/Lua/5.1/lua/?/init.lua" 62 | LuaPackagePath "C:/Apache24/htdocs/sailor/?.lua" 63 | LuaPackagePath "C:/Apache24/htdocs/sailor/?/init.lua" 64 | 65 | 66 | ####Alternative Installation with mod_plua 67 | 68 | Download mod_plua from . 69 | 70 | Install and configure it as explained at . 71 | 72 | ####Alternative Installation with CGILua 73 | 74 | TODO 75 | 76 | ####Done! 77 | 78 | Run bin\httpd.exe in the Apache24 directory. 79 | 80 | Now go to in your browser. You should see the default Sailor page. 81 | 82 | ###Installation for Nginx 83 | 84 | Download the latest Nginx version from: 85 | 86 | 87 | 88 | If you've not done it yet, you may need to install the Visual C++ Redistributable Setup: 89 | 90 | 91 | 92 | Unzip the nginx ZIP to a directory of your choice. 93 | 94 | Copy the `lua5.1.dll` and `lua51.dll` files from C:/Program Files/Lua/5.1/ to the root of the nginx directory, replacing lua51.dll from the original package. 95 | 96 | Copy the files in the `src/sailor/blank-app` directory of this repository to the html/sailor directory of the nginx dir. 97 | 98 | ####Configuring Nginx 99 | 100 | Rename the `conf\nginx-win.conf` file to `nginx.conf`. 101 | 102 | Open the nginx.conf file and add to the http block: 103 | 104 | lua_package_path 'C:/Program Files/Lua/5.1/lua/?.lua;html/sailor/?.lua;'; 105 | lua_package_cpath 'C:/Program Files/Lua/5.1/clibs/?.dll;'; 106 | 107 | You must also add to the server block: 108 | 109 | location ~ ^/sailor/(.+) { 110 | lua_need_request_body on; 111 | lua_code_cache off; 112 | content_by_lua_file html/sailor/$1; 113 | index index.lua index.lp; 114 | } 115 | location ~* ^.+\.(?:css|eot|js|json|png|svg|ttf|woff)$ { } 116 | 117 | ####Done! 118 | 119 | Now run nginx.exe and go to in your browser. 120 | 121 | ###Installation for Lighttpd 122 | 123 | Download the latest Lighttpd from: 124 | 125 | 126 | 127 | Unzip the Lighttpd ZIP to a directory of your choice. 128 | 129 | Copy the files in the `src/sailor/blank-app` directory of this repository to the htdocs/sailor directory of the Lighttpd dir. 130 | 131 | ####Configuring Lighttpd 132 | 133 | Open the `conf\lighttpd.conf` file, uncomment mod_magnet in server.modules, and add the following lines right after index-file.names: 134 | 135 | $HTTP["url"] =~ "^/sailor/index.lua" { 136 | magnet.attract-physical-path-to = ( server_root + "/htdocs/sailor/index-magnet.lua") 137 | } 138 | $HTTP["url"] =~ "^/sailor/(conf|controllers|models|runtime|views)/" { 139 | url.access-deny = ("") 140 | dir-listing.activate = "disable" 141 | } 142 | $HTTP["url"] =~ "^/sailor/themes/" { 143 | url.access-deny = (".lp") 144 | dir-listing.activate = "disable" 145 | } 146 | 147 | ####Done! 148 | 149 | Now run LightTPD.exe and go to in your browser. 150 | --------------------------------------------------------------------------------