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 = {
year: "year",
month: "month",
day: "day",
slug: "slug",
}
private def get_post_filters(md) : Hash(Symbol, String)
FILTERS.reduce({} of Symbol => String) 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: "
",
tag: "
%{name} (%{num_posts} posts)
",
})
private def draw_tags(context, tags)
# create page
page = PageHTMLView.new(
title: "Tags",
body: TAG_TEMPLATES[:body].run({
"tags": tags.map { |row|
TAG_TEMPLATES[:tag].run(row.reduce({} of String => String) 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