Merge: gamnit: use the same code to limit fps than mnit and fix errors
authorJean Privat <jean@pryen.org>
Wed, 9 Dec 2015 03:50:20 +0000 (22:50 -0500)
committerJean Privat <jean@pryen.org>
Wed, 9 Dec 2015 03:50:20 +0000 (22:50 -0500)
Fixed a few bugs in `mnit::mnit_fps`, a typo invalidated the value of `current_fps` and the use of long Ints were broken probably since they are tagged. Using floats should fix this last issue.

Finally, the goal of this PR is to copy the module `mnit::mnit_fps` to `gamnit::limit_fps` and adapt it to the gamnit framework. There is some code duplication, but the mnit version should be deleted with the engine when gamnit is completed.

Pull-Request: #1879
Reviewed-by: Jean Privat <jean@pryen.org>

13 files changed:
contrib/nitiwiki/Makefile
contrib/nitiwiki/README.md
contrib/nitiwiki/src/wiki_edit.nit [new file with mode: 0644]
contrib/pep8analysis/www/index.html
lib/buffered_ropes.ini [deleted file]
lib/buffered_ropes.nit [deleted file]
lib/core/file.nit
lib/core/text/abstract_text.nit
lib/markdown/markdown.nit
lib/nitcorn/examples/src/xymus_net.nit
src/modelize/modelize_property.nit
tests/test_flatrope.nit
tests/test_text.nit

index 57d2cda..f040751 100644 (file)
@@ -1,9 +1,12 @@
-all: nitiwiki
+all: nitiwiki bin/nitiwiki_server
 
 nitiwiki:
        mkdir -p bin
        ../../bin/nitc src/nitiwiki.nit -o bin/nitiwiki
 
+bin/nitiwiki_server: $(shell ../../bin/nitls -M src/wiki_edit.nit)
+       ../../bin/nitc -o $@ src/wiki_edit.nit
+
 check: nitiwiki
        cd tests; make
 
index f9c7c45..757fa36 100644 (file)
@@ -250,7 +250,7 @@ Every template can access to:
 * `SUBTITLE`: Wiki description
 * `LOGO`: Wiki logo image path
 
-Additionnal macros can be used in specialized templates.
+Additional macros can be used in specialized templates.
 
 ### Main template
 
@@ -363,3 +363,17 @@ from git:
 Be sure to set `wiki.rsync_dir` in order to correctly push your changes.
 When using `--rsync`, keep in mind that the rendered output must be configured
 to work on the web server.
+
+### Serve and edit with nitiwiki_server
+
+nitiwiki_server is a lightweight web server to publish the generated files
+and accept modifications from a web form.
+
+The binary available in `bin/nitiwiki_server` is configured for simple usage or demo.
+The source of the server, at `src/wiki_edit`, can be tweaked for more advanced use.
+It is also possible to import the source and add an instance of `EditAction` to a custom nitcorn server.
+
+To launch the server, change directory to the root of the wiki and run `nitiwiki_server`.
+It uses `config.ini` from the local directory and listen on localhost:8080.
+The template should define the macro `%EDIT%` and `config.ini` should define `wiki.edit=/edit/`.
+To limit who can edit the wiki, list the md5 sum of accepted passwords (one per line) in the local file `passwords`.
diff --git a/contrib/nitiwiki/src/wiki_edit.nit b/contrib/nitiwiki/src/wiki_edit.nit
new file mode 100644 (file)
index 0000000..7dd6012
--- /dev/null
@@ -0,0 +1,200 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Web server to server generated files and modify the wiki from a web form
+module wiki_edit
+
+import nitcorn
+import markdown
+import md5
+
+intrude import wiki_html
+
+# Page for editing markdown source
+class WikiEditForm
+       super WikiArticle
+
+       # Part of the title before the name of the page
+       var title_prefix: String
+
+       # Markdown content, for previews
+       redef var md
+
+       # Custom HTML code, for forms and links
+       var html: String
+
+       init do content = (md or else "").md_to_html.to_s + html
+
+       redef fun dir_href do return "edit" / href
+
+       redef fun tpl_article
+       do
+               var s = super
+               s.title = title_prefix + title
+               return s
+       end
+
+       # Fill and return a new `HttpResponse` with this page content
+       fun to_http_response: HttpResponse
+       do
+               var resp = new HttpResponse(200)
+               resp.body = tpl_page.write_to_string
+               return resp
+       end
+end
+
+# Action to serve edit forms, show previews and apply changes
+class EditAction
+       super Action
+
+       # Full public URL for the root of this wiki
+       var root_url: String
+
+       # Path to the wiki config
+       var config_file_path: String
+
+       # Configuration of the Wiki, loaded once
+       var wiki_config = new WikiConfig(config_file_path) is lazy
+
+       # Path to the root of the wiki
+       private var wiki_root: String = config_file_path.dirname is lazy
+
+       # Path to the source files
+       private var source_dir: String = (wiki_root / wiki_config.source_dir).simplify_path + "/" is lazy
+
+       # List of acceptable password to apply modifications
+       #
+       # If `null`, no password checks are applied and all modifications are accepted.
+       var passwords: nullable Collection[String]
+
+       # Reload the wiki instance with the latest changes
+       fun wiki: Nitiwiki
+       do
+               var wiki = new Nitiwiki(wiki_config)
+               wiki.parse
+               return wiki
+       end
+
+       redef fun answer(http_request, turi)
+       do
+               var action = http_request.string_arg("action")
+               var markdown = http_request.post_args.get_or_default("content", "")
+
+               var file_path = turi.strip_leading_slash
+               file_path = wiki_root / file_path
+
+               var abs_file_path = file_path.to_absolute_path
+               var abs_source_dir = source_dir.to_absolute_path
+
+               if not abs_file_path.has_prefix(abs_source_dir) then
+                       # Attempting to access a file outside the source directory
+                       var entity = new WikiEditForm(wiki, turi.strip_leading_slash,
+                               "Access denied: ", "", "<p>Target outside of the source directory</p>")
+                       return entity.to_http_response
+               end
+
+               if action == "Submit" then
+                       var passwords = passwords
+                       var password = http_request.post_args.get_or_null("password")
+                       if passwords != null and (password == null or not passwords.has(password.md5)) then
+                               # Deny modification
+                               var entity = new WikiEditForm(wiki, turi.strip_leading_slash,
+                                       "Changes rejected: ", "", "<p>Password invalid</p>")
+                               return entity.to_http_response
+                       end
+
+                       # Save markdown source
+                       markdown = markdown.replace('\r', "")
+                       markdown.write_to_file file_path
+
+                       # Update HTML files
+                       var wiki = wiki
+                       wiki.render
+
+                       var link
+                       if turi.has_prefix("/pages/") then
+                               link = root_url / turi.substring_from(7)
+                       else link = root_url / turi
+                       link = link.strip_extension(".md") + ".html"
+
+                       # Show confirmation
+                       var body = """
+<p>Your edits were recorded and the file is updated: <a href="{{{link}}}">{{{link}}}</a></p>
+"""
+                       var entity = new WikiEditForm(wiki, turi.strip_leading_slash, "Changes saved: ", "", body)
+                       return entity.to_http_response
+               else
+                       # Show edit form, and preview when requested
+
+                       # When not in a preview, use the local content of the file
+                       if action != "Preview" then markdown = file_path.to_path.read_all
+
+                       var form = """
+<form method="POST" action="/edit{{{turi}}}">
+       You may edit the file. When you are done, click on "Submit".<br/>
+       <textarea name="content" rows="30" cols="80">{{{markdown.html_escape}}}</textarea><br/>
+"""
+                       if passwords != null then form += """
+       Password: <input type="password" name="password"><br/>
+"""
+                       form += """
+       <input type="submit" name="action" value="Preview">
+       <input type="submit" name="action" value="Submit">
+</form>
+"""
+
+                       # Show processed markdown only on preview
+                       if action != "Preview" then markdown = ""
+
+                       var entity = new WikiEditForm(wiki, turi.strip_leading_slash, "Edit source: ", markdown, form)
+                       return entity.to_http_response
+               end
+       end
+end
+
+redef class String
+       private fun strip_leading_slash: String
+       do
+               if has_prefix("/") then return substring_from(1)
+               return self
+       end
+
+       private fun to_absolute_path: String
+       do
+               return (getcwd / self).simplify_path
+       end
+end
+
+var config_file_path = "config.ini"
+var iface = "localhost:8080"
+var password_file_path = "passwords"
+
+# Load passwords for file
+var passwords = if password_file_path.file_exists then
+               password_file_path.to_path.read_lines
+       else null
+
+var vh = new VirtualHost(iface)
+
+# Serve Markdown editing form
+var action = new EditAction("http://" + iface, config_file_path, passwords)
+vh.routes.add new Route("/edit", action)
+
+# Serve the static (and generated) content
+var path_to_public_files = config_file_path.dirname / action.wiki_config.out_dir
+vh.routes.add new Route(null, new FileServer(path_to_public_files))
+
+var factory = new HttpFactory.and_libevent
+factory.config.virtual_hosts.add vh
+factory.run
index 6f7598a..46b62be 100644 (file)
     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
       <ul class="nav navbar-nav">
         <li><a href="http://xymus.net/ens/">Enseignement</a></li>
-        <li class="active"><a href="http://pep8.xymus.net/">Pep/8 Analysis</a></li>
+        <li><a href="http://xymus.net/opportunity/">Opportunité</a></li>
         <li><a href="http://tnitter.xymus.net/">Tnitter</a></li>
+        <li class="active"><a href="http://pep8.xymus.net/">Pep/8 Analysis</a></li>
         <li><a href="http://benitlux.xymus.net/">Benitlux</a></li>
-               <li><a href="http://xymus.net/opportunity/">Opportunité</a></li>
-        <li><a href="http://nitlanguage.org/">Nit</a></li>
       </ul>
 
       <ul class="nav navbar-nav pull-right">
diff --git a/lib/buffered_ropes.ini b/lib/buffered_ropes.ini
deleted file mode 100644 (file)
index 280bea6..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name=buffered_ropes
-tags=algo,text,lib
-maintainer=Lucas Bajolet <r4pass@hotmail.com>
-license=Apache-2.0
-[upstream]
-browse=https://github.com/nitlang/nit/tree/master/lib/buffered_ropes.nit
-git=https://github.com/nitlang/nit.git
-git.directory=lib/buffered_ropes.nit
-homepage=http://nitlanguage.org
-issues=https://github.com/nitlang/nit/issues
diff --git a/lib/buffered_ropes.nit b/lib/buffered_ropes.nit
deleted file mode 100644 (file)
index fcc90c7..0000000
+++ /dev/null
@@ -1,346 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# This file is free software, which comes along with NIT.  This software is
-# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
-# PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
-# is kept unaltered, and a notification of the changes is added.
-# You  are  allowed  to  redistribute it and sell it, alone or is a part of
-# another product.
-
-# Ropes with a special kind of Leaves that act similar to a `Buffer`
-#
-# When using this module, re-allocations are limited by the introduction
-# of a larger-than-necessary buffered area for the native part of a `String`
-# in an append-only fashion.
-#
-# Concretely, when concatenating two small strings of length `n` + `m` < `maxlen`
-# What happens is that a `maxlen` byte buffer is allocated, ready to receive more
-# bytes a posteriori without necessarily reallocating a new byte array.
-#
-# Theoretically, this should lower the number of concatenations
-# and reallocations when concatenating `String` objects.
-module buffered_ropes
-
-intrude import core::text::ropes
-
-# Hidden buffer, used to simulate a `FlatBuffer` on a short string.
-#
-# This is to be used by low-level APIs because of its lack of
-# safety, if you use it, make sure you know what you are doing !
-#
-# Practically, it is the underlying representation of a `Leaf` in
-# the `Rope` block, its advantage is that it saves a bit more space
-# for future concatenations, without risking to overwrite previously
-# used space, making it suitable for Strings.
-#
-# Note for future use : Should there be parallel capacity in Nit at
-# some point, this is NOT thread safe !
-private class ManualBuffer
-       var ns: NativeString is noinit
-       # Current position in the `NativeString`
-       #
-       # It is used by the clients of `ManualBuffer` as a guard
-       # to detect if the concatenation in the `ManualBuffer`
-       # is safe or not.
-       #
-       # i.e. :
-       # Say we have two strings `x` and `y` referencing the
-       # same `ManualBuffer` `b`, `y` is the concatenation of
-       # `x` and another string.
-       #
-       # If we try to concatenate a `String` `z` to `x`, a new
-       # `ManualBuffer` will be created since `pos` and `x.length`
-       # do not match.
-       #
-       # However, if we concatenate the same `String` to `y`,
-       # the contents of `z` will be copied to the `ManualBuffer`.
-       var pos = 0
-
-       init do ns = new NativeString(maxlen)
-
-       fun [](i: Int): Byte do return ns[i]
-end
-
-# Simple implementation of the iterator on Substrings for `Leaf`
-#
-# Basically just returns `self` encapsulated in a `FlatString`.
-private class LeafSubstrings
-       super IndexedIterator[FlatText]
-
-       var leaf: Leaf
-       var str: FlatString is noinit
-       var avail = true
-
-       init do
-               str = new FlatString.with_infos(leaf.buf.ns, leaf.length, 0, leaf.length - 1)
-       end
-
-       redef fun is_ok do return avail
-
-       redef fun next do avail = false
-
-       redef fun index do return 0
-
-       redef fun item do return str
-end
-
-# Leaf of a `Rope`, used as a buffered area for speedy concatenation.
-private class Leaf
-       super String
-       super Rope
-
-       var buf: ManualBuffer
-       var bns: NativeString is noinit
-       redef var length is noinit
-
-       # Unsafe, but since it is an experiment, don't mind
-       redef fun bytelen do return length
-
-       redef fun empty do return new Leaf(new ManualBuffer)
-
-       redef fun to_cstring do
-               var len = length
-               var ns = new NativeString(len + 1)
-               ns[len] = 0u8
-               buf.ns.copy_to(ns, len, 0, 0)
-               return ns
-       end
-
-       redef fun substrings do return new LeafSubstrings(self)
-
-       redef fun [](i) do return buf[i].to_i.code_point
-
-       init do
-               bns = buf.ns
-               length = buf.pos
-       end
-
-       redef fun output do new FlatString.with_infos(buf.ns, length, 0, length - 1).output
-
-       redef fun to_upper do
-               var x = new FlatBuffer
-               for i in chars do x.add(i.to_upper)
-               return x.to_s
-       end
-
-       redef fun to_lower do
-               var x = new FlatBuffer
-               for i in chars do x.add(i.to_lower)
-               return x.to_s
-       end
-
-       redef fun reversed do
-               var x = new ManualBuffer
-               var nns = x.ns
-               var ns = bns
-               var mlen = length
-               var j = mlen - 1
-               for i in [0 .. mlen[ do
-                       nns[j] = ns[i]
-                       j -= 1
-               end
-               x.pos = mlen - 1
-               return new Leaf(x)
-       end
-
-       redef fun substring(from, len) do
-               return new FlatString.with_infos(buf.ns, len, from, from + len - 1)
-       end
-
-       redef fun insert_at(s, pos) do
-               var l = substring(0, pos)
-               var r = substring_from(pos)
-               return l + s + r
-       end
-
-       redef fun +(o) do
-               var s = o.to_s
-               var slen = s.bytelen
-               var mlen = bytelen
-               if slen == 0 then return self
-               if mlen == 0 then return s
-               var nlen = mlen + slen
-               if nlen > maxlen then return new Concat(self, s)
-               if s isa FlatString then
-                       var bpos = buf.pos
-                       var sits = s.items
-                       if bpos == mlen then
-                               sits.copy_to(buf.ns, slen, s.first_byte, bpos)
-                               buf.pos = bpos + slen
-                               return new Leaf(buf)
-                       else
-                               var b = new ManualBuffer
-                               var nbns = b.ns
-                               bns.copy_to(nbns, mlen, 0, 0)
-                               sits.copy_to(nbns, slen, s.first_byte, mlen)
-                               b.pos = nlen
-                               return new Leaf(b)
-                       end
-               else if s isa Leaf then
-                       var bpos = buf.pos
-                       var sbns = s.bns
-                       if bpos == mlen then
-                               sbns.copy_to(bns, slen, 0, bpos)
-                               buf.pos += slen
-                               return new Leaf(buf)
-                       else
-                               var b = new ManualBuffer
-                               var nbns = b.ns
-                               bns.copy_to(nbns, mlen, 0, 0)
-                               sbns.copy_to(nbns, slen, 0, mlen)
-                               b.pos = nlen
-                               return new Leaf(b)
-                       end
-               else if s isa Concat then
-                       if not s.left isa Concat then
-                               return new Concat(self + s.left, s.right)
-                       end
-                       return new Concat(self, s)
-               else
-                       var bpos = buf.pos
-                       var b = buf
-                       if bpos != mlen then
-                               b = new ManualBuffer
-                               bns.copy_to(b.ns, mlen, 0, 0)
-                       end
-                       for i in s.bytes do
-                               bns[bpos] = i
-                               bpos += 1
-                       end
-                       return new Leaf(b)
-               end
-       end
-end
-
-redef class Concat
-       redef fun to_cstring do
-               var len = length
-               var ns = new NativeString(len + 1)
-               ns[len] = 0u8
-               var off = 0
-               for i in substrings do
-                       var ilen = i.length
-                       if i isa FlatString then
-                               i.items.copy_to(ns, ilen, i.first_byte, off)
-                       else if i isa Leaf then
-                               i.buf.ns.copy_to(ns, ilen, 0, off)
-                       else
-                               abort
-                       end
-                       off += ilen
-               end
-               return ns
-       end
-
-       redef fun +(o) do
-               var s = o.to_s
-               var slen = s.length
-               if s isa FlatString then
-                       var r = right
-                       var rlen = r.length
-                       if rlen + slen > maxlen then return new Concat(left, new Concat(r, s))
-                       return new Concat(left, r + s)
-               else if s isa Concat then
-                       return new Concat(self, s)
-               else if s isa Leaf then
-                       var r = right
-                       var rlen = r.length
-                       if rlen + slen > maxlen then return new Concat(left, new Concat(r, s))
-                       return new Concat(left, r + s)
-               else
-                       abort
-               end
-       end
-end
-
-redef class FlatString
-       redef fun +(o) do
-               var s = o.to_s
-               var slen = s.length
-               var mlen = length
-               if slen == 0 then return self
-               if mlen == 0 then return s
-               if s isa FlatString then
-                       if slen + mlen > maxlen then return new Concat(self, s)
-                       var mits = items
-                       var sifrom = s.first_byte
-                       var mifrom = first_byte
-                       var sits = s.items
-                       var b = new ManualBuffer
-                       var bns = b.ns
-                       mits.copy_to(bns, mlen, mifrom, 0)
-                       sits.copy_to(bns, slen, sifrom, mlen)
-                       b.pos = mlen + slen
-                       return new Leaf(b)
-               else if s isa Concat then
-                       var sl = s.left
-                       var sllen = sl.length
-                       if sllen + mlen > maxlen then return new Concat(self, s)
-                       return new Concat(sl + self, s.right)
-               else if s isa Leaf then
-                       if slen + mlen > maxlen then return new Concat(self, s)
-                       var mifrom = first_byte
-                       var sb = s.buf
-                       var b = new ManualBuffer
-                       var bns = b.ns
-                       items.copy_to(bns, mlen, mifrom, 0)
-                       sb.ns.copy_to(bns, slen, 0, mlen)
-                       b.pos = mlen + slen
-                       return new Leaf(b)
-               else
-                       abort
-               end
-       end
-end
-
-redef class Array[E]
-
-       # Fast implementation
-       redef fun to_s do
-               var l = length
-               if l == 0 then return ""
-               if l == 1 then if self[0] == null then return "" else return self[0].to_s
-               var its = _items
-               var na = new NativeArray[String](l)
-               var i = 0
-               var sl = 0
-               var mypos = 0
-               while i < l do
-                       var itsi = its[i]
-                       if itsi == null then
-                               i += 1
-                               continue
-                       end
-                       var tmp = itsi.to_s
-                       sl += tmp.length
-                       na[mypos] = tmp
-                       i += 1
-                       mypos += 1
-               end
-               var ns = new NativeString(sl + 1)
-               ns[sl] = 0u8
-               i = 0
-               var off = 0
-               while i < mypos do
-                       var tmp = na[i]
-                       var tpl = tmp.length
-                       if tmp isa FlatString then
-                               tmp.items.copy_to(ns, tpl, tmp.first_byte, off)
-                               off += tpl
-                       else
-                               for j in tmp.substrings do
-                                       var slen = j.length
-                                       if j isa FlatString then
-                                               j.items.copy_to(ns, slen, j.first_byte, off)
-                                       else if j isa Leaf then
-                                               j.buf.ns.copy_to(ns, slen, 0, off)
-                                       end
-                                       off += slen
-                               end
-                       end
-                       i += 1
-               end
-               return ns.to_s_with_length(sl)
-       end
-end
index 03955eb..fd848e1 100644 (file)
@@ -959,10 +959,13 @@ redef class String
        end
 
        # Return the canonicalized absolute pathname (see POSIX function `realpath`)
+       #
+       # Require: `file_exists`
        fun realpath: String do
                var cs = to_cstring.file_realpath
+               assert file_exists
                var res = cs.to_s_with_copy
-               # cs.free_malloc # FIXME memory leak
+               cs.free
                return res
        end
 
index 76823e2..cacee99 100644 (file)
@@ -1709,6 +1709,16 @@ redef class Char
                return (self >= 'a' and self <= 'z') or (self >= 'A' and self <= 'Z')
        end
 
+       # Is `self` an hexadecimal digit ?
+       #
+       #     assert 'A'.is_hexdigit
+       #     assert not 'G'.is_hexdigit
+       #     assert 'a'.is_hexdigit
+       #     assert not 'g'.is_hexdigit
+       #     assert '5'.is_hexdigit
+       fun is_hexdigit: Bool do return (self >= '0' and self <= '9') or (self >= 'A' and self <= 'F') or
+                                       (self >= 'a' and self <= 'f')
+
        # Returns true if the char is an alpha or a numeric digit
        #
        #     assert 'a'.is_alphanumeric
index 7907409..037830f 100644 (file)
@@ -168,6 +168,7 @@ class MarkdownProcessor
                                var c = input[i]
                                if c == '\n' then
                                        eol = true
+                               else if c == '\r' then
                                else if c == '\t' then
                                        var np = pos + (4 - (pos & 3))
                                        while pos < np do
index e0a4a10..07c830b 100644 (file)
@@ -24,6 +24,7 @@ import privileges
 import tnitter
 import benitlux::benitlux_controller
 import opportunity::opportunity_controller
+import nitiwiki::wiki_edit
 
 # Header for the whole site
 class MasterHeader
@@ -57,11 +58,10 @@ class MasterHeader
     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
       <ul class="nav navbar-nav">
         <li{{{actives.get_or_default("ens", "")}}}><a href="http://xymus.net/ens/">Enseignement</a></li>
-        <li><a href="http://pep8.xymus.net/">Pep/8 Analysis</a></li>
+        <li{{{actives.get_or_default("opportunity", "")}}}><a href="http://xymus.net/opportunity/">Opportunité</a></li>
         <li{{{actives.get_or_default("tnitter", "")}}}><a href="http://tnitter.xymus.net/">Tnitter</a></li>
+        <li><a href="http://pep8.xymus.net/">Pep/8 Analysis</a></li>
         <li{{{actives.get_or_default("benitlux", "")}}}><a href="http://benitlux.xymus.net/">Benitlux</a></li>
-        <li{{{actives.get_or_default("opportunity", "")}}}><a href="http://xymus.net/opportunity/">Opportunité</a></li>
-        <li><a href="http://nitlanguage.org/">Nit</a></li>
       </ul>
 
       <ul class="nav navbar-nav pull-right">
@@ -169,7 +169,6 @@ tnitter_vh.routes.add new Route("/rest/", new TnitterREST)
 tnitter_vh.routes.add new Route("/push/", new TnitterPush)
 tnitter_vh.routes.add new Route(null, tnitter)
 
-
 # Pep/8 Analysis is only a file server. It is available at `pep8.xymus.net`
 # and through the global/default file server at `xymus.net/pep8/`
 #
@@ -187,11 +186,17 @@ default_vh.routes.add new Route("/benitlux/", benitlux_sub)
 benitlux_vh.routes.add new Route("/rest/", benitlux_rest)
 benitlux_vh.routes.add new Route(null, benitlux_sub)
 
+# Opportunity service
 var opportunity = new OpportunityWelcome
 var opportunity_rest = new OpportunityRESTAction
 default_vh.routes.add new Route("/opportunity/rest/", opportunity_rest)
 default_vh.routes.add new Route("/opportunity/", opportunity)
 
+# Nitiwiki modification form
+var passwords = "nitiwiki_passwords".to_path.read_lines
+assert passwords.not_empty
+default_vh.routes.add new Route("/edit", new EditAction("http://xymus.net/", "/home/xymus/projects/wiki/config.ini", passwords))
+
 # We use a special file server for the path `xymus.net/ens` only to display
 # a different header.
 var file_server_ens = new FileServer("/var/www/ens/")
index afd299f..b00c7f4 100644 (file)
@@ -1355,6 +1355,8 @@ redef class AAttrPropdef
                        if nexpr != null then
                                if nexpr isa ANewExpr then
                                        mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, nexpr.n_type, true)
+                               else if nexpr isa AAsCastExpr then
+                                       mtype = modelbuilder.resolve_mtype_unchecked(mmodule, mclassdef, nexpr.n_type, true)
                                else if nexpr isa AIntegerExpr then
                                        var cla: nullable MClass = null
                                        if nexpr.value isa Int then
index 8b1d6bc..19e550c 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 #alt1 import core
-#alt1 import buffered_ropes
 
 var st = "quick brown fox over the lazy dog"
 
index ea3db1e..820b5c3 100644 (file)
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 #alt2 import core
-#alt2 import buffered_ropes
 
 var str = "Woe to you, oh earth and sea for the Devil sends the beast with wrath because he knows the time is short. Let him who hath understanding reckon the number of the beast, for it is a human number, its number is Six Hundred and Sixty-Six."
 var spaces = "           "