diff options
author | Benjamin Barenblat <bbaren@mit.edu> | 2015-06-25 21:00:19 -0400 |
---|---|---|
committer | Benjamin Barenblat <bbaren@mit.edu> | 2015-06-25 21:00:19 -0400 |
commit | 0073ec1eed94b00de29129aca9ac08e8d17120c9 (patch) | |
tree | 62237c8f2dceab1af7adcf1193828cdd5c2939e4 | |
parent | 18b26edf08b15fc895da18cc11c23764bc8700bc (diff) |
Implement basic locking
-rw-r--r-- | lock.ur | 27 | ||||
-rw-r--r-- | lock.urs | 16 | ||||
-rw-r--r-- | main.ur | 156 | ||||
-rw-r--r-- | urwiki.urp | 2 |
4 files changed, 115 insertions, 86 deletions
@@ -0,0 +1,27 @@ +(* Copyright 2015 the Massachusetts Institute of Technology + +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. *) + +table lock : { Title : string, + Holder : client } + PRIMARY KEY Title + +fun take page_title = + new_holder <- self; + result <- tryDml (INSERT INTO lock (Title, Holder) + VALUES ({[page_title]}, {[new_holder]})); + return (Option.isNone result) + +fun release page_title = + requestor <- self; + dml (DELETE FROM lock + WHERE Holder = {[requestor]}) diff --git a/lock.urs b/lock.urs new file mode 100644 index 0000000..f7282cc --- /dev/null +++ b/lock.urs @@ -0,0 +1,16 @@ +(* Copyright 2015 the Massachusetts Institute of Technology + +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. *) + +val take : string -> transaction bool + +val release : string -> transaction unit @@ -20,16 +20,11 @@ type id = int table commit : { Id : id, Created : time, + Title : string, Content : string } PRIMARY KEY Id sequence commit_id_next -table article : { Title : string, - Head : id } - PRIMARY KEY Title, - CONSTRAINT Head - FOREIGN KEY Head REFERENCES commit(Id) - datatype mode = View | Edit val eq_mode = mkEq (fn x y => @@ -44,90 +39,79 @@ fun only_in mode_source target_mode = then null else Style.invisible) -fun commit_article title text : transaction unit = +fun current_revision title = + commits <- queryL1 (SELECT commit.Created, commit.Title, commit.Content + FROM commit + WHERE commit.Title = {[title]} + ORDER BY commit.Created ASC); + return (case commits of + Nil => {Title = title, Body = "Not found."} + | art :: _ => {Title = art.Title, + Body = show art.Content}) + +fun create_commit title text : transaction unit = id <- nextval commit_id_next; creation_time <- now; - dml (INSERT INTO commit (Id, Created, Content) - VALUES ({[id]}, {[creation_time]}, {[text]})); - (* TODO(bbaren): This is ugly. Use CAS instead? *) - sql_error <- tryDml (INSERT INTO article (Title, Head) - VALUES ({[title]}, {[id]})); - case sql_error of - None => - (* We created a new article. *) - return () - | Some _ => - (* The article already exists. *) - dml (UPDATE article - SET Head = {[id]} - WHERE Title = {[title]}) + dml (INSERT INTO commit (Id, Created, Title, Content) + VALUES ({[id]}, {[creation_time]}, {[title]}, {[text]})) fun wiki requested_article_title = (* Look up the article. *) - extant_articles <- - queryL (SELECT article.Title, commit.Content - FROM article LEFT JOIN commit ON article.Head = commit.Id - WHERE article.Title = {[requested_article_title]}); - let - val article = - case extant_articles of - Nil => {Title = requested_article_title, Body = "Not found."} - | art :: Nil => {Title = art.Article.Title, - Body = show art.Commit.Content} - | _ :: _ :: _ => error - <xml> - Multiple articles with title - ‘{[requested_article_title]}’ - </xml> - in - (* Stuff the article text in a source so we can live-update it as the user - edits. *) - article_body_source <- source article.Body; - (* Initially, we're in View mode, and we can switch to Edit mode on user - request. *) - page_mode <- source View; - return - <xml> - <head> - <title> - {[if article.Title = Configuration.main_page_name - then Configuration.wiki_title - else article.Title ^ " – " ^ Configuration.wiki_title]} - </title> - <link rel="stylesheet" href="/urwiki.css" /> - </head> - <body> - (* Page headings *) - <div> - <h1>{[Configuration.wiki_title]}</h1> - <ul> - <li> - <a link={wiki Configuration.main_page_name}> - {[Configuration.main_page_name]} - </a> - </li> - </ul> + article <- current_revision requested_article_title; + (* Stuff the article text in a source so we can live-update it as the user + edits. *) + article_body_source <- source article.Body; + (* Initially, we're in View mode, and we can switch to Edit mode on user + request. *) + page_mode <- source View; + return + <xml> + <head> + <title> + {[if article.Title = Configuration.main_page_name + then Configuration.wiki_title + else article.Title ^ " – " ^ Configuration.wiki_title]} + </title> + <link rel="stylesheet" href="/urwiki.css" /> + </head> + <body> + (* Page headings *) + <div> + <h1>{[Configuration.wiki_title]}</h1> + <ul> + <li> + <a link={wiki Configuration.main_page_name}> + {[Configuration.main_page_name]} + </a> + </li> + </ul> + </div> + (* Article *) + <dyn signal={text <- signal article_body_source; + return <xml>{[text]}</xml>} /><br /> + (* Editing panel *) + <div> + (* Controls for View mode *) + <div dynClass={only_in page_mode View}> + <button + value="Edit" + onclick={fn _ => + lock_result <- rpc (Lock.take article.Title); + if lock_result + then set page_mode Edit + else return ()} /> </div> - (* Article *) - <dyn signal={text <- signal article_body_source; - return <xml>{[text]}</xml>} /><br /> - (* Editing panel *) - <div> - (* Controls for View mode *) - <div dynClass={only_in page_mode View}> - <button value="Edit" onclick={fn _ => set page_mode Edit} /> - </div> - (* Controls for Edit mode *) - <div dynClass={only_in page_mode Edit}> - <ctextarea source={article_body_source} /><br /> - <button - value="Commit" - onclick={fn _ => - text <- get article_body_source; - rpc (commit_article article.Title text); - set page_mode View} /> - </div> + (* Controls for Edit mode *) + <div dynClass={only_in page_mode Edit}> + <ctextarea source={article_body_source} /><br /> + <button + value="Commit" + onclick={fn _ => + text <- get article_body_source; + rpc (create_commit article.Title text); + rpc (Lock.release article.Title); + set page_mode View} /> </div> - </body> - </xml> - end + </div> + </body> + </xml> @@ -1,5 +1,7 @@ allow url /urwiki.css file /urwiki.css urwiki.css +$/option style +lock main |