webmcp
view framework/bin/mcp.lua @ 233:827c44692141
Allow setting certain global variables in autoloader
| author | jbe | 
|---|---|
| date | Sat Feb 28 23:28:41 2015 +0100 (2015-02-28) | 
| parents | 82cc171e8510 | 
| children | 36346c33a020 | 
 line source
     1 #!/usr/bin/env moonbridge
     3 WEBMCP_VERSION = "2.0.0_devel"
     5 -- allow control of global environment
     6 local _G = _G
     7 local global_metatable = {
     8   __index = _G,
     9   __newindex = function(self, key, value)
    10     _G[key] = value
    11   end
    12 }
    13 _ENV = setmetatable({}, global_metatable)
    15 -- check if interactive mode
    16 if listen then  -- defined by moonbridge
    17   WEBMCP_MODE = "listen"
    18 else
    19   WEBMCP_MODE = "interactive"
    20 end
    22 -- configuration names are provided as 4th, 5th, etc. argument
    23 WEBMCP_CONFIG_NAMES = {select(4, ...)}
    25 -- determine framework and bath path from command line arguments
    26 -- or print usage synopsis (if applicable)
    27 do
    28   local arg1, arg2, arg3 = ...
    29   local helpout
    30   if
    31     arg1 == "-h" or arg1 == "--help" or
    32     arg2 == "-h" or arg2 == "--help"  -- if first arg is provided by wrapper
    33   then
    34     helpout = io.stdout
    35   elseif
    36     #WEBMCP_CONFIG_NAMES < 1 or
    37     (WEBMCP_MODE == "interactive") ~= (arg3 == "INTERACTIVE")
    38   then
    39     helpout = io.stderr
    40   end
    41   if helpout then
    42     helpout:write("Usage: moonbridge -- <framework path>/bin/mcp.lua <framework path> <app base path> <app name> <config name> [<config name> ...]\n")
    43     helpout:write("   or: lua -i <framework path>/bin/mcp.lua <framework path> <app base path> INTERACTIVE <config name> [<config name> ...]\n")
    44     if helpout == io.stderr then
    45       return 1
    46     else
    47       return 0
    48     end
    49   end
    50   local function append_trailing_slash(str)
    51     return string.gsub(str, "([^/])$", function(last) return last .. "/" end)
    52   end
    53   WEBMCP_FRAMEWORK_PATH = append_trailing_slash(arg1)
    54   WEBMCP_BASE_PATH      = append_trailing_slash(arg2)
    55   if WEBMCP_MODE == "listen" then
    56     WEBMCP_APP_NAME = arg3
    57   end
    58 end
    60 -- setup search paths for libraries
    61 do
    62   if string.match(package.path, "^[^;]") then
    63     package.path = ";" .. package.path
    64   end
    65   package.path = WEBMCP_FRAMEWORK_PATH .. "lib/?.lua" .. package.path
    66   -- find out which file name extension shared libraries have
    67   local slib_exts = {}
    68   for ext in string.gmatch(package.cpath, "%?%.([A-Za-z0-9_-]+)") do
    69     if not slib_exts[ext] then
    70       slib_exts[#slib_exts+1] = ext
    71       slib_exts[ext] = true
    72     end
    73   end
    74   local paths = {}
    75   for i, ext in ipairs(slib_exts) do
    76     paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "accelerator/?." .. ext
    77   end
    78   for i, ext in ipairs(slib_exts) do
    79     paths[#paths+1] = WEBMCP_FRAMEWORK_PATH .. "lib/?." .. ext
    80   end
    81   paths[#paths+1] = package.cpath
    82   package.cpath = table.concat(paths, ";")
    83 end
    85 -- autoloader system for WebMCP environment "$WEBMCP_FRAMEWORK_PATH/env/",
    86 -- application environment extensions "$WEBMCP_BASE_PATH/env/"
    87 -- and models "$WEBMCP_BASE_PATH/model/"
    88 do
    89   local weakkey_mt = { __mode = "k" }
    90   local autoloader_category = setmetatable({}, weakkey_mt)
    91   local autoloader_path     = setmetatable({}, weakkey_mt)
    92   local autoloader_mt       = {}
    93   local function install_autoloader(self, category, path_fragment)
    94     autoloader_category[self] = category
    95     autoloader_path[self]     = path_fragment
    96     setmetatable(self, autoloader_mt)
    97   end
    98   local function try_exec(filename)
    99     local file = io.open(filename, "r")
   100     if file then
   101       local filedata = file:read("*a")
   102       io.close(file)
   103       local func, errmsg = load(filedata, "=" .. filename, nil, _ENV)
   104       if func then
   105         func()
   106         return true
   107       else
   108         error(errmsg, 0)
   109       end
   110     else
   111       return false
   112     end
   113   end
   114   function autoloader_mt.__index(self, key)
   115     local category, base_path, merge_base_path, file_key
   116     local merge = false
   117     if
   118       string.find(key, "^[a-z_][A-Za-z0-9_]*$") and
   119       not string.find(key, "^__")
   120     then
   121       category        = "env"
   122       base_path       = WEBMCP_FRAMEWORK_PATH .. "env/"
   123       merge           = true
   124       merge_base_path = WEBMCP_BASE_PATH .. "env/"
   125       file_key        = key
   126     elseif string.find(key, "^[A-Z][A-Za-z0-9]*$") then
   127       category        = "model"
   128       base_path       = WEBMCP_BASE_PATH .. "model/"
   129       local first = true
   130       file_key = string.gsub(key, "[A-Z]",
   131         function(c)
   132           if first then
   133             first = false
   134             return string.lower(c)
   135           else
   136             return "_" .. string.lower(c)
   137           end
   138         end
   139       )
   140     else
   141       return
   142     end
   143     local required_category = autoloader_category[self]
   144     if required_category and required_category ~= category then return end
   145     local path_fragment = autoloader_path[self]
   146     local path = base_path .. path_fragment .. file_key
   147     local merge_path
   148     if merge then
   149       merge_path = merge_base_path .. path_fragment .. file_key
   150     end
   151     local function try_dir(dirname)
   152       local dir = io.open(dirname)
   153       if dir then
   154         io.close(dir)
   155         local obj = {}
   156         install_autoloader(obj, category, path_fragment .. file_key .. "/")
   157         rawset(self, key, obj)
   158         try_exec(path .. "/__init.lua")
   159         if merge then try_exec(merge_path .. "/__init.lua") end
   160         return true
   161       else
   162         return false
   163       end
   164     end
   165     if self == _ENV then
   166       _G[key] = false  -- allow writing global variable
   167     end
   168     if merge and try_exec(merge_path .. ".lua") then
   169     elseif merge and try_dir(merge_path .. "/") then
   170     elseif try_exec(path .. ".lua") then
   171     elseif try_dir(path .. "/") then
   172     else end
   173     local value = rawget(self, key)
   174     if self == _ENV and value == false then
   175       _G[key] = nil
   176       return nil
   177     else
   178       return value
   179     end
   180   end
   181   install_autoloader(_G, nil, "")
   182   try_exec(WEBMCP_FRAMEWORK_PATH .. "env/__init.lua")
   183   try_exec(WEBMCP_BASE_PATH .. "env/__init.lua")
   184 end
   186 -- replace Moonbridge listen function
   187 local moonbridge_listen = listen
   188 local listeners = {}
   189 function listen(args)
   190   listeners[#listeners+1] = args
   191 end
   193 -- prohibit (unintended) definition of new global variables
   194 function global_metatable.__newindex()
   195   error("Setting of global variable prohibited")
   196 end
   198 -- execute configurations and pre-fork initializers
   199 for i, config_name in ipairs(WEBMCP_CONFIG_NAMES) do
   200   execute.config(config_name)
   201 end
   202 execute.prefork_initializers()
   204 -- interactive console mode
   205 if WEBMCP_MODE == "interactive" then
   206   _G.multirand = require "multirand"  -- TODO: cleaner solution
   207   execute.postfork_initializers()
   208   trace.disable()  -- avoids memory leakage (TODO: needs general solution for moonbridge?)
   209 end
   211 -- invoke moonbridge
   212 if WEBMCP_MODE == "listen" then
   213   local http = require("moonbridge_http")
   214   for i, listener in ipairs(listeners) do
   215     --listener.prepare = execute.postfork_initializers
   216     listener.prepare = function()
   217       _G.multirand = require "multirand"
   218       execute.postfork_initializers()
   219     end
   220     listener.connect = http.generate_handler(
   221       request.handler,
   222       request.get_http_options()
   223     )
   224     listener.finish = execute.finalizers
   225     moonbridge_listen(listener)
   226   end
   227 end
