Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 486 lines (427 sloc) 16.45 kb
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
1 %%%----------------------------------------------------------------------
2 %%% File : yaws_rss.erl
3 %%% Created : 15 Dec 2004 by Torbjorn Tornkvist <tobbe@tornkvist.org>
4 %%%
5 %%% @doc A Yaws RSS feed interface.
6 %%%
7 %%% @author Torbjörn Törnkvist <tobbe@tornkvist.org>
8 %%% @end
9 %%%
10 %%% $Id$
11 %%%----------------------------------------------------------------------
12 -module(yaws_rss).
13
14 -behaviour(gen_server).
15
16 %% External exports
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
17 -export([start/0, start_link/0, open/1, open/2, close/0, close/2,
18 insert/5, insert/6, insert/7, retrieve/2]).
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
19
20 -export([t_setup/0, t_exp/0, t_xopen/0]).
21
22 %% gen_server callbacks
23 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
24
25 -record(s, {
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
26 open_apps = [], % activated applications
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
27 expire = false, % false | days
28 rm_exp = false, % remove expired items
29 max=infinite, % maximum number of elements in DB
30 days=7, % maximum number of days in DB
fbf6580 Yaws is now RSS capable, see doc/README.rss
Tobbe Tornquist authored
31 counter}). % item counter
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
32
33 -define(SERVER, ?MODULE).
34 -define(DB, ?MODULE).
35 -define(DB_FNAME, "yaws_rss.dets").
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
36 -define(ITEM(App, Tag, Counter, Item), {{App, Tag, Counter}, Item}).
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
37
38 %%%----------------------------------------------------------------------
39 %%% API
40 %%%----------------------------------------------------------------------
41 start_link() ->
42 gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
43
44 start() ->
45 gen_server:start({local, ?SERVER}, ?MODULE, [], []).
46
47 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
48 %%% @spec open(App::atom(), Dir::string()) ->
49 %%% {ok, DB::db()} | {error, string()}
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
50 %%%
51 %%% @type db(). An opaque handle leading to an RSS database.
52 %%%
53 %%% @doc See {@open/2}
54 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
55 open(App) ->
56 open(App, []).
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
57
58 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
59 %%% @spec open(App::atom(), Dir::string(), Opts::list()) ->
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
60 %%% {ok, DB::db()} | {error, string()}
61 %%%
62 %%% @doc Open a RSS database, located at <em>Dir</em>.
63 %%% Per default <em>dets</em> is used as database,
64 %%% but by using the <em>db_mod</em> option it is
65 %%% possible to use your own database.</br>
66 %%% These are the options:
67 %%% <p><dl>
68 %%%
69 %%% <dt>{db_mod, Module}</dt>
70 %%% <dd>If specified, the following functions will be
71 %%% called:<ul>
72 %%% <li>Module:open(Opts)</li>
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
73 %%% <li>Module:insert(App,Tag,Title,Link,Desc,Creator,GregSec)</li>
74 %%% <li>Module:retrieve(App,Tag) -&gt; {Title, Link, Desc, Creator, GregSecs}</li>
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
75 %%% <li>Module:close(DbName)</li></ul>
76 %%% This means that the default DB won't be used, and
77 %%% no expiration handling will be done. Only the producing of
78 %%% XML will thus be done. Also, the whole <em>Opts</em> will be
79 %%% passed un-interpreted to the other DB module.</dd>
80 %%%
81 %%% <dt>{expire, Expire}</dt>
82 %%% <dd>Specifies what method to use to expire items. Possible values
83 %%% are: <em>false</em>, <em>days</em>, meaning
84 %%% never expire, expire after a number of days.
85 %%% Default is to never expire items.</dd>
86 %%%
87 %%% <dt>{days, Number}</dt>
88 %%% <dd>Specifies the number of days befor an item is expired.
89 %%% Default is 7 days.</dd>
90 %%%
91 %%% <dt>{rm_exp, Bool}</dt>
92 %%% <dd>Specifies if expired items should be removed from
93 %%% the database. Default is to not remove any items.</dd>
94 %%%
95 %%% <dt>{max, Number}</dt>
96 %%% <dd>Specifies the maximum number of items that should
97 %%% be stored in the database. The default in <em>infinite</em></dd>
98 %%% </dl></p>
99 %%% <p>If no database exist, a new will be created.
100 %%% The returned database handle is to be used with {@link close/1}.
101 %%% @end
102 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
103 open(App, Opts) ->
104 gen_server:call(?SERVER, {open, App, Opts}, infinity).
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
105
106 %%%
107 %%% @spec close() -> ok | {error, string()}
108 %%%
109 %%% @doc Close the RSS database.
110 %%%
111 close() ->
112 gen_server:call(?SERVER, {close, ?DB}, infinity).
113
114 %%%
115 %%% @spec close(DbMod::atom(), DbName::atom()) ->
116 %%% ok | {error, string()}
117 %%%
118 %%% @doc Close the user provided RSS database.
119 %%% A call to; <em>DbMod:close(DbName)</em> will be made.
120 %%%
121 close(DBmod, DBname) ->
122 gen_server:call(?SERVER, {close, DBmod, DBname}, infinity).
123
124 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
125 %%% @spec insert(App::atom(), Tag::atom(), Title::string(),
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
126 %%% Link::string(), Desc::string()) ->
127 %%% ok | {error, string()}
128 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
129 %%% @doc Insert an RSS item into the <em>{App,Tag}</em> RSS feed.
130 %%% An application (App) can maintain several feeds each
131 %%% one refered to with a symbolic name (Tag).
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
132 %%% <em>Link</em> should be a URL pointing to the item.
133 %%% <p>In case another database backend is used, the
134 %%% <em>Tag</em> has the format: <em>{DbModule, OpaqueTag}</em>
135 %%% where <em>DbModule</em> is the database backend module
136 %%% to be called, and <em>OpaqueTag</em> the Tag that is
137 %%% used in <em>DbModule:insert(Tag, ...)</em></p>
138 %%% @end
139 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
140 insert(App, Tag, Title, Link, Desc) ->
141 insert(App, Tag, Title, Link, Desc, "").
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
142
143 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
144 %%% @spec insert(App::atom(), Tag::atom(), Title::string(),
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
145 %%% Link::string(), Desc::string(),
146 %%% Creator::string()) ->
147 %%% ok | {error, string()}
148 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
149 %%% @doc Works as {@link insert/5} but takes an extra argument
150 %%% <em>Creator</em> which may contains an identification
151 %%% of who created the item.
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
152 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
153 insert(App, Tag, Title, Link, Desc, Creator) ->
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
154 GregSecs = calendar:datetime_to_gregorian_seconds({date(),time()}),
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
155 insert(App, Tag, Title, Link, Desc, Creator, GregSecs).
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
156
157 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
158 %%% @spec insert(App::atom(), Tag::atom(), Title::string(),
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
159 %%% Link::string(), Desc::string(),
160 %%% Creator::string(), GregSecs::integer()) ->
161 %%% ok | {error, string()}
162 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
163 %%% @doc Works as {@link insert/6} but takes an extra argument
164 %%% <em>GregSecs</em> which is the creation time of the item
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
165 %%% in Gregorian Seconds.
166 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
167 insert(App, Tag, Title, Link, Desc, Creator, GregSecs) ->
168 Args = {App, Tag, Title, Link, Desc, Creator, GregSecs},
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
169 gen_server:call(?SERVER, {insert, Args}, infinity).
170
171
172 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
173 %%% @spec retrieve(App::atom(), Tag::atom()) ->
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
174 %%% {ok, RSScontent::IoList()} | {error, string()}
175 %%%
176 %%% @type IoList. A deep list of strings and/or binaries.
177 %%%
178 %%% @doc Retrieve the <em>RSScontent</em> (in XML and all...)
179 %%% to be delivered to a RSS client.
180 %%% <p>In case another database backend is used, the
181 %%% <em>Tag</em> has the format: <em>{DbModule, OpaqueTag}</em>
182 %%% where <em>DbModule</em> is the database backend module
183 %%% to be called, and <em>OpaqueTag</em> the Tag that is
184 %%% used in <em>DbModule:retrieve(Tag)</em> which must return
185 %%% a list of tuples: <em>{Title, Link, Desc, Creator, GregSecs}</em></p>
186 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
187 retrieve(App, Tag) ->
188 gen_server:call(?SERVER, {retrieve, App, Tag}, infinity).
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
189
190 %%%----------------------------------------------------------------------
191 %%% Callback functions from gen_server
192 %%%----------------------------------------------------------------------
193
194 %%----------------------------------------------------------------------
195 %% Func: init/1
196 %% Returns: {ok, State} |
197 %% {ok, State, Timeout} |
198 %% ignore |
199 %% {stop, Reason}
200 %%----------------------------------------------------------------------
201 init([]) ->
202 {ok, #s{}}.
203
204 %%----------------------------------------------------------------------
205 %% Func: handle_call/3
206 %% Returns: {reply, Reply, State} |
207 %% {reply, Reply, State, Timeout} |
208 %% {noreply, State} |
209 %% {noreply, State, Timeout} |
210 %% {stop, Reason, Reply, State} | (terminate/2 is called)
211 %% {stop, Reason, State} (terminate/2 is called)
212 %%----------------------------------------------------------------------
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
213 handle_call({open, App, Opts}, _From, State) ->
214 {NewState, Res} = do_open_dir(State, App, Opts),
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
215 {reply, Res, NewState};
216 %%
217 handle_call({close, DB}, _From, State) ->
218 dets:close(DB),
219 {reply, ok, State};
220 %%
221 handle_call({close, DBMod, DBname}, _From, State) ->
222 catch apply(DBMod, close, [DBname]),
223 {reply, ok, State};
224 %%
225 handle_call({insert, Args}, _From, State) ->
226 {NewState, Res} = do_insert(State, Args),
227 {reply, Res, NewState};
228 %%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
229 handle_call({retrieve, App, Tag}, _From, State) ->
230 {NewState, Res} = do_retrieve(State, App, Tag),
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
231 {reply, Res, NewState}.
232
233 %%----------------------------------------------------------------------
234 %% Func: handle_cast/2
235 %% Returns: {noreply, State} |
236 %% {noreply, State, Timeout} |
237 %% {stop, Reason, State} (terminate/2 is called)
238 %%----------------------------------------------------------------------
239 handle_cast(_Msg, State) ->
240 {noreply, State}.
241
242 %%----------------------------------------------------------------------
243 %% Func: handle_info/2
244 %% Returns: {noreply, State} |
245 %% {noreply, State, Timeout} |
246 %% {stop, Reason, State} (terminate/2 is called)
247 %%----------------------------------------------------------------------
248 handle_info(_Info, State) ->
249 {noreply, State}.
250
251 %%----------------------------------------------------------------------
252 %% Func: terminate/2
253 %% Purpose: Shutdown the server
254 %% Returns: any (ignored by gen_server)
255 %%----------------------------------------------------------------------
256 terminate(_Reason, _State) ->
257 ok.
258
259 %%%----------------------------------------------------------------------
260 %%% Internal functions
261 %%%----------------------------------------------------------------------
262
263 %%%
264 %%% Check what database store that should be used.
265 %%% Per default 'dets' is used.
266 %%%
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
267 do_open_dir(State, App, Opts) ->
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
268 case get_db_mod(Opts, dets) of
269 dets ->
fbf6580 Yaws is now RSS capable, see doc/README.rss
Tobbe Tornquist authored
270 File = yaws_config:yaws_dir() ++ "/" ++ a2l(?DB) ++ ".dets",
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
271 Expire = get_expire(Opts, #s.expire),
272 Max = get_max(Opts, #s.max),
273 Days = get_days(Opts, #s.days),
274 RmExp = get_rm_exp(Opts, #s.rm_exp),
275 case dets:is_dets_file(File) of
276 false ->
277 {State, {error, "not a proper dets file"}};
278 _ ->
279 case catch dets:open_file(?DB, [{file, File}]) of
fbf6580 Yaws is now RSS capable, see doc/README.rss
Tobbe Tornquist authored
280 {ok,DB} = Res ->
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
281 {State#s{
282 open_apps = u_insert(App, State#s.open_apps),
283 expire = Expire,
284 days = Days,
285 rm_exp = RmExp,
fbf6580 Yaws is now RSS capable, see doc/README.rss
Tobbe Tornquist authored
286 max = Max,
287 counter = init_counter(DB)},
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
288 Res};
289 {error, _Reason} ->
290 {State, {error, "open dets file"}}
291 end
292 end;
293 DBmod ->
294 {State, catch apply(DBmod, open, Opts)}
295 end.
296
fbf6580 Yaws is now RSS capable, see doc/README.rss
Tobbe Tornquist authored
297 init_counter(DB) ->
298 case dets:lookup(DB, counter) of
299 [] -> dets:insert(DB, {counter, 0}), 0;
300 [{counter,N}] -> N
301 end.
302
303 set_counter(DB, N) ->
304 dets:insert(DB, {counter, N}).
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
305
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
306 do_insert(State, {App, {DbMod,Tag}, Title, Link, Desc, Creator, GregSecs}) ->
307 {State, catch apply(DbMod, insert, [App, Tag,Title,Link,Desc,Creator,GregSecs])};
308 do_insert(State, {App, Tag, Title, Link, Desc, Creator, GregSecs}) ->
309 case lists:member(App, State#s.open_apps) of
310 true ->
311 Counter = if (State#s.max > 0) ->
312 (State#s.counter + 1) rem State#s.max;
313 true ->
314 State#s.counter + 1
315 end,
316 Item = {Title, Link, Desc, Creator, GregSecs},
317 Res = dets:insert(?DB, ?ITEM(App, Tag, Counter, Item)),
fbf6580 Yaws is now RSS capable, see doc/README.rss
Tobbe Tornquist authored
318 io:format("Inserted item ~p~n",[Counter]),
319 set_counter(?DB, Counter),
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
320 {State#s{counter = Counter}, Res};
321 false ->
322 {State, {error, "no open DB"}}
323 end.
324
325
326 do_retrieve(State, App, {DbMod,Tag}) ->
327 {State, catch apply(DbMod, retrieve, [App, Tag])};
328 do_retrieve(State, App, Tag) ->
329 case lists:member(App, State#s.open_apps) of
330 true ->
331 F = fun(?ITEM(Xa, Xt, _Counter, Item), Acc) when Xa == App, Xt == Tag ->
332 [Item|Acc];
333 (_, Acc) ->
334 Acc
335 end,
336 Items = sort_items(expired(State, dets:foldl(F, [], ?DB))),
fbf6580 Yaws is now RSS capable, see doc/README.rss
Tobbe Tornquist authored
337 io:format("Retrieve ~p items for App=~p~n",[length(Items),App]),
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
338 Xml = to_xml(Items),
339 {State, {ok, Xml}};
340 false ->
341 {State, {error, "no open DB"}}
342 end.
343
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
344
345
346 -define(ONE_DAY, 86400). % 24*60*60 seconds
347 -define(X(GregSecs), {Title, Link, Desc, Creator, GregSecs}).
348
349 %%% Filter away expired items !!
350 expired(State, List) when State#s.expire == days ->
351 Gs = calendar:datetime_to_gregorian_seconds({date(),time()}),
352 Old = Gs - (?ONE_DAY * State#s.days),
353 F = fun(?X(GregSecs), Acc) when GregSecs > Old ->
354 [?X(GregSecs) | Acc];
355 (_, Acc) ->
356 Acc
357 end,
358 lists:foldl(F, [], List);
359 expired(_State, List) ->
360 List.
361
362 -undef(X).
363
364
365
366 %%%
367 %%% Sort on creation date !!
368 %%% Item = {Title, Link, Desc, Creator, GregSecs},
369 %%%
370 sort_items(Is) ->
371 lists:keysort(5,Is).
372
373
374 to_xml([{Title, Link, Desc, Creator, GregSecs}|Tail]) ->
b6717e7 Fixed the RSS date format, as suggested by Daniel Kaminski.
Tobbe Tornquist authored
375 Date = w3cdtf(GregSecs),
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
376 [["<item>\n",
377 "<title>", Title, "</title>\n",
378 "<link>", Link, "</link>\n",
379 "<description>", Desc, "</description>\n",
380 "<dc:creator>", Creator, "</dc:creator>\n",
381 "<dc:date>", Date, "</dc:date>\n",
382 "</item>\n"] |
383 to_xml(Tail)];
384 to_xml([]) ->
385 [].
386
081c135 Updated the RSS date format, as suggested by Daniel Kaminski.
Tobbe Tornquist authored
387
388 w3cdtf(GregSecs)-> Date = calendar:gregorian_seconds_to_datetime(GregSecs),
389 {{Y,Mo,D},{H, Mi, S}} = Date,
390 [UDate|_] = calendar:local_time_to_universal_time_dst(Date),
391 {DiffD,{DiffH,DiffMi,_}}=calendar:time_difference(UDate,Date),
392 if
393 DiffH<12 ->
394 i2l(Y) ++ "-" ++ add_zero(i2l(Mo)) ++ "-"
395 ++ add_zero(i2l(D)) ++ "T" ++ add_zero(i2l(H)) ++
396 ":" ++ add_zero(i2l(Mi)) ++ ":"
397 ++ add_zero(i2l(S)) ++
398 "+" ++ add_zero(i2l(DiffH)) ++ ":"
399 ++ add_zero(i2l(DiffMi));
400 DiffH>12,DiffD==0 ->
401 i2l(Y) ++ "-" ++ add_zero(i2l(Mo)) ++ "-"
402 ++ add_zero(i2l(D)) ++ "T" ++ add_zero(i2l(H)) ++
403 ":" ++ add_zero(i2l(Mi)) ++ ":"
404 ++ add_zero(i2l(S)) ++
405 "+" ++ add_zero(i2l(DiffH)) ++ ":"
406 ++ add_zero(i2l(DiffMi));
407 DiffH>12,DiffD/=0 ->
408 i2l(Y) ++ "-" ++ add_zero(i2l(Mo)) ++ "-"
409 ++ add_zero(i2l(D)) ++ "T" ++ add_zero(i2l(H)) ++
410 ":" ++ add_zero(i2l(Mi)) ++ ":"
411 ++ add_zero(i2l(S)) ++
412 "-" ++ add_zero(i2l(24-DiffH)) ++ ":"
413 ++ add_zero(i2l(if DiffMi==0 -> DiffMi;DiffMi/=0 ->60-DiffMi end));
414 DiffH==0 ->
415 i2l(Y) ++ "-" ++ add_zero(i2l(Mo)) ++ "-"
416 ++ add_zero(i2l(D)) ++ "T" ++ add_zero(i2l(H)) ++
417 ":" ++ add_zero(i2l(Mi)) ++ ":"
418 ++ add_zero(i2l(S)) ++ "Z"
419 end.
b6717e7 Fixed the RSS date format, as suggested by Daniel Kaminski.
Tobbe Tornquist authored
420
421 add_zero([A]) -> [$0,A];
422 add_zero(L) -> L.
423
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
424
425 get_db_mod(Opts, Def) -> lkup(db_mod, Opts, Def).
426 get_expire(Opts, Def) -> lkup(expire, Opts, Def).
427 get_max(Opts, Def) -> lkup(max, Opts, Def).
428 get_days(Opts, Def) -> lkup(days, Opts, Def).
429 get_rm_exp(Opts, Def ) -> lkup(rm_exp, Opts, Def).
430
431 lkup(Key, List, Def) ->
432 case lists:keysearch(Key, 1, List) of
433 {value,{_,Value}} -> Value;
434 _ -> Def
435 end.
436
437
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
438 u_insert(H, [H|T]) -> T;
439 u_insert(E, [H|T]) -> [H|u_insert(E,T)];
440 u_insert(E, []) -> [E].
441
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
442
443 i2l(I) when integer(I) -> integer_to_list(I);
444 i2l(L) when list(L) -> L.
445
446 a2l(A) when atom(A) -> atom_to_list(A);
447 a2l(L) when list(L) -> L.
448
449
450
451
452 t_setup() ->
fbf6580 Yaws is now RSS capable, see doc/README.rss
Tobbe Tornquist authored
453 %%open([{db_file, "yaws_rss.dets"}, {max,7}]),
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
454 insert(test,xml,"Normalizing XML, Part 2",
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
455 "http://www.xml.com/pub/a/2002/12/04/normalizing.html",
456 "In this second and final look at applying relational "
457 "normalization techniques to W3C XML Schema data modeling, "
458 "Will Provost discusses when not to normalize, the scope "
459 "of uniqueness and the fourth and fifth normal forms."),
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
460 insert(test,xml,"The .NET Schema Object Model",
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
461 "http://www.xml.com/pub/a/2002/12/04/som.html",
462 "Priya Lakshminarayanan describes in detail the use of "
463 "the .NET Schema Object Model for programmatic manipulation "
464 "of W3C XML Schemas."),
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
465 insert(test,xml,"SVG's Past and Promising Future",
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
466 "http://www.xml.com/pub/a/2002/12/04/svg.html",
467 "In this month's SVG column, Antoine Quint looks back at "
468 "SVG's journey through 2002 and looks forward to 2003.").
469
470
471 t_exp() ->
472 %%open([{db_file, "yaws_rss.dets"}, {expire,days}]),
30d2427 Added RSS config parameters ro config parser and some more documentation...
Tobbe Tornquist authored
473 insert(test,xml,"Expired article",
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
474 "http://www.xml.com/pub/a/2002/12/04/normalizing.html",
475 "In this second and final look at applying relational "
476 "normalization techniques to W3C XML Schema data modeling, "
477 "Will Provost discusses when not to normalize, the scope "
478 "of uniqueness and the fourth and fifth normal forms.",
479 "tobbe",
480 63269561882). % 6/12-2004
481
482 t_xopen() ->
483 open([{db_file, "yaws_rss.dets"},
484 {expire,days},
485 {days, 20}]).
Something went wrong with that request. Please try again.