require "html" require "../handler" require "../views/html/page" class Guff::Handlers::BlogHandler < Guff::Handler ROUTES = [{ type: :post, tags: ["_blog"], re: %r{ ^/ # match YYYY/MM/DD/SLUG.html (?\d{4}) / (?\d{2}) / (?\d{2}) / (?[a-z0-9._-]+) \.html $ }x, }, { type: :list, tags: ["_blog"], re: %r{ ^/ # match YYYY/MM/DD (?\d{4}) / (?\d{2}) / (?\d{2}) /? $ }x, }, { type: :list, tags: ["_blog"], re: %r{ ^/ # match YYYY/MM (?\d{4}) / (?\d{2}) /? $ }x, }, { type: :list, tags: ["_blog"], re: %r{ ^/ # match YYYY (?\d{4}) /? $ }x, }, { type: :post, tags: ["_post"], re: %r{ ^/ # match slug (?[a-z0-9._-]+) \.html $ }x, }, { type: :list, tags: ["_blog"], re: %r{ ^/ # match tags/foo/bar/baz tags?\/(?(?:[a-z0-9._-]+)(?:/(?:[a-z0-9._-]+))*) /?$ }x, }, { type: :tags, tags: ["_blog"], re: %r{ ^/ # match tags tags? /?$ }x, }, { type: :list, tags: ["_blog"], re: %r{ # match index ^/$ }x, }] def call(context : HTTP::Server::Context) path = context.request.path || "" call_next(context) unless ROUTES.reduce(false) do |matched, route| unless matched if md = (route[:re] as Regex).match(path) # log blog route match puts "blog: route = %s, md = %s" % [ route.to_s, md.to_s ] # build tags tags = route[:tags] as Array(String) case route[:type] when :tags # route to list of tags # mark as matched matched = true # draw result draw_tags(context, @models.tag.get_tags( tags: tags, )) else # route to post or list of posts # add tags from path if md["tags"]? tags += md["tags"].split('/') end # search for matching posts posts = @models.post.get_posts( filters: get_post_filters(md), tags: [tags], ) if posts.size > 0 # mark as matched matched = true # draw result draw_posts(context, route, posts) end end end end matched end end ####################### # post filter methods # ####################### FILTERS = { posted_year: "year", posted_month: "month", posted_day: "day", slug: "slug", } private def get_post_filters(md) : Hash(Symbol, String) FILTERS.reduce({ state: "posted" }) do |r, k, s| r[k] = md[s] if md[s]? r end end ###################### # draw posts methods # ###################### private def draw_posts(context, route, posts) case route[:type] when :list name = "Posts" rows = posts.rows pager = true when :post post = posts.rows.first name = post.name rows = [post] pager = false else # never reached raise "unknown route type: #{route[:type]}" end # create page page = PageHTMLView.new( title: name, body: rows.map { |post| post.to_s }.join, ) # render page context.response.content_type = page.content_type context.response.puts page end ##################### # draw tags methods # ##################### TAG_TEMPLATES = TemplateCache.new({ body: "
Tags
    %{tags}
", tag: "
  • %{name} (%{num_posts} posts)
  • ", }) private def draw_tags(context, tags) # generate tag url prefix p = (context.request.path || "").match(/\/$/) ? "" : "./tag/" # create page page = PageHTMLView.new( title: "Tags", body: TAG_TEMPLATES[:body].run({ "tags": tags.map { |row| TAG_TEMPLATES[:tag].run(row.reduce({ "p": p }) do |r, k, v| r[k] = HTML.escape(v.to_s) r end) }.join, }) ) # render page context.response.content_type = page.content_type context.response.puts page end end