Commits

Anonymous committed 5e32ef9

Version 1.0.4

ui.link{...} with POST target can now be parameterized with BOTH content and text to allow HTML content for JavaScript browsers and a text-only version for accessiblity

Changes related to database selectors:
- Support for row-based locking
- New method :count(), caching and returning the number of rows, which WOULD have been returned by :exec()
- Bugfix: WHERE and HAVING expressions are now enclosed in parenthesis to avoid problems with operator precedence

ui.script{...} now supports external .js files

Changes in langtool.lua to cope with escaped new-line chars (\n)

Comments (0)

Files changed (6)

doc/autodoc-header.htmlpart

         color: #505050;
       }
     </style>
-    <title>WebMCP 1.0.3 Documentation</title>
+    <title>WebMCP 1.0.4 Documentation</title>
   </head>
   <body>
-    <h1>WebMCP 1.0.3 Documentation</h1>
+    <h1>WebMCP 1.0.4 Documentation</h1>
     <p>
       WebMCP is a completely new web development framework, and has not been extensively tested yet. The API might change at any time, but in future releases there will be a list of all changes, which break downward compatibility.
     </p>

framework/bin/langtool.lua

               (not string.find(key, "^%s*%.%.[^%.]")) and
               (not string.find(key, "^%s*,[^,]"))
             then
+              local key = key:gsub("\\n", "\n")
               translations[key] = false
             end
           end
               (not string.find(key, "^%s*%.%.[^%.]")) and
               (not string.find(key, "^%s*,[^,]"))
             then
+              local key = key:gsub("\\n", "\n")
               translations[key] = false
             end
           end
   for num, key in ipairs(translation_keys) do
     local value = translations[key]
     if value then
-      file:write(string.format("[%q] = %q;\n", key, value))
+      file:write((string.format("[%q] = %q;\n", key, value):gsub("\\\n", "\\n"))) -- double () important to hide second result of gsub
     else
-      file:write(string.format("[%q] = false;\n", key))
+      file:write((string.format("[%q] = false;\n", key):gsub("\\\n", "\\n"))) -- double () important to hide second result of gsub
     end
   end
   file:write("}\n")

framework/cgi-bin/webmcp.lua

 #!/usr/bin/env lua
 
-_WEBMCP_VERSION = "1.0.3"
+_WEBMCP_VERSION = "1.0.4"
 
 -- include "../lib/" in search path for libraries
 do

framework/env/ui/link.lua

   params    = params,     -- optional parameters to be passed to the view or action
   routing   = routing,    -- optional routing information for action links, as described for ui.form{...}
   text      = text,       -- link text
-  content   = content     -- alternative name for 'text' option, preferred for functions
+  content   = content     -- link content (overrides link text, except for submit buttons for action calls without JavaScript)
 }
 
 This function inserts a link into the active slot. It may be either an internal application link ('module' given and 'view' or 'action' given), or a link to an external web page ('external' given), or a link to a file in the static file directory of the application ('static' given).
 
 function ui.link(args)
   local args = args or {}
-  local content = args.text or args.content  -- TODO: decide which argument name to use
+  local content = args.content or args.text
   assert(content, "ui.link{...} needs a text.")
   local function wrapped_content()
     -- TODO: icon/image

framework/env/ui/script.lua

       slot.put(args.script)
     end
   end
+  if args.external then
+    attr.src = encode.url{ external = args.external }
+  elseif args.static then
+    attr.src = encode.url{ static = args.static }
+  end
   if noscript then
     ui.tag{ tag = "noscript", attr = attr, content = noscript }
   end
-  if script then
+  if attr.src then
+    ui.tag{ tag = "script", attr = attr, content = "" }
+  elseif script then
     ui.tag{ tag = "script", attr = attr, content = script }
   end
 end

libraries/mondelefant/mondelefant.lua

   self._distinct = false
   self._distinct_on = {sep = ", ", expression}
   self._from = { sep = " " }
-  self._where = { sep = " AND " }
+  self._where = { sep = ") AND (" }
   self._group_by = { sep = ", " }
-  self._having = { sep = " AND " }
+  self._having = { sep = ") AND (" }
   self._combine = { sep = " " }
   self._order_by = { sep = ", " }
   self._limit = nil
   self._offset = nil
-  --[[
-  self._lock = nil
-  self._lock_tables = { sep = ", " }
-  --]]
+  self._read_lock = { sep = ", " }
+  self._write_lock = { sep = ", " }
   self._class = nil
   self._attach = nil
   return self
   return self
 end
 
+function selector_prototype:for_share()
+  self._read_lock.all = true
+  return self
+end
+
+function selector_prototype:for_share_of(expression)
+  add(self._read_lock, expression)
+  return self
+end
+
+function selector_prototype:for_update()
+  self._write_lock.all = true
+  return self
+end
+
+function selector_prototype:for_update_of(expression)
+  add(self._write_lock, expression)
+  return self
+end
+
 function selector_prototype:reset_fields()
   for idx in ipairs(self._fields) do
     self._fields[idx] = nil
   if #self._mode == "empty_list" then
     add(parts, "WHERE FALSE")
   elseif #self._where > 0 then
-    add(parts, {"WHERE $", self._where})
+    add(parts, {"WHERE ($)", self._where})
   end
   if #self._group_by > 0 then
     add(parts, {"GROUP BY $", self._group_by})
   end
   if #self._having > 0 then
-    add(parts, {"HAVING $", self._having})
+    add(parts, {"HAVING ($)", self._having})
   end
   for i, v in ipairs(self._combine) do
     add(parts, v)
   if self._offset then
     add(parts, "OFFSET " .. self._offset)
   end
+  if self._write_lock.all then
+    add(parts, "FOR UPDATE")
+  else
+    if self._read_lock.all then
+      add(parts, "FOR SHARE")
+    elseif #self._read_lock > 0 then
+      add(parts, {"FOR SHARE OF $", self._read_lock})
+    end
+    if #self._write_lock > 0 then
+      add(parts, {"FOR UPDATE OF $", self._write_lock})
+    end
+  end
   return self._db_conn:assemble_command{"$", parts}
 end
 
   end
 end
 
+-- NOTE: This function caches the result!
+function selector_prototype:count()
+  if not self._count then
+    local count_selector = self:get_db_conn():new_selector()
+    count_selector:add_field('count(1)')
+    count_selector:add_from(self)
+    count_selector:single_object_mode()
+    self._count = count_selector:exec().count
+  end
+  return self._count
+end
+
 
 
 -----------------