Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 475 lines (417 sloc) 16.117 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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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 documentat…
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
b6717e7 Fixed the RSS date format, as suggested by Daniel Kaminski.
Tobbe Tornquist authored
387 w3cdtf(GregSecs) ->
388 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 [{{_,_,_},{UH,UMi,_}}] = UDate,
392 {DifH,DifMi}={H-UH,Mi-UMi},
393 if
394 DifH>0 ->
395 i2l(Y) ++ "-" ++ add_zero(i2l(Mo)) ++ "-" ++ add_zero(i2l(D)) ++
396 "T" ++ add_zero(i2l(H)) ++ ":" ++ add_zero(i2l(Mi)) ++ ":" ++
397 add_zero(i2l(S)) ++ "+" ++ add_zero(i2l(abs(DifH))) ++ ":" ++
398 add_zero(i2l(abs(DifMi)));
399 DifH<0 ->
400 i2l(Y) ++ "-" ++ add_zero(i2l(Mo)) ++ "-" ++ add_zero(i2l(D)) ++
401 "T" ++ add_zero(i2l(H)) ++ ":" ++ add_zero(i2l(Mi)) ++ ":" ++
402 add_zero(i2l(S)) ++ "-" ++ add_zero(i2l(abs(DifH))) ++ ":" ++
403 add_zero(i2l(abs(DifMi)));
404 DifH==0 ->
405 i2l(Y) ++ "-" ++ add_zero(i2l(Mo)) ++ "-" ++ add_zero(i2l(D)) ++
406 "T" ++ add_zero(i2l(H)) ++ ":" ++ add_zero(i2l(Mi)) ++ ":" ++
407 add_zero(i2l(S)) ++ "Z"
408 end.
409
410 add_zero([A]) -> [$0,A];
411 add_zero(L) -> L.
412
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
413
414 get_db_mod(Opts, Def) -> lkup(db_mod, Opts, Def).
415 get_expire(Opts, Def) -> lkup(expire, Opts, Def).
416 get_max(Opts, Def) -> lkup(max, Opts, Def).
417 get_days(Opts, Def) -> lkup(days, Opts, Def).
418 get_rm_exp(Opts, Def ) -> lkup(rm_exp, Opts, Def).
419
420 lkup(Key, List, Def) ->
421 case lists:keysearch(Key, 1, List) of
422 {value,{_,Value}} -> Value;
423 _ -> Def
424 end.
425
426
30d2427 Added RSS config parameters ro config parser and some more documentat…
Tobbe Tornquist authored
427 u_insert(H, [H|T]) -> T;
428 u_insert(E, [H|T]) -> [H|u_insert(E,T)];
429 u_insert(E, []) -> [E].
430
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
431
432 i2l(I) when integer(I) -> integer_to_list(I);
433 i2l(L) when list(L) -> L.
434
435 a2l(A) when atom(A) -> atom_to_list(A);
436 a2l(L) when list(L) -> L.
437
438
439
440
441 t_setup() ->
fbf6580 Yaws is now RSS capable, see doc/README.rss
Tobbe Tornquist authored
442 %%open([{db_file, "yaws_rss.dets"}, {max,7}]),
30d2427 Added RSS config parameters ro config parser and some more documentat…
Tobbe Tornquist authored
443 insert(test,xml,"Normalizing XML, Part 2",
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
444 "http://www.xml.com/pub/a/2002/12/04/normalizing.html",
445 "In this second and final look at applying relational "
446 "normalization techniques to W3C XML Schema data modeling, "
447 "Will Provost discusses when not to normalize, the scope "
448 "of uniqueness and the fourth and fifth normal forms."),
30d2427 Added RSS config parameters ro config parser and some more documentat…
Tobbe Tornquist authored
449 insert(test,xml,"The .NET Schema Object Model",
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
450 "http://www.xml.com/pub/a/2002/12/04/som.html",
451 "Priya Lakshminarayanan describes in detail the use of "
452 "the .NET Schema Object Model for programmatic manipulation "
453 "of W3C XML Schemas."),
30d2427 Added RSS config parameters ro config parser and some more documentat…
Tobbe Tornquist authored
454 insert(test,xml,"SVG's Past and Promising Future",
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
455 "http://www.xml.com/pub/a/2002/12/04/svg.html",
456 "In this month's SVG column, Antoine Quint looks back at "
457 "SVG's journey through 2002 and looks forward to 2003.").
458
459
460 t_exp() ->
461 %%open([{db_file, "yaws_rss.dets"}, {expire,days}]),
30d2427 Added RSS config parameters ro config parser and some more documentat…
Tobbe Tornquist authored
462 insert(test,xml,"Expired article",
6e45786 Added RSS store/interface.
Tobbe Tornquist authored
463 "http://www.xml.com/pub/a/2002/12/04/normalizing.html",
464 "In this second and final look at applying relational "
465 "normalization techniques to W3C XML Schema data modeling, "
466 "Will Provost discusses when not to normalize, the scope "
467 "of uniqueness and the fourth and fifth normal forms.",
468 "tobbe",
469 63269561882). % 6/12-2004
470
471 t_xopen() ->
472 open([{db_file, "yaws_rss.dets"},
473 {expire,days},
474 {days, 20}]).
Something went wrong with that request. Please try again.