Scattered Thoughts

At the moment all the posts on the front page are displayed in full. It's already getting a bit crowded so I want to just show the a small part of each post. Not particularly exciting but a little fiddly.

This would be pretty easy to accomplish by using css to restrict the size of the boxes but then we would still be sending all the content. Taking the first n characters doesn't work either because the resulting html will be broken.

The output of simple_markup's parser is a list of <p>s so a simple solution is to ask for just one <p>. main.ml now contains:

let interact () =
  let len =
    bitmatch (bitstring_of_chan_max stdin 4) with
      | { len:4*8 } -> Int32.to_int len in
  bitmatch (bitstring_of_chan_max stdin len) with
    | { abridge:8 ; input:(len-1)*8:string } ->
  let markup = parse_text input in
  let html =
    to_html
      ~render_pre:render_pre
      ~render_link:render_link
      ~render_img:render_img markup in
  let html =
    match abridge with
      | 0 -> html
      | _ -> [List.hd html] in
  let html_pretty = Xhtmlpretty.xhtml_list_print html in
  let output =
    let len = String.length html_pretty in
    BITSTRING { (Int32.of_int len):4*8 ; html_pretty:len*8:string } in
  Bitstring.bitstring_to_chan output stdout;
  flush stdout

The first part of the port command determines whether or not the output will be abridged. It only requires 1 bit to communicate this but I would prefer to keep the remaining string aligned so we use a whole byte.

Having got into the habit of doing erlang-style error handling I originally wrote the match as...

let html =
  match abridge with
    | 0 -> html
    | 1 -> [List.hd html] in

...with a match failure occuring for anything else. Unfortunately, omake seems to build with -warn-error by default so this will not compile. Handling this properly in ocaml requires defining an exception type and adding an extra case whereas in erlang just letting the match fail would be sufficient.

The port command in markup.erl needs changing to match:

handle_call({to_html, Markup, Abridge}, _From, State) ->
  Command =
    case Abridge of
      false -> [0, Markup];
      true -> [1, Markup]
    end,
  true = port_command(State#state.port, Command),
  Response = case collect_response(State#state.port) of
    {ok, Html} -> {reply, Html, State}
  end,
  Response.

item_template.et gets passed a boolean argument telling it whether to abridge the body of the post and insert a 'read more' link.

<%
  {Items, Abridge} = Data,
  [ item(Item, Abridge) || Item <- Items ]
%>

<%! end of template %>

<%@ item(Item, Abridge) %>
<div class="item">
  <div class="header">
    <div class="title">
      <% anchor(by_pubdate(Item#item.pubdate), Item#item.title) %>
    </div>
    <div class="date"><% date(Item#item.pubdate) %></div>
  </div>
  <div class="divider"></div>
  <div class="body"><% markup:to_html(Item#item.body, Abridge) %></div>
  <div class="more">
    <%
      case Abridge of
        true -> anchor(by_pubdate(Item#item.pubdate), "read more...");
        false -> ""
      end
    %>
  </div>
  <div class="tags">
    <% [ anchor(by_tag(Tag), Tag) || Tag <- Item#item.tags ] %>
  </div>
</div>

item_all_resource.erl gets a new 'abridge' argument which is used on the front page.

to_html(Reqdata, Context) ->
  Items = get_items(Reqdata),
  ok = erltl:compile("src/item_template.et"),
  Abridge =
    case lists:keyfind("abridge",1,wrq:req_qs(Reqdata)) of
      {"abridge","true"} -> true;
      _ -> false
    end,
  Body = item_template:render({Items, Abridge}),
  ok = erltl:compile("src/blog_template.et"),
  Html = blog_template:render(Body),
  {Html, Reqdata, Context}.
P.S.
I am often available for freelance work - check out my resume
Copyright © Jamie Brandon 2011