1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
require "json"
require "./handler"
require "./api-methods"
private macro define_method_calls(hash)
case namespace
{% for namespace, methods in hash %}
when "{{ namespace.id }}"
case method
{% for method in methods %}
when "{{ method }}"
do_{{ namespace.id }}_{{ method }}(context, get_method_args(
context.request.query_params,
"{{ namespace.id }}",
"{{ method.id }}"
))
{% end %}
else
raise "unknown method"
end
{% end %}
else
raise "unknown namespace"
end
end
module Guff
class APIHandler < Handler
include APIMethods
include APIContentType
PATH_RE = %r{
^/api
(?:
# method call
(?:
/
(?<namespace>[a-z0-9_-]+)
/
(?<method>[a-z0-9_]+)
)
|
# index.html
/(?:index(?:\.html|)|)
|
# implicit index (no trailing slash)
)
$
}mx
def call(context : HTTP::Server::Context)
if md = (context.request.path || "").match(PATH_RE)
if md["namespace"]?
# method call
do_call(context, md["namespace"], md["method"])
else
# api index
do_docs(context)
end
else
call_next(context)
end
end
private def do_call(
context : HTTP::Server::Context,
namespace : String,
method : String
)
# set response type
context.response.content_type = get_content_type
# method call
json = begin
#
# macro expands to equivalent of the following for each
# namespace and method:
#
# args = get_method_args(context, namespace, method)
# send("do_#{namespace}_#{method}".intern, context, args)
#
define_method_calls({
post: [
get_posts,
],
tag: [
get_tags,
remove_tags,
],
test: [
version,
get_posts,
error,
],
})
rescue e
{ "error": e.to_s }.to_json
end
# send body
context.response.puts json
end
private def do_docs(context : HTTP::Server::Context)
APIDocsHTMLView.run(context)
end
end
end
|