├── LICENSE ├── README ├── TODO ├── bin └── mercury ├── lp.lua ├── mercury.lua ├── rockspecs └── mercury-scm-0.rockspec └── samples ├── fibonacci_codegen.lua ├── fibonacci_haml.lua ├── fibonacci_luapages.lua ├── greetings.lua └── no_pollution.lua /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Daniele Alessandri 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Mercury 2 | ... because Sinatra is not the only one performing on the stage. 3 | ================================================================================ 4 | 5 | -- Introduction ---------------------------------------------------------------- 6 | 7 | Mercury aims to be a Sinatra-like web framework (or DSL, if you like) for 8 | creating web applications in Lua, quickly and painlessly. 9 | 10 | require 'mercury' 11 | 12 | module('hello', package.seeall, mercury.application) 13 | 14 | get('/', function() 15 | return "Hello world!" 16 | end) 17 | 18 | Mercury is currently in its very early stages of development, it started like 19 | an hack and right now it still is nothing more than that. Having said that, I 20 | thought that things might get interesting and so I decided to release the 21 | actual source code and start working on it to get it properly done. 22 | 23 | -- About the name -------------------------------------------------------------- 24 | 25 | "Mercury" because: 26 | * like Sinatra, he is a legend of the music history. That is, Freddie Mercury. 27 | * most of the cool web projects coded in Lua have astronomical references: 28 | - the Kepler Project (web development platform) 29 | - Orbit (MVC framework) 30 | - Cosmo (templating system) 31 | - Sputnik (wiki engine) 32 | * it is the smallest planet in our solar system and the nearest to the Sun... 33 | * ... and Mercury is the smallest web framework and the nearest to the "core". 34 | 35 | -- How to execute the examples ------------------------------------------------- 36 | 37 | If you have installed Mercury using LuaRocks you should have the 'mercury' 38 | executable file available in your PATH, you just need to enter in the samples 39 | directory and launch one of the applications like this: 40 | 41 | mercury greetings 42 | 43 | If, instead, you have simply cloned the repository, you can launch one of the 44 | example applications from the root directory of the project like this: 45 | 46 | LUA_PATH="samples/?.lua;;" ./bin/mercury greetings 47 | 48 | -- Contributors ---------------------------------------------------------------- 49 | 50 | * Mauricio Henrique Bomfim (http://github.com/mauriciobomfim) 51 | Support for Lua Pages as a templating engine for Mercury. 52 | 53 | * Norman Clarke (http://github.com/norman) 54 | Rockspec file for LuaRocks and various fixes. 55 | 56 | * Francois Perrad (http://github.com/fperrad) 57 | Support for CodeGen as a templating engine for Mercury. 58 | 59 | -- Notes ----------------------------------------------------------------------- 60 | 61 | Want to help or contribute? Drop me a mail to suppakilla{at}gmail.com 62 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * Change the directory layout of the repository. 2 | 3 | * Write docs, more samples and a test suite. 4 | 5 | * Implement before/after hooks (using coroutines). 6 | 7 | * Implement halt and the not_found and error handlers. 8 | 9 | * Make streamable responses more user-friendly. 10 | 11 | * The rest that is currently missing... :-) 12 | -------------------------------------------------------------------------------- /bin/mercury: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | --[[ 3 | Mercury: http://github.com/nrk/mercury/ 4 | Xavante: http://www.keplerproject.org/xavante/ 5 | 6 | Usage: mercury APPNAME 7 | ]] 8 | 9 | pcall(require, 'luarocks.require') 10 | require 'xavante' 11 | require 'wsapi.xavante' 12 | 13 | if not arg[1] then 14 | print([[ 15 | Please specify a mercury application to boot with Xavante, e.g.: 16 | 17 | mercury greetings 18 | ]]) 19 | os.exit(1) 20 | end 21 | 22 | -- TODO: checks and more options 23 | if arg[1] == nil then error("please specify a mercury application") end 24 | local application = arg[1]:gsub('^(.-)%.lua$', '%1') 25 | 26 | xavante.HTTP{ 27 | server = {host = "127.0.0.1", port = 7654}, 28 | 29 | defaultHost = { 30 | rules = { 31 | { 32 | match = { "^/(.-)$" }, 33 | with = wsapi.xavante.makeHandler(application) 34 | } 35 | } 36 | } 37 | } 38 | 39 | xavante.start() 40 | -------------------------------------------------------------------------------- /lp.lua: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------- 2 | -- Lua Pages Template Engine. 3 | -- 4 | -- Based on: http://www.lua.inf.puc-rio.br/~mascarenhas/template 5 | -- From: Fabio Mascarenhas 6 | --------------------------------------------------------------------------- 7 | 8 | local find, format, gsub, strsub = 9 | string.find, string.format, string.gsub, string.sub 10 | local concat, tinsert = table.concat, table.insert 11 | local getfenv, setfenv, setmetatable = getfenv, setfenv, setmetatable 12 | local assert, loadstring, tostring = assert, loadstring, tostring 13 | local base = _G 14 | 15 | module(...) 16 | 17 | -- 18 | -- Builds a piece of Lua code which outputs the (part of the) given string. 19 | -- @param s String. 20 | -- @param i Number with the initial position in the string. 21 | -- @param f Number with the final position in the string (default == -1). 22 | -- @return String with the correspondent Lua code which outputs the part of the string. 23 | -- 24 | local function out(s, i, f) 25 | s = strsub(s, i, f or -1) 26 | if s == "" then return s end 27 | -- we could use `%q' here, but this way we have better control 28 | s = gsub(s, "([\\\n\'])", "\\%1") 29 | -- substitute '\r' by '\'+'r' and let `loadstring' reconstruct it 30 | s = gsub(s, "\r", "\\r") 31 | return format(" __outfunc('%s')", s) 32 | end 33 | 34 | 35 | ---------------------------------------------------------------------------- 36 | -- Translate the template to Lua code. 37 | -- @param s String to translate. 38 | -- @return String with translated code. 39 | ---------------------------------------------------------------------------- 40 | function translate(s) 41 | s = gsub(s, "<%%(.-)%%>", "") 42 | local res = {} 43 | local start = 1 -- start of print("XXXX") untranslated part in `s' 44 | while true do 45 | local ip, fp, target, exp, code = find(s, "<%?(%w*)[ \t]*(=?)(.-)%?>", start) 46 | if not ip then break end 47 | tinsert(res, out(s, start, ip-1)) 48 | if target ~= "" and target ~= "lua" then 49 | -- not for Lua; pass whole instruction to the output 50 | tinsert(res, out(s, ip, fp)) 51 | else 52 | if exp == "=" then -- expression? 53 | tinsert(res, format(" __outfunc(%s)", code)) 54 | else -- command 55 | tinsert(res, format(" %s", code)) 56 | end 57 | end 58 | start = fp + 1 59 | end 60 | tinsert(res, out(s, start)) 61 | return [[ 62 | local __env, __outfunc = ... 63 | local __restorenv 64 | local __result = {} 65 | if not __outfunc then 66 | local insert = tinsert 67 | local tostring = tostring 68 | __outfunc = function (s) 69 | if s then 70 | insert(__result, tostring(s)) 71 | end 72 | end 73 | end 74 | do 75 | local env = getfenv(1) 76 | local setfenv = setfenv 77 | setfenv(1, __env) 78 | __restoreenv = function () 79 | setfenv(2, env) 80 | end 81 | end 82 | ]] .. concat(res, "\n") .. [[ 83 | __restoreenv() 84 | return concat(__result) 85 | ]] 86 | end 87 | 88 | ---------------------------------------------------------------------------- 89 | -- Internal compilation cache. 90 | 91 | local cache = {} 92 | setmetatable(cache, { __index = function (tab, key) 93 | local new = {} 94 | tab[key] = new 95 | return new 96 | end }) 97 | 98 | ---------------------------------------------------------------------------- 99 | -- Translates a template into a Lua function. 100 | -- Does NOT execute the resulting function. 101 | -- Uses a cache of templates. 102 | -- @param string String with the template to be translated. 103 | -- @param chunkname String with the name of the chunk, for debugging purposes. 104 | -- @return Function with the resulting translation. 105 | 106 | function compile(string, chunkname) 107 | chunkname = chunkname or string 108 | local f, err = cache[string][chunkname] 109 | if f then return f end 110 | f = assert(loadstring(translate(string), chunkname)) 111 | setfenv(f, { tinsert = tinsert, concat = concat, 112 | getfenv = getfenv, setfenv = setfenv, 113 | tostring = tostring}) 114 | cache[string][chunkname] = f 115 | return f 116 | end 117 | 118 | ---------------------------------------------------------------------------- 119 | -- "Fills" the template using the environment env, and returns the result 120 | -- @param template String with template 121 | -- @param env Global environment for template 122 | -- @return String with result of template application 123 | 124 | function fill(template, env) 125 | local env = env or {} 126 | local prog = compile(template) 127 | local out = {} 128 | local outfunc = function (s) 129 | tinsert(out, s) 130 | end 131 | setmetatable(env, { __index = base }) -- combine with global environment 132 | prog(env, outfunc) 133 | return concat(out) 134 | end 135 | 136 | -------------------------------------------------------------------------------- /mercury.lua: -------------------------------------------------------------------------------- 1 | require 'wsapi.request' 2 | require 'wsapi.response' 3 | require 'wsapi.util' 4 | 5 | module('mercury', package.seeall) 6 | 7 | local mercury_env = getfenv() 8 | local route_env = setmetatable({ }, { __index = _G }) 9 | local route_table = { GET = {}, POST = {}, PUT = {}, DELETE = {} } 10 | 11 | local application_methods = { 12 | get = function(path, method, options) add_route('GET', path, method) end, 13 | post = function(path, method, options) add_route('POST', path, method) end, 14 | put = function(path, method, options) add_route('PUT', path, method) end, 15 | delete = function(path, method, options) add_route('DELETE', path, method) end, 16 | helper = function(name, method) set_helper(route_env, name, method) end, 17 | helpers = function(helpers) 18 | if type(helpers) == 'table' then 19 | set_helpers(route_env, helpers) 20 | elseif type(helpers) == 'function' then 21 | local temporary_env = setmetatable({}, { 22 | __newindex = function(e,k,v) 23 | set_helper(route_env, k, v) 24 | end, 25 | }) 26 | setfenv(helpers, temporary_env)() 27 | else 28 | -- TODO: raise an error? 29 | end 30 | end, 31 | } 32 | 33 | function set_helpers(environment, methods) 34 | for name, method in pairs(methods) do 35 | set_helper(environment, name, method) 36 | end 37 | end 38 | 39 | function set_helper(environment, name, method) 40 | if type(method) ~= 'function' then 41 | error('"' .. name .. '" is an invalid helper, only functions are allowed.') 42 | end 43 | environment[name] = setfenv(method, environment) 44 | end 45 | 46 | -- 47 | -- *** ext functions *** -- 48 | -- 49 | 50 | function merge_tables(...) 51 | local numargs, out = select('#', ...), {} 52 | for i = 1, numargs do 53 | local t = select(i, ...) 54 | if type(t) == "table" then 55 | for k, v in pairs(t) do out[k] = v end 56 | end 57 | end 58 | return out 59 | end 60 | 61 | -- 62 | -- *** route environment *** -- 63 | -- 64 | 65 | (function() setfenv(1, route_env) 66 | -- This is a glorious trick to setup a different environment for routes since 67 | -- Lua functions inherits the environment in which they are *created*! This 68 | -- will not be compatible with Lua 5.2, for which the new _ENV should provide 69 | -- a much more clean way to achieve a similar result. 70 | 71 | -- TODO: Create a function like setup_route_environment(fun) 72 | local templating_engines = { 73 | haml = function(template, options, locals) 74 | local haml = haml.new(options) 75 | return function(env) 76 | return haml:render(template, mercury_env.merge_tables(env, locals)) 77 | end 78 | end, 79 | cosmo = function(template, values) 80 | return function(env) 81 | return cosmo.fill(template, values) 82 | end 83 | end, 84 | string = function(template, ...) 85 | return function(env) 86 | return string.format(template, unpack(arg)) 87 | end 88 | end, 89 | lp = function(template, values) 90 | return function(env) 91 | local lp = require 'lp' 92 | return lp.fill(template, mercury_env.merge_tables(env, values)) 93 | end 94 | end, 95 | codegen = function(template, top, values) 96 | local CodeGen = require 'CodeGen' 97 | return function(env) 98 | local tmpl = CodeGen(template, values, env) 99 | return tmpl(top) 100 | end 101 | end, 102 | } 103 | 104 | local route_methods = { 105 | pass = function() 106 | coroutine.yield({ pass = true }) 107 | end, 108 | -- Use a table to group template-related methods to prevent name clashes. 109 | t = setmetatable({ }, { 110 | __index = function(env, name) 111 | local engine = templating_engines[name] 112 | 113 | if type(engine) == nil then 114 | error('cannot find template renderer "'.. name ..'"') 115 | end 116 | 117 | return function(...) 118 | coroutine.yield({ template = engine(...) }) 119 | end 120 | end 121 | }), 122 | } 123 | 124 | for k, v in pairs(route_methods) do route_env[k] = v end 125 | 126 | setfenv(1, mercury_env) end)() 127 | 128 | -- 129 | -- *** application *** -- 130 | -- 131 | 132 | function application(application, fun) 133 | if type(application) == 'string' then 134 | application = { _NAME = application } 135 | else 136 | application = application or {} 137 | end 138 | 139 | for k, v in pairs(application_methods) do 140 | application[k] = v 141 | end 142 | 143 | application.run = function(wsapi_env) 144 | return run(application, wsapi_env) 145 | end 146 | 147 | local mt = { __index = _G } 148 | 149 | if fun then 150 | setfenv(fun, setmetatable(application, mt))() 151 | else 152 | setmetatable(application, mt) 153 | end 154 | 155 | return application 156 | end 157 | 158 | function add_route(verb, path, handler, options) 159 | table.insert(route_table[verb], { 160 | pattern = compile_url_pattern(path), 161 | handler = setfenv(handler, route_env), 162 | options = options, 163 | }) 164 | end 165 | 166 | function error_500(response, output) 167 | response.status = 500 168 | response.headers = { ['Content-type'] = 'text/html' } 169 | response:write( 170 | '
An error has occurred while serving this page.\n\n' ..
171 | 'Error details:\n' .. output:gsub("\n", "
") ..
172 | ''
173 | )
174 | return response:finish()
175 | end
176 |
177 | function compile_url_pattern(pattern)
178 | local compiled_pattern = {
179 | original = pattern,
180 | params = { },
181 | }
182 |
183 | -- Lua pattern matching is blazing fast compared to regular expressions,
184 | -- but at the same time it is tricky when you need to mimic some of
185 | -- their behaviors.
186 | pattern = pattern:gsub("[%(%)%.%%%+%-%%?%[%^%$%*]", function(char)
187 | if char == '*' then return ':*' else return '%' .. char end
188 | end)
189 |
190 | pattern = pattern:gsub(':([%w%*]+)(/?)', function(param, slash)
191 | if param == '*' then
192 | table.insert(compiled_pattern.params, 'splat')
193 | return '(.-)' .. slash
194 | else
195 | table.insert(compiled_pattern.params, param)
196 | return '([^/?]+)' .. slash
197 | end
198 |
199 | end)
200 |
201 | if pattern:sub(-1) ~= '/' then pattern = pattern .. '/' end
202 | compiled_pattern.pattern = '^' .. pattern .. '?$'
203 |
204 | return compiled_pattern
205 | end
206 |
207 | function extract_parameters(pattern, matches)
208 | local params = { }
209 | for i,k in ipairs(pattern.params) do
210 | if (k == 'splat') then
211 | if not params.splat then params.splat = {} end
212 | table.insert(params.splat, wsapi.util.url_decode(matches[i]))
213 | else
214 | params[k] = wsapi.util.url_decode(matches[i])
215 | end
216 | end
217 | return params
218 | end
219 |
220 | function extract_post_parameters(request, params)
221 | for k,v in pairs(request.POST) do
222 | if not params[k] then params[k] = v end
223 | end
224 | end
225 |
226 | function url_match(pattern, path)
227 | local matches = { string.match(path, pattern.pattern) }
228 | if #matches > 0 then
229 | return true, extract_parameters(pattern, matches)
230 | else
231 | return false, nil
232 | end
233 | end
234 |
235 | function prepare_route(route, request, response, params)
236 | route_env.params = params
237 | route_env.request = request
238 | route_env.response = response
239 | return route.handler
240 | end
241 |
242 | function router(application, state, request, response)
243 | local verb, path = state.vars.REQUEST_METHOD, state.vars.PATH_INFO
244 |
245 | return coroutine.wrap(function()
246 | local routes = verb == "HEAD" and route_table["GET"] or route_table[verb]
247 | for _, route in ipairs(routes) do
248 | local match, params = url_match(route.pattern, path)
249 | if match then
250 | if verb == 'POST' then extract_post_parameters(request, params) end
251 | coroutine.yield(prepare_route(route, request, response, params))
252 | end
253 | end
254 | end)
255 | end
256 |
257 | function initialize(application, wsapi_env)
258 | -- TODO: Taken from Orbit! It will change soon to adapt request
259 | -- and response to a more suitable model.
260 | local web = {
261 | status = 200,
262 | headers = { ["Content-Type"]= "text/html" },
263 | cookies = {}
264 | }
265 |
266 | web.vars = wsapi_env
267 | web.prefix = application.prefix or wsapi_env.SCRIPT_NAME
268 | web.suffix = application.suffix
269 | web.doc_root = wsapi_env.DOCUMENT_ROOT
270 |
271 | if wsapi_env.APP_PATH == '' then
272 | web.real_path = application.real_path or '.'
273 | else
274 | web.real_path = wsapi_env.APP_PATH
275 | end
276 |
277 | local wsapi_req = wsapi.request.new(wsapi_env)
278 | local wsapi_res = wsapi.response.new(web.status, web.headers)
279 |
280 | web.set_cookie = function(_, name, value)
281 | wsapi_res:set_cookie(name, value)
282 | end
283 |
284 | web.delete_cookie = function(_, name, path)
285 | wsapi_res:delete_cookie(name, path)
286 | end
287 |
288 | web.path_info = wsapi_req.path_info
289 |
290 | if not wsapi_env.PATH_TRANSLATED == '' then
291 | web.path_translated = wsapi_env.PATH_TRANSLATED
292 | else
293 | web.path_translated = wsapi_env.SCRIPT_FILENAME
294 | end
295 |
296 | web.script_name = wsapi_env.SCRIPT_NAME
297 | web.method = string.lower(wsapi_req.method)
298 | web.input = wsapi_req.params
299 | web.cookies = wsapi_req.cookies
300 |
301 | return web, wsapi_req, wsapi_res
302 | end
303 |
304 | function run(application, wsapi_env)
305 | local state, request, response = initialize(application, wsapi_env)
306 |
307 | for route in router(application, state, request, response) do
308 | local coroute = coroutine.create(route)
309 | local success, output = coroutine.resume(coroute)
310 |
311 | if not success then
312 | return error_500(response, output)
313 | end
314 |
315 | if not output then
316 | -- render an empty body
317 | return response:finish()
318 | end
319 |
320 | local output_type = type(output)
321 | if output_type == 'function' then
322 | -- First attempt at streaming responses using coroutines.
323 | return response.status, response.headers, coroutine.wrap(output)
324 | elseif output_type == 'string' then
325 | response:write(output)
326 | return response:finish()
327 | elseif output.template then
328 | response:write(output.template(getfenv(route)) or 'template rendered an empty body')
329 | return response:finish()
330 | else
331 | if not output.pass then
332 | return error_500(response, output)
333 | end
334 | end
335 | end
336 |
337 | local function emit_no_routes_matched()
338 | coroutine.yield('REQUEST DATA:
' .. tostring(request) .. '
')
342 | coroutine.yield('RESPONSE DATA:
' .. tostring(response) .. '
')
343 | end
344 | coroutine.yield('')
345 | end
346 |
347 | return 404, { ['Content-type'] = 'text/html' }, coroutine.wrap(emit_no_routes_matched)
348 | end
349 |
--------------------------------------------------------------------------------
/rockspecs/mercury-scm-0.rockspec:
--------------------------------------------------------------------------------
1 | package = "mercury"
2 | version = "scm-0"
3 | source = {
4 | url = "git://github.com/nrk/mercury.git"
5 | }
6 | description = {
7 | summary = "A small framework for creating web apps in Lua",
8 | detailed = [[
9 | Mercury aims to be a Sinatra-like web framework (or DSL, if you like) for
10 | creating web applications in Lua, quickly and painlessly.
11 | ]],
12 | license = "MIT/X11",
13 | homepage = "http://github.com/nrk/mercury"
14 | }
15 | dependencies = {
16 | "lua >= 5.1",
17 | "wsapi",
18 | "xavante",
19 | "wsapi-xavante"
20 | }
21 |
22 | build = {
23 | type = "none",
24 | install = {
25 | lua = {
26 | "mercury.lua",
27 | ["mercury.lp"] = "lp.lua"
28 | },
29 | bin = {
30 | ["mercury"] = "bin/mercury"
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/samples/fibonacci_codegen.lua:
--------------------------------------------------------------------------------
1 | require 'luarocks.require'
2 | require 'mercury'
3 |
4 | --[[
5 | lua-CodeGen is a "safe" template engine.
6 | see http://fperrad.github.com/lua-CodeGen/
7 | ]]
8 |
9 | module('fibonacci_codegen', package.seeall, mercury.application)
10 |
11 | local templates = {
12 | index = [[
13 |
14 |
15 |
16 | Fibonacci numbers generator
17 |
20 |
30 |
31 |
32 | Welcome to the Fibonacci numbers generator!!!
33 |
34 | You can try one of the following samples:
35 |
36 | ${samples/_sample_link()}
37 |
38 |
39 |
40 |
47 |
48 |
49 |
50 |
51 |
52 | ]],
53 | _sample_link = [[
54 |
55 | Generate numbers up to ${it}
56 |
57 | ]],
58 |
59 | fibonacci = [[
60 |
61 |
62 | Fibonacci numbers generator
63 |
64 |
65 |
66 | Generating Fibonacci numbers up to ${params.limit}!!!
67 |
68 |
69 | ${numbers/_value_item()}
70 |
71 |
72 |
73 | ]],
74 | _value_item = [[
75 | ${it}
76 | ]],
77 | }
78 |
79 | local function get_samples(how_many)
80 | local samples = {}
81 | for i = 1, how_many do
82 | table.insert(samples, math.random(1, 1000000))
83 | end
84 | return samples
85 | end
86 |
87 | local function fibonacci(maxn)
88 | return coroutine.wrap(function()
89 | local x, y = 0, 1
90 | while x <= maxn do
91 | coroutine.yield(x)
92 | x, y = y, x + y
93 | end
94 | end)
95 | end
96 |
97 | get('/', function()
98 | t.codegen(templates, 'index', { samples = get_samples(4) })
99 | end)
100 |
101 | get('/fibonacci/:limit', function()
102 | local numbers = {}
103 | for val in fibonacci(tonumber(params.limit)) do
104 | numbers[#numbers+1] = val
105 | end
106 | t.codegen(templates, 'fibonacci', { numbers = numbers })
107 | end)
108 |
109 | post('/fibonacci/', function()
110 | local numbers = {}
111 | for val in fibonacci(tonumber(params.limit)) do
112 | numbers[#numbers+1] = val
113 | end
114 | t.codegen(templates, 'fibonacci', { numbers = numbers })
115 | end)
116 |
--------------------------------------------------------------------------------
/samples/fibonacci_haml.lua:
--------------------------------------------------------------------------------
1 | require 'luarocks.require'
2 | require 'mercury'
3 | require 'haml'
4 |
5 | module('fibonacci_haml', package.seeall, mercury.application)
6 |
7 | local templates = {
8 | index = [[
9 | %html
10 | %head
11 | %title Fibonacci numbers generator
12 | %style(type="text/css")
13 | :plain
14 | #fibonateUpTo { width: 50px; border: 1px solid #000; }
15 | :javascript
16 | function check_fibonacci() {
17 | var your_number = parseInt(document.getElementById('your_number').value);
18 | if (isNaN(your_number)) {
19 | alert("You must specify a valid number");
20 | return false;
21 | }
22 | return true;
23 | }
24 | %body
25 | %p Welcome to the Fibonacci numbers generator!
26 | %p You can try one of the following samples:
27 | %ul
28 | - for _, maxn in pairs(samples) do
29 | - local link = "/fibonacci/" .. maxn
30 | %li
31 | %a(href=link)= "Generate numbers up to " .. maxn
32 | %p
33 | %form(method="post" action="./fibonacci/" onsubmit="return check_fibonacci()")
34 | %label(for="your_number")
35 | If you prefer, you can generate Fibonacci numbers up to
36 | %input(id="your_number" name="limit" type="text")
37 | %input(type="submit" value="Try it yourself!")
38 | ]],
39 |
40 | fibonacci = [[
41 | %html
42 | %head
43 | %title Fibonacci numbers generator
44 | %body
45 | %p
46 | :plain
47 | Generating Fibonacci numbers up to #{params.limit}!
48 | %ul
49 | - for number in fibonacci(tonumber(params.limit)) do
50 | %li= number
51 | ]],
52 | }
53 |
54 | helpers(function()
55 | function get_samples(how_many)
56 | local samples = {}
57 | for i = 1, how_many do
58 | table.insert(samples, math.random(1, 1000000))
59 | end
60 | return samples
61 | end
62 |
63 | function fibonacci(maxn)
64 | return coroutine.wrap(function()
65 | local x, y = 0, 1
66 | while x <= maxn do
67 | coroutine.yield(x)
68 | x, y = y, x + y
69 | end
70 | end)
71 | end
72 | end)
73 |
74 | get('/', function()
75 | t.haml(templates.index, nil, { samples = get_samples(4) })
76 | end)
77 |
78 | get('/fibonacci/:limit', function()
79 | t.haml(templates.fibonacci)
80 | end)
81 |
82 | post('/fibonacci/', function()
83 | t.haml(templates.fibonacci)
84 | end)
85 |
--------------------------------------------------------------------------------
/samples/fibonacci_luapages.lua:
--------------------------------------------------------------------------------
1 | require 'luarocks.require'
2 | require 'mercury'
3 |
4 | module('fibonacci_luapages', package.seeall, mercury.application)
5 |
6 | local templates = {
7 | index = [[
8 |
9 |
10 |
11 | Fibonacci numbers generator
12 |
15 |
25 |
26 |
27 | Welcome to the Fibonacci numbers generator!
28 |
29 | You can try one of the following samples:
30 |
31 | <% for _, maxn in pairs(samples) do
32 | local link = "/fibonacci/" .. maxn
33 | %>
34 | -
35 | Generate numbers up to <%= maxn %>
36 |
37 | <% end %>
38 |
39 |
40 |
41 |
48 |
49 |
50 |
51 |
52 |
53 | ]],
54 |
55 | fibonacci = [[
56 |
57 |
58 | Fibonacci numbers generator
59 |
60 |
61 |
62 | Generating Fibonacci numbers up to <%= params.limit %>!
63 |
64 |
65 | <% for number in fibonacci(tonumber(params.limit)) do %>
66 | - <%= number %>
67 | <% end %>
68 |
69 |
70 |
71 | ]],
72 | }
73 |
74 | helpers(function()
75 | function get_samples(how_many)
76 | local samples = {}
77 | for i = 1, how_many do
78 | table.insert(samples, math.random(1, 1000000))
79 | end
80 | return samples
81 | end
82 |
83 | function fibonacci(maxn)
84 | return coroutine.wrap(function()
85 | local x, y = 0, 1
86 | while x <= maxn do
87 | coroutine.yield(x)
88 | x, y = y, x + y
89 | end
90 | end)
91 | end
92 | end)
93 |
94 | get('/', function()
95 | t.lp(templates.index, { samples = get_samples(4) })
96 | end)
97 |
98 | get('/fibonacci/:limit', function()
99 | t.lp(templates.fibonacci)
100 | end)
101 |
102 | post('/fibonacci/', function()
103 | t.lp(templates.fibonacci)
104 | end)
105 |
--------------------------------------------------------------------------------
/samples/greetings.lua:
--------------------------------------------------------------------------------
1 | require 'mercury'
2 |
3 | module('greetings', package.seeall, mercury.application)
4 |
5 | local languages = {
6 | en = 'Hi %s, how are you?',
7 | it = 'Ciao %s, come stai?',
8 | ja = '今日は%sさん、お元気ですか。 '
9 | }
10 |
11 | local response_body = [[
12 |
13 |
14 | Sinatra in Lua? Sure, it is called "Mercury"!
15 |
16 |
17 |
18 | %s
19 |
20 |
21 | ]]
22 |
23 | local function localized_message(language)
24 | return languages[language] or languages.en
25 | end
26 |
27 | get('/', function()
28 | return response_body:format([[
29 | Welcome to the first Mercury application ever built!
30 |
40 | ]])
41 | end)
42 |
43 | post('/say_hi/', function()
44 | if params.name == '' or not params.name then
45 | return response_body:format([[
46 | Sorry but I do not believe you, you can not have no name ;-)
47 | Please try again.
48 | ]])
49 | end
50 |
51 | local message = localized_message(params.lang)
52 |
53 | return response_body:format(
54 | message:format(params.name) ..
55 | '
If you do not like POST-based greetings, then ' ..
56 | string.format('you can try this!',
57 | params.lang or 'en',
58 | params.name
59 | )
60 | )
61 | end)
62 |
63 | get('/say_hi/:lang/:name/', function()
64 | local message = localized_message(params.lang)
65 | return response_body:format(message:format(params.name))
66 | end)
--------------------------------------------------------------------------------
/samples/no_pollution.lua:
--------------------------------------------------------------------------------
1 | -- You can also define your application in a dedicated environment without
2 | -- polluting Lua's global scope, you can think of it as a sort of Sinatra::Base
3 |
4 | require 'mercury'
5 |
6 | module(..., package.seeall)
7 |
8 | local myapp = mercury.application('no_pollution', function()
9 | local app_name = _NAME
10 |
11 | get('/', function()
12 | return string.format('Welcome to %s!
', app_name)
13 | end)
14 |
15 | get('/hello/:name', function()
16 | return string.format('Hello %s!', params.name)
17 | end)
18 | end)
19 |
20 | run = myapp.run
21 |
--------------------------------------------------------------------------------