commit e7e0ebb160e3e2636a8c1afd20dee2b15e4374c1
parent 69e4c48687c778834244e47a84dcc8bd34a79c84
Author: Andrew Laack <andrew@laack.co>
Date: Wed, 2 Sep 2026 11:53:21 -0500
ocaml formatting
Diffstat:
13 files changed, 486 insertions(+), 460 deletions(-)
diff --git a/learning/.ocamlformat b/learning/.ocamlformat
@@ -0,0 +1 @@
+version = 0.29.0
diff --git a/learning/bin/main.ml b/learning/bin/main.ml
@@ -1,7 +1,5 @@
open Learning
-let () =
- Dream.run ~error_handler:(Server.handler)
- @@ Dream.logger
- @@ Dream.router Server.routes
-
+let () =
+ Dream.run ~error_handler:Server.handler
+ @@ Dream.logger @@ Dream.router Server.routes
diff --git a/learning/lib/basic_functions.ml b/learning/lib/basic_functions.ml
@@ -1,26 +1,22 @@
-let remove_file_extension file_name =
- let fin_period_pos = String.rindex file_name '.' in
- String.sub file_name 0 fin_period_pos
+let remove_file_extension file_name =
+ let fin_period_pos = String.rindex file_name '.' in
+ String.sub file_name 0 fin_period_pos
-let count_prefix char data =
- let rec go idx =
- if String.length data > idx && data.[idx] = char
- then
- go (idx + 1)
- else idx
- in go 0
+let count_prefix char data =
+ let rec go idx =
+ if String.length data > idx && data.[idx] = char then go (idx + 1) else idx
+ in
+ go 0
(* Assumes length >= 3*)
let has_sl_postfix s =
- let len = String.length(s) in
- s.[len - 1] = 'l' && s.[len - 2] = 's' && s.[len - 3] = '.'
+ let len = String.length s in
+ s.[len - 1] = 'l' && s.[len - 2] = 's' && s.[len - 3] = '.'
-let prepend prefix s =
- prefix ^ s
-
-let arr_to_string arr =
- let rec go acc idx =
- if idx >= Array.length(arr) then acc
- else go (acc ^ arr.(idx)) (idx + 1)
- in go "" 0
+let prepend prefix s = prefix ^ s
+let arr_to_string arr =
+ let rec go acc idx =
+ if idx >= Array.length arr then acc else go (acc ^ arr.(idx)) (idx + 1)
+ in
+ go "" 0
diff --git a/learning/lib/parser.ml b/learning/lib/parser.ml
@@ -1,113 +1,149 @@
-
(* TODO: These should be doing prefix matching...*)
-type line_format =
- | Normal
- | HeaderL1
- | HeaderL2
- | HeaderL3
- | Embedded
- | Preformatted
- | Hidden
- | Quote
- | Link
- | ListItem
-
-let line_format_map format =
- match format with
- | Normal -> "Normal", 0
- | HeaderL1 -> "HeaderL1", 2
- | HeaderL2 -> "HeaderL2", 3
- | HeaderL3 -> "HeaderL3", 4
- | Embedded -> "Embedded", 4
- | Preformatted -> "Preformatted", 0
- | Hidden -> "Hidden", 3
- | Quote -> "Quote", 2
- | Link -> "Link", 3
- | ListItem -> "ListItem", 2
-
-let line_format_str format =
- fst (line_format_map format)
-
-let line_format_char_cut format =
- snd (line_format_map format)
-
-type page_line = {
- content: string;
- line_type: line_format;
-}
-
-let permute_line line line_type =
- let cut = line_format_char_cut line_type in String.sub line cut (String.length(line) - cut)
-
-let is_hidden line preformatted =
- if not preformatted && String.length(line) > 2 && line.[0] = '?' && line.[1] = '?' && line.[2] = ' ' then true else false
+type line_format =
+ | Normal
+ | HeaderL1
+ | HeaderL2
+ | HeaderL3
+ | Embedded
+ | Preformatted
+ | Hidden
+ | Quote
+ | Link
+ | ListItem
+
+let line_format_map format =
+ match format with
+ | Normal -> ("Normal", 0)
+ | HeaderL1 -> ("HeaderL1", 2)
+ | HeaderL2 -> ("HeaderL2", 3)
+ | HeaderL3 -> ("HeaderL3", 4)
+ | Embedded -> ("Embedded", 4)
+ | Preformatted -> ("Preformatted", 0)
+ | Hidden -> ("Hidden", 3)
+ | Quote -> ("Quote", 2)
+ | Link -> ("Link", 3)
+ | ListItem -> ("ListItem", 2)
+
+let line_format_str format = fst (line_format_map format)
+let line_format_char_cut format = snd (line_format_map format)
+
+type page_line = { content : string; line_type : line_format }
+
+let permute_line line line_type =
+ let cut = line_format_char_cut line_type in
+ String.sub line cut (String.length line - cut)
+
+let is_hidden line preformatted =
+ if
+ (not preformatted)
+ && String.length line > 2
+ && line.[0] = '?'
+ && line.[1] = '?'
+ && line.[2] = ' '
+ then true
+ else false
let is_list line preformatted =
- if not preformatted && String.length(line) > 1 && line.[0] = '*' && line.[1] = ' ' then true else false
+ if
+ (not preformatted)
+ && String.length line > 1
+ && line.[0] = '*'
+ && line.[1] = ' '
+ then true
+ else false
let is_quote line preformatted =
- if not preformatted && String.length(line) > 1 && line.[0] = '>' && line.[1] = ' ' then true else false
+ if
+ (not preformatted)
+ && String.length line > 1
+ && line.[0] = '>'
+ && line.[1] = ' '
+ then true
+ else false
let is_embedded_link line preformatted =
- if not preformatted && String.length(line) > 3 && line.[0] = '=' && line.[1] = '>' && line.[2] = 'e' && line.[3] = ' ' then true else false
-
-let is_link line preformatted =
- if not preformatted && not (is_embedded_link line preformatted) && String.length(line) > 2 && line.[0] = '=' && line.[1] = '>' && line.[2] = ' ' then true else false
-
-let header_count line preformatted =
- let count = Basic_functions.count_prefix '#' line in
- if not preformatted && String.length line > count && line.[count] = ' ' then count else 0
+ if
+ (not preformatted)
+ && String.length line > 3
+ && line.[0] = '='
+ && line.[1] = '>'
+ && line.[2] = 'e'
+ && line.[3] = ' '
+ then true
+ else false
+
+let is_link line preformatted =
+ if
+ (not preformatted)
+ && (not (is_embedded_link line preformatted))
+ && String.length line > 2
+ && line.[0] = '='
+ && line.[1] = '>'
+ && line.[2] = ' '
+ then true
+ else false
+
+let header_count line preformatted =
+ let count = Basic_functions.count_prefix '#' line in
+ if (not preformatted) && String.length line > count && line.[count] = ' ' then
+ count
+ else 0
+
+let is_h1 line preformatted =
+ header_count line preformatted
+ = 1 (* TODO: Make this more consistent with the others *)
-let is_h1 line preformatted = header_count line preformatted = 1 (* TODO: Make this more consistent with the others *)
let is_h2 line preformatted = header_count line preformatted = 2
let is_h3 line preformatted = header_count line preformatted = 3
-
-let is_preformatted _ preformatted =
- preformatted
-
-let get_line_type line preformatted =
- let funs = [
- (Hidden, is_hidden);
- (Preformatted, is_preformatted);
- (ListItem, is_list);
- (Quote, is_quote);
- (Embedded, is_embedded_link);
- (Link, is_link);
- (HeaderL1, is_h1);
- (HeaderL2, is_h2);
- (HeaderL3, is_h3);
- ] in
- let lt = (List.find_opt (fun (_, f) -> f line preformatted) funs) in
- match lt with
- | Some v -> fst v
- | None -> Normal
-
-let parse_simple_line line preformatted =
- let cl_type = get_line_type line preformatted in
-
- {line_type = cl_type; content = (permute_line line cl_type)}
-
-
-let is_format_line cl =
- String.length cl = 3 && cl.[0] = '`' && cl.[1] = '`' && cl.[2] = '`'
+let is_preformatted _ preformatted = preformatted
+
+let get_line_type line preformatted =
+ let funs =
+ [
+ (Hidden, is_hidden);
+ (Preformatted, is_preformatted);
+ (ListItem, is_list);
+ (Quote, is_quote);
+ (Embedded, is_embedded_link);
+ (Link, is_link);
+ (HeaderL1, is_h1);
+ (HeaderL2, is_h2);
+ (HeaderL3, is_h3);
+ ]
+ in
+ let lt = List.find_opt (fun (_, f) -> f line preformatted) funs in
+ match lt with Some v -> fst v | None -> Normal
+
+let parse_simple_line line preformatted =
+ let cl_type = get_line_type line preformatted in
+
+ { line_type = cl_type; content = permute_line line cl_type }
+
+let is_format_line cl =
+ String.length cl = 3 && cl.[0] = '`' && cl.[1] = '`' && cl.[2] = '`'
let read_simple_learning_file fileName =
- let ic = open_in fileName in
- let rec readAll preformatted acc =
- try
- let cl = input_line ic in
- let current_format = if is_format_line cl then not preformatted else preformatted in
- if not current_format && not preformatted then
- let current_line = parse_simple_line cl current_format in
- if String.length acc != 0
- then
- parse_simple_line acc true :: current_line :: readAll current_format ""
- else
- current_line :: readAll current_format ""
- else
- readAll current_format (if not (is_format_line cl) then (if String.length acc != 0 then acc ^ "\n" ^ cl else cl) else acc)
- with End_of_file ->
- close_in_noerr ic;
- if String.length acc != 0 then parse_simple_line acc true :: [] else []
- in readAll false ""
+ let ic = open_in fileName in
+ let rec readAll preformatted acc =
+ try
+ let cl = input_line ic in
+ let current_format =
+ if is_format_line cl then not preformatted else preformatted
+ in
+ if (not current_format) && not preformatted then
+ let current_line = parse_simple_line cl current_format in
+ if String.length acc != 0 then
+ parse_simple_line acc true :: current_line
+ :: readAll current_format ""
+ else current_line :: readAll current_format ""
+ else
+ readAll current_format
+ (if not (is_format_line cl) then
+ if String.length acc != 0 then acc ^ "\n" ^ cl else cl
+ else acc)
+ with End_of_file ->
+ close_in_noerr ic;
+ if String.length acc != 0 then parse_simple_line acc true :: [] else []
+ in
+ readAll false ""
diff --git a/learning/lib/render.ml b/learning/lib/render.ml
@@ -1,41 +1,48 @@
-open Jingoo
+open Jingoo
-let to_tval value =
- Jg_types.Tobj [("name", Jg_types.Tstr value);]
+let to_tval value = Jg_types.Tobj [ ("name", Jg_types.Tstr value) ]
-let get_pages_in_order course_name =
- let arr = Sys.readdir (Shared.course_dir ^ course_name) in
- Array.sort compare arr;
- let lst : string list = List.filter Basic_functions.has_sl_postfix (Array.to_list arr) in
- lst
+let get_pages_in_order course_name =
+ let arr = Sys.readdir (Shared.course_dir ^ course_name) in
+ Array.sort compare arr;
+ let lst : string list =
+ List.filter Basic_functions.has_sl_postfix (Array.to_list arr)
+ in
+ lst
-let get_course_home course_name =
- List.map to_tval (get_pages_in_order course_name)
+let get_course_home course_name =
+ List.map to_tval (get_pages_in_order course_name)
-let get_course_array =
- let arr = Sys.readdir Shared.course_dir in
- Array.sort compare arr;
- arr
+let get_course_array =
+ let arr = Sys.readdir Shared.course_dir in
+ Array.sort compare arr;
+ arr
let get_courses =
- let arr = get_course_array in
- Jg_types.Tlist
- (Array.to_list (Array.map to_tval arr))
+ let arr = get_course_array in
+ Jg_types.Tlist (Array.to_list (Array.map to_tval arr))
-let render_course_list =
- (Jg_template.from_string Templates.course_list_jingoo ~models:[("courses", get_courses)])
+let render_course_list =
+ Jg_template.from_string Templates.course_list_jingoo
+ ~models:[ ("courses", get_courses) ]
let render_sidebar s =
- (Jg_template.from_string Templates.sidebar_jingoo ~models:[("courses", get_courses)]) ^ s
+ Jg_template.from_string Templates.sidebar_jingoo
+ ~models:[ ("courses", get_courses) ]
+ ^ s
let page_line_to_tobj (line : Parser.page_line) =
- Jg_types.Tobj [("content", Jg_types.Tstr line.content); ("line_type", Jg_types.Tstr (Parser.line_format_str line.line_type))]
+ Jg_types.Tobj
+ [
+ ("content", Jg_types.Tstr line.content);
+ ("line_type", Jg_types.Tstr (Parser.line_format_str line.line_type));
+ ]
-let render_course_home course_name =
- (Jg_template.from_string Templates.course_home_jingoo ~models:[("pages", (Jg_types.Tlist (get_course_home course_name)))])
+let render_course_home course_name =
+ Jg_template.from_string Templates.course_home_jingoo
+ ~models:[ ("pages", Jg_types.Tlist (get_course_home course_name)) ]
-let get_path_to_page course page =
- Shared.course_dir ^ course ^ "/" ^ page
+let get_path_to_page course page = Shared.course_dir ^ course ^ "/" ^ page
(*
These two functions are called together. This is inefficient bc they both find the current index and then
@@ -45,18 +52,28 @@ let get_path_to_page course page =
*)
let get_previous_page_link course page =
- let pages = get_pages_in_order course in
- match List.find_index (fun x -> x = page) pages with
- | Some current_idx when current_idx - 1 >= 0 -> List.nth pages (current_idx - 1)
- | _ -> ""
+ let pages = get_pages_in_order course in
+ match List.find_index (fun x -> x = page) pages with
+ | Some current_idx when current_idx - 1 >= 0 ->
+ List.nth pages (current_idx - 1)
+ | _ -> ""
let get_next_page_link course page =
- let pages = get_pages_in_order course in
- match List.find_index (fun x -> x = page) pages with
- | Some current_idx when current_idx + 1 < List.length pages -> List.nth pages (current_idx + 1)
- | _ -> ""
-
-let render_page course page =
- let page_lines : Parser.page_line list = Parser.read_simple_learning_file @@ get_path_to_page course page in
- (Jg_template.from_string Templates.course_page_jingoo ~models:(["lines", Jg_types.Tlist (List.map page_line_to_tobj page_lines);("previous_page_link", Jg_types.Tstr (get_previous_page_link course page)) ; ("next_page_link", Jg_types.Tstr (get_next_page_link course page))]))
+ let pages = get_pages_in_order course in
+ match List.find_index (fun x -> x = page) pages with
+ | Some current_idx when current_idx + 1 < List.length pages ->
+ List.nth pages (current_idx + 1)
+ | _ -> ""
+let render_page course page =
+ let page_lines : Parser.page_line list =
+ Parser.read_simple_learning_file @@ get_path_to_page course page
+ in
+ Jg_template.from_string Templates.course_page_jingoo
+ ~models:
+ [
+ ("lines", Jg_types.Tlist (List.map page_line_to_tobj page_lines));
+ ( "previous_page_link",
+ Jg_types.Tstr (get_previous_page_link course page) );
+ ("next_page_link", Jg_types.Tstr (get_next_page_link course page));
+ ]
diff --git a/learning/lib/server.ml b/learning/lib/server.ml
@@ -1,28 +1,22 @@
(* TODO: Make this a whitelist based on the page / course lists *)
(* TODO: Favicon *)
-let is_file filename =
- if Sys.file_exists filename then
- true
- else
- false
+let is_file filename = if Sys.file_exists filename then true else false
let is_directory path =
- if Sys.file_exists path then
- (if Sys.is_directory path then
- true
- else
- false)
- else
- false
+ if Sys.file_exists path then if Sys.is_directory path then true else false
+ else false
-let is_safe_path name =
+let is_safe_path name =
String.length name > 0
- && name <> "."
- && name <> ".."
- && String.for_all (fun c ->
- (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
- || (c >= '0' && c <= '9') || c = '-' || c = '_' || c = '.') name
+ && name <> "." && name <> ".."
+ && String.for_all
+ (fun c ->
+ (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9')
+ || c = '-' || c = '_' || c = '.')
+ name
(* TODO: Ensure server responds correctly to invalid requests. *)
(*
@@ -33,70 +27,53 @@ let is_safe_path name =
*)
let my_error_template _error debug_info suggested_response =
- let status = Dream.status suggested_response in
- let code = Dream.status_to_int status
- and reason = Dream.status_to_string status in
+ let status = Dream.status suggested_response in
+ let code = Dream.status_to_int status
+ and reason = Dream.status_to_string status in
Dream.set_header suggested_response "Content-Type" Dream.text_html;
- Dream.set_body suggested_response ("<h1>" ^ (string_of_int code) ^ " - " ^ reason ^ "</h1>");
- Lwt.return suggested_response
+ Dream.set_body suggested_response
+ ("<h1>" ^ string_of_int code ^ " - " ^ reason ^ "</h1>");
+ Lwt.return suggested_response
-let get_course_name request =
- Dream.param request "course_name"
-
-let get_page_name request =
- Dream.param request "page_name"
+let get_course_name request = Dream.param request "course_name"
+let get_page_name request = Dream.param request "page_name"
(* TODO: When should trailing slash be in a url? *)
(* TODO: If sticking with course_name/ then course_name ^ "/" redirect *)
-let is_valid_course name =
- let pth = Shared.course_dir ^ name in
- (is_safe_path name && is_directory pth)
-
-let is_valid_page course_name page_name =
- let pth = Shared.course_dir ^ course_name ^ "/" ^ page_name in
- (is_safe_path course_name && is_safe_path page_name && is_file pth)
+let is_valid_course name =
+ let pth = Shared.course_dir ^ name in
+ is_safe_path name && is_directory pth
+let is_valid_page course_name page_name =
+ let pth = Shared.course_dir ^ course_name ^ "/" ^ page_name in
+ is_safe_path course_name && is_safe_path page_name && is_file pth
-let routes = [
- Dream.get "/course/:course_name/" (fun request ->
- let name = get_course_name request in
+let routes =
+ [
+ Dream.get "/course/:course_name/" (fun request ->
+ let name = get_course_name request in
if is_valid_course name then
- Dream.html (
- Render.render_sidebar @@ Render.render_course_home (name)
- )
- else
- Dream.empty `Not_Found
- );
-
+ Dream.html (Render.render_sidebar @@ Render.render_course_home name)
+ else Dream.empty `Not_Found);
Dream.get "/course/:course_name/page/:page_name" (fun request ->
- let course_name = get_course_name request in
- let page_name = get_page_name request in
+ let course_name = get_course_name request in
+ let page_name = get_page_name request in
if is_valid_course course_name then
- if is_valid_page course_name page_name then
- Dream.html @@ Render.render_sidebar @@ Render.render_page course_name page_name
- else
- Dream.empty `Not_Found
- else
- Dream.empty `Not_Found
- );
-
-
+ if is_valid_page course_name page_name then
+ Dream.html @@ Render.render_sidebar
+ @@ Render.render_page course_name page_name
+ else Dream.empty `Not_Found
+ else Dream.empty `Not_Found);
Dream.get "/static/**" (Dream.static "./static");
- Dream.get "/" (fun _ -> Dream.html (Render.render_sidebar @@ Render.render_course_list));
+ Dream.get "/" (fun _ ->
+ Dream.html (Render.render_sidebar @@ Render.render_course_list));
Dream.get "/robots.txt" (Dream.from_filesystem "./static" "robots.txt");
-
- Dream.get "/course/:course_name/page/assets/**" (fun request ->
-
- let name = get_course_name request in
- if is_valid_course name then
- Dream.static
- (Shared.course_dir ^ name
- ^ "/assets") request
- else
- Dream.empty `Not_Found
- );
-
-]
+ Dream.get "/course/:course_name/page/assets/**" (fun request ->
+ let name = get_course_name request in
+ if is_valid_course name then
+ Dream.static (Shared.course_dir ^ name ^ "/assets") request
+ else Dream.empty `Not_Found);
+ ]
let handler = Dream.error_template my_error_template
diff --git a/learning/lib/shared.ml b/learning/lib/shared.ml
@@ -1,2 +1,3 @@
(* ALWAYS pass in directory to watch. Fallback should only be for tests. *)
-let course_dir = if Array.length Sys.argv > 1 then Sys.argv.(1) ^ "/" else "./test_courses/"
+let course_dir =
+ if Array.length Sys.argv > 1 then Sys.argv.(1) ^ "/" else "./test_courses/"
diff --git a/learning/lib/templates.ml b/learning/lib/templates.ml
@@ -1,68 +1,68 @@
-let sidebar_jingoo =
- "<link rel=\"stylesheet\" href=\"/static/styles.css\" type=\"text/css\">
-<div class=\"sidenav\">
- <a href=\"/\">Home</a>
- {% for course in courses %}
- <a href=\"/course/{{ course.name }}/\">{{ course.name }}</a>
- {% endfor %}
-</div>"
+let sidebar_jingoo =
+ "<link rel=\"stylesheet\" href=\"/static/styles.css\" type=\"text/css\">\n\
+ <div class=\"sidenav\">\n\
+ \ <a href=\"/\">Home</a>\n\
+ \ {% for course in courses %}\n\
+ \ <a href=\"/course/{{ course.name }}/\">{{ course.name }}</a>\n\
+ \ {% endfor %}\n\
+ </div>"
-let course_page_jingoo =
- "<div class=\"main\">
- {% for line in lines %}
- {% if line.line_type == \"Preformatted\"%}
- <pre><pre-formatted>{{line.content}}</pre-formatted></pre>
- {% else if line.line_type == \"Quote\"%}
- <blockquote>{{line.content}}</blockquote>
- {% else if line.line_type == \"ListItem\"%}
- <li>{{line.content}}</li>
- {% else if line.line_type == \"Link\"%}
- <content-block>
- <a href=\"{{line.content}}\">{{line.content}}</a>
- </content-block>
- {% else if line.line_type == \"Hidden\"%}
- <details>
- <summary>{{line.content}}</summary>
- </details>
- {% else if line.line_type == \"HeaderL1\"%}
- <h1>{{line.content}}</h1>
- {% else if line.line_type == \"HeaderL2\"%}
- <h2>{{line.content}}</h2>
- {% else if line.line_type == \"HeaderL3\"%}
- <h3>{{line.content}}</h3>
- {% else if line.line_type == \"Embedded\"%}
- <iframe
- style=\"width: 100%; height: 600px;overflow:auto\";
- src=\"{{line.content}}\">
- </iframe>
- {% else if line.line_type == \"Normal\"%}
- <content-block>{{line.content}}</content-block>
- {% endif %}
- {% endfor %}
- {% if previous_page_link != \"\" %}
- <a class=\"page-nav\" href=\"{{previous_page_link}}\">previous</a>
- {% endif %}
- {% if next_page_link != \"\" %}
- <a class=\"page-nav-next\" href=\"{{next_page_link}}\">next</a>
- {% endif %}
-</div>"
+let course_page_jingoo =
+ "<div class=\"main\">\n\
+ \ {% for line in lines %}\n\
+ \ {% if line.line_type == \"Preformatted\"%}\n\
+ \ <pre><pre-formatted>{{line.content}}</pre-formatted></pre>\n\
+ \ {% else if line.line_type == \"Quote\"%}\n\
+ \ <blockquote>{{line.content}}</blockquote>\n\
+ \ {% else if line.line_type == \"ListItem\"%}\n\
+ \ <li>{{line.content}}</li>\n\
+ \ {% else if line.line_type == \"Link\"%}\n\
+ \ <content-block>\n\
+ \ <a href=\"{{line.content}}\">{{line.content}}</a>\n\
+ \ </content-block>\n\
+ \ {% else if line.line_type == \"Hidden\"%}\n\
+ \ <details>\n\
+ \ <summary>{{line.content}}</summary>\n\
+ \ </details>\n\
+ \ {% else if line.line_type == \"HeaderL1\"%}\n\
+ \ <h1>{{line.content}}</h1>\n\
+ \ {% else if line.line_type == \"HeaderL2\"%}\n\
+ \ <h2>{{line.content}}</h2>\n\
+ \ {% else if line.line_type == \"HeaderL3\"%}\n\
+ \ <h3>{{line.content}}</h3>\n\
+ \ {% else if line.line_type == \"Embedded\"%}\n\
+ \ <iframe\n\
+ \ style=\"width: 100%; height: 600px;overflow:auto\";\n\
+ \ src=\"{{line.content}}\">\n\
+ \ </iframe>\n\
+ \ {% else if line.line_type == \"Normal\"%}\n\
+ \ <content-block>{{line.content}}</content-block>\n\
+ \ {% endif %}\n\
+ \ {% endfor %}\n\
+ \ {% if previous_page_link != \"\" %}\n\
+ \ <a class=\"page-nav\" href=\"{{previous_page_link}}\">previous</a>\n\
+ \ {% endif %}\n\
+ \ {% if next_page_link != \"\" %}\n\
+ \ <a class=\"page-nav-next\" href=\"{{next_page_link}}\">next</a>\n\
+ \ {% endif %}\n\
+ </div>"
-let course_home_jingoo =
- "<div class=\"main\">
- <h1> Pages </h1>
- <ul>
- {% for page in pages %}
- <li><a href=\"page/{{ page.name }}\">{{ page.name }}</a></li>
- {% endfor %}
- </ul>
-</div>"
+let course_home_jingoo =
+ "<div class=\"main\">\n\
+ \ <h1> Pages </h1>\n\
+ \ <ul>\n\
+ \ {% for page in pages %}\n\
+ \ <li><a href=\"page/{{ page.name }}\">{{ page.name }}</a></li>\n\
+ \ {% endfor %}\n\
+ \ </ul>\n\
+ </div>"
-let course_list_jingoo =
- "<div class=\"main\">
- <h1> courses </h1>
- <ul>
- {% for course in courses %}
- <li><a href=\"/course/{{ course.name }}/\">{{ course.name }}</a></li>
- {% endfor %}
- </ul>
-</div>"
+let course_list_jingoo =
+ "<div class=\"main\">\n\
+ \ <h1> courses </h1>\n\
+ \ <ul>\n\
+ \ {% for course in courses %}\n\
+ \ <li><a href=\"/course/{{ course.name }}/\">{{ course.name }}</a></li>\n\
+ \ {% endfor %}\n\
+ \ </ul>\n\
+ </div>"
diff --git a/learning/test/dune b/learning/test/dune
@@ -1,5 +1,5 @@
(test
(name test_learning)
(libraries learning)
- (deps (glob_files_rec ./test_courses/*))
- )
+ (deps
+ (glob_files_rec ./test_courses/*)))
diff --git a/learning/test/test_learning.ml b/learning/test/test_learning.ml
@@ -1,3 +1,3 @@
-let () = Test_line_parsing.test_line_parsing ()
-let () = Test_rendering.test_rendering ()
+let () = Test_line_parsing.test_line_parsing ()
+let () = Test_rendering.test_rendering ()
let () = Test_server.test_server ()
diff --git a/learning/test/test_line_parsing.ml b/learning/test/test_line_parsing.ml
@@ -1,90 +1,90 @@
open Learning
-let test_line_parsing () =
- assert (Parser.is_preformatted "" true);
- assert (not (Parser.is_preformatted "" false));
+let test_line_parsing () =
+ assert (Parser.is_preformatted "" true);
+ assert (not (Parser.is_preformatted "" false));
- (* Line-based type tests.*)
- (* Only ``` is a format line, no additional characters.*)
- assert (Parser.is_format_line "```");
- assert (not (Parser.is_format_line "``` "));
- assert (not (Parser.is_format_line "```a"));
- assert (not (Parser.is_format_line "```0"));
+ (* Line-based type tests.*)
+ (* Only ``` is a format line, no additional characters.*)
+ assert (Parser.is_format_line "```");
+ assert (not (Parser.is_format_line "``` "));
+ assert (not (Parser.is_format_line "```a"));
+ assert (not (Parser.is_format_line "```0"));
- (* Preformatted always overrides all line types *)
- assert ((Parser.get_line_type "Test" true) = Preformatted);
- assert ((Parser.get_line_type "# Test" true) = Preformatted);
- assert ((Parser.get_line_type "## Test" true) = Preformatted);
- assert ((Parser.get_line_type "### Test" true) = Preformatted);
- assert ((Parser.get_line_type "=>e Test" true) = Preformatted);
- assert ((Parser.get_line_type "```" true) = Preformatted);
- assert ((Parser.get_line_type "?? Test" true) = Preformatted);
- assert ((Parser.get_line_type "> Test" true) = Preformatted);
- assert ((Parser.get_line_type "=> Test" true) = Preformatted);
- assert ((Parser.get_line_type "* Test" true) = Preformatted);
+ (* Preformatted always overrides all line types *)
+ assert (Parser.get_line_type "Test" true = Preformatted);
+ assert (Parser.get_line_type "# Test" true = Preformatted);
+ assert (Parser.get_line_type "## Test" true = Preformatted);
+ assert (Parser.get_line_type "### Test" true = Preformatted);
+ assert (Parser.get_line_type "=>e Test" true = Preformatted);
+ assert (Parser.get_line_type "```" true = Preformatted);
+ assert (Parser.get_line_type "?? Test" true = Preformatted);
+ assert (Parser.get_line_type "> Test" true = Preformatted);
+ assert (Parser.get_line_type "=> Test" true = Preformatted);
+ assert (Parser.get_line_type "* Test" true = Preformatted);
- (* Not preformatted, basic type assertions. *)
- assert ((Parser.get_line_type "Test" false) = Normal);
- assert ((Parser.get_line_type "# Test" false) = HeaderL1);
- assert ((Parser.get_line_type "## Test" false) = HeaderL2);
- assert ((Parser.get_line_type "### Test" false) = HeaderL3);
- assert ((Parser.get_line_type "=>e Test" false) = Embedded);
- assert ((Parser.get_line_type "?? Test" false) = Hidden);
- assert ((Parser.get_line_type "> Test" false) = Quote);
- assert ((Parser.get_line_type "=> Test" false) = Link);
- assert ((Parser.get_line_type "* Test" false) = ListItem);
+ (* Not preformatted, basic type assertions. *)
+ assert (Parser.get_line_type "Test" false = Normal);
+ assert (Parser.get_line_type "# Test" false = HeaderL1);
+ assert (Parser.get_line_type "## Test" false = HeaderL2);
+ assert (Parser.get_line_type "### Test" false = HeaderL3);
+ assert (Parser.get_line_type "=>e Test" false = Embedded);
+ assert (Parser.get_line_type "?? Test" false = Hidden);
+ assert (Parser.get_line_type "> Test" false = Quote);
+ assert (Parser.get_line_type "=> Test" false = Link);
+ assert (Parser.get_line_type "* Test" false = ListItem);
- (* Special lines missing expected spacing *)
- assert ((Parser.get_line_type "Test" false) = Normal);
- assert ((Parser.get_line_type "#Test" false) = Normal);
- assert ((Parser.get_line_type "##Test" false) = Normal);
- assert ((Parser.get_line_type "###Test" false) = Normal);
- assert ((Parser.get_line_type "=>eTest" false) = Normal);
- assert ((Parser.get_line_type "??Test" false) = Normal);
- assert ((Parser.get_line_type ">Test" false) = Normal);
- assert ((Parser.get_line_type "=>Test" false) = Normal);
- assert ((Parser.get_line_type "*Test" false) = Normal);
+ (* Special lines missing expected spacing *)
+ assert (Parser.get_line_type "Test" false = Normal);
+ assert (Parser.get_line_type "#Test" false = Normal);
+ assert (Parser.get_line_type "##Test" false = Normal);
+ assert (Parser.get_line_type "###Test" false = Normal);
+ assert (Parser.get_line_type "=>eTest" false = Normal);
+ assert (Parser.get_line_type "??Test" false = Normal);
+ assert (Parser.get_line_type ">Test" false = Normal);
+ assert (Parser.get_line_type "=>Test" false = Normal);
+ assert (Parser.get_line_type "*Test" false = Normal);
- (* No weird error considerations / oboes. *)
- assert ((Parser.get_line_type "" false) = Normal);
- assert ((Parser.get_line_type "#" false) = Normal);
- assert ((Parser.get_line_type "##" false) = Normal);
- assert ((Parser.get_line_type "###" false) = Normal);
- assert ((Parser.get_line_type "=>e" false) = Normal);
- assert ((Parser.get_line_type "??" false) = Normal);
- assert ((Parser.get_line_type ">" false) = Normal);
- assert ((Parser.get_line_type "=>" false) = Normal);
- assert ((Parser.get_line_type "*" false) = Normal);
- assert ((Parser.get_line_type "# " false) = HeaderL1);
- assert ((Parser.get_line_type "## " false) = HeaderL2);
- assert ((Parser.get_line_type "### " false) = HeaderL3);
- assert ((Parser.get_line_type "=>e " false) = Embedded);
- assert ((Parser.get_line_type "?? " false) = Hidden);
- assert ((Parser.get_line_type "> " false) = Quote);
- assert ((Parser.get_line_type "=> " false) = Link);
- assert ((Parser.get_line_type "* " false) = ListItem);
+ (* No weird error considerations / oboes. *)
+ assert (Parser.get_line_type "" false = Normal);
+ assert (Parser.get_line_type "#" false = Normal);
+ assert (Parser.get_line_type "##" false = Normal);
+ assert (Parser.get_line_type "###" false = Normal);
+ assert (Parser.get_line_type "=>e" false = Normal);
+ assert (Parser.get_line_type "??" false = Normal);
+ assert (Parser.get_line_type ">" false = Normal);
+ assert (Parser.get_line_type "=>" false = Normal);
+ assert (Parser.get_line_type "*" false = Normal);
+ assert (Parser.get_line_type "# " false = HeaderL1);
+ assert (Parser.get_line_type "## " false = HeaderL2);
+ assert (Parser.get_line_type "### " false = HeaderL3);
+ assert (Parser.get_line_type "=>e " false = Embedded);
+ assert (Parser.get_line_type "?? " false = Hidden);
+ assert (Parser.get_line_type "> " false = Quote);
+ assert (Parser.get_line_type "=> " false = Link);
+ assert (Parser.get_line_type "* " false = ListItem);
- (*E2E Parsing of individual lines. *)
- (* Character stripping expectations. *)
- assert((Parser.parse_simple_line "Test" false).content = "Test");
- assert((Parser.parse_simple_line "# Test" false).content = "Test");
- assert((Parser.parse_simple_line "## Test" false).content = "Test");
- assert((Parser.parse_simple_line "### Test" false).content = "Test");
- assert((Parser.parse_simple_line "=>e Test" false).content = "Test");
- assert((Parser.parse_simple_line "Test" true).content = "Test");
- assert((Parser.parse_simple_line "?? Test" false).content = "Test");
- assert((Parser.parse_simple_line "> Test" false).content = "Test");
- assert((Parser.parse_simple_line "=> Test" false).content = "Test");
- assert((Parser.parse_simple_line "* Test" false).content = "Test");
-
- (* Character stripping doesn't break on empty line content. *)
- assert((Parser.parse_simple_line "" false).content = "");
- assert((Parser.parse_simple_line "# " false).content = "");
- assert((Parser.parse_simple_line "## " false).content = "");
- assert((Parser.parse_simple_line "### " false).content = "");
- assert((Parser.parse_simple_line "=>e " false).content = "");
- assert((Parser.parse_simple_line "" true).content = "");
- assert((Parser.parse_simple_line "?? " false).content = "");
- assert((Parser.parse_simple_line "> " false).content = "");
- assert((Parser.parse_simple_line "=> " false).content = "");
- assert((Parser.parse_simple_line "* " false).content = "");
+ (*E2E Parsing of individual lines. *)
+ (* Character stripping expectations. *)
+ assert ((Parser.parse_simple_line "Test" false).content = "Test");
+ assert ((Parser.parse_simple_line "# Test" false).content = "Test");
+ assert ((Parser.parse_simple_line "## Test" false).content = "Test");
+ assert ((Parser.parse_simple_line "### Test" false).content = "Test");
+ assert ((Parser.parse_simple_line "=>e Test" false).content = "Test");
+ assert ((Parser.parse_simple_line "Test" true).content = "Test");
+ assert ((Parser.parse_simple_line "?? Test" false).content = "Test");
+ assert ((Parser.parse_simple_line "> Test" false).content = "Test");
+ assert ((Parser.parse_simple_line "=> Test" false).content = "Test");
+ assert ((Parser.parse_simple_line "* Test" false).content = "Test");
+
+ (* Character stripping doesn't break on empty line content. *)
+ assert ((Parser.parse_simple_line "" false).content = "");
+ assert ((Parser.parse_simple_line "# " false).content = "");
+ assert ((Parser.parse_simple_line "## " false).content = "");
+ assert ((Parser.parse_simple_line "### " false).content = "");
+ assert ((Parser.parse_simple_line "=>e " false).content = "");
+ assert ((Parser.parse_simple_line "" true).content = "");
+ assert ((Parser.parse_simple_line "?? " false).content = "");
+ assert ((Parser.parse_simple_line "> " false).content = "");
+ assert ((Parser.parse_simple_line "=> " false).content = "");
+ assert ((Parser.parse_simple_line "* " false).content = "")
diff --git a/learning/test/test_rendering.ml b/learning/test/test_rendering.ml
@@ -2,14 +2,13 @@
open Learning
-let courses = Array.to_list (Render.get_course_array)
-let expected_courses = ["course_0";"course_1";"course_empty"]
+let courses = Array.to_list Render.get_course_array
+let expected_courses = [ "course_0"; "course_1"; "course_empty" ]
let course_0_page_0 = Render.render_page "course_0" "0_example.sl"
let course_0_page_1 = Render.render_page "course_0" "1_example.sl"
let course_1_page_0 = Render.render_page "course_1" "0_example.sl"
let pages_in_order = Render.get_pages_in_order "course_0"
-
let contains s1 s2 =
let len1 = String.length s1 and len2 = String.length s2 in
let rec go idx =
@@ -19,28 +18,25 @@ let contains s1 s2 =
in
go 0
-
let test_rendering () =
+ (* Verify non-sl extension pages are hidden for course listings. *)
+ assert (List.length pages_in_order = 2);
- (* Verify non-sl extension pages are hidden for course listings. *)
- assert ((List.length pages_in_order) = 2);
-
- (*Verify happy path course listing.*)
- assert (courses = expected_courses);
- assert ((Render.render_page "course_0" "0_example.sl") != "");
+ (*Verify happy path course listing.*)
+ assert (courses = expected_courses);
+ assert (Render.render_page "course_0" "0_example.sl" != "");
- (*
+ (*
Verify sensible expectations about each line. We assume the
mapping between sl headers and html headers, and then check
text existence.
*)
+ assert (contains course_0_page_0 "<h1>");
+ assert (contains course_0_page_0 "<h2>");
+ assert (contains course_0_page_0 "<h3>");
+ assert (contains course_0_page_0 "This is an example sl file.");
- assert (contains course_0_page_0 "<h1>");
- assert (contains course_0_page_0 "<h2>");
- assert (contains course_0_page_0 "<h3>");
- assert (contains course_0_page_0 "This is an example sl file.");
-
- (*
+ (*
Verify the following don't render when mis-specified:
- # -> h1
- ## -> h2
@@ -53,32 +49,30 @@ let test_rendering () =
- * -> ul
*)
- (* TODO: These are brittle; add classes for rendering / validation in templates. *)
- assert (not (contains course_0_page_1 "<h1>TEST"));
- assert (not (contains course_0_page_1 "<h2>TEST"));
- assert (not (contains course_0_page_1 "<h3>TEST"));
- assert (not (contains course_0_page_1 "href=\"TEST"));
- assert (not (contains course_0_page_1 "iframe"));
- assert (not (contains course_0_page_1 "<pre-formatted>TEST"));
- assert (not (contains course_0_page_1 "summary"));
- assert (not (contains course_0_page_1 "quote"));
- assert (not (contains course_0_page_1 "<li>"));
+ (* TODO: These are brittle; add classes for rendering / validation in templates. *)
+ assert (not (contains course_0_page_1 "<h1>TEST"));
+ assert (not (contains course_0_page_1 "<h2>TEST"));
+ assert (not (contains course_0_page_1 "<h3>TEST"));
+ assert (not (contains course_0_page_1 "href=\"TEST"));
+ assert (not (contains course_0_page_1 "iframe"));
+ assert (not (contains course_0_page_1 "<pre-formatted>TEST"));
+ assert (not (contains course_0_page_1 "summary"));
+ assert (not (contains course_0_page_1 "quote"));
+ assert (not (contains course_0_page_1 "<li>"));
- (*
+ (*
Test well-formed variant of above page renders each
line type as expected.
*)
- assert (contains course_1_page_0 "<h1>TEST");
- assert (contains course_1_page_0 "<h2>TEST");
- assert (contains course_1_page_0 "<h3>TEST");
- assert (contains course_1_page_0 "href=\"TEST");
- assert (contains course_1_page_0 "iframe");
- assert (contains course_1_page_0 "<pre-formatted>TEST");
- assert (contains course_1_page_0 "summary");
- assert (contains course_1_page_0 "quote");
- assert (contains course_1_page_0 "<li>");
-
-
+ assert (contains course_1_page_0 "<h1>TEST");
+ assert (contains course_1_page_0 "<h2>TEST");
+ assert (contains course_1_page_0 "<h3>TEST");
+ assert (contains course_1_page_0 "href=\"TEST");
+ assert (contains course_1_page_0 "iframe");
+ assert (contains course_1_page_0 "<pre-formatted>TEST");
+ assert (contains course_1_page_0 "summary");
+ assert (contains course_1_page_0 "quote");
+ assert (contains course_1_page_0 "<li>")
(*
type line_format =
diff --git a/learning/test/test_server.ml b/learning/test/test_server.ml
@@ -1,43 +1,49 @@
open Learning
-(*main.ml server definition:
+let test_server = Dream.test @@ Dream.router Server.routes
+let root_response = test_server (Dream.request ~target:"/" "")
-let () =
- Dream.run ~error_handler:(Server.handler)
- @@ Dream.logger
- @@ Dream.router Server.routes
+let course_0_response =
+ test_server (Dream.request ~target:"/course/course_0/" "")
-*)
+let course_1_response =
+ test_server (Dream.request ~target:"/course/course_1/" "")
-let test_server =
- Dream.test
- @@ Dream.router Server.routes
+let course_0_page_0_response =
+ test_server (Dream.request ~target:"/course/course_0/page/0_example.sl" "")
-let root_response = test_server (Dream.request ~target:"/" "")
-let course_0_response = test_server (Dream.request ~target:"/course/course_0/" "")
-let course_1_response = test_server (Dream.request ~target:"/course/course_1/" "")
-let course_0_page_0_response = test_server (Dream.request ~target:"/course/course_0/page/0_example.sl" "")
-let course_dne_response = test_server (Dream.request ~target:"/course/this_course_doesnt_exist/" "")
-let course_dne_page_dne_response = test_server (Dream.request ~target:"/course/this_course_doesnt_exist/page/this_page_doesnt_exist.sl" "")
-let page_dne_response = test_server (Dream.request ~target:"/course/course_0/page/this_page_doesnt_exist.sl" "")
-let empty_page_response = test_server (Dream.request ~target:"/course/course_1/page/1_example.sl" "")
-
-let test_server () =
- assert (not (Server.is_safe_path "../"));
- assert (not (Server.is_safe_path ".."));
- assert (not (Server.is_safe_path "smt/../../../"));
- assert (not (Server.is_safe_path "smt/../"));
- assert (Server.is_safe_path "smt.sl");
- assert (Server.is_safe_path "smt");
- assert (Server.is_safe_path "0_course_1.sl");
- assert (Server.is_safe_path "1_course_1.sl");
-
- (* Requests to test dream server handling routes. *)
- assert (Dream.status_to_int (Dream.status root_response) = 200);
- assert (Dream.status_to_int (Dream.status course_0_response) = 200);
- assert (Dream.status_to_int (Dream.status course_1_response) = 200);
- assert (Dream.status_to_int (Dream.status course_dne_response) = 404);
- assert (Dream.status_to_int (Dream.status course_dne_page_dne_response) = 404);
- assert (Dream.status_to_int (Dream.status page_dne_response) = 404);
- assert (Dream.status_to_int (Dream.status course_0_page_0_response) = 200);
- assert (Dream.status_to_int (Dream.status empty_page_response) = 200);
+let course_dne_response =
+ test_server (Dream.request ~target:"/course/this_course_doesnt_exist/" "")
+
+let course_dne_page_dne_response =
+ test_server
+ (Dream.request
+ ~target:"/course/this_course_doesnt_exist/page/this_page_doesnt_exist.sl"
+ "")
+
+let page_dne_response =
+ test_server
+ (Dream.request ~target:"/course/course_0/page/this_page_doesnt_exist.sl" "")
+
+let empty_page_response =
+ test_server (Dream.request ~target:"/course/course_1/page/1_example.sl" "")
+
+let test_server () =
+ assert (not (Server.is_safe_path "../"));
+ assert (not (Server.is_safe_path ".."));
+ assert (not (Server.is_safe_path "smt/../../../"));
+ assert (not (Server.is_safe_path "smt/../"));
+ assert (Server.is_safe_path "smt.sl");
+ assert (Server.is_safe_path "smt");
+ assert (Server.is_safe_path "0_course_1.sl");
+ assert (Server.is_safe_path "1_course_1.sl");
+
+ (* Requests to test dream server handling routes. *)
+ assert (Dream.status_to_int (Dream.status root_response) = 200);
+ assert (Dream.status_to_int (Dream.status course_0_response) = 200);
+ assert (Dream.status_to_int (Dream.status course_1_response) = 200);
+ assert (Dream.status_to_int (Dream.status course_dne_response) = 404);
+ assert (Dream.status_to_int (Dream.status course_dne_page_dne_response) = 404);
+ assert (Dream.status_to_int (Dream.status page_dne_response) = 404);
+ assert (Dream.status_to_int (Dream.status course_0_page_0_response) = 200);
+ assert (Dream.status_to_int (Dream.status empty_page_response) = 200)