justin / webmachine (http://blog.therestfulway.com/)
A REST-based system for building web applications.
Clone this repository (size: 2.6 MB): HTTPS / SSH
$ hg clone http://bitbucket.org/justin/webmachine/
| commit 113: | 0b8aac8fa334 |
| parent 112: | 9957a15e5779 |
| branch: | default |
| tags: | tip |
some webmachine_util tests
3 months ago
| # Introduced |
|---|
1 |
e68ae3bcc090 |
%% @author Bryan Fink <bryan@basho.com> |
2 |
e68ae3bcc090 |
%% @author Andy Gross <andy@basho.com> |
3 |
e68ae3bcc090 |
%% @author Justin Sheehy <justin@basho.com> |
4 |
e68ae3bcc090 |
%% @copyright 2008-2009 Basho Technologies, Inc. |
5 |
e68ae3bcc090 |
|
6 |
e68ae3bcc090 |
-module(demo_fs_resource). |
7 |
e68ae3bcc090 |
-export([init/1]). |
8 |
e68ae3bcc090 |
-export([allowed_methods/2, |
9 |
e68ae3bcc090 |
resource_exists/2, |
10 |
e68ae3bcc090 |
last_modified/2, |
11 |
e68ae3bcc090 |
content_types_provided/2, |
12 |
e68ae3bcc090 |
content_types_accepted/2, |
13 |
e68ae3bcc090 |
delete_resource/2, |
14 |
e68ae3bcc090 |
post_is_create/2, |
15 |
e68ae3bcc090 |
create_path/2, |
16 |
e68ae3bcc090 |
provide_content/2, |
17 |
e68ae3bcc090 |
accept_content/2, |
18 |
e68ae3bcc090 |
generate_etag/2]). |
19 |
e68ae3bcc090 |
|
20 |
e68ae3bcc090 |
-record(context, {root,response_body=undefined,metadata=[]}). |
21 |
e68ae3bcc090 |
|
22 |
e68ae3bcc090 |
-include_lib("kernel/include/file.hrl"). |
23 |
e68ae3bcc090 |
-include_lib("webmachine/include/webmachine.hrl"). |
24 |
e68ae3bcc090 |
|
25 |
e68ae3bcc090 |
init(ConfigProps) -> |
26 |
e68ae3bcc090 |
{root, Root} = proplists:lookup(root, ConfigProps), |
27 |
e68ae3bcc090 |
{ok, #context{root=Root}}. |
28 |
e68ae3bcc090 |
|
29 |
e68ae3bcc090 |
allowed_methods(ReqData, Context) -> |
30 |
e68ae3bcc090 |
{['HEAD', 'GET', 'PUT', 'DELETE', 'POST'], ReqData, Context}. |
31 |
e68ae3bcc090 |
|
32 |
e68ae3bcc090 |
file_path(Context, Name) -> |
33 |
e68ae3bcc090 |
RelName = case hd(Name) of |
34 |
e68ae3bcc090 |
"/" -> tl(Name); |
35 |
e68ae3bcc090 |
_ -> Name |
36 |
e68ae3bcc090 |
end, |
37 |
e68ae3bcc090 |
filename:join([Context#context.root, RelName]). |
38 |
e68ae3bcc090 |
|
39 |
e68ae3bcc090 |
file_exists(Context, Name) -> |
40 |
e68ae3bcc090 |
NamePath = file_path(Context, Name), |
41 |
e68ae3bcc090 |
case filelib:is_regular(NamePath) of |
42 |
e68ae3bcc090 |
true -> |
43 |
e68ae3bcc090 |
{true, NamePath}; |
44 |
e68ae3bcc090 |
false -> |
45 |
e68ae3bcc090 |
false |
46 |
e68ae3bcc090 |
end. |
47 |
e68ae3bcc090 |
|
48 |
e68ae3bcc090 |
resource_exists(ReqData, Context) -> |
49 |
e68ae3bcc090 |
Path = wrq:disp_path(ReqData), |
50 |
e68ae3bcc090 |
case file_exists(Context, Path) of |
51 |
e68ae3bcc090 |
{true, _} -> |
52 |
e68ae3bcc090 |
{true, ReqData, Context}; |
53 |
e68ae3bcc090 |
_ -> |
54 |
e68ae3bcc090 |
case Path of |
55 |
e68ae3bcc090 |
"p" -> {true, ReqData, Context}; |
56 |
e68ae3bcc090 |
_ -> {false, ReqData, Context} |
57 |
e68ae3bcc090 |
end |
58 |
e68ae3bcc090 |
end. |
59 |
e68ae3bcc090 |
|
60 |
e68ae3bcc090 |
maybe_fetch_object(Context, Path) -> |
61 |
e68ae3bcc090 |
% if returns {true, NewContext} then NewContext has response_body |
62 |
e68ae3bcc090 |
case Context#context.response_body of |
63 |
e68ae3bcc090 |
undefined -> |
64 |
e68ae3bcc090 |
case file_exists(Context, Path) of |
65 |
e68ae3bcc090 |
{true, FullPath} -> |
66 |
e68ae3bcc090 |
{ok, Value} = file:read_file(FullPath), |
67 |
e68ae3bcc090 |
{true, Context#context{response_body=Value}}; |
68 |
e68ae3bcc090 |
false -> |
69 |
e68ae3bcc090 |
{false, Context} |
70 |
e68ae3bcc090 |
end; |
71 |
e68ae3bcc090 |
_Body -> |
72 |
e68ae3bcc090 |
{true, Context} |
73 |
e68ae3bcc090 |
end. |
74 |
e68ae3bcc090 |
|
75 |
e68ae3bcc090 |
content_types_provided(ReqData, Context) -> |
76 |
e68ae3bcc090 |
CT = webmachine_util:guess_mime(wrq:disp_path(ReqData)), |
77 |
e68ae3bcc090 |
{[{CT, provide_content}], ReqData, |
78 |
e68ae3bcc090 |
Context#context{metadata=[{'content-type', CT}|Context#context.metadata]}}. |
79 |
e68ae3bcc090 |
|
80 |
e68ae3bcc090 |
content_types_accepted(ReqData, Context) -> |
81 |
e68ae3bcc090 |
CT = case wrq:get_req_header("content-type", ReqData) of |
82 |
e68ae3bcc090 |
undefined -> "application/octet-stream"; |
83 |
e68ae3bcc090 |
X -> X |
84 |
e68ae3bcc090 |
end, |
85 |
e68ae3bcc090 |
{MT, _Params} = webmachine_util:media_type_to_detail(CT), |
86 |
e68ae3bcc090 |
{[{MT, accept_content}], ReqData, |
87 |
e68ae3bcc090 |
Context#context{metadata=[{'content-type', MT}|Context#context.metadata]}}. |
88 |
e68ae3bcc090 |
|
89 |
e68ae3bcc090 |
accept_content(ReqData, Context) -> |
90 |
e68ae3bcc090 |
Path = wrq:disp_path(ReqData), |
91 |
e68ae3bcc090 |
FP = file_path(Context, Path), |
92 |
e68ae3bcc090 |
ok = filelib:ensure_dir(filename:dirname(FP)), |
93 |
e68ae3bcc090 |
ReqData1 = case file_exists(Context, Path) of |
94 |
e68ae3bcc090 |
{true, _} -> |
95 |
e68ae3bcc090 |
ReqData; |
96 |
e68ae3bcc090 |
_ -> |
97 |
e68ae3bcc090 |
LOC = "http://" ++ |
98 |
e68ae3bcc090 |
wrq:get_req_header("host", ReqData) ++ |
99 |
e68ae3bcc090 |
"/fs/" ++ Path, |
100 |
e68ae3bcc090 |
wrq:set_resp_header("Location", LOC, ReqData) |
101 |
e68ae3bcc090 |
end, |
102 |
e68ae3bcc090 |
Value = wrq:req_body(ReqData1), |
103 |
e68ae3bcc090 |
case file:write_file(FP, Value) of |
104 |
e68ae3bcc090 |
ok -> |
105 |
e68ae3bcc090 |
{true, wrq:set_resp_body(Value, ReqData1), Context}; |
106 |
e68ae3bcc090 |
Err -> |
107 |
e68ae3bcc090 |
{{error, Err}, ReqData1, Context} |
108 |
e68ae3bcc090 |
end. |
109 |
e68ae3bcc090 |
|
110 |
e68ae3bcc090 |
post_is_create(ReqData, Context) -> |
111 |
e68ae3bcc090 |
{true, ReqData, Context}. |
112 |
e68ae3bcc090 |
|
113 |
e68ae3bcc090 |
create_path(ReqData, Context) -> |
114 |
e68ae3bcc090 |
case wrq:get_req_header("slug", ReqData) of |
115 |
e68ae3bcc090 |
undefined -> {undefined, ReqData, Context}; |
116 |
e68ae3bcc090 |
Slug -> |
117 |
e68ae3bcc090 |
case file_exists(Context, Slug) of |
118 |
e68ae3bcc090 |
{true, _} -> {undefined, ReqData, Context}; |
119 |
e68ae3bcc090 |
_ -> {Slug, ReqData, Context} |
120 |
e68ae3bcc090 |
end |
121 |
e68ae3bcc090 |
end. |
122 |
e68ae3bcc090 |
|
123 |
e68ae3bcc090 |
delete_resource(ReqData, Context) -> |
124 |
e68ae3bcc090 |
case file:delete(file_path( |
125 |
e68ae3bcc090 |
Context, wrq:disp_path(ReqData))) of |
126 |
e68ae3bcc090 |
ok -> {true, ReqData, Context}; |
127 |
e68ae3bcc090 |
_ -> {false, ReqData, Context} |
128 |
e68ae3bcc090 |
end. |
129 |
e68ae3bcc090 |
|
130 |
e68ae3bcc090 |
provide_content(ReqData, Context) -> |
131 |
e68ae3bcc090 |
case maybe_fetch_object(Context, wrq:disp_path(ReqData)) of |
132 |
e68ae3bcc090 |
{true, NewContext} -> |
133 |
e68ae3bcc090 |
Body = NewContext#context.response_body, |
134 |
e68ae3bcc090 |
{Body, ReqData, Context}; |
135 |
e68ae3bcc090 |
{false, NewContext} -> |
136 |
e68ae3bcc090 |
{error, ReqData, NewContext} |
137 |
e68ae3bcc090 |
end. |
138 |
e68ae3bcc090 |
|
139 |
e68ae3bcc090 |
last_modified(ReqData, Context) -> |
140 |
e68ae3bcc090 |
{true, FullPath} = file_exists(Context, |
141 |
e68ae3bcc090 |
wrq:disp_path(ReqData)), |
142 |
e68ae3bcc090 |
LMod = filelib:last_modified(FullPath), |
143 |
e68ae3bcc090 |
{LMod, ReqData, Context#context{metadata=[{'last-modified', |
144 |
e68ae3bcc090 |
httpd_util:rfc1123_date(LMod)}|Context#context.metadata]}}. |
145 |
e68ae3bcc090 |
|
146 |
e68ae3bcc090 |
hash_body(Body) -> mochihex:to_hex(binary_to_list(crypto:sha(Body))). |
147 |
e68ae3bcc090 |
|
148 |
e68ae3bcc090 |
generate_etag(ReqData, Context) -> |
149 |
e68ae3bcc090 |
case maybe_fetch_object(Context, wrq:disp_path(ReqData)) of |
150 |
e68ae3bcc090 |
{true, BodyContext} -> |
151 |
e68ae3bcc090 |
ETag = hash_body(BodyContext#context.response_body), |
152 |
e68ae3bcc090 |
{ETag, ReqData, |
153 |
e68ae3bcc090 |
BodyContext#context{metadata=[{etag,ETag}| |
154 |
e68ae3bcc090 |
BodyContext#context.metadata]}}; |
155 |
e68ae3bcc090 |
_ -> |
156 |
e68ae3bcc090 |
{undefined, ReqData, Context} |
157 |
e68ae3bcc090 |
end. |
